Alexis Hernandez
7 years ago
7 changed files with 213 additions and 0 deletions
@ -0,0 +1,16 @@ |
|||||
|
package controllers |
||||
|
|
||||
|
import javax.inject.Inject |
||||
|
|
||||
|
import com.xsn.explorer.services.TransactionService |
||||
|
import controllers.common.{MyJsonController, MyJsonControllerComponents} |
||||
|
|
||||
|
class TransactionsController @Inject() ( |
||||
|
transactionService: TransactionService, |
||||
|
cc: MyJsonControllerComponents) |
||||
|
extends MyJsonController(cc) { |
||||
|
|
||||
|
def getTransaction(txid: String) = publicNoInput { _ => |
||||
|
transactionService.getTransaction(txid) |
||||
|
} |
||||
|
} |
@ -0,0 +1,16 @@ |
|||||
|
package controllers.common |
||||
|
|
||||
|
import com.alexitc.playsonify.AbstractAuthenticatorService |
||||
|
import com.alexitc.playsonify.core.FutureApplicationResult |
||||
|
import org.scalactic.Good |
||||
|
import play.api.libs.json.JsValue |
||||
|
import play.api.mvc.Request |
||||
|
|
||||
|
import scala.concurrent.Future |
||||
|
|
||||
|
class MyAuthenticatorService extends AbstractAuthenticatorService[Nothing] { |
||||
|
|
||||
|
override def authenticate(request: Request[JsValue]): FutureApplicationResult[Nothing] = { |
||||
|
Future.successful(Good(???)) |
||||
|
} |
||||
|
} |
@ -0,0 +1,25 @@ |
|||||
|
package controllers.common |
||||
|
|
||||
|
import com.alexitc.playsonify.AbstractJsonController |
||||
|
import com.alexitc.playsonify.models.{ErrorId, ServerError} |
||||
|
import org.slf4j.LoggerFactory |
||||
|
|
||||
|
abstract class MyJsonController(cc: MyJsonControllerComponents) extends AbstractJsonController[Nothing](cc) { |
||||
|
|
||||
|
protected val logger = LoggerFactory.getLogger(this.getClass) |
||||
|
|
||||
|
override def onServerError(error: ServerError, errorId: ErrorId): Unit = { |
||||
|
val message = s"Unexpected server error, id = ${errorId.string}, error = $error" |
||||
|
|
||||
|
error |
||||
|
.cause |
||||
|
.orElse { |
||||
|
logger.warn(message) |
||||
|
None |
||||
|
} |
||||
|
.foreach { cause => |
||||
|
// we'll log as error when there is an exception involved |
||||
|
logger.error(message, cause) |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,15 @@ |
|||||
|
package controllers.common |
||||
|
|
||||
|
import javax.inject.Inject |
||||
|
|
||||
|
import com.alexitc.playsonify.{JsonControllerComponents, PublicErrorRenderer} |
||||
|
import play.api.mvc.MessagesControllerComponents |
||||
|
|
||||
|
import scala.concurrent.ExecutionContext |
||||
|
|
||||
|
class MyJsonControllerComponents @Inject() ( |
||||
|
override val messagesControllerComponents: MessagesControllerComponents, |
||||
|
override val executionContext: ExecutionContext, |
||||
|
override val publicErrorRenderer: PublicErrorRenderer, |
||||
|
override val authenticatorService: MyAuthenticatorService) |
||||
|
extends JsonControllerComponents[Nothing] |
@ -0,0 +1,121 @@ |
|||||
|
package controllers |
||||
|
|
||||
|
import com.alexitc.playsonify.PublicErrorRenderer |
||||
|
import com.alexitc.playsonify.core.FutureApplicationResult |
||||
|
import com.xsn.explorer.errors.TransactionNotFoundError |
||||
|
import com.xsn.explorer.models.TransactionId |
||||
|
import com.xsn.explorer.services.XSNService |
||||
|
import controllers.common.MyAPISpec |
||||
|
import org.scalactic.{Bad, Good} |
||||
|
import play.api.inject.bind |
||||
|
import play.api.libs.json.{JsValue, Json} |
||||
|
import play.api.test.Helpers._ |
||||
|
|
||||
|
import scala.concurrent.Future |
||||
|
|
||||
|
class TransactionsControllerSpec extends MyAPISpec { |
||||
|
|
||||
|
val customXSNService = new XSNService { |
||||
|
val map = Map( |
||||
|
TransactionId.from("024aba1d535cfe5dd3ea465d46a828a57b00e1df012d7a2d158e0f7484173f7c").get -> |
||||
|
s""" |
||||
|
|{ |
||||
|
| "blockhash": "000003fb382f6892ae96594b81aa916a8923c70701de4e7054aac556c7271ef7", |
||||
|
| "blocktime": 1520276270, |
||||
|
| "confirmations": 5347, |
||||
|
| "height": 1, |
||||
|
| "hex": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03510101ffffffff010000000000000000232103e8c52f2c5155771492907095753a43ce776e1fa7c5e769a67a9f3db4467ec029ac00000000", |
||||
|
| "locktime": 0, |
||||
|
| "size": 98, |
||||
|
| "time": 1520276270, |
||||
|
| "txid": "024aba1d535cfe5dd3ea465d46a828a57b00e1df012d7a2d158e0f7484173f7c", |
||||
|
| "version": 1, |
||||
|
| "vin": [ |
||||
|
| { |
||||
|
| "coinbase": "510101", |
||||
|
| "sequence": 4294967295 |
||||
|
| } |
||||
|
| ], |
||||
|
| "vout": [ |
||||
|
| { |
||||
|
| "n": 0, |
||||
|
| "scriptPubKey": { |
||||
|
| "addresses": [ |
||||
|
| "XdJnCKYNwzCz8ATv8Eu75gonaHyfr9qXg9" |
||||
|
| ], |
||||
|
| "asm": "03e8c52f2c5155771492907095753a43ce776e1fa7c5e769a67a9f3db4467ec029 OP_CHECKSIG", |
||||
|
| "hex": "2103e8c52f2c5155771492907095753a43ce776e1fa7c5e769a67a9f3db4467ec029ac", |
||||
|
| "reqSigs": 1, |
||||
|
| "type": "pubkey" |
||||
|
| }, |
||||
|
| "value": 0, |
||||
|
| "valueSat": 0 |
||||
|
| } |
||||
|
| ] |
||||
|
|} |
||||
|
""".stripMargin.trim |
||||
|
) |
||||
|
|
||||
|
override def getTransaction(txid: TransactionId): FutureApplicationResult[JsValue] = { |
||||
|
val result = map.get(txid) |
||||
|
.map(s => Json.toJson(s)) |
||||
|
.map(Good(_)) |
||||
|
.getOrElse { |
||||
|
Bad(TransactionNotFoundError).accumulating |
||||
|
} |
||||
|
|
||||
|
Future.successful(result) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
override val application = guiceApplicationBuilder |
||||
|
.overrides(bind[XSNService].to(customXSNService)) |
||||
|
.build() |
||||
|
|
||||
|
"GET /transactions/:txid" should { |
||||
|
def url(txid: String) = s"/transactions/$txid" |
||||
|
|
||||
|
"return an existing transaction" in { |
||||
|
val txid = "024aba1d535cfe5dd3ea465d46a828a57b00e1df012d7a2d158e0f7484173f7c" |
||||
|
val response = GET(url(txid)) |
||||
|
|
||||
|
status(response) mustEqual OK |
||||
|
// TODO: Match result |
||||
|
//val json = contentAsJson(response) |
||||
|
//(json \ "txid").as[String] mustEqual txid |
||||
|
} |
||||
|
|
||||
|
"fail on wrong transaction format" in { |
||||
|
// 63 characters |
||||
|
val txid = "000001d535cfe5dd3ea465d46a828a57b00e1df012d7a2d158e0f7484173f7c" |
||||
|
val response = GET(url(txid)) |
||||
|
|
||||
|
status(response) mustEqual BAD_REQUEST |
||||
|
val json = contentAsJson(response) |
||||
|
val errorList = (json \ "errors").as[List[JsValue]] |
||||
|
|
||||
|
errorList.size mustEqual 1 |
||||
|
val error = errorList.head |
||||
|
|
||||
|
(error \ "type").as[String] mustEqual PublicErrorRenderer.FieldValidationErrorType |
||||
|
(error \ "field").as[String] mustEqual "transactionId" |
||||
|
(error \ "message").as[String].nonEmpty mustEqual true |
||||
|
} |
||||
|
|
||||
|
"fail on unknown transaction" in { |
||||
|
val txid = "0000001d535cfe5dd3ea465d46a828a57b00e1df012d7a2d158e0f7484173f7c" |
||||
|
val response = GET(url(txid)) |
||||
|
|
||||
|
status(response) mustEqual BAD_REQUEST |
||||
|
val json = contentAsJson(response) |
||||
|
val errorList = (json \ "errors").as[List[JsValue]] |
||||
|
|
||||
|
errorList.size mustEqual 1 |
||||
|
val error = errorList.head |
||||
|
|
||||
|
(error \ "type").as[String] mustEqual PublicErrorRenderer.FieldValidationErrorType |
||||
|
(error \ "field").as[String] mustEqual "transactionId" |
||||
|
(error \ "message").as[String].nonEmpty mustEqual true |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,18 @@ |
|||||
|
package controllers.common |
||||
|
|
||||
|
import com.alexitc.playsonify.test.PlayAPISpec |
||||
|
import org.slf4j.LoggerFactory |
||||
|
import play.api.mvc.Result |
||||
|
import play.api.test.FakeRequest |
||||
|
import play.api.test.Helpers._ |
||||
|
|
||||
|
import scala.concurrent.Future |
||||
|
|
||||
|
trait MyAPISpec extends PlayAPISpec { |
||||
|
|
||||
|
protected val logger = LoggerFactory.getLogger(this.getClass) |
||||
|
|
||||
|
override protected def log[T](request: FakeRequest[T], response: Future[Result]): Unit = { |
||||
|
logger.info(s"> REQUEST, $request; < RESPONSE, status = ${status(response)}, body = ${contentAsString(response)}") |
||||
|
} |
||||
|
} |
Loading…
Reference in new issue