mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 15:27:01 +00:00
bugfix/trait_way_enum_wrong_serialize: move reusable Serializer to obp-commons, make trait way enum be sub type of SimpleEnum, Delete TransactionCommons type, it cause many wrong result when do serialize and deserialize.
This commit is contained in:
parent
19adf9f38e
commit
b9b2dd89ab
@ -191,7 +191,7 @@ object MessageDocsSwaggerDefinitions
|
||||
isBeneficiary = isBeneficiaryExample.value.toBoolean // True if the originAccount can send money to the Counterparty
|
||||
)
|
||||
|
||||
val transactionCommons = TransactionCommons(
|
||||
val transaction = Transaction(
|
||||
`uuid`= transactionIdExample.value,
|
||||
id = TransactionId(transactionIdExample.value),
|
||||
thisAccount = bankAccountCommons,
|
||||
|
||||
@ -8,16 +8,13 @@ import code.api.cache.Caching
|
||||
import code.api.util.ApiRole.rolesMappedToClasses
|
||||
import code.api.v3_1_0.ListResult
|
||||
import code.util.Helper.MdcLoggable
|
||||
import com.openbankproject.commons.model.JsonFieldReName
|
||||
import com.openbankproject.commons.util.{EnumValueSerializer, Functions, JsonAbleSerializer, ReflectUtils}
|
||||
import com.openbankproject.commons.util._
|
||||
import com.tesobe.CacheKeyFromArguments
|
||||
import net.liftweb.json.JsonAST.JValue
|
||||
import net.liftweb.json.{TypeInfo, compactRender, _}
|
||||
import net.liftweb.util.StringHelpers
|
||||
import net.liftweb.json.{TypeInfo, _}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import scala.reflect.ManifestFactory
|
||||
import scala.reflect.runtime.{universe => ru}
|
||||
|
||||
trait CustomJsonFormats {
|
||||
implicit val formats: Formats = CustomJsonFormats.formats
|
||||
@ -25,11 +22,11 @@ trait CustomJsonFormats {
|
||||
|
||||
object CustomJsonFormats {
|
||||
|
||||
val formats: Formats = net.liftweb.json.DefaultFormats + BigDecimalSerializer + StringSerializer + FiledRenameSerializer + ListResultSerializer + EnumValueSerializer + JsonAbleSerializer
|
||||
val formats: Formats = JsonSerializers.commonFormats + ListResultSerializer
|
||||
|
||||
val losslessFormats: Formats = net.liftweb.json.DefaultFormats.lossless + BigDecimalSerializer + StringSerializer + FiledRenameSerializer + ListResultSerializer + EnumValueSerializer + JsonAbleSerializer
|
||||
val losslessFormats: Formats = net.liftweb.json.DefaultFormats.lossless + ListResultSerializer ++ JsonSerializers.serializers
|
||||
|
||||
val emptyHintFormats = DefaultFormats.withHints(ShortTypeHints(List())) + BigDecimalSerializer + StringSerializer + FiledRenameSerializer + ListResultSerializer + EnumValueSerializer + JsonAbleSerializer
|
||||
val emptyHintFormats = DefaultFormats.withHints(ShortTypeHints(List())) + ListResultSerializer ++ JsonSerializers.serializers
|
||||
|
||||
implicit val nullTolerateFormats = formats + JNothingSerializer
|
||||
|
||||
@ -37,139 +34,7 @@ object CustomJsonFormats {
|
||||
val dateFormat = net.liftweb.json.DefaultFormats.dateFormat
|
||||
|
||||
override val typeHints = ShortTypeHints(rolesMappedToClasses)
|
||||
} + BigDecimalSerializer + StringSerializer + FiledRenameSerializer + ListResultSerializer + EnumValueSerializer + JsonAbleSerializer
|
||||
}
|
||||
|
||||
object StringSerializer extends Serializer[String] {
|
||||
private val IntervalClass = classOf[String]
|
||||
|
||||
override def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), String] = {
|
||||
case (TypeInfo(IntervalClass, _), json) if !json.isInstanceOf[JString] =>
|
||||
compactRender(json)
|
||||
}
|
||||
|
||||
override def serialize(implicit format: Formats): PartialFunction[Any, JValue] = Functions.doNothing
|
||||
}
|
||||
|
||||
object BigDecimalSerializer extends Serializer[BigDecimal] {
|
||||
private val IntervalClass = classOf[BigDecimal]
|
||||
|
||||
override def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), BigDecimal] = {
|
||||
case (TypeInfo(IntervalClass, _), json) => json match {
|
||||
case JString(s) => BigDecimal(s)
|
||||
case x => throw new MappingException("Can't convert " + x + " to BigDecimal")
|
||||
}
|
||||
}
|
||||
|
||||
override def serialize(implicit format: Formats): PartialFunction[Any, JValue] = {
|
||||
case x: BigDecimal => JString(x.toString())
|
||||
}
|
||||
}
|
||||
|
||||
object FiledRenameSerializer extends Serializer[JsonFieldReName] {
|
||||
private val clazz = classOf[JsonFieldReName]
|
||||
|
||||
def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), JsonFieldReName] = {
|
||||
case (typeInfo @ TypeInfo(entityType, _), json) if(isNeedRenameFieldNames(entityType, json))=> json match {
|
||||
case JObject(fieldList) => {
|
||||
val renamedJObject = APIUtil.camelifyMethod(json)
|
||||
|
||||
val optionalFields = getAnnotedFields(entityType, ru.typeOf[optional])
|
||||
.map{
|
||||
case (name, tp) if(tp <:< ru.typeOf[Long] || tp <:< ru.typeOf[Int] || tp <:< ru.typeOf[Short] || tp <:< ru.typeOf[Byte] || tp <:< ru.typeOf[Int]) => (name, JInt(0))
|
||||
case (name, tp) if(tp <:< ru.typeOf[Double] || tp <:< ru.typeOf[Float]) => (name, JDouble(0))
|
||||
case (name, tp) if(tp <:< ru.typeOf[Boolean]) => (name, JBool(false))
|
||||
case (name, tp) => (name, JNull)
|
||||
}
|
||||
|
||||
val addedNullValues: JValue = if(optionalFields.isEmpty) {
|
||||
renamedJObject
|
||||
} else {
|
||||
val children = renamedJObject.asInstanceOf[JObject].obj
|
||||
val childrenNames = children.map(_.name)
|
||||
val nullFields = optionalFields.filter(pair => !children.contains(pair._1)).map(pair => JField(pair._1, pair._2)).toList
|
||||
val jObject: JValue = JObject(children ++: nullFields)
|
||||
jObject
|
||||
}
|
||||
|
||||
val idFieldToIdValueName: Map[String, String] = getSomeIdFieldInfo(entityType)
|
||||
val processedIdJObject = if(idFieldToIdValueName.isEmpty){
|
||||
addedNullValues
|
||||
} else {
|
||||
addedNullValues.mapField {
|
||||
case JField(name, jValue) if idFieldToIdValueName.contains(name) => JField(name, JObject(JField(idFieldToIdValueName(name), jValue)))
|
||||
case jField => jField
|
||||
}
|
||||
}
|
||||
Extraction.extract(processedIdJObject,typeInfo).asInstanceOf[JsonFieldReName]
|
||||
}
|
||||
case x => throw new MappingException("Can't convert " + x + " to JsonFieldReName")
|
||||
}
|
||||
}
|
||||
|
||||
def serialize(implicit format: Formats): PartialFunction[Any, JValue] = {
|
||||
case x: JsonFieldReName => {
|
||||
val ignoreFieldNames = getObjAnnotedFields(x, ru.typeOf[ignore])
|
||||
val renamedJFields = ReflectUtils.getConstructorArgs(x)
|
||||
.filter(pair => !ignoreFieldNames.contains(pair._1))
|
||||
.map(pair => {
|
||||
val paramName = StringHelpers.snakify(pair._1)
|
||||
val paramValue = pair._2
|
||||
isSomeId(paramValue) match {
|
||||
case false => JField(paramName, Extraction.decompose(paramValue))
|
||||
case true => {
|
||||
val idValue = ReflectUtils.getConstructorArgs(paramValue).head._2
|
||||
JField(paramName, Extraction.decompose(idValue))
|
||||
}
|
||||
}
|
||||
}) .toList
|
||||
JObject(renamedJFields)
|
||||
}
|
||||
}
|
||||
|
||||
private[this] def isNeedRenameFieldNames(entityType: Class[_], jValue: JValue): Boolean = {
|
||||
val isJsonFieldRename = clazz.isAssignableFrom(entityType)
|
||||
|
||||
isJsonFieldRename &&
|
||||
jValue.isInstanceOf[JObject] &&
|
||||
jValue.asInstanceOf[JObject].obj.exists(jField => StringHelpers.camelifyMethod(jField.name) != jField.name)
|
||||
}
|
||||
|
||||
// check given object is some Id, only type name ends with "Id" and have a single param constructor
|
||||
private def isSomeId(obj: Any) = obj match {
|
||||
case null => false
|
||||
case _ => obj.getClass.getSimpleName.endsWith("Id") && ReflectUtils.getPrimaryConstructor(obj).asMethod.paramLists.headOption.exists(_.size == 1)
|
||||
}
|
||||
private def isSomeIdType(tp: ru.Type) = tp.typeSymbol.name.toString.endsWith("Id") && ReflectUtils.getConstructorParamInfo(tp).size == 1
|
||||
|
||||
/**
|
||||
* extract constructor params those type is some id, and return the field name to the id constructor value name
|
||||
* for example:
|
||||
* case class Foo(name: String, bankId: BankId(value:String))
|
||||
* getSomeIdFieldInfo(typeOf[Foo]) == Map(("bankId" -> "value"))
|
||||
* @param clazz to do extract class
|
||||
* @return field name to id type single value name
|
||||
*/
|
||||
private def getSomeIdFieldInfo(clazz: Class[_]) = {
|
||||
val paramNameToType: Map[String, ru.Type] = ReflectUtils.getConstructorInfo(clazz)
|
||||
paramNameToType
|
||||
.filter(nameToType => isSomeIdType(nameToType._2))
|
||||
.map(nameToType => {
|
||||
val (name, paramType) = nameToType
|
||||
val singleParamName = ReflectUtils.getConstructorParamInfo(paramType).head._1
|
||||
(name, singleParamName)
|
||||
}
|
||||
)
|
||||
}
|
||||
private def getAnnotedFields(clazz: Class[_], annotationType: ru.Type): Map[String, ru.Type] = {
|
||||
val symbol = ReflectUtils.classToSymbol(clazz)
|
||||
ReflectUtils.getPrimaryConstructor(symbol.toType)
|
||||
.paramLists.headOption.getOrElse(Nil)
|
||||
.filter(param => param.annotations.exists(_.tree.tpe <:< annotationType))
|
||||
.map(it => (it.name.toString, it.info))
|
||||
.toMap
|
||||
}
|
||||
private def getObjAnnotedFields(obj: Any, annotationType: ru.Type): Map[String, ru.Type] = getAnnotedFields(obj.getClass, annotationType)
|
||||
} + ListResultSerializer ++ JsonSerializers.serializers
|
||||
}
|
||||
|
||||
object ListResultSerializer extends Serializer[ListResult[_]] {
|
||||
@ -321,12 +186,3 @@ object JNothingSerializer extends Serializer[Any] with MdcLoggable {
|
||||
|
||||
|
||||
|
||||
@scala.annotation.meta.field
|
||||
@scala.annotation.meta.param
|
||||
class ignore extends scala.annotation.StaticAnnotation
|
||||
|
||||
@scala.annotation.meta.field
|
||||
@scala.annotation.meta.param
|
||||
class optional extends scala.annotation.StaticAnnotation
|
||||
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ import java.util.Date
|
||||
import akka.pattern.ask
|
||||
import code.actorsystem.ObpLookupSystem
|
||||
import code.api.ResourceDocs1_4_0.MessageDocsSwaggerDefinitions
|
||||
import code.api.ResourceDocs1_4_0.MessageDocsSwaggerDefinitions.{bankAccountCommons, bankCommons, transactionCommons, _}
|
||||
import code.api.ResourceDocs1_4_0.MessageDocsSwaggerDefinitions.{bankAccountCommons, bankCommons, transaction, _}
|
||||
import code.api.util.APIUtil.{AdapterImplementation, MessageDoc, OBPReturnType, parseDate}
|
||||
import code.api.util.ErrorMessages.{AdapterFunctionNotImplemented, AdapterUnknownError}
|
||||
import code.api.util.ExampleValue._
|
||||
@ -287,7 +287,7 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit {
|
||||
InBoundGetTransactions(
|
||||
inboundAdapterCallContext,
|
||||
inboundStatus,
|
||||
List(transactionCommons)
|
||||
List(transaction)
|
||||
)
|
||||
),
|
||||
adapterImplementation = Some(AdapterImplementation("Transactions", 10))
|
||||
@ -321,7 +321,7 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit {
|
||||
InBoundGetTransaction(
|
||||
inboundAdapterCallContext,
|
||||
inboundStatus,
|
||||
transactionCommons
|
||||
transaction
|
||||
)
|
||||
),
|
||||
adapterImplementation = Some(AdapterImplementation("Transactions", 11))
|
||||
|
||||
@ -10,7 +10,7 @@ import code.bankconnectors.LocalMappedConnector._
|
||||
import code.model.dataAccess.MappedBank
|
||||
import code.util.Helper.MdcLoggable
|
||||
import com.openbankproject.commons.dto._
|
||||
import com.openbankproject.commons.model.{CreditLimit, _}
|
||||
import com.openbankproject.commons.model.{CreditLimit, Transaction, _}
|
||||
import net.liftweb.common.Box
|
||||
|
||||
import scala.collection.immutable.List
|
||||
@ -154,8 +154,8 @@ object Transformer {
|
||||
}
|
||||
|
||||
|
||||
def toInternalTransaction(t: Transaction): TransactionCommons = {
|
||||
TransactionCommons(
|
||||
def toInternalTransaction(t: Transaction): Transaction = {
|
||||
Transaction(
|
||||
uuid = t.uuid ,
|
||||
id = t.id ,
|
||||
thisAccount = BankAccountCommons(
|
||||
|
||||
@ -2218,7 +2218,7 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable
|
||||
status=inboundStatusMessageStatusExample.value,
|
||||
errorCode=inboundStatusMessageErrorCodeExample.value,
|
||||
text=inboundStatusMessageTextExample.value))),
|
||||
data=List( TransactionCommons(uuid=transactionUuidExample.value,
|
||||
data=List( Transaction(uuid=transactionUuidExample.value,
|
||||
id=TransactionId(transactionIdExample.value),
|
||||
thisAccount= BankAccountCommons(accountId=AccountId(accountIdExample.value),
|
||||
accountType=accountTypeExample.value,
|
||||
@ -2265,7 +2265,7 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable
|
||||
import com.openbankproject.commons.dto.{OutBoundGetTransactionsLegacy => OutBound, InBoundGetTransactionsLegacy => InBound}
|
||||
val url = getUrl(callContext, "getTransactionsLegacy")
|
||||
val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull , bankId, accountID, OBPQueryParam.getLimit(queryParams), OBPQueryParam.getOffset(queryParams), OBPQueryParam.getFromDate(queryParams), OBPQueryParam.getToDate(queryParams))
|
||||
val result: OBPReturnType[Box[List[TransactionCommons]]] = sendRequest[InBound](url, HttpMethods.POST, req, callContext).map(convertToTuple(callContext))
|
||||
val result: OBPReturnType[Box[List[Transaction]]] = sendRequest[InBound](url, HttpMethods.POST, req, callContext).map(convertToTuple(callContext))
|
||||
result
|
||||
}
|
||||
|
||||
@ -2320,7 +2320,7 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable
|
||||
status=inboundStatusMessageStatusExample.value,
|
||||
errorCode=inboundStatusMessageErrorCodeExample.value,
|
||||
text=inboundStatusMessageTextExample.value))),
|
||||
data=List( TransactionCommons(uuid=transactionUuidExample.value,
|
||||
data=List( Transaction(uuid=transactionUuidExample.value,
|
||||
id=TransactionId(transactionIdExample.value),
|
||||
thisAccount= BankAccountCommons(accountId=AccountId(accountIdExample.value),
|
||||
accountType=accountTypeExample.value,
|
||||
@ -2367,7 +2367,7 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable
|
||||
import com.openbankproject.commons.dto.{OutBoundGetTransactions => OutBound, InBoundGetTransactions => InBound}
|
||||
val url = getUrl(callContext, "getTransactions")
|
||||
val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull , bankId, accountID, OBPQueryParam.getLimit(queryParams), OBPQueryParam.getOffset(queryParams), OBPQueryParam.getFromDate(queryParams), OBPQueryParam.getToDate(queryParams))
|
||||
val result: OBPReturnType[Box[List[TransactionCommons]]] = sendRequest[InBound](url, HttpMethods.POST, req, callContext).map(convertToTuple(callContext))
|
||||
val result: OBPReturnType[Box[List[Transaction]]] = sendRequest[InBound](url, HttpMethods.POST, req, callContext).map(convertToTuple(callContext))
|
||||
result
|
||||
}
|
||||
|
||||
@ -2519,7 +2519,7 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable
|
||||
status=inboundStatusMessageStatusExample.value,
|
||||
errorCode=inboundStatusMessageErrorCodeExample.value,
|
||||
text=inboundStatusMessageTextExample.value))),
|
||||
data= TransactionCommons(uuid=transactionUuidExample.value,
|
||||
data= Transaction(uuid=transactionUuidExample.value,
|
||||
id=TransactionId(transactionIdExample.value),
|
||||
thisAccount= BankAccountCommons(accountId=AccountId(accountIdExample.value),
|
||||
accountType=accountTypeExample.value,
|
||||
@ -2566,7 +2566,7 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable
|
||||
import com.openbankproject.commons.dto.{OutBoundGetTransactionLegacy => OutBound, InBoundGetTransactionLegacy => InBound}
|
||||
val url = getUrl(callContext, "getTransactionLegacy")
|
||||
val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull , bankId, accountID, transactionId)
|
||||
val result: OBPReturnType[Box[TransactionCommons]] = sendRequest[InBound](url, HttpMethods.POST, req, callContext).map(convertToTuple(callContext))
|
||||
val result: OBPReturnType[Box[Transaction]] = sendRequest[InBound](url, HttpMethods.POST, req, callContext).map(convertToTuple(callContext))
|
||||
result
|
||||
}
|
||||
|
||||
@ -2618,7 +2618,7 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable
|
||||
status=inboundStatusMessageStatusExample.value,
|
||||
errorCode=inboundStatusMessageErrorCodeExample.value,
|
||||
text=inboundStatusMessageTextExample.value))),
|
||||
data= TransactionCommons(uuid=transactionUuidExample.value,
|
||||
data= Transaction(uuid=transactionUuidExample.value,
|
||||
id=TransactionId(transactionIdExample.value),
|
||||
thisAccount= BankAccountCommons(accountId=AccountId(accountIdExample.value),
|
||||
accountType=accountTypeExample.value,
|
||||
@ -2665,7 +2665,7 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable
|
||||
import com.openbankproject.commons.dto.{OutBoundGetTransaction => OutBound, InBoundGetTransaction => InBound}
|
||||
val url = getUrl(callContext, "getTransaction")
|
||||
val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull , bankId, accountID, transactionId)
|
||||
val result: OBPReturnType[Box[TransactionCommons]] = sendRequest[InBound](url, HttpMethods.POST, req, callContext).map(convertToTuple(callContext))
|
||||
val result: OBPReturnType[Box[Transaction]] = sendRequest[InBound](url, HttpMethods.POST, req, callContext).map(convertToTuple(callContext))
|
||||
result
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
-- auto generated MS sql server procedures script, create on 2020-06-26T23:21:46Z
|
||||
-- auto generated MS sql server procedures script, create on 2020-06-29T16:54:14Z
|
||||
|
||||
-- drop procedure obp_get_adapter_info
|
||||
DROP PROCEDURE IF EXISTS obp_get_adapter_info;
|
||||
@ -3240,6 +3240,10 @@ this is example of parameter @outbound_json
|
||||
},
|
||||
"data":[
|
||||
{
|
||||
"uuid":"Transaction uuid string",
|
||||
"id":{
|
||||
"value":"2fg8a7e4-6d02-40e3-a129-0b2bf89de8ub"
|
||||
},
|
||||
"thisAccount":{
|
||||
"accountId":{
|
||||
"value":"8ca8a7e4-6d02-40e3-a129-0b2bf89de9f0"
|
||||
@ -3272,8 +3276,6 @@ this is example of parameter @outbound_json
|
||||
],
|
||||
"accountHolder":"bankAccount accountHolder string"
|
||||
},
|
||||
"description":"For the piano lesson in June 2018 - Invoice No: 68",
|
||||
"uuid":"Transaction uuid string",
|
||||
"otherAccount":{
|
||||
"nationalIdentifier":"Counterparty nationalIdentifier string",
|
||||
"kind":"Counterparty kind string",
|
||||
@ -3293,14 +3295,12 @@ this is example of parameter @outbound_json
|
||||
"isBeneficiary":true
|
||||
},
|
||||
"transactionType":"DEBIT",
|
||||
"balance":"50.89",
|
||||
"amount":"19.64",
|
||||
"id":{
|
||||
"value":"2fg8a7e4-6d02-40e3-a129-0b2bf89de8ub"
|
||||
},
|
||||
"currency":"EUR",
|
||||
"description":"For the piano lesson in June 2018 - Invoice No: 68",
|
||||
"startDate":"2019-09-06T16:00:00Z",
|
||||
"finishDate":"2019-09-07T16:00:00Z",
|
||||
"startDate":"2019-09-06T16:00:00Z"
|
||||
"balance":"50.89"
|
||||
}
|
||||
]
|
||||
}'
|
||||
@ -3610,6 +3610,10 @@ this is example of parameter @outbound_json
|
||||
]
|
||||
},
|
||||
"data":{
|
||||
"uuid":"Transaction uuid string",
|
||||
"id":{
|
||||
"value":"2fg8a7e4-6d02-40e3-a129-0b2bf89de8ub"
|
||||
},
|
||||
"thisAccount":{
|
||||
"accountId":{
|
||||
"value":"8ca8a7e4-6d02-40e3-a129-0b2bf89de9f0"
|
||||
@ -3642,8 +3646,6 @@ this is example of parameter @outbound_json
|
||||
],
|
||||
"accountHolder":"bankAccount accountHolder string"
|
||||
},
|
||||
"description":"For the piano lesson in June 2018 - Invoice No: 68",
|
||||
"uuid":"Transaction uuid string",
|
||||
"otherAccount":{
|
||||
"nationalIdentifier":"Counterparty nationalIdentifier string",
|
||||
"kind":"Counterparty kind string",
|
||||
@ -3663,14 +3665,12 @@ this is example of parameter @outbound_json
|
||||
"isBeneficiary":true
|
||||
},
|
||||
"transactionType":"DEBIT",
|
||||
"balance":"50.89",
|
||||
"amount":"19.64",
|
||||
"id":{
|
||||
"value":"2fg8a7e4-6d02-40e3-a129-0b2bf89de8ub"
|
||||
},
|
||||
"currency":"EUR",
|
||||
"description":"For the piano lesson in June 2018 - Invoice No: 68",
|
||||
"startDate":"2019-09-06T16:00:00Z",
|
||||
"finishDate":"2019-09-07T16:00:00Z",
|
||||
"startDate":"2019-09-06T16:00:00Z"
|
||||
"balance":"50.89"
|
||||
}
|
||||
}'
|
||||
);
|
||||
@ -3806,7 +3806,7 @@ this is example of parameter @outbound_json
|
||||
"string"
|
||||
],
|
||||
"allows":[
|
||||
{}
|
||||
"DEBIT"
|
||||
],
|
||||
"account":{
|
||||
"accountId":{
|
||||
@ -3842,12 +3842,12 @@ this is example of parameter @outbound_json
|
||||
},
|
||||
"replacement":{
|
||||
"requestedDate":"2020-01-26T16:00:00Z",
|
||||
"reasonRequested":{}
|
||||
"reasonRequested":"FIRST"
|
||||
},
|
||||
"pinResets":[
|
||||
{
|
||||
"requestedDate":"2020-01-26T16:00:00Z",
|
||||
"reasonRequested":{}
|
||||
"reasonRequested":"FORGOT"
|
||||
}
|
||||
],
|
||||
"collected":{
|
||||
@ -4133,7 +4133,7 @@ this is example of parameter @outbound_json
|
||||
"string"
|
||||
],
|
||||
"allows":[
|
||||
{}
|
||||
"DEBIT"
|
||||
],
|
||||
"account":{
|
||||
"accountId":{
|
||||
@ -4169,12 +4169,12 @@ this is example of parameter @outbound_json
|
||||
},
|
||||
"replacement":{
|
||||
"requestedDate":"2020-01-26T16:00:00Z",
|
||||
"reasonRequested":{}
|
||||
"reasonRequested":"FIRST"
|
||||
},
|
||||
"pinResets":[
|
||||
{
|
||||
"requestedDate":"2020-01-26T16:00:00Z",
|
||||
"reasonRequested":{}
|
||||
"reasonRequested":"FORGOT"
|
||||
}
|
||||
],
|
||||
"collected":{
|
||||
@ -4291,12 +4291,12 @@ this is example of parameter @outbound_json
|
||||
"bankId":"gh.29.uk",
|
||||
"replacement":{
|
||||
"requestedDate":"2020-01-26T16:00:00Z",
|
||||
"reasonRequested":{}
|
||||
"reasonRequested":"FIRST"
|
||||
},
|
||||
"pinResets":[
|
||||
{
|
||||
"requestedDate":"2020-01-26T16:00:00Z",
|
||||
"reasonRequested":{}
|
||||
"reasonRequested":"FORGOT"
|
||||
}
|
||||
],
|
||||
"collected":{
|
||||
@ -4352,7 +4352,7 @@ this is example of parameter @outbound_json
|
||||
"string"
|
||||
],
|
||||
"allows":[
|
||||
{}
|
||||
"DEBIT"
|
||||
],
|
||||
"account":{
|
||||
"accountId":{
|
||||
@ -4388,12 +4388,12 @@ this is example of parameter @outbound_json
|
||||
},
|
||||
"replacement":{
|
||||
"requestedDate":"2020-01-26T16:00:00Z",
|
||||
"reasonRequested":{}
|
||||
"reasonRequested":"FIRST"
|
||||
},
|
||||
"pinResets":[
|
||||
{
|
||||
"requestedDate":"2020-01-26T16:00:00Z",
|
||||
"reasonRequested":{}
|
||||
"reasonRequested":"FORGOT"
|
||||
}
|
||||
],
|
||||
"collected":{
|
||||
@ -4510,12 +4510,12 @@ this is example of parameter @outbound_json
|
||||
"bankId":"gh.29.uk",
|
||||
"replacement":{
|
||||
"requestedDate":"2020-01-26T16:00:00Z",
|
||||
"reasonRequested":{}
|
||||
"reasonRequested":"FIRST"
|
||||
},
|
||||
"pinResets":[
|
||||
{
|
||||
"requestedDate":"2020-01-26T16:00:00Z",
|
||||
"reasonRequested":{}
|
||||
"reasonRequested":"FORGOT"
|
||||
}
|
||||
],
|
||||
"collected":{
|
||||
@ -4571,7 +4571,7 @@ this is example of parameter @outbound_json
|
||||
"string"
|
||||
],
|
||||
"allows":[
|
||||
{}
|
||||
"DEBIT"
|
||||
],
|
||||
"account":{
|
||||
"accountId":{
|
||||
@ -4607,12 +4607,12 @@ this is example of parameter @outbound_json
|
||||
},
|
||||
"replacement":{
|
||||
"requestedDate":"2020-01-26T16:00:00Z",
|
||||
"reasonRequested":{}
|
||||
"reasonRequested":"FIRST"
|
||||
},
|
||||
"pinResets":[
|
||||
{
|
||||
"requestedDate":"2020-01-26T16:00:00Z",
|
||||
"reasonRequested":{}
|
||||
"reasonRequested":"FORGOT"
|
||||
}
|
||||
],
|
||||
"collected":{
|
||||
|
||||
@ -73,7 +73,7 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable {
|
||||
val connectorName = "stored_procedure_vDec2019"
|
||||
|
||||
//---------------- dynamic start -------------------please don't modify this line
|
||||
// ---------- created on 2020-06-26T23:17:54Z
|
||||
// ---------- created on 2020-06-29T16:53:12Z
|
||||
|
||||
messageDocs += getAdapterInfoDoc
|
||||
def getAdapterInfoDoc = MessageDoc(
|
||||
@ -989,7 +989,7 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable {
|
||||
exampleInboundMessage = (
|
||||
InBoundGetTransactions(inboundAdapterCallContext=MessageDocsSwaggerDefinitions.inboundAdapterCallContext,
|
||||
status=MessageDocsSwaggerDefinitions.inboundStatus,
|
||||
data=List( TransactionCommons(uuid=transactionUuidExample.value,
|
||||
data=List( Transaction(uuid=transactionUuidExample.value,
|
||||
id=TransactionId(transactionIdExample.value),
|
||||
thisAccount= BankAccountCommons(accountId=AccountId(accountIdExample.value),
|
||||
accountType=accountTypeExample.value,
|
||||
@ -1036,7 +1036,7 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable {
|
||||
import com.openbankproject.commons.dto.{OutBoundGetTransactions => OutBound, InBoundGetTransactions => InBound}
|
||||
val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, bankId, accountID, OBPQueryParam.getLimit(queryParams), OBPQueryParam.getOffset(queryParams), OBPQueryParam.getFromDate(queryParams), OBPQueryParam.getToDate(queryParams))
|
||||
val response: Future[Box[InBound]] = sendRequest[InBound]("obp_get_transactions", req, callContext)
|
||||
response.map(convertToTuple[List[TransactionCommons]](callContext))
|
||||
response.map(convertToTuple[List[Transaction]](callContext))
|
||||
}
|
||||
|
||||
messageDocs += getTransactionsCoreDoc
|
||||
@ -1122,7 +1122,7 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable {
|
||||
exampleInboundMessage = (
|
||||
InBoundGetTransaction(inboundAdapterCallContext=MessageDocsSwaggerDefinitions.inboundAdapterCallContext,
|
||||
status=MessageDocsSwaggerDefinitions.inboundStatus,
|
||||
data= TransactionCommons(uuid=transactionUuidExample.value,
|
||||
data= Transaction(uuid=transactionUuidExample.value,
|
||||
id=TransactionId(transactionIdExample.value),
|
||||
thisAccount= BankAccountCommons(accountId=AccountId(accountIdExample.value),
|
||||
accountType=accountTypeExample.value,
|
||||
@ -1169,7 +1169,7 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable {
|
||||
import com.openbankproject.commons.dto.{OutBoundGetTransaction => OutBound, InBoundGetTransaction => InBound}
|
||||
val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, bankId, accountID, transactionId)
|
||||
val response: Future[Box[InBound]] = sendRequest[InBound]("obp_get_transaction", req, callContext)
|
||||
response.map(convertToTuple[TransactionCommons](callContext))
|
||||
response.map(convertToTuple[Transaction](callContext))
|
||||
}
|
||||
|
||||
messageDocs += getPhysicalCardForBankDoc
|
||||
@ -5648,8 +5648,8 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable {
|
||||
response.map(convertToTuple[Boolean](callContext))
|
||||
}
|
||||
|
||||
// ---------- created on 2020-06-26T23:17:54Z
|
||||
//---------------- dynamic end ---------------------please don't modify this line
|
||||
// ---------- created on 2020-06-29T16:53:12Z
|
||||
//---------------- dynamic end ---------------------please don't modify this line
|
||||
|
||||
private val availableOperation = DynamicEntityOperation.values.map(it => s""""$it"""").mkString("[", ", ", "]")
|
||||
|
||||
|
||||
@ -2,8 +2,8 @@ package code.management
|
||||
|
||||
import java.util.Date
|
||||
|
||||
import code.api.util.{APIUtil, BigDecimalSerializer, CustomJsonFormats}
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.api.util.{APIUtil, CustomJsonFormats}
|
||||
import code.bankconnectors.Connector
|
||||
import code.tesobe.ErrorMessage
|
||||
import code.util.Helper.MdcLoggable
|
||||
@ -12,8 +12,8 @@ import net.liftweb.common.Full
|
||||
import net.liftweb.http._
|
||||
import net.liftweb.http.js.JsExp
|
||||
import net.liftweb.http.rest.RestHelper
|
||||
import net.liftweb.json.Extraction
|
||||
import net.liftweb.json.JsonAST.{JArray, JField, JObject, JString}
|
||||
import net.liftweb.json.{Extraction}
|
||||
import net.liftweb.util.Helpers._
|
||||
|
||||
/**
|
||||
|
||||
@ -26,6 +26,10 @@
|
||||
<groupId>net.liftweb</groupId>
|
||||
<artifactId>lift-common_${scala.version}</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.liftweb</groupId>
|
||||
<artifactId>lift-util_${scala.version}</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.scala-lang</groupId>
|
||||
<artifactId>scala-reflect</artifactId>
|
||||
|
||||
@ -134,14 +134,14 @@ case class OutBoundGetTransactions(outboundAdapterCallContext: OutboundAdapterCa
|
||||
offset: Int,
|
||||
fromDate: String,
|
||||
toDate: String) extends TopicTrait
|
||||
case class InBoundGetTransactions(inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: List[TransactionCommons]) extends InBoundTrait[List[TransactionCommons]]
|
||||
case class InBoundGetTransactions(inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: List[Transaction]) extends InBoundTrait[List[Transaction]]
|
||||
|
||||
|
||||
case class OutBoundGetTransaction(outboundAdapterCallContext: OutboundAdapterCallContext,
|
||||
bankId: BankId,
|
||||
accountId: AccountId,
|
||||
transactionId: TransactionId) extends TopicTrait
|
||||
case class InBoundGetTransaction(inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: TransactionCommons) extends InBoundTrait[TransactionCommons]
|
||||
case class InBoundGetTransaction(inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: Transaction) extends InBoundTrait[Transaction]
|
||||
|
||||
case class OutBoundMakePaymentv210(outboundAdapterCallContext: OutboundAdapterCallContext,
|
||||
fromAccount: BankAccountCommons,
|
||||
@ -880,14 +880,14 @@ case class OutBoundGetTransactionsLegacy (outboundAdapterCallContext: OutboundAd
|
||||
offset: Int,
|
||||
fromDate: String,
|
||||
toDate: String) extends TopicTrait
|
||||
case class InBoundGetTransactionsLegacy (inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: List[TransactionCommons]) extends InBoundTrait[List[TransactionCommons]]
|
||||
case class InBoundGetTransactionsLegacy (inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: List[Transaction]) extends InBoundTrait[List[Transaction]]
|
||||
|
||||
|
||||
case class OutBoundGetTransactionLegacy (outboundAdapterCallContext: OutboundAdapterCallContext,
|
||||
bankId: BankId,
|
||||
accountID: AccountId,
|
||||
transactionId: TransactionId) extends TopicTrait
|
||||
case class InBoundGetTransactionLegacy (inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: TransactionCommons) extends InBoundTrait[TransactionCommons]
|
||||
case class InBoundGetTransactionLegacy (inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: Transaction) extends InBoundTrait[Transaction]
|
||||
|
||||
|
||||
case class OutBoundCreatePhysicalCardLegacy (outboundAdapterCallContext: OutboundAdapterCallContext,
|
||||
|
||||
@ -665,7 +665,7 @@ case class TransactionRequestBody (
|
||||
val description : String
|
||||
)
|
||||
|
||||
class Transaction(
|
||||
case class Transaction(
|
||||
//A universally unique id
|
||||
val uuid: String,
|
||||
//id is unique for transactions of @thisAccount
|
||||
@ -693,40 +693,6 @@ class Transaction(
|
||||
|
||||
case class UserCommons(userPrimaryKey : UserPrimaryKey, userId: String,idGivenByProvider: String, provider : String, emailAddress : String, name : String) extends User
|
||||
|
||||
// because Transaction#thisAccount is trait, can't be deserialize, So here supply a case class to do deserialize
|
||||
case class TransactionCommons(
|
||||
//A universally unique id
|
||||
override val uuid: String,
|
||||
override val id : TransactionId,
|
||||
override val thisAccount : BankAccountCommons,
|
||||
override val otherAccount : Counterparty,
|
||||
override val transactionType : String,
|
||||
override val amount : BigDecimal,
|
||||
override val currency : String,
|
||||
override val description : Option[String],
|
||||
override val startDate : Date,
|
||||
override val finishDate : Date,
|
||||
override val balance : BigDecimal
|
||||
) extends Transaction(uuid, id, thisAccount, otherAccount, transactionType, amount, currency,description, startDate, finishDate, balance) with JsonAble {
|
||||
// if constructor override val value pass to parent class constructor, lift json will not work to do serialize, so here manually do serialize.
|
||||
override def toJValue(implicit format: Formats): json.JValue = {
|
||||
val map = Map(
|
||||
"uuid" -> uuid,
|
||||
"id" -> id,
|
||||
"thisAccount" -> thisAccount,
|
||||
"otherAccount" -> otherAccount,
|
||||
"transactionType" -> transactionType,
|
||||
"amount" -> amount,
|
||||
"currency" -> currency,
|
||||
"description" -> description,
|
||||
"startDate" -> startDate,
|
||||
"finishDate" -> finishDate,
|
||||
"balance" -> balance,
|
||||
)
|
||||
json.Extraction.decompose(map)
|
||||
}
|
||||
}
|
||||
|
||||
case class InternalBasicUser(
|
||||
userId:String,
|
||||
emailAddress: String,
|
||||
|
||||
@ -28,6 +28,10 @@ package com.openbankproject.commons.model
|
||||
|
||||
import java.util.Date
|
||||
|
||||
import com.openbankproject.commons.model.enums.{SimpleEnum, SimpleEnumCollection}
|
||||
|
||||
import scala.collection.immutable.ListMap
|
||||
|
||||
/**
|
||||
* Represents a physical card (credit, debit, etc.)
|
||||
*
|
||||
@ -84,64 +88,59 @@ case class PhysicalCard (
|
||||
) extends PhysicalCardTrait
|
||||
|
||||
|
||||
sealed trait CardAction
|
||||
sealed trait CardAction extends SimpleEnum
|
||||
|
||||
case object CardAction {
|
||||
case object CardAction extends SimpleEnumCollection[CardAction] {
|
||||
//TODO: are these good, or should they be changed (also, are there more actions to add?)
|
||||
case object CREDIT extends CardAction
|
||||
case object DEBIT extends CardAction
|
||||
case object CASH_WITHDRAWAL extends CardAction
|
||||
|
||||
def valueOf(value: String) = value match {
|
||||
case "credit" => CREDIT
|
||||
case "debit" => DEBIT
|
||||
case "cash_withdrawal" => CASH_WITHDRAWAL
|
||||
case _ => throw new IllegalArgumentException ("Incorrect CardAction value: " + value)
|
||||
}
|
||||
val availableValues = "credit" :: "debit" :: "cash_withdrawal" :: Nil
|
||||
override def nameToValue: Map[String, CardAction] = ListMap(
|
||||
"credit"-> CREDIT,
|
||||
"debit" -> DEBIT,
|
||||
"cash_withdrawal" -> CASH_WITHDRAWAL
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
sealed trait Network
|
||||
sealed trait Network extends SimpleEnum
|
||||
//TODO: what kind of networks are there?
|
||||
object Network extends SimpleEnumCollection[Network] {
|
||||
override def nameToValue: Map[String, Network] = ListMap()
|
||||
}
|
||||
|
||||
|
||||
case class CardReplacementInfo(requestedDate : Date, reasonRequested: CardReplacementReason)
|
||||
|
||||
sealed trait CardReplacementReason
|
||||
sealed trait CardReplacementReason extends SimpleEnum
|
||||
|
||||
case object CardReplacementReason {
|
||||
case object CardReplacementReason extends SimpleEnumCollection[CardReplacementReason] {
|
||||
case object LOST extends CardReplacementReason
|
||||
case object STOLEN extends CardReplacementReason
|
||||
case object RENEW extends CardReplacementReason
|
||||
case object FIRST extends CardReplacementReason
|
||||
|
||||
def valueOf(value: String) = value match {
|
||||
case "LOST" => LOST
|
||||
case "STOLEN" => STOLEN
|
||||
case "RENEW" => RENEW
|
||||
case "FIRST" => FIRST
|
||||
case _ => throw new IllegalArgumentException ("Incorrect CardReplacementReason value: " + value)
|
||||
}
|
||||
val availableValues = "LOST" :: "STOLEN" :: "RENEW" :: "FIRST" :: Nil
|
||||
override def nameToValue: Map[String, CardReplacementReason] = ListMap(
|
||||
"LOST" -> LOST,
|
||||
"STOLEN" -> STOLEN,
|
||||
"RENEW" -> RENEW,
|
||||
"FIRST" -> FIRST
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
case class PinResetInfo(requestedDate: Date, reasonRequested: PinResetReason)
|
||||
|
||||
sealed trait PinResetReason
|
||||
sealed trait PinResetReason extends SimpleEnum
|
||||
|
||||
case object PinResetReason {
|
||||
case object PinResetReason extends SimpleEnumCollection[PinResetReason]{
|
||||
case object FORGOT extends PinResetReason
|
||||
case object GOOD_SECURITY_PRACTICE extends PinResetReason
|
||||
|
||||
def valueOf(value: String) = value match {
|
||||
case "FORGOT" => FORGOT
|
||||
case "GOOD_SECURITY_PRACTICE" => GOOD_SECURITY_PRACTICE
|
||||
case _ => throw new IllegalArgumentException ("Incorrect PinResetReason value: " + value)
|
||||
}
|
||||
val availableValues = "FORGOT" :: "GOOD_SECURITY_PRACTICE" :: Nil
|
||||
override def nameToValue: Map[String, PinResetReason] = ListMap(
|
||||
"FORGOT" -> FORGOT,
|
||||
"GOOD_SECURITY_PRACTICE" -> GOOD_SECURITY_PRACTICE
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -2,10 +2,10 @@ package com.openbankproject.commons.model.enums
|
||||
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
import com.openbankproject.commons.util.{EnumValue, OBPEnumeration}
|
||||
import com.openbankproject.commons.util.{EnumValue, JsonAble, OBPEnumeration}
|
||||
import net.liftweb.common.Box
|
||||
import net.liftweb.json.JsonAST.JString
|
||||
import net.liftweb.json.{JBool, JDouble, JInt, JValue}
|
||||
import net.liftweb.json.{Formats, JBool, JDouble, JInt, JValue}
|
||||
|
||||
sealed trait AccountAttributeType extends EnumValue
|
||||
object AccountAttributeType extends OBPEnumeration[AccountAttributeType]{
|
||||
@ -135,4 +135,23 @@ object AttributeCategory extends OBPEnumeration[AttributeCategory]{
|
||||
object TransactionRequestStatus extends Enumeration {
|
||||
type TransactionRequestStatus = Value
|
||||
val INITIATED, PENDING, NEXT_CHALLENGE_PENDING, FAILED, COMPLETED, FORWARDED, REJECTED = Value
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-------------------simple enum definition, just some sealed trait way, start-------------
|
||||
trait SimpleEnum extends JsonAble {
|
||||
override def toJValue(implicit format: Formats): JValue = {
|
||||
val simpleName = this.getClass.getSimpleName.replaceFirst("\\$$", "")
|
||||
JString(simpleName)
|
||||
}
|
||||
}
|
||||
|
||||
trait SimpleEnumCollection[+T] {
|
||||
def nameToValue: Map[String, T]
|
||||
|
||||
def valueOf(value: String): T = nameToValue.get(value)
|
||||
.getOrElse(throw new IllegalArgumentException ("Incorrect CardAction value: " + value))
|
||||
|
||||
val availableValues = nameToValue.keys.toList
|
||||
}
|
||||
//-------------------simple enum definition, just some sealed trait way, end-------------
|
||||
@ -1,34 +0,0 @@
|
||||
package com.openbankproject.commons.util
|
||||
|
||||
import net.liftweb.json._
|
||||
|
||||
trait JsonAble {
|
||||
def toJValue(implicit format: Formats): JValue
|
||||
}
|
||||
object JsonAble {
|
||||
def unapply(jsonAble: JsonAble)(implicit format: Formats): Option[JValue] = Option(jsonAble).map(_.toJValue)
|
||||
}
|
||||
|
||||
object JsonAbleSerializer extends Serializer[JsonAble] {
|
||||
|
||||
override def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), JsonAble] = Functions.doNothing
|
||||
|
||||
override def serialize(implicit format: Formats): PartialFunction[Any, JValue] = {
|
||||
case JsonAble(jValue) => jValue
|
||||
}
|
||||
}
|
||||
|
||||
object EnumValueSerializer extends Serializer[EnumValue] {
|
||||
private val IntervalClass = classOf[EnumValue]
|
||||
|
||||
override def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), EnumValue] = {
|
||||
case (TypeInfo(clazz, _), json) if(IntervalClass.isAssignableFrom(clazz)) => json match {
|
||||
case JString(s) => OBPEnumeration.withName(clazz.asInstanceOf[Class[EnumValue]], s)
|
||||
case x => throw new MappingException(s"Can't convert $x to $clazz")
|
||||
}
|
||||
}
|
||||
|
||||
override def serialize(implicit format: Formats): PartialFunction[Any, JValue] = {
|
||||
case x: EnumValue => JString(x.toString())
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,230 @@
|
||||
package com.openbankproject.commons.util
|
||||
|
||||
import java.lang.reflect.Modifier
|
||||
|
||||
import com.openbankproject.commons.model.JsonFieldReName
|
||||
import com.openbankproject.commons.model.enums.{SimpleEnum, SimpleEnumCollection}
|
||||
import net.liftweb.json.JsonAST.JValue
|
||||
import net.liftweb.json._
|
||||
import net.liftweb.util.StringHelpers
|
||||
|
||||
import scala.reflect.ManifestFactory
|
||||
import scala.reflect.runtime.{universe => ru}
|
||||
|
||||
object JsonSerializers {
|
||||
val serializers = AbstractTypeDeserializer :: SimpleEnumDeserializer ::
|
||||
BigDecimalSerializer :: StringDeserializer :: FiledRenameSerializer ::
|
||||
EnumValueSerializer :: JsonAbleSerializer :: Nil
|
||||
|
||||
implicit val commonFormats = net.liftweb.json.DefaultFormats ++ serializers
|
||||
}
|
||||
|
||||
trait JsonAble {
|
||||
def toJValue(implicit format: Formats): JValue
|
||||
}
|
||||
object JsonAble {
|
||||
def unapply(jsonAble: JsonAble)(implicit format: Formats): Option[JValue] = Option(jsonAble).map(_.toJValue)
|
||||
}
|
||||
|
||||
object JsonAbleSerializer extends Serializer[JsonAble] {
|
||||
|
||||
override def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), JsonAble] = Functions.doNothing
|
||||
|
||||
override def serialize(implicit format: Formats): PartialFunction[Any, JValue] = {
|
||||
case JsonAble(jValue) => jValue
|
||||
}
|
||||
}
|
||||
|
||||
object EnumValueSerializer extends Serializer[EnumValue] {
|
||||
private val IntervalClass = classOf[EnumValue]
|
||||
|
||||
override def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), EnumValue] = {
|
||||
case (TypeInfo(clazz, _), json) if(IntervalClass.isAssignableFrom(clazz)) => json match {
|
||||
case JString(s) => OBPEnumeration.withName(clazz.asInstanceOf[Class[EnumValue]], s)
|
||||
case x => throw new MappingException(s"Can't convert $x to $clazz")
|
||||
}
|
||||
}
|
||||
|
||||
override def serialize(implicit format: Formats): PartialFunction[Any, JValue] = {
|
||||
case x: EnumValue => JString(x.toString())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* deSerialize trait or abstract type json, this Serializer should always put at formats chain first, e.g:
|
||||
* DefaultFormats + AbstractTypeDeserializer + ...others
|
||||
*/
|
||||
object AbstractTypeDeserializer extends Serializer[AnyRef] {
|
||||
|
||||
override def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), AnyRef] = {
|
||||
case (TypeInfo(clazz, _), json) if Modifier.isAbstract(clazz.getModifiers) && ReflectUtils.isObpClass(clazz) =>
|
||||
val commonClass = Class.forName(s"com.openbankproject.commons.model.${clazz.getSimpleName}Commons")
|
||||
implicit val manifest = ManifestFactory.classType[AnyRef](commonClass)
|
||||
json.extract[AnyRef](format, manifest)
|
||||
}
|
||||
|
||||
override def serialize(implicit format: Formats): PartialFunction[Any, JValue] = Functions.doNothing
|
||||
}
|
||||
|
||||
object SimpleEnumDeserializer extends Serializer[SimpleEnum] {
|
||||
private val simpleEnumClazz = classOf[SimpleEnum]
|
||||
override def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), SimpleEnum] = {
|
||||
case (TypeInfo(clazz, _), json) if simpleEnumClazz.isAssignableFrom(clazz) =>
|
||||
val JString(enumValue) = json.asInstanceOf[JString]
|
||||
|
||||
ReflectUtils.getObject(clazz.getName) // get Companion instance
|
||||
.asInstanceOf[SimpleEnumCollection[SimpleEnum]]
|
||||
.valueOf(enumValue)
|
||||
}
|
||||
|
||||
override def serialize(implicit format: Formats): PartialFunction[Any, JValue] = Functions.doNothing
|
||||
}
|
||||
|
||||
object BigDecimalSerializer extends Serializer[BigDecimal] {
|
||||
private val IntervalClass = classOf[BigDecimal]
|
||||
|
||||
override def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), BigDecimal] = {
|
||||
case (TypeInfo(IntervalClass, _), json) => json match {
|
||||
case JString(s) => BigDecimal(s)
|
||||
case x => throw new MappingException("Can't convert " + x + " to BigDecimal")
|
||||
}
|
||||
}
|
||||
|
||||
override def serialize(implicit format: Formats): PartialFunction[Any, JValue] = {
|
||||
case x: BigDecimal => JString(x.toString())
|
||||
}
|
||||
}
|
||||
|
||||
object StringDeserializer extends Serializer[String] {
|
||||
private val IntervalClass = classOf[String]
|
||||
|
||||
override def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), String] = {
|
||||
case (TypeInfo(IntervalClass, _), json) if !json.isInstanceOf[JString] =>
|
||||
compactRender(json)
|
||||
}
|
||||
|
||||
override def serialize(implicit format: Formats): PartialFunction[Any, JValue] = Functions.doNothing
|
||||
}
|
||||
|
||||
/**
|
||||
* when do serialize, fields name to snakify,
|
||||
* when do deserialize, fields name to camelify
|
||||
*/
|
||||
object FiledRenameSerializer extends Serializer[JsonFieldReName] {
|
||||
private val clazz = classOf[JsonFieldReName]
|
||||
|
||||
def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), JsonFieldReName] = {
|
||||
case (typeInfo @ TypeInfo(entityType, _), json) if(isNeedRenameFieldNames(entityType, json))=> json match {
|
||||
case JObject(fieldList) => {
|
||||
val renamedJObject = json.transformField {
|
||||
case JField(name, value) => JField(StringHelpers.camelifyMethod(name), value)
|
||||
}
|
||||
|
||||
val optionalFields = getAnnotedFields(entityType, ru.typeOf[optional])
|
||||
.map{
|
||||
case (name, tp) if(tp <:< ru.typeOf[Long] || tp <:< ru.typeOf[Int] || tp <:< ru.typeOf[Short] || tp <:< ru.typeOf[Byte] || tp <:< ru.typeOf[Int]) => (name, JInt(0))
|
||||
case (name, tp) if(tp <:< ru.typeOf[Double] || tp <:< ru.typeOf[Float]) => (name, JDouble(0))
|
||||
case (name, tp) if(tp <:< ru.typeOf[Boolean]) => (name, JBool(false))
|
||||
case (name, tp) => (name, JNull)
|
||||
}
|
||||
|
||||
val addedNullValues: JValue = if(optionalFields.isEmpty) {
|
||||
renamedJObject
|
||||
} else {
|
||||
val children = renamedJObject.asInstanceOf[JObject].obj
|
||||
val childrenNames = children.map(_.name)
|
||||
val nullFields = optionalFields.filter(pair => !children.contains(pair._1)).map(pair => JField(pair._1, pair._2)).toList
|
||||
val jObject: JValue = JObject(children ++: nullFields)
|
||||
jObject
|
||||
}
|
||||
|
||||
val idFieldToIdValueName: Map[String, String] = getSomeIdFieldInfo(entityType)
|
||||
val processedIdJObject = if(idFieldToIdValueName.isEmpty){
|
||||
addedNullValues
|
||||
} else {
|
||||
addedNullValues.mapField {
|
||||
case JField(name, jValue) if idFieldToIdValueName.contains(name) => JField(name, JObject(JField(idFieldToIdValueName(name), jValue)))
|
||||
case jField => jField
|
||||
}
|
||||
}
|
||||
Extraction.extract(processedIdJObject,typeInfo).asInstanceOf[JsonFieldReName]
|
||||
}
|
||||
case x => throw new MappingException("Can't convert " + x + " to JsonFieldReName")
|
||||
}
|
||||
}
|
||||
|
||||
def serialize(implicit format: Formats): PartialFunction[Any, JValue] = {
|
||||
case x: JsonFieldReName => {
|
||||
val ignoreFieldNames = getObjAnnotedFields(x, ru.typeOf[ignore])
|
||||
val renamedJFields = ReflectUtils.getConstructorArgs(x)
|
||||
.filter(pair => !ignoreFieldNames.contains(pair._1))
|
||||
.map(pair => {
|
||||
val paramName = StringHelpers.snakify(pair._1)
|
||||
val paramValue = pair._2
|
||||
isSomeId(paramValue) match {
|
||||
case false => JField(paramName, Extraction.decompose(paramValue))
|
||||
case true => {
|
||||
val idValue = ReflectUtils.getConstructorArgs(paramValue).head._2
|
||||
JField(paramName, Extraction.decompose(idValue))
|
||||
}
|
||||
}
|
||||
}) .toList
|
||||
JObject(renamedJFields)
|
||||
}
|
||||
}
|
||||
|
||||
private[this] def isNeedRenameFieldNames(entityType: Class[_], jValue: JValue): Boolean = {
|
||||
val isJsonFieldRename = clazz.isAssignableFrom(entityType)
|
||||
|
||||
isJsonFieldRename &&
|
||||
jValue.isInstanceOf[JObject] &&
|
||||
jValue.asInstanceOf[JObject].obj.exists(jField => StringHelpers.camelifyMethod(jField.name) != jField.name)
|
||||
}
|
||||
|
||||
// check given object is some Id, only type name ends with "Id" and have a single param constructor
|
||||
private def isSomeId(obj: Any) = obj match {
|
||||
case null => false
|
||||
case _ => obj.getClass.getSimpleName.endsWith("Id") && ReflectUtils.getPrimaryConstructor(obj).asMethod.paramLists.headOption.exists(_.size == 1)
|
||||
}
|
||||
private def isSomeIdType(tp: ru.Type) = tp.typeSymbol.name.toString.endsWith("Id") && ReflectUtils.getConstructorParamInfo(tp).size == 1
|
||||
|
||||
/**
|
||||
* extract constructor params those type is some id, and return the field name to the id constructor value name
|
||||
* for example:
|
||||
* case class Foo(name: String, bankId: BankId(value:String))
|
||||
* getSomeIdFieldInfo(typeOf[Foo]) == Map(("bankId" -> "value"))
|
||||
* @param clazz to do extract class
|
||||
* @return field name to id type single value name
|
||||
*/
|
||||
private def getSomeIdFieldInfo(clazz: Class[_]) = {
|
||||
val paramNameToType: Map[String, ru.Type] = ReflectUtils.getConstructorInfo(clazz)
|
||||
paramNameToType
|
||||
.filter(nameToType => isSomeIdType(nameToType._2))
|
||||
.map(nameToType => {
|
||||
val (name, paramType) = nameToType
|
||||
val singleParamName = ReflectUtils.getConstructorParamInfo(paramType).head._1
|
||||
(name, singleParamName)
|
||||
}
|
||||
)
|
||||
}
|
||||
private def getAnnotedFields(clazz: Class[_], annotationType: ru.Type): Map[String, ru.Type] = {
|
||||
val symbol = ReflectUtils.classToSymbol(clazz)
|
||||
ReflectUtils.getPrimaryConstructor(symbol.toType)
|
||||
.paramLists.headOption.getOrElse(Nil)
|
||||
.filter(param => param.annotations.exists(_.tree.tpe <:< annotationType))
|
||||
.map(it => (it.name.toString, it.info))
|
||||
.toMap
|
||||
}
|
||||
private def getObjAnnotedFields(obj: Any, annotationType: ru.Type): Map[String, ru.Type] = getAnnotedFields(obj.getClass, annotationType)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@scala.annotation.meta.field
|
||||
@scala.annotation.meta.param
|
||||
class ignore extends scala.annotation.StaticAnnotation
|
||||
|
||||
@scala.annotation.meta.field
|
||||
@scala.annotation.meta.param
|
||||
class optional extends scala.annotation.StaticAnnotation
|
||||
@ -23,6 +23,8 @@ object ReflectUtils {
|
||||
|
||||
def isObpType(tp: Type): Boolean = tp != null && tp.typeSymbol.isClass && OBP_TYPE_REGEX.findFirstIn(tp.typeSymbol.fullName).isDefined
|
||||
|
||||
def isObpClass(clazz: Class[_]): Boolean = clazz != null && OBP_TYPE_REGEX.findFirstIn(clazz.getName).isDefined
|
||||
|
||||
/**
|
||||
* get given instance FieldMirror, and operate it. this function is just for helper of getField and setField function
|
||||
* @param obj
|
||||
|
||||
5
pom.xml
5
pom.xml
@ -86,6 +86,11 @@
|
||||
<artifactId>lift-common_${scala.version}</artifactId>
|
||||
<version>${lift.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.liftweb</groupId>
|
||||
<artifactId>lift-util_${scala.version}</artifactId>
|
||||
<version>${lift.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-collections4</artifactId>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user