From 9bd1e530bdde10a0074241986b90bdaa9092b86d Mon Sep 17 00:00:00 2001 From: Vitalii Zymukha Date: Wed, 28 Aug 2019 18:01:09 +0200 Subject: [PATCH] Add marshal/unmarshal exception processing --- .../unknownnpc/psw/api/APIException.scala | 6 +- .../unknownnpc/psw/api/Serializer.scala | 4 +- .../com/github/unknownnpc/psw/api/Utils.scala | 10 +++ .../psw/api/action/ActionContext.scala | 18 ++-- .../CardBalanceReqResSerializer.scala | 85 ++++++++++--------- .../WalletHistoryReqResSerializer.scala | 77 +++++++++-------- .../CardBalanceReqResSerializerTest.scala | 4 +- .../WalletHistoryReqResSerializerTest.scala | 4 +- .../github/unknownnpc/psw/qiwi/QiwiAPI.scala | 2 +- .../AccountBalanceReqResSerializer.scala | 24 +++--- .../WalletHistoryReqResSerializer.scala | 54 ++++++------ .../AccountBalanceReqResSerializerTest.scala | 6 +- .../WalletHistoryReqResSerializerTest.scala | 10 +-- .../wm/serializer/X3ReqResSerializer.scala | 55 ++++++------ .../wm/serializer/X9ReqResSerializer.scala | 53 ++++++------ .../serializer/X3ReqResSerializerTest.scala | 4 +- .../serializer/X9ReqResSerializerTest.scala | 4 +- 17 files changed, 230 insertions(+), 190 deletions(-) create mode 100644 api/src/main/scala/com/github/unknownnpc/psw/api/Utils.scala diff --git a/api/src/main/scala/com/github/unknownnpc/psw/api/APIException.scala b/api/src/main/scala/com/github/unknownnpc/psw/api/APIException.scala index 0866bb7..eeb5ddf 100644 --- a/api/src/main/scala/com/github/unknownnpc/psw/api/APIException.scala +++ b/api/src/main/scala/com/github/unknownnpc/psw/api/APIException.scala @@ -7,5 +7,9 @@ class InvalidParam(message: String = "Invalid external param", cause: Throwable) extends APIException(message, cause) final case -class ExternalAPIException(message: String = "External request error", cause: Throwable) +class ExternalAPICallException(message: String = "External request error", cause: Throwable) + extends APIException(message, cause) + +final case +class ExternalAPIPayloadParseException(message: String = "Unable to parse payload", cause: Throwable) extends APIException(message, cause) diff --git a/api/src/main/scala/com/github/unknownnpc/psw/api/Serializer.scala b/api/src/main/scala/com/github/unknownnpc/psw/api/Serializer.scala index b2dc212..1f7da01 100644 --- a/api/src/main/scala/com/github/unknownnpc/psw/api/Serializer.scala +++ b/api/src/main/scala/com/github/unknownnpc/psw/api/Serializer.scala @@ -2,8 +2,8 @@ package com.github.unknownnpc.psw.api trait Serializer[IN, OUT, REQ, RES] { - def toReq(obj: IN): REQ + def toReq(obj: IN): Either[ExternalAPIPayloadParseException, REQ] - def fromRes(out: RES): OUT + def fromRes(out: RES): Either[ExternalAPIPayloadParseException, OUT] } diff --git a/api/src/main/scala/com/github/unknownnpc/psw/api/Utils.scala b/api/src/main/scala/com/github/unknownnpc/psw/api/Utils.scala new file mode 100644 index 0000000..69b0990 --- /dev/null +++ b/api/src/main/scala/com/github/unknownnpc/psw/api/Utils.scala @@ -0,0 +1,10 @@ +package com.github.unknownnpc.psw.api + +object Utils { + + def safeParse[T](fn: => T): Either[ExternalAPIPayloadParseException, T] = { + import scala.util.control.Exception.allCatch + allCatch.either(fn).left.map(e => ExternalAPIPayloadParseException(cause = e)) + } + +} diff --git a/api/src/main/scala/com/github/unknownnpc/psw/api/action/ActionContext.scala b/api/src/main/scala/com/github/unknownnpc/psw/api/action/ActionContext.scala index bd07ee5..31982cc 100644 --- a/api/src/main/scala/com/github/unknownnpc/psw/api/action/ActionContext.scala +++ b/api/src/main/scala/com/github/unknownnpc/psw/api/action/ActionContext.scala @@ -1,20 +1,22 @@ package com.github.unknownnpc.psw.api.action import com.github.unknownnpc.psw.api.executor.Executor -import com.github.unknownnpc.psw.api.{APIException, ExternalAPIException, Serializer} +import com.github.unknownnpc.psw.api.{APIException, ExternalAPICallException, Serializer} -import scala.util.Try +import scala.util.control.Exception.allCatch trait ActionContext[IN, OUT, REQ, RES] { executor: Executor[REQ, RES] => def run(in: IN)(implicit ser: Serializer[IN, OUT, REQ, RES]): Either[APIException, OUT] = { - Try(ser.fromRes( - executor.execute( - ser.toReq(in) - ) - )).toEither.left - .map(e => ExternalAPIException(cause = e)) + + for { + request <- ser.toReq(in).right + rawResponse <- allCatch.either(executor.execute(request)) + .left.map(e => ExternalAPICallException(cause = e)).right + response <- ser.fromRes(rawResponse).right + } yield response + } } diff --git a/privat24/src/main/scala/com/github/unknownnpc/psw/p24/serializer/CardBalanceReqResSerializer.scala b/privat24/src/main/scala/com/github/unknownnpc/psw/p24/serializer/CardBalanceReqResSerializer.scala index df84c4d..b574b57 100644 --- a/privat24/src/main/scala/com/github/unknownnpc/psw/p24/serializer/CardBalanceReqResSerializer.scala +++ b/privat24/src/main/scala/com/github/unknownnpc/psw/p24/serializer/CardBalanceReqResSerializer.scala @@ -1,6 +1,7 @@ package com.github.unknownnpc.psw.p24.serializer -import com.github.unknownnpc.psw.api.Serializer +import com.github.unknownnpc.psw.api.Utils.safeParse +import com.github.unknownnpc.psw.api.{ExternalAPIPayloadParseException, Serializer} import com.github.unknownnpc.psw.p24.model._ import org.apache.http.client.methods.HttpPost import org.apache.http.entity.{ContentType, StringEntity} @@ -11,52 +12,54 @@ private[serializer] class CardBalanceReqResSerializer extends Serializer[Request private val urlTarget: String = "https://api.privatbank.ua/p24api/balance" - override def toReq(obj: Request): HttpPost = { + override def toReq(obj: Request): Either[ExternalAPIPayloadParseException, HttpPost] = { + safeParse { + def formHttpPostReq(payload: String): HttpPost = { + val httpPost = new HttpPost(urlTarget) + httpPost.setEntity(new StringEntity(payload, ContentType.APPLICATION_XML)) + httpPost + } - def formHttpPostReq(payload: String): HttpPost = { - val httpPost = new HttpPost(urlTarget) - httpPost.setEntity(new StringEntity(payload, ContentType.APPLICATION_XML)) - httpPost + formHttpPostReq( + formRequestXmlStr(obj) + ) } - - formHttpPostReq( - formRequestXmlStr(obj) - ) } - override def fromRes(out: String): CardBalanceResponse = { - val responseXml = XML.loadString(unPrettyOut(out)) - - CardBalanceResponse( - Merchant( - (responseXml \ "merchant" \ "id").text.toLong, - Option((responseXml \ "merchant" \ "signature").text) - ), - CardBalanceResponseData( - (responseXml \ "data" \ "oper").text, - CardBalanceResponseCardBalance( - BigDecimal((responseXml \ "data" \ "info" \ "cardbalance" \ "av_balance").text), - p24ResponseDateTimeFormatter.parse( - (responseXml \ "data" \ "info" \ "cardbalance" \ "bal_date").text - ), - (responseXml \ "data" \ "info" \ "cardbalance" \ "bal_dyn").text, - BigDecimal((responseXml \ "data" \ "info" \ "cardbalance" \ "balance").text), - BigDecimal((responseXml \ "data" \ "info" \ "cardbalance" \ "fin_limit").text), - BigDecimal((responseXml \ "data" \ "info" \ "cardbalance" \ "trade_limit").text) + override def fromRes(out: String): Either[ExternalAPIPayloadParseException, CardBalanceResponse] = { + safeParse { + val responseXml = XML.loadString(unPrettyOut(out)) + + CardBalanceResponse( + Merchant( + (responseXml \ "merchant" \ "id").text.toLong, + Option((responseXml \ "merchant" \ "signature").text) ), - CardBalanceResponseCard( - (responseXml \ "data" \ "info" \ "cardbalance" \ "card" \ "account").text, - (responseXml \ "data" \ "info" \ "cardbalance" \ "card" \ "card_number").text, - (responseXml \ "data" \ "info" \ "cardbalance" \ "card" \ "acc_name").text, - (responseXml \ "data" \ "info" \ "cardbalance" \ "card" \ "acc_type").text, - (responseXml \ "data" \ "info" \ "cardbalance" \ "card" \ "currency").text, - (responseXml \ "data" \ "info" \ "cardbalance" \ "card" \ "card_type").text, - (responseXml \ "data" \ "info" \ "cardbalance" \ "card" \ "main_card_number").text, - (responseXml \ "data" \ "info" \ "cardbalance" \ "card" \ "card_stat").text, - (responseXml \ "data" \ "info" \ "cardbalance" \ "card" \ "src").text + CardBalanceResponseData( + (responseXml \ "data" \ "oper").text, + CardBalanceResponseCardBalance( + BigDecimal((responseXml \ "data" \ "info" \ "cardbalance" \ "av_balance").text), + p24ResponseDateTimeFormatter.parse( + (responseXml \ "data" \ "info" \ "cardbalance" \ "bal_date").text + ), + (responseXml \ "data" \ "info" \ "cardbalance" \ "bal_dyn").text, + BigDecimal((responseXml \ "data" \ "info" \ "cardbalance" \ "balance").text), + BigDecimal((responseXml \ "data" \ "info" \ "cardbalance" \ "fin_limit").text), + BigDecimal((responseXml \ "data" \ "info" \ "cardbalance" \ "trade_limit").text) + ), + CardBalanceResponseCard( + (responseXml \ "data" \ "info" \ "cardbalance" \ "card" \ "account").text, + (responseXml \ "data" \ "info" \ "cardbalance" \ "card" \ "card_number").text, + (responseXml \ "data" \ "info" \ "cardbalance" \ "card" \ "acc_name").text, + (responseXml \ "data" \ "info" \ "cardbalance" \ "card" \ "acc_type").text, + (responseXml \ "data" \ "info" \ "cardbalance" \ "card" \ "currency").text, + (responseXml \ "data" \ "info" \ "cardbalance" \ "card" \ "card_type").text, + (responseXml \ "data" \ "info" \ "cardbalance" \ "card" \ "main_card_number").text, + (responseXml \ "data" \ "info" \ "cardbalance" \ "card" \ "card_stat").text, + (responseXml \ "data" \ "info" \ "cardbalance" \ "card" \ "src").text + ) ) ) - ) + } } - } diff --git a/privat24/src/main/scala/com/github/unknownnpc/psw/p24/serializer/WalletHistoryReqResSerializer.scala b/privat24/src/main/scala/com/github/unknownnpc/psw/p24/serializer/WalletHistoryReqResSerializer.scala index 34741c0..fff3644 100644 --- a/privat24/src/main/scala/com/github/unknownnpc/psw/p24/serializer/WalletHistoryReqResSerializer.scala +++ b/privat24/src/main/scala/com/github/unknownnpc/psw/p24/serializer/WalletHistoryReqResSerializer.scala @@ -1,6 +1,7 @@ package com.github.unknownnpc.psw.p24.serializer -import com.github.unknownnpc.psw.api.Serializer +import com.github.unknownnpc.psw.api.Utils.safeParse +import com.github.unknownnpc.psw.api.{ExternalAPIPayloadParseException, Serializer} import com.github.unknownnpc.psw.p24.model._ import org.apache.http.client.methods.HttpPost import org.apache.http.entity.{ContentType, StringEntity} @@ -21,48 +22,50 @@ private[serializer] class WalletHistoryReqResSerializer extends Serializer[Reque * @param walletHistoryReq the request entity. * @return the p24 request on the xml format. */ - override def toReq(req: Request): HttpPost = { + override def toReq(req: Request): Either[ExternalAPIPayloadParseException, HttpPost] = { + safeParse { + def formHttpPostReq(payload: String): HttpPost = { + val httpPost = new HttpPost(urlTarget) + httpPost.setEntity(new StringEntity(payload, ContentType.APPLICATION_XML)) + httpPost + } - def formHttpPostReq(payload: String): HttpPost = { - val httpPost = new HttpPost(urlTarget) - httpPost.setEntity(new StringEntity(payload, ContentType.APPLICATION_XML)) - httpPost - } - - val reqString = formRequestXmlStr(req) + val reqString = formRequestXmlStr(req) - formHttpPostReq(reqString) + formHttpPostReq(reqString) + } } - override def fromRes(out: String): WalletHistoryResponse = { - val responseXml = XML.loadString(unPrettyOut(out)) + override def fromRes(out: String): Either[ExternalAPIPayloadParseException, WalletHistoryResponse] = { + safeParse { + val responseXml = XML.loadString(unPrettyOut(out)) - WalletHistoryResponse( - Merchant( - (responseXml \ "merchant" \ "id").text.toLong, - Option((responseXml \ "merchant" \ "signature").text) - ), - WalletHistoryResponseData( - (responseXml \ "data" \ "oper").text, - WalletHistoryResponseInfo( - (responseXml \ "data" \ "info" \ "statements" \ "@status").text, - (responseXml \ "data" \ "info" \ "statements" \ "@credit").text, - (responseXml \ "data" \ "info" \ "statements" \ "@debet").text, - (responseXml \ "data" \ "info" \ "statements" \ "statement").map(statementXml => { - WalletHistoryResponseStatementEntity( - (statementXml \ "@card").text, - (statementXml \ "@appcode").text, - p24ResponseDateFormatter.parse((statementXml \ "@trandate").text), - (statementXml \ "@amount").text, - (statementXml \ "@cardamount").text, - (statementXml \ "@rest").text, - (statementXml \ "@terminal").text, - (statementXml \ "@description").text - ) - }).toList + WalletHistoryResponse( + Merchant( + (responseXml \ "merchant" \ "id").text.toLong, + Option((responseXml \ "merchant" \ "signature").text) + ), + WalletHistoryResponseData( + (responseXml \ "data" \ "oper").text, + WalletHistoryResponseInfo( + (responseXml \ "data" \ "info" \ "statements" \ "@status").text, + (responseXml \ "data" \ "info" \ "statements" \ "@credit").text, + (responseXml \ "data" \ "info" \ "statements" \ "@debet").text, + (responseXml \ "data" \ "info" \ "statements" \ "statement").map(statementXml => { + WalletHistoryResponseStatementEntity( + (statementXml \ "@card").text, + (statementXml \ "@appcode").text, + p24ResponseDateFormatter.parse((statementXml \ "@trandate").text), + (statementXml \ "@amount").text, + (statementXml \ "@cardamount").text, + (statementXml \ "@rest").text, + (statementXml \ "@terminal").text, + (statementXml \ "@description").text + ) + }).toList + ) ) ) - ) + } } - } diff --git a/privat24/src/test/scala/com/github/unknownnpc/psw/p24/serializer/CardBalanceReqResSerializerTest.scala b/privat24/src/test/scala/com/github/unknownnpc/psw/p24/serializer/CardBalanceReqResSerializerTest.scala index 779387b..d360c37 100644 --- a/privat24/src/test/scala/com/github/unknownnpc/psw/p24/serializer/CardBalanceReqResSerializerTest.scala +++ b/privat24/src/test/scala/com/github/unknownnpc/psw/p24/serializer/CardBalanceReqResSerializerTest.scala @@ -24,7 +24,7 @@ class CardBalanceReqResSerializerTest extends FunSpec with Matchers { ) ) - val requestSample = P24Serializer.cardBalanceReqResSerializer.toReq(request) + val requestSample = P24Serializer.cardBalanceReqResSerializer.toReq(request).right.get requestSample should not be null requestSample.getAllHeaders.toList shouldBe List() @@ -68,7 +68,7 @@ class CardBalanceReqResSerializerTest extends FunSpec with Matchers { """.stripMargin - val result = P24Serializer.cardBalanceReqResSerializer.fromRes(responseSample) + val result = P24Serializer.cardBalanceReqResSerializer.fromRes(responseSample).right.get result should not be null result.merchant.id shouldBe 75482 diff --git a/privat24/src/test/scala/com/github/unknownnpc/psw/p24/serializer/WalletHistoryReqResSerializerTest.scala b/privat24/src/test/scala/com/github/unknownnpc/psw/p24/serializer/WalletHistoryReqResSerializerTest.scala index 2cefc17..0236e32 100644 --- a/privat24/src/test/scala/com/github/unknownnpc/psw/p24/serializer/WalletHistoryReqResSerializerTest.scala +++ b/privat24/src/test/scala/com/github/unknownnpc/psw/p24/serializer/WalletHistoryReqResSerializerTest.scala @@ -37,7 +37,7 @@ class WalletHistoryReqResSerializerTest extends FunSpec with Matchers { ) ) - val requestSample = P24Serializer.walletHistoryReqResSerializer.toReq(request) + val requestSample = P24Serializer.walletHistoryReqResSerializer.toReq(request).right.get requestSample should not be null requestSample.getAllHeaders.toList shouldBe List() @@ -84,7 +84,7 @@ class WalletHistoryReqResSerializerTest extends FunSpec with Matchers { | """.stripMargin - val result = P24Serializer.walletHistoryReqResSerializer.fromRes(responseSample) + val result = P24Serializer.walletHistoryReqResSerializer.fromRes(responseSample).right.get result should not be null result.merchant.id shouldBe idVal diff --git a/qiwi/src/main/scala/com/github/unknownnpc/psw/qiwi/QiwiAPI.scala b/qiwi/src/main/scala/com/github/unknownnpc/psw/qiwi/QiwiAPI.scala index 5054611..5e7b171 100644 --- a/qiwi/src/main/scala/com/github/unknownnpc/psw/qiwi/QiwiAPI.scala +++ b/qiwi/src/main/scala/com/github/unknownnpc/psw/qiwi/QiwiAPI.scala @@ -51,7 +51,7 @@ private[qiwi] class QiwiAPI(token: String, httpClient: CloseableHttpClient) { * Requests wallets balance * https://developer.qiwi.com/ru/qiwi-wallet-personal/#balances_list * - * @param personId the personId valie, eg: 30501234567 + * @param personId the personId value, eg: 30501234567 * @return the entity with response or error */ def retrieveAccountBalance(personId: String): Either[APIException, AccountBalanceResponse] = { diff --git a/qiwi/src/main/scala/com/github/unknownnpc/psw/qiwi/serializer/AccountBalanceReqResSerializer.scala b/qiwi/src/main/scala/com/github/unknownnpc/psw/qiwi/serializer/AccountBalanceReqResSerializer.scala index 1a8bb13..75cf8a9 100644 --- a/qiwi/src/main/scala/com/github/unknownnpc/psw/qiwi/serializer/AccountBalanceReqResSerializer.scala +++ b/qiwi/src/main/scala/com/github/unknownnpc/psw/qiwi/serializer/AccountBalanceReqResSerializer.scala @@ -1,6 +1,7 @@ package com.github.unknownnpc.psw.qiwi.serializer -import com.github.unknownnpc.psw.api.Serializer +import com.github.unknownnpc.psw.api.Utils.safeParse +import com.github.unknownnpc.psw.api.{ExternalAPIPayloadParseException, Serializer} import com.github.unknownnpc.psw.qiwi.model.{AccountBalanceRequest, AccountBalanceResponse} import org.apache.http.client.methods.HttpGet @@ -8,22 +9,25 @@ private[serializer] class AccountBalanceReqResSerializer extends Serializer[Acco private val urlTarget: String = "https://edge.qiwi.com/funding-sources/v2/persons/%s/accounts" - override def toReq(req: AccountBalanceRequest): HttpGet = { - val fullRequestUrl = String.format(urlTarget, req.wallet) - val httpGet = new HttpGet(fullRequestUrl) - httpGet.setHeader("Authorization", "Bearer " + req.apiToken) - httpGet.setHeader("Accept", "application/json") + override def toReq(req: AccountBalanceRequest): Either[ExternalAPIPayloadParseException, HttpGet] = { + safeParse { + val fullRequestUrl = String.format(urlTarget, req.wallet) + val httpGet = new HttpGet(fullRequestUrl) + httpGet.setHeader("Authorization", "Bearer " + req.apiToken) + httpGet.setHeader("Accept", "application/json") - httpGet + httpGet + } } - override def fromRes(out: String): AccountBalanceResponse = { + override def fromRes(out: String): Either[ExternalAPIPayloadParseException, AccountBalanceResponse] = { import org.json4s._ import org.json4s.native.Serialization import org.json4s.native.Serialization.read implicit val formats = Serialization.formats(NoTypeHints) - - read[AccountBalanceResponse](out) + safeParse { + read[AccountBalanceResponse](out) + } } } diff --git a/qiwi/src/main/scala/com/github/unknownnpc/psw/qiwi/serializer/WalletHistoryReqResSerializer.scala b/qiwi/src/main/scala/com/github/unknownnpc/psw/qiwi/serializer/WalletHistoryReqResSerializer.scala index 5e1219f..6d59bad 100644 --- a/qiwi/src/main/scala/com/github/unknownnpc/psw/qiwi/serializer/WalletHistoryReqResSerializer.scala +++ b/qiwi/src/main/scala/com/github/unknownnpc/psw/qiwi/serializer/WalletHistoryReqResSerializer.scala @@ -3,7 +3,8 @@ package com.github.unknownnpc.psw.qiwi.serializer import java.text.SimpleDateFormat import java.util.{Date, TimeZone} -import com.github.unknownnpc.psw.api.Serializer +import com.github.unknownnpc.psw.api.Utils.safeParse +import com.github.unknownnpc.psw.api.{ExternalAPIPayloadParseException, Serializer} import com.github.unknownnpc.psw.qiwi.model.{ResStatus, WalletHistoryRequest, WalletHistoryResponse} import org.apache.http.client.methods.HttpGet import org.json4s.CustomSerializer @@ -24,40 +25,43 @@ private[serializer] class WalletHistoryReqResSerializer extends Serializer[Walle setTimeZone(TimeZone.getTimeZone("GMT")) } - override def toReq(req: WalletHistoryRequest): HttpGet = { + override def toReq(req: WalletHistoryRequest): Either[ExternalAPIPayloadParseException, HttpGet] = { + safeParse { + def queryParam(name: String, value: String): String = s"$name=$value" - def queryParam(name: String, value: String): String = s"$name=$value" + val paramsList = for { + operation <- req.operation.map(t => queryParam(OperationParam, t.toString)) + sources <- Some(req.sources).map(sources => sources.map(s => queryParam(SourcesParam, s.id.toString))) + startEndDates <- req.startEndDates.map(se => { + val startStr = qiwiReqDateFormatter.format(se._1) + val endStr = qiwiReqDateFormatter.format(se._2) + List(queryParam(StartDateParam, startStr), queryParam(EndDateParam, endStr)) + }) + nextPage <- req.nextPage.map(np => { + val nextTxnDateStr = qiwiReqDateFormatter.format(np._1) + List(queryParam(NextTxnDateParam, nextTxnDateStr), queryParam(NextTxnIdParam, np._2.toString)) + }) + } yield List(operation) ++ sources ++ startEndDates ++ nextPage - val paramsList = for { - operation <- req.operation.map(t => queryParam(OperationParam, t.toString)) - sources <- Some(req.sources).map(sources => sources.map(s => queryParam(SourcesParam, s.id.toString))) - startEndDates <- req.startEndDates.map(se => { - val startStr = qiwiReqDateFormatter.format(se._1) - val endStr = qiwiReqDateFormatter.format(se._2) - List(queryParam(StartDateParam, startStr), queryParam(EndDateParam, endStr)) - }) - nextPage <- req.nextPage.map(np => { - val nextTxnDateStr = qiwiReqDateFormatter.format(np._1) - List(queryParam(NextTxnDateParam, nextTxnDateStr), queryParam(NextTxnIdParam, np._2.toString)) - }) - } yield List(operation) ++ sources ++ startEndDates ++ nextPage + val paramsStr = paramsList.map(_.mkString("&")).getOrElse("") + val fullRequestUrl = String.format(urlTarget, req.personId, req.rows.toString) + paramsStr + val httpGet = new HttpGet(fullRequestUrl) + httpGet.setHeader("Authorization", "Bearer " + req.apiToken) + httpGet.setHeader("Accept", "application/json") - val paramsStr = paramsList.map(_.mkString("&")).getOrElse("") - val fullRequestUrl = String.format(urlTarget, req.personId, req.rows.toString) + paramsStr - val httpGet = new HttpGet(fullRequestUrl) - httpGet.setHeader("Authorization", "Bearer " + req.apiToken) - httpGet.setHeader("Accept", "application/json") - - httpGet + httpGet + } } - override def fromRes(out: String): WalletHistoryResponse = { + override def fromRes(out: String): Either[ExternalAPIPayloadParseException, WalletHistoryResponse] = { import org.json4s._ import org.json4s.native.Serialization import org.json4s.native.Serialization.read implicit val formats = Serialization.formats(NoTypeHints) + CustomDateSerializer + ResStatusSerializer - read[WalletHistoryResponse](out) + safeParse { + read[WalletHistoryResponse](out) + } } object CustomDateSerializer extends CustomSerializer[Date](_ => ( { diff --git a/qiwi/src/test/scala/com/github/unknownnpc/psw/qiwi/serializer/AccountBalanceReqResSerializerTest.scala b/qiwi/src/test/scala/com/github/unknownnpc/psw/qiwi/serializer/AccountBalanceReqResSerializerTest.scala index d3b9fbc..08f9277 100644 --- a/qiwi/src/test/scala/com/github/unknownnpc/psw/qiwi/serializer/AccountBalanceReqResSerializerTest.scala +++ b/qiwi/src/test/scala/com/github/unknownnpc/psw/qiwi/serializer/AccountBalanceReqResSerializerTest.scala @@ -7,7 +7,7 @@ class AccountBalanceReqResSerializerTest extends FunSpec with Matchers { it("should serialize to request correctly when all params exist") { val request = AccountBalanceRequest("token", "personId") - val requestSample = QiwiSerializer.accountBalanceReqResSerializer.toReq(request) + val requestSample = QiwiSerializer.accountBalanceReqResSerializer.toReq(request).right.get requestSample should not be null requestSample.getAllHeaders.toList should have size 2 requestSample.getURI.toString shouldBe "https://edge.qiwi.com/funding-sources/v2/persons/personId/accounts" @@ -16,7 +16,7 @@ class AccountBalanceReqResSerializerTest extends FunSpec with Matchers { it("should serialize to request with valid headers") { val token = "token123" val request = AccountBalanceRequest(token, "wallet") - val requestSample = QiwiSerializer.accountBalanceReqResSerializer.toReq(request) + val requestSample = QiwiSerializer.accountBalanceReqResSerializer.toReq(request).right.get requestSample.getAllHeaders.head.getName shouldBe "Authorization" requestSample.getAllHeaders.head.getValue shouldBe s"Bearer $token" requestSample.getAllHeaders.lift(1).get.getName shouldBe "Accept" @@ -47,7 +47,7 @@ class AccountBalanceReqResSerializerTest extends FunSpec with Matchers { | ] |}""".stripMargin - val response = QiwiSerializer.accountBalanceReqResSerializer.fromRes(responseStr) + val response = QiwiSerializer.accountBalanceReqResSerializer.fromRes(responseStr).right.get response.accounts.head.alias shouldBe "qw_wallet_rub" response.accounts.head.fsAlias shouldBe "qb_wallet" response.accounts.head.bankAlias shouldBe "QIWI" diff --git a/qiwi/src/test/scala/com/github/unknownnpc/psw/qiwi/serializer/WalletHistoryReqResSerializerTest.scala b/qiwi/src/test/scala/com/github/unknownnpc/psw/qiwi/serializer/WalletHistoryReqResSerializerTest.scala index 4497fe8..1b4944b 100644 --- a/qiwi/src/test/scala/com/github/unknownnpc/psw/qiwi/serializer/WalletHistoryReqResSerializerTest.scala +++ b/qiwi/src/test/scala/com/github/unknownnpc/psw/qiwi/serializer/WalletHistoryReqResSerializerTest.scala @@ -15,7 +15,7 @@ class WalletHistoryReqResSerializerTest extends FunSpec with Matchers { Some(fromDate, toDate), Some(toDate, 2L) ) - val requestSample = QiwiSerializer.walletHistoryReqResSerializer.toReq(request) + val requestSample = QiwiSerializer.walletHistoryReqResSerializer.toReq(request).right.get requestSample should not be null requestSample.getAllHeaders.toList should have size 2 @@ -25,7 +25,7 @@ class WalletHistoryReqResSerializerTest extends FunSpec with Matchers { it("should serialize to request correctly when all params default") { val request = WalletHistoryRequest("token", "wallet", 11) - val requestSample = QiwiSerializer.walletHistoryReqResSerializer.toReq(request) + val requestSample = QiwiSerializer.walletHistoryReqResSerializer.toReq(request).right.get requestSample should not be null requestSample.getAllHeaders.toList should have size 2 requestSample.getURI.toString shouldBe "https://edge.qiwi.com/payment-history/v2/persons/wallet/payments?rows=11&" @@ -34,7 +34,7 @@ class WalletHistoryReqResSerializerTest extends FunSpec with Matchers { it("should serialize to request with valid headers") { val token = "token123" val request = WalletHistoryRequest(token, "wallet", 11) - val requestSample = QiwiSerializer.walletHistoryReqResSerializer.toReq(request) + val requestSample = QiwiSerializer.walletHistoryReqResSerializer.toReq(request).right.get requestSample.getAllHeaders.head.getName shouldBe "Authorization" requestSample.getAllHeaders.head.getValue shouldBe s"Bearer $token" requestSample.getAllHeaders.lift(1).get.getName shouldBe "Accept" @@ -91,7 +91,7 @@ class WalletHistoryReqResSerializerTest extends FunSpec with Matchers { |} """.stripMargin - val response = QiwiSerializer.walletHistoryReqResSerializer.fromRes(responseStr) + val response = QiwiSerializer.walletHistoryReqResSerializer.fromRes(responseStr).right.get response.nextTxnId shouldBe Some(9001) response.nextTxnDate should not be null @@ -126,7 +126,7 @@ class WalletHistoryReqResSerializerTest extends FunSpec with Matchers { it("serialize from empty response") { val responseStr = "{\"data\":[],\"nextTxnId\":null,\"nextTxnDate\":null}" - val response = QiwiSerializer.walletHistoryReqResSerializer.fromRes(responseStr) + val response = QiwiSerializer.walletHistoryReqResSerializer.fromRes(responseStr).right.get response.data should have length 0 response.nextTxnDate shouldBe None response.nextTxnId shouldBe None diff --git a/webmoney/src/main/scala/com/github/unknownnpc/psw/wm/serializer/X3ReqResSerializer.scala b/webmoney/src/main/scala/com/github/unknownnpc/psw/wm/serializer/X3ReqResSerializer.scala index 4b815da..b0e1d4f 100644 --- a/webmoney/src/main/scala/com/github/unknownnpc/psw/wm/serializer/X3ReqResSerializer.scala +++ b/webmoney/src/main/scala/com/github/unknownnpc/psw/wm/serializer/X3ReqResSerializer.scala @@ -1,6 +1,7 @@ package com.github.unknownnpc.psw.wm.serializer -import com.github.unknownnpc.psw.api.Serializer +import com.github.unknownnpc.psw.api.Utils.safeParse +import com.github.unknownnpc.psw.api.{ExternalAPIPayloadParseException, Serializer} import com.github.unknownnpc.psw.wm.model._ import org.apache.http.client.methods.HttpPost @@ -10,7 +11,7 @@ private[serializer] class X3ReqResSerializer extends Serializer[X3Request, X3Res private val urlTarget: String = "https://w3s.webmoney.ru/asp/XMLOperations.asp" - override def toReq(obj: X3Request): HttpPost = { + override def toReq(obj: X3Request): Either[ExternalAPIPayloadParseException, HttpPost] = { def operation(op: X3RequestOperation): Elem = { scala.xml.XML.loadString(s"<${op.name}>${op.value}") @@ -31,31 +32,35 @@ private[serializer] class X3ReqResSerializer extends Serializer[X3Request, X3Res } - formPostReq(xmlReq(obj).toString(), urlTarget) + safeParse { + formPostReq(xmlReq(obj).toString(), urlTarget) + } } - override def fromRes(out: String): X3Response = { - val unPrettyOut = out.replaceAll(">\\s+<", "><") - val responseXml = XML.loadString(unPrettyOut) - X3Response( - (responseXml \ "reqn").text.toLong, - RetVal.values.find(v => v.id.toString == (responseXml \ "retval").text).get, - (responseXml \ "retdesc").text, - X3ResponseOperations( - (responseXml \ "operations" \ "@cnt").text, - (responseXml \ "operations" \ "operation").map(op => { - X3OperationInfo( - (op \ "@id").text, - (op \ "@ts").text, - op.child.map(c => { - X3ResponseOperationType.values - .find(rot => rot.toString == c.label) - .getOrElse(X3ResponseOperationType.unknown) -> c.text - }).toMap.filter(_._1 != X3ResponseOperationType.timelock), - op.child.exists(n => n.label.eq("timelock")) - ) - }).toList + override def fromRes(out: String): Either[ExternalAPIPayloadParseException, X3Response] = { + safeParse { + val unPrettyOut = out.replaceAll(">\\s+<", "><") + val responseXml = XML.loadString(unPrettyOut) + X3Response( + (responseXml \ "reqn").text.toLong, + RetVal.values.find(v => v.id.toString == (responseXml \ "retval").text).get, + (responseXml \ "retdesc").text, + X3ResponseOperations( + (responseXml \ "operations" \ "@cnt").text, + (responseXml \ "operations" \ "operation").map(op => { + X3OperationInfo( + (op \ "@id").text, + (op \ "@ts").text, + op.child.map(c => { + X3ResponseOperationType.values + .find(rot => rot.toString == c.label) + .getOrElse(X3ResponseOperationType.unknown) -> c.text + }).toMap.filter(_._1 != X3ResponseOperationType.timelock), + op.child.exists(n => n.label.eq("timelock")) + ) + }).toList + ) ) - ) + } } } diff --git a/webmoney/src/main/scala/com/github/unknownnpc/psw/wm/serializer/X9ReqResSerializer.scala b/webmoney/src/main/scala/com/github/unknownnpc/psw/wm/serializer/X9ReqResSerializer.scala index 0078ef7..67062bb 100644 --- a/webmoney/src/main/scala/com/github/unknownnpc/psw/wm/serializer/X9ReqResSerializer.scala +++ b/webmoney/src/main/scala/com/github/unknownnpc/psw/wm/serializer/X9ReqResSerializer.scala @@ -1,6 +1,7 @@ package com.github.unknownnpc.psw.wm.serializer -import com.github.unknownnpc.psw.api.Serializer +import com.github.unknownnpc.psw.api.Utils.safeParse +import com.github.unknownnpc.psw.api.{ExternalAPIPayloadParseException, Serializer} import com.github.unknownnpc.psw.wm.model._ import org.apache.http.client.methods.HttpPost @@ -10,7 +11,7 @@ private[serializer] class X9ReqResSerializer extends Serializer[X9Request, X9Res private val urlTarget: String = "https://w3s.webmoney.ru/asp/XMLPurses.asp" - override def toReq(obj: X9Request): HttpPost = { + override def toReq(obj: X9Request): Either[ExternalAPIPayloadParseException, HttpPost] = { def xmlReq(obj: X9Request): Elem = { @@ -23,31 +24,35 @@ private[serializer] class X9ReqResSerializer extends Serializer[X9Request, X9Res } - formPostReq(xmlReq(obj).toString(), urlTarget) + safeParse { + formPostReq(xmlReq(obj).toString(), urlTarget) + } } - override def fromRes(out: String): X9Response = { - val unPrettyOut = out.replaceAll(">\\s+<", "><") - val responseXml = XML.loadString(unPrettyOut) - X9Response( - (responseXml \ "reqn").text.toLong, - RetVal.values.find(v => v.id.toString == (responseXml \ "retval").text).get, - (responseXml \ "retdesc").text, - X9ResponsePurses( - (responseXml \ "purses" \ "@cnt").text, - (responseXml \ "purses" \ "purse").map(op => { - X9ResponsePurse( - (op \ "@id").text, - (op \ "pursename").text, - (op \ "amount").text, - (op \ "desc").text, - (op \ "outsideopen").text, - (op \ "lastintr").text, - (op \ "lastouttr").text - ) - }).toList + override def fromRes(out: String): Either[ExternalAPIPayloadParseException, X9Response] = { + safeParse { + val unPrettyOut = out.replaceAll(">\\s+<", "><") + val responseXml = XML.loadString(unPrettyOut) + X9Response( + (responseXml \ "reqn").text.toLong, + RetVal.values.find(v => v.id.toString == (responseXml \ "retval").text).get, + (responseXml \ "retdesc").text, + X9ResponsePurses( + (responseXml \ "purses" \ "@cnt").text, + (responseXml \ "purses" \ "purse").map(op => { + X9ResponsePurse( + (op \ "@id").text, + (op \ "pursename").text, + (op \ "amount").text, + (op \ "desc").text, + (op \ "outsideopen").text, + (op \ "lastintr").text, + (op \ "lastouttr").text + ) + }).toList + ) ) - ) + } } } diff --git a/webmoney/src/test/scala/com/github/unknownnpc/psw/wm/serializer/X3ReqResSerializerTest.scala b/webmoney/src/test/scala/com/github/unknownnpc/psw/wm/serializer/X3ReqResSerializerTest.scala index 8bc3df7..243187b 100644 --- a/webmoney/src/test/scala/com/github/unknownnpc/psw/wm/serializer/X3ReqResSerializerTest.scala +++ b/webmoney/src/test/scala/com/github/unknownnpc/psw/wm/serializer/X3ReqResSerializerTest.scala @@ -20,7 +20,7 @@ class X3ReqResSerializerTest extends FunSpec with Matchers { X3RequestOperation("wminvid", "wminvid"), X3RequestOperation("orderid", "orderid") )) - val requestSample = WebMoneySerializer.x3ReqResSerializer.toReq(request) + val requestSample = WebMoneySerializer.x3ReqResSerializer.toReq(request).right.get requestSample should not be null requestSample.getAllHeaders.toList shouldBe List() val requestBody = EntityUtils.toString(requestSample.getEntity) @@ -62,7 +62,7 @@ class X3ReqResSerializerTest extends FunSpec with Matchers { | | """.stripMargin - val result = WebMoneySerializer.x3ReqResSerializer.fromRes(rawResponse) + val result = WebMoneySerializer.x3ReqResSerializer.fromRes(rawResponse).right.get result should not be null result.reqn shouldBe 1 result.retval shouldBe RetVal.OK diff --git a/webmoney/src/test/scala/com/github/unknownnpc/psw/wm/serializer/X9ReqResSerializerTest.scala b/webmoney/src/test/scala/com/github/unknownnpc/psw/wm/serializer/X9ReqResSerializerTest.scala index 28b4b86..555fea2 100644 --- a/webmoney/src/test/scala/com/github/unknownnpc/psw/wm/serializer/X9ReqResSerializerTest.scala +++ b/webmoney/src/test/scala/com/github/unknownnpc/psw/wm/serializer/X9ReqResSerializerTest.scala @@ -11,7 +11,7 @@ class X9ReqResSerializerTest extends FunSpec with Matchers { it("serialize to request") { val testDate = new Date(0) val request = X9Request("wmid", "sign", "reqN") - val requestSample = WebMoneySerializer.x9ReqResSerializer.toReq(request) + val requestSample = WebMoneySerializer.x9ReqResSerializer.toReq(request).right.get requestSample should not be null requestSample.getAllHeaders.toList shouldBe List() @@ -48,7 +48,7 @@ class X9ReqResSerializerTest extends FunSpec with Matchers { | """.stripMargin - val result = WebMoneySerializer.x9ReqResSerializer.fromRes(rawResponse) + val result = WebMoneySerializer.x9ReqResSerializer.fromRes(rawResponse).right.get result should not be null result.reqn shouldBe 1