Improve validation error messages (#4141)

* Improve validation error messages

* fix unit tests
This commit is contained in:
rorp 2022-03-01 03:27:32 -08:00 committed by GitHub
parent 1bab51c1c6
commit 44b2ca3c3d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 71 additions and 62 deletions

View file

@ -1,9 +1,7 @@
package org.bitcoins.server.routes package org.bitcoins.server.routes
import akka.http.scaladsl.server.Route
import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Directive1 import akka.http.scaladsl.server.{Directive1, Route}
import akka.http.scaladsl.server.ValidationRejection
import scala.util.Try import scala.util.Try
@ -12,7 +10,7 @@ trait ServerRoute {
def withValidServerCommand[R](validator: Try[R]): Directive1[R] = def withValidServerCommand[R](validator: Try[R]): Directive1[R] =
validator.fold( validator.fold(
e => reject(ValidationRejection("failure", Some(e))), e => complete(Server.httpBadRequest(e)),
provide provide
) )
} }

View file

@ -2,7 +2,6 @@ package org.bitcoins.server
import akka.http.scaladsl.model.ContentTypes._ import akka.http.scaladsl.model.ContentTypes._
import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.server.ValidationRejection
import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest} import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest}
import org.bitcoins.core.api.chain.ChainApi import org.bitcoins.core.api.chain.ChainApi
import org.bitcoins.core.api.chain.db._ import org.bitcoins.core.api.chain.db._
@ -14,12 +13,7 @@ import org.bitcoins.core.currency.{Bitcoins, CurrencyUnit, Satoshis}
import org.bitcoins.core.dlc.accounting.DLCWalletAccounting import org.bitcoins.core.dlc.accounting.DLCWalletAccounting
import org.bitcoins.core.hd._ import org.bitcoins.core.hd._
import org.bitcoins.core.number.{UInt32, UInt64} import org.bitcoins.core.number.{UInt32, UInt64}
import org.bitcoins.core.protocol.BlockStamp.{ import org.bitcoins.core.protocol.BlockStamp.{BlockHash, BlockHeight, BlockTime}
BlockHash,
BlockHeight,
BlockTime,
InvalidBlockStamp
}
import org.bitcoins.core.protocol.blockchain.BlockHeader import org.bitcoins.core.protocol.blockchain.BlockHeader
import org.bitcoins.core.protocol.dlc.models.DLCMessage._ import org.bitcoins.core.protocol.dlc.models.DLCMessage._
import org.bitcoins.core.protocol.dlc.models._ import org.bitcoins.core.protocol.dlc.models._
@ -48,7 +42,6 @@ import org.bitcoins.wallet.MockWalletApi
import org.scalamock.scalatest.MockFactory import org.scalamock.scalatest.MockFactory
import org.scalatest.wordspec.AnyWordSpec import org.scalatest.wordspec.AnyWordSpec
import scodec.bits.ByteVector import scodec.bits.ByteVector
import ujson.Value.InvalidData
import ujson._ import ujson._
import java.time.{ZoneId, ZonedDateTime} import java.time.{ZoneId, ZonedDateTime}
@ -1279,18 +1272,20 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
ServerCommand("sendtoaddress", Arr(Null, Null, Null, Bool(false)))) ServerCommand("sendtoaddress", Arr(Null, Null, Null, Bool(false))))
Post() ~> route1 ~> check { Post() ~> route1 ~> check {
rejection == ValidationRejection( assert(contentType == `application/json`)
"failure", assert(status == StatusCodes.BadRequest)
Some(InvalidData(Null, "Expected ujson.Str"))) assert(
responseAs[String] == s"""{"result":null,"error":"Expected ujson.Str (data: null)"}""")
} }
val route2 = walletRoutes.handleCommand( val route2 = walletRoutes.handleCommand(
ServerCommand("sendtoaddress", Arr("Null", Null, Null, Bool(false)))) ServerCommand("sendtoaddress", Arr("Null", Null, Null, Bool(false))))
Post() ~> route2 ~> check { Post() ~> route2 ~> check {
rejection == ValidationRejection( assert(contentType == `application/json`)
"failure", assert(status == StatusCodes.BadRequest)
Some(InvalidData("Null", "Expected a valid address"))) assert(
responseAs[String] == s"""{"result":null,"error":"Expected a valid address (data: \\"Null\\")"}""")
} }
val route3 = walletRoutes.handleCommand( val route3 = walletRoutes.handleCommand(
@ -1298,9 +1293,10 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
Arr(Str(testAddressStr), Null, Null, Bool(false)))) Arr(Str(testAddressStr), Null, Null, Bool(false))))
Post() ~> route3 ~> check { Post() ~> route3 ~> check {
rejection == ValidationRejection( assert(contentType == `application/json`)
"failure", assert(status == StatusCodes.BadRequest)
Some(InvalidData(Null, "Expected ujson.Num"))) assert(
responseAs[String] == s"""{"result":null,"error":"Expected ujson.Num (data: null)"}""")
} }
val route4 = walletRoutes.handleCommand( val route4 = walletRoutes.handleCommand(
@ -1308,9 +1304,10 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
Arr(Str(testAddressStr), Str("abc"), Null, Bool(false)))) Arr(Str(testAddressStr), Str("abc"), Null, Bool(false))))
Post() ~> route4 ~> check { Post() ~> route4 ~> check {
rejection == ValidationRejection( assert(contentType == `application/json`)
"failure", assert(status == StatusCodes.BadRequest)
Some(InvalidData("abc", "Expected ujson.Num"))) assert(
responseAs[String] == s"""{"result":null,"error":"Expected ujson.Num (data: \\"abc\\")"}""")
} }
} }
@ -1351,18 +1348,20 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
ServerCommand("sendfromoutpoints", Arr(Arr(), Null, Null, Null))) ServerCommand("sendfromoutpoints", Arr(Arr(), Null, Null, Null)))
Post() ~> route1 ~> check { Post() ~> route1 ~> check {
rejection == ValidationRejection( assert(contentType == `application/json`)
"failure", assert(status == StatusCodes.BadRequest)
Some(InvalidData(Null, "Expected ujson.Str"))) assert(
responseAs[String] == s"""{"result":null,"error":"Expected ujson.Str (data: null)"}""")
} }
val route2 = walletRoutes.handleCommand( val route2 = walletRoutes.handleCommand(
ServerCommand("sendfromoutpoints", Arr(Arr(), "Null", Null, Null))) ServerCommand("sendfromoutpoints", Arr(Arr(), "Null", Null, Null)))
Post() ~> route2 ~> check { Post() ~> route2 ~> check {
rejection == ValidationRejection( assert(contentType == `application/json`)
"failure", assert(status == StatusCodes.BadRequest)
Some(InvalidData("Null", "Expected a valid address"))) assert(
responseAs[String] == s"""{"result":null,"error":"Expected a valid address (data: \\"Null\\")"}""")
} }
val route3 = walletRoutes.handleCommand( val route3 = walletRoutes.handleCommand(
@ -1370,9 +1369,10 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
Arr(Arr(), Str(testAddressStr), Null, Null))) Arr(Arr(), Str(testAddressStr), Null, Null)))
Post() ~> route3 ~> check { Post() ~> route3 ~> check {
rejection == ValidationRejection( assert(contentType == `application/json`)
"failure", assert(status == StatusCodes.BadRequest)
Some(InvalidData(Null, "Expected ujson.Num"))) assert(
responseAs[String] == s"""{"result":null,"error":"Expected ujson.Num (data: null)"}""")
} }
val route4 = walletRoutes.handleCommand( val route4 = walletRoutes.handleCommand(
@ -1380,9 +1380,10 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
Arr(Arr(), Str(testAddressStr), Str("abc"), Null))) Arr(Arr(), Str(testAddressStr), Str("abc"), Null)))
Post() ~> route4 ~> check { Post() ~> route4 ~> check {
rejection == ValidationRejection( assert(contentType == `application/json`)
"failure", assert(status == StatusCodes.BadRequest)
Some(InvalidData("abc", "Expected ujson.Num"))) assert(
responseAs[String] == s"""{"result":null,"error":"Expected ujson.Num (data: \\"abc\\")"}""")
} }
val route5 = walletRoutes.handleCommand( val route5 = walletRoutes.handleCommand(
@ -1390,9 +1391,10 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
Arr(Null, Str(testAddressStr), Num(100), Num(4)))) Arr(Null, Str(testAddressStr), Num(100), Num(4))))
Post() ~> route5 ~> check { Post() ~> route5 ~> check {
rejection == ValidationRejection( assert(contentType == `application/json`)
"failure", assert(status == StatusCodes.BadRequest)
Some(InvalidData(Null, "Expected ujson.Arr"))) assert(
responseAs[String] == s"""{"result":null,"error":"Expected ujson.Arr (data: null)"}""")
} }
} }
@ -1457,18 +1459,20 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
ServerCommand("sendwithalgo", Arr(Null, Null, Null, Null))) ServerCommand("sendwithalgo", Arr(Null, Null, Null, Null)))
Post() ~> route1 ~> check { Post() ~> route1 ~> check {
rejection == ValidationRejection( assert(contentType == `application/json`)
"failure", assert(status == StatusCodes.BadRequest)
Some(InvalidData(Null, "Expected ujson.Str"))) assert(
responseAs[String] == s"""{"result":null,"error":"Expected ujson.Str (data: null)"}""")
} }
val route2 = walletRoutes.handleCommand( val route2 = walletRoutes.handleCommand(
ServerCommand("sendwithalgo", Arr("Null", Null, Null, Null))) ServerCommand("sendwithalgo", Arr("Null", Null, Null, Null)))
Post() ~> route2 ~> check { Post() ~> route2 ~> check {
rejection == ValidationRejection( assert(contentType == `application/json`)
"failure", assert(status == StatusCodes.BadRequest)
Some(InvalidData("Null", "Expected a valid address"))) assert(
responseAs[String] == s"""{"result":null,"error":"Expected a valid address (data: \\"Null\\")"}""")
} }
val route3 = walletRoutes.handleCommand( val route3 = walletRoutes.handleCommand(
@ -1476,9 +1480,10 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
Arr(Str(testAddressStr), Null, Null, Null))) Arr(Str(testAddressStr), Null, Null, Null)))
Post() ~> route3 ~> check { Post() ~> route3 ~> check {
rejection == ValidationRejection( assert(contentType == `application/json`)
"failure", assert(status == StatusCodes.BadRequest)
Some(InvalidData(Null, "Expected ujson.Num"))) assert(
responseAs[String] == s"""{"result":null,"error":"Expected ujson.Num (data: null)"}""")
} }
val route4 = walletRoutes.handleCommand( val route4 = walletRoutes.handleCommand(
@ -1486,9 +1491,10 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
Arr(Str(testAddressStr), Str("abc"), Null, Null))) Arr(Str(testAddressStr), Str("abc"), Null, Null)))
Post() ~> route4 ~> check { Post() ~> route4 ~> check {
rejection == ValidationRejection( assert(contentType == `application/json`)
"failure", assert(status == StatusCodes.BadRequest)
Some(InvalidData("abc", "Expected ujson.Num"))) assert(
responseAs[String] == s"""{"result":null,"error":"Expected ujson.Num (data: \\"abc\\")"}""")
} }
val route5 = walletRoutes.handleCommand( val route5 = walletRoutes.handleCommand(
@ -1496,9 +1502,10 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
Arr(Str(testAddressStr), Num(100), Num(4), Null))) Arr(Str(testAddressStr), Num(100), Num(4), Null)))
Post() ~> route5 ~> check { Post() ~> route5 ~> check {
rejection == ValidationRejection( assert(contentType == `application/json`)
"failure", assert(status == StatusCodes.BadRequest)
Some(InvalidData(Null, "Expected ujson.Str"))) assert(
responseAs[String] == s"""{"result":null,"error":"Expected ujson.Str (data: null)"}""")
} }
} }
@ -1740,8 +1747,10 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
Arr(Null, Str("abcd"), Str("efgh"), true, true))) Arr(Null, Str("abcd"), Str("efgh"), true, true)))
Post() ~> route5 ~> check { Post() ~> route5 ~> check {
rejection == ValidationRejection("failure", assert(contentType == `application/json`)
Some(InvalidBlockStamp("abcd"))) assert(status == StatusCodes.BadRequest)
assert(responseAs[
String] == s"""{"result":null,"error":"Invalid blockstamp: abcd"}""")
} }
val route6 = val route6 =
@ -1751,9 +1760,10 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
Arr(Arr(55), Null, Str("2018-10-27T12:34:56"), true, true))) Arr(Arr(55), Null, Str("2018-10-27T12:34:56"), true, true)))
Post() ~> route6 ~> check { Post() ~> route6 ~> check {
rejection == ValidationRejection( assert(contentType == `application/json`)
"failure", assert(status == StatusCodes.BadRequest)
Some(InvalidBlockStamp("2018-10-27T12:34:56"))) assert(
responseAs[String] == s"""{"result":null,"error":"Invalid blockstamp: 2018-10-27T12:34:56"}""")
} }
val route7 = val route7 =
@ -1761,9 +1771,10 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
ServerCommand("rescan", Arr(Null, Num(-1), Null, true, false))) ServerCommand("rescan", Arr(Null, Num(-1), Null, true, false)))
Post() ~> route7 ~> check { Post() ~> route7 ~> check {
rejection == ValidationRejection( assert(contentType == `application/json`)
"failure", assert(status == StatusCodes.BadRequest)
Some(InvalidData(Num(-1), "Expected a positive integer"))) assert(
responseAs[String] == s"""{"result":null,"error":"Expected a positive integer (data: -1)"}""")
} }
(mockWalletApi.isEmpty: () => Future[Boolean]) (mockWalletApi.isEmpty: () => Future[Boolean])