diff --git a/obp-api/pom.xml b/obp-api/pom.xml
index 97d22200c..d0c685d6d 100644
--- a/obp-api/pom.xml
+++ b/obp-api/pom.xml
@@ -8,7 +8,7 @@
com.tesobe
obp-parent
../pom.xml
- 1.5.2
+ 1.5.3
obp-api
war
diff --git a/obp-api/src/main/resources/props/sample.props.template b/obp-api/src/main/resources/props/sample.props.template
index dad3d07e8..b1b0c4079 100644
--- a/obp-api/src/main/resources/props/sample.props.template
+++ b/obp-api/src/main/resources/props/sample.props.template
@@ -828,4 +828,8 @@ dynamic_endpoints_url_prefix=dynamic
# Note: The user is also refreshed after every login.
# You can also explicitly refresh the user using the refresh user endpoint.
# refresh_user.interval=30
-# --------------------------------------------------------------------
\ No newline at end of file
+# --------------------------------------------------------------------
+
+## Inbound and Outbound ignore field names
+inbound.ignore.fields=
+outbound.ignore.fields=outboundAdapterCallContext.generalContext,outboundAdapterCallContext.outboundAdapterAuthInfo.linkedCustomers,outboundAdapterCallContext.outboundAdapterAuthInfo.authViews,outboundAdapterCallContext.outboundAdapterAuthInfo.authViews.view,outboundAdapterCallContext.outboundAdapterAuthInfo.authViews.account,outboundAdapterCallContext.outboundAdapterAuthInfo.authViews.view,outboundAdapterCallContext.outboundAdapterAuthInfo.authViews.account,inboundAdapterCallContext.generalContext
diff --git a/obp-api/src/main/scala/code/api/util/CustomJsonFormats.scala b/obp-api/src/main/scala/code/api/util/CustomJsonFormats.scala
index e0208332f..e4c6ae6b9 100644
--- a/obp-api/src/main/scala/code/api/util/CustomJsonFormats.scala
+++ b/obp-api/src/main/scala/code/api/util/CustomJsonFormats.scala
@@ -8,13 +8,20 @@ import code.api.cache.Caching
import code.api.util.ApiRole.rolesMappedToClasses
import code.api.v3_1_0.ListResult
import code.util.Helper.MdcLoggable
+import code.util.JsonUtils
+import com.openbankproject.commons.util.Functions.Memo
import com.openbankproject.commons.util._
import com.tesobe.CacheKeyFromArguments
+import net.liftweb.json
import net.liftweb.json.JsonAST.JValue
import net.liftweb.json.{TypeInfo, _}
+import org.apache.commons.lang3.StringUtils
+import scala.collection.immutable.List
import scala.concurrent.duration._
import scala.reflect.ManifestFactory
+import scala.reflect.runtime.universe
+import scala.reflect.runtime.universe._
trait CustomJsonFormats {
implicit val formats: Formats = CustomJsonFormats.formats
@@ -184,5 +191,58 @@ object JNothingSerializer extends Serializer[Any] with MdcLoggable {
}
}
+object FieldIgnoreSerializer extends Serializer[AnyRef] {
+ private val memo = new Memo[universe.Type, List[String]]()
+ override def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, json.JValue), AnyRef] = Functions.doNothing
+ private lazy val propsConfigIgnoreFields = Array(
+ APIUtil.getPropsValue("outbound.ignore.fields", "").split("""\s*,\s*"""),
+ APIUtil.getPropsValue("inbound.ignore.fields", "").split("""\s*,\s*""")
+ ).flatten.filterNot(StringUtils.isBlank).toList
+
+
+ override def serialize(implicit format: Formats): PartialFunction[Any, json.JValue] = {
+ case x if isInOutBoundType(x) =>
+ val ignoreFieldNames: List[String] = getIgnores(ReflectUtils.getType(x)) ::: propsConfigIgnoreFields
+ val zson = json.Extraction.decompose(x)(CustomJsonFormats.formats)
+ ignoreFieldNames match {
+ case Nil => zson
+ case ignores => JsonUtils.deleteFields(zson, ignores)
+ }
+ }
+
+ private def isInOutBoundType(any: Any) = {
+ if(ReflectUtils.isObpObject(any)) {
+ val className = any.getClass.getSimpleName
+ className.startsWith("OutBound") || className.startsWith("InBound")
+ } else {
+ false
+ }
+ }
+
+ def getIgnores(tp: universe.Type): List[String] = {
+ if(!ReflectUtils.isObpType(tp)) {
+ return Nil
+ }
+ memo.memoize(tp, it => {
+ val fields: List[universe.Symbol] = it.decls.filter(decl => decl.isTerm && (decl.asTerm.isVal || decl.asTerm.isVar)).toList
+ val (ignoreFields, notIgnoreFields) = fields.partition(_.annotations.exists(_.tree.tpe <:< typeOf[ignore]))
+ val annotedFieldNames = ignoreFields.map(_.name.decodedName.toString.trim)
+ val subAnnotedFieldNames = notIgnoreFields.flatMap(it => {
+ val fieldName = it.name.decodedName.toString.trim
+ val fieldType: universe.Type = it.info match {
+ case x if x <:< typeOf[Iterable[_]] && !(x <:< typeOf[Map[_,_]]) =>
+ x.typeArgs.head
+ case x if x <:< typeOf[Array[_]] =>
+ x.typeArgs.head
+ case x => x
+ }
+
+ getIgnores(fieldType)
+ .map(it => s"$fieldName.$it")
+ })
+ annotedFieldNames ++ subAnnotedFieldNames
+ })
+ }
+}
diff --git a/obp-api/src/main/scala/code/api/v2_2_0/JSONFactory2.2.0.scala b/obp-api/src/main/scala/code/api/v2_2_0/JSONFactory2.2.0.scala
index c74a0e468..d2bcc9883 100644
--- a/obp-api/src/main/scala/code/api/v2_2_0/JSONFactory2.2.0.scala
+++ b/obp-api/src/main/scala/code/api/v2_2_0/JSONFactory2.2.0.scala
@@ -31,7 +31,7 @@ import java.util.Date
import java.util.regex.Pattern
import code.actorsystem.ObpActorConfig
-import code.api.util.{APIUtil, ApiPropsWithAlias, CustomJsonFormats}
+import code.api.util.{APIUtil, ApiPropsWithAlias, CustomJsonFormats, FieldIgnoreSerializer}
import code.api.util.APIUtil.{MessageDoc, getPropsValue}
import code.api.v1_2_1.BankRoutingJsonV121
import com.openbankproject.commons.model.{AccountRoutingJsonV121, AmountOfMoneyJsonV121}
@@ -368,7 +368,7 @@ case class CustomerViewJsonV220(
-object JSONFactory220 extends CustomJsonFormats {
+object JSONFactory220 {
def stringOrNull(text : String) =
if(text == null || text.isEmpty)
@@ -847,6 +847,8 @@ object JSONFactory220 extends CustomJsonFormats {
MessageDocsJson(messageDocsList.map(createMessageDocJson))
}
+ private implicit val formats = CustomJsonFormats.formats + FieldIgnoreSerializer
+
def createMessageDocJson(md: MessageDoc): MessageDocJson = {
val inBoundType = ReflectUtils.getType(md.exampleInboundMessage)
diff --git a/obp-api/src/main/scala/code/util/JsonUtils.scala b/obp-api/src/main/scala/code/util/JsonUtils.scala
index 8693c00f6..59c20eb5e 100644
--- a/obp-api/src/main/scala/code/util/JsonUtils.scala
+++ b/obp-api/src/main/scala/code/util/JsonUtils.scala
@@ -522,4 +522,36 @@ object JsonUtils {
case _: JArray => typeOf[JArray]
}
}
+
+ /**
+ * delete a group of field, field can be nested.
+ * @param jValue
+ * @param fields
+ * @return a new JValue that not contains given fields.
+ */
+ def deleteFields(jValue: JValue, fields: List[String]) = fields match {
+ case Nil => jValue
+ case x => x.foldLeft(jValue)(deleteField)
+ }
+
+ /**
+ * delete one field, the field can be nested, e.g: "foo.bar.barzz"
+ * @param jValue
+ * @param fieldName
+ * @return a new JValue that not contains given field.
+ */
+ def deleteField(jValue:JValue, fieldName: String): JValue = jValue match {
+ case JNull | JNothing => jValue
+ case _: JObject =>
+ if(!fieldName.contains(".")) {
+ jValue.removeField(_.name == fieldName)
+ } else {
+ val Array(field, nestedField) = StringUtils.split(fieldName, ".", 2)
+ jValue.transformField {
+ case JField(name, value) if name == field => JField(name, deleteField(value, nestedField))
+ }
+ }
+ case JArray(arr) => JArray(arr.map(deleteField(_, fieldName)))
+ }
+
}
diff --git a/obp-commons/pom.xml b/obp-commons/pom.xml
index 4368a9875..4eff88ebe 100644
--- a/obp-commons/pom.xml
+++ b/obp-commons/pom.xml
@@ -7,7 +7,7 @@
com.tesobe
obp-parent
../pom.xml
- 1.5.2
+ 1.5.3
obp-commons
jar
diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/util/Functions.scala b/obp-commons/src/main/scala/com/openbankproject/commons/util/Functions.scala
index b2c966529..903f2f4ab 100644
--- a/obp-commons/src/main/scala/com/openbankproject/commons/util/Functions.scala
+++ b/obp-commons/src/main/scala/com/openbankproject/commons/util/Functions.scala
@@ -63,6 +63,24 @@ object Functions {
coll.filterNot(it => it.isInstanceOf[Array[_]] || it.isInstanceOf[GenTraversableOnce[_]])
}
+ /**
+ * momoize function, to avoid re calculate values
+ * @tparam A key
+ * @tparam R cached value
+ */
+ class Memo[A, R] {
+ private val cache = new java.util.concurrent.atomic.AtomicReference(Map[A, R]())
+
+ def memoize(x: A, f: A => R): R = {
+ val c = cache.get
+ def addToCache() = {
+ val ret = f(x)
+ cache.set(c + (x -> ret))
+ ret
+ }
+ c.getOrElse(x, addToCache)
+ }
+ }
// implicit functions place in this object
object Implicits {
diff --git a/pom.xml b/pom.xml
index 6a948879f..638eccd89 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
4.0.0
com.tesobe
obp-parent
- 1.5.2
+ 1.5.3
pom
Open Bank Project API Parent
2011