diff --git a/.gitignore b/.gitignore index e2db29849..bb86ade8d 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,8 @@ .cache target obp-api/src/main/resources/ -obp-api/src/test/resources/ +obp-api/src/test/resources/** +!obp-api/src/test/resources/frozen_type_meta_data *.iml obp-api/src/main/resources/log4j.properties obp-api/src/main/scripts/kafka/kafka_* diff --git a/obp-api/src/main/scala/code/api/APIBuilder/swagger/APIBuilderSwagger.scala b/obp-api/src/main/scala/code/api/APIBuilder/swagger/APIBuilderSwagger.scala index cab09d1d8..454a5c93a 100644 --- a/obp-api/src/main/scala/code/api/APIBuilder/swagger/APIBuilderSwagger.scala +++ b/obp-api/src/main/scala/code/api/APIBuilder/swagger/APIBuilderSwagger.scala @@ -33,8 +33,7 @@ import net.liftweb.json.{JArray, JValue} import scala.meta._ -object APIBuilderSwagger -{ +object APIBuilderSwagger { def main(args: Array[String]): Unit = overwriteApiCode(apiSource,jsonFactorySource) val jsonJValueFromFile: JValue = APIUtil.getJValueFromFile("src/main/scala/code/api/APIBuilder/swagger/swaggerResource.json") diff --git a/obp-api/src/main/scala/code/api/util/ScannedApis.scala b/obp-api/src/main/scala/code/api/util/ScannedApis.scala index 2dd96c9b7..d92bc633d 100644 --- a/obp-api/src/main/scala/code/api/util/ScannedApis.scala +++ b/obp-api/src/main/scala/code/api/util/ScannedApis.scala @@ -21,5 +21,8 @@ object ScannedApis { /** * this map value are all scanned objects those extends ScannedApiVersion, the key is it apiVersion field */ - lazy val versionMapScannedApis: Map[ScannedApiVersion, ScannedApis] = ClassScanUtils.getSubTypeObjects(classOf[ScannedApis]).map(it=> (it.apiVersion, it)).toMap + lazy val versionMapScannedApis: Map[ScannedApiVersion, ScannedApis] = + ClassScanUtils.getSubTypeObjects[ScannedApis] + .map(it=> (it.apiVersion, it)) + .toMap } diff --git a/obp-api/src/main/scala/code/api/util/VersionedOBPApis.scala b/obp-api/src/main/scala/code/api/util/VersionedOBPApis.scala new file mode 100644 index 000000000..49d1b5221 --- /dev/null +++ b/obp-api/src/main/scala/code/api/util/VersionedOBPApis.scala @@ -0,0 +1,15 @@ +package code.api.util + +import code.api.util.APIUtil.{OBPEndpoint, ResourceDoc} + +import scala.collection.mutable.ArrayBuffer + +trait VersionedOBPApis { + def version : ApiVersion + + def versionStatus: String + + def allResourceDocs: ArrayBuffer[ResourceDoc] + + def routes: List[OBPEndpoint] +} diff --git a/obp-api/src/main/scala/code/api/v1_2_1/OBPAPI1.2.1.scala b/obp-api/src/main/scala/code/api/v1_2_1/OBPAPI1.2.1.scala index 55f48a034..5c7b8bc80 100644 --- a/obp-api/src/main/scala/code/api/v1_2_1/OBPAPI1.2.1.scala +++ b/obp-api/src/main/scala/code/api/v1_2_1/OBPAPI1.2.1.scala @@ -28,12 +28,12 @@ package code.api.v1_2_1 import code.api.OBPRestHelper import code.api.util.APIUtil.{OBPEndpoint, ResourceDoc, getAllowedEndpoints} -import code.api.util.ApiVersion +import code.api.util.{ApiVersion, VersionedOBPApis} import code.util.Helper.MdcLoggable // Added so we can add resource docs for this version of the API -object OBPAPI1_2_1 extends OBPRestHelper with APIMethods121 with MdcLoggable { +object OBPAPI1_2_1 extends OBPRestHelper with APIMethods121 with MdcLoggable with VersionedOBPApis{ val version : ApiVersion = ApiVersion.v1_2_1 // "1.2.1" diff --git a/obp-api/src/main/scala/code/api/v1_3_0/OBPAPI1_3_0.scala b/obp-api/src/main/scala/code/api/v1_3_0/OBPAPI1_3_0.scala index ab451b6b5..28dcf5f1a 100644 --- a/obp-api/src/main/scala/code/api/v1_3_0/OBPAPI1_3_0.scala +++ b/obp-api/src/main/scala/code/api/v1_3_0/OBPAPI1_3_0.scala @@ -2,7 +2,7 @@ package code.api.v1_3_0 import code.api.OBPRestHelper import code.api.util.APIUtil.{OBPEndpoint, ResourceDoc, getAllowedEndpoints} -import code.api.util.ApiVersion +import code.api.util.{ApiVersion, VersionedOBPApis} import code.api.v1_2_1.APIMethods121 import code.util.Helper.MdcLoggable @@ -11,7 +11,7 @@ import code.util.Helper.MdcLoggable //has APIMethods121 as all api calls that went unchanged from 1.2.1 to 1.3.0 will use the old //implementation -object OBPAPI1_3_0 extends OBPRestHelper with APIMethods130 with APIMethods121 with MdcLoggable { +object OBPAPI1_3_0 extends OBPRestHelper with APIMethods130 with APIMethods121 with MdcLoggable with VersionedOBPApis{ val version : ApiVersion = ApiVersion.v1_3_0 // "1.3.0" val versionStatus = "STABLE" diff --git a/obp-api/src/main/scala/code/api/v1_4_0/OBPAPI1_4_0.scala b/obp-api/src/main/scala/code/api/v1_4_0/OBPAPI1_4_0.scala index 18fabf9b3..73e97db0a 100644 --- a/obp-api/src/main/scala/code/api/v1_4_0/OBPAPI1_4_0.scala +++ b/obp-api/src/main/scala/code/api/v1_4_0/OBPAPI1_4_0.scala @@ -2,11 +2,11 @@ package code.api.v1_4_0 import code.api.OBPRestHelper import code.api.util.APIUtil.{OBPEndpoint, ResourceDoc, getAllowedEndpoints} -import code.api.util.ApiVersion +import code.api.util.{ApiVersion, VersionedOBPApis} import code.util.Helper.MdcLoggable -object OBPAPI1_4_0 extends OBPRestHelper with APIMethods140 with MdcLoggable { +object OBPAPI1_4_0 extends OBPRestHelper with APIMethods140 with MdcLoggable with VersionedOBPApis{ val version : ApiVersion = ApiVersion.v1_4_0 //"1.4.0" val versionStatus = "STABLE" diff --git a/obp-api/src/main/scala/code/api/v2_0_0/OBPAPI2_0_0.scala b/obp-api/src/main/scala/code/api/v2_0_0/OBPAPI2_0_0.scala index e13429c19..b34a23e74 100644 --- a/obp-api/src/main/scala/code/api/v2_0_0/OBPAPI2_0_0.scala +++ b/obp-api/src/main/scala/code/api/v2_0_0/OBPAPI2_0_0.scala @@ -28,12 +28,12 @@ package code.api.v2_0_0 import code.api.OBPRestHelper import code.api.util.APIUtil.{OBPEndpoint, ResourceDoc, getAllowedEndpoints} -import code.api.util.ApiVersion +import code.api.util.{ApiVersion, VersionedOBPApis} import code.api.v1_3_0.APIMethods130 import code.api.v1_4_0.APIMethods140 import code.util.Helper.MdcLoggable -object OBPAPI2_0_0 extends OBPRestHelper with APIMethods130 with APIMethods140 with APIMethods200 with MdcLoggable { +object OBPAPI2_0_0 extends OBPRestHelper with APIMethods130 with APIMethods140 with APIMethods200 with MdcLoggable with VersionedOBPApis{ val version : ApiVersion = ApiVersion.v2_0_0 // "2.0.0" diff --git a/obp-api/src/main/scala/code/api/v2_1_0/OBPAPI2_1_0.scala b/obp-api/src/main/scala/code/api/v2_1_0/OBPAPI2_1_0.scala index fe9aedae1..c70a76562 100644 --- a/obp-api/src/main/scala/code/api/v2_1_0/OBPAPI2_1_0.scala +++ b/obp-api/src/main/scala/code/api/v2_1_0/OBPAPI2_1_0.scala @@ -28,7 +28,7 @@ package code.api.v2_1_0 import code.api.OBPRestHelper import code.api.util.APIUtil.{OBPEndpoint, ResourceDoc, getAllowedEndpoints} -import code.api.util.{APIUtil, ApiVersion} +import code.api.util.{APIUtil, ApiVersion, VersionedOBPApis} import code.api.v1_3_0.APIMethods130 import code.api.v1_4_0.APIMethods140 import code.api.v2_0_0.APIMethods200 @@ -36,7 +36,7 @@ import code.util.Helper.MdcLoggable import scala.collection.immutable.Nil -object OBPAPI2_1_0 extends OBPRestHelper with APIMethods130 with APIMethods140 with APIMethods200 with APIMethods210 with MdcLoggable { +object OBPAPI2_1_0 extends OBPRestHelper with APIMethods130 with APIMethods140 with APIMethods200 with APIMethods210 with MdcLoggable with VersionedOBPApis{ diff --git a/obp-api/src/main/scala/code/api/v2_2_0/OBPAPI2_2_0.scala b/obp-api/src/main/scala/code/api/v2_2_0/OBPAPI2_2_0.scala index 4c933d04d..261f4fa1e 100644 --- a/obp-api/src/main/scala/code/api/v2_2_0/OBPAPI2_2_0.scala +++ b/obp-api/src/main/scala/code/api/v2_2_0/OBPAPI2_2_0.scala @@ -3,7 +3,7 @@ package code.api.v2_2_0 import code.api.OBPRestHelper import code.api.util.APIUtil.{OBPEndpoint, ResourceDoc, getAllowedEndpoints} -import code.api.util.{APIUtil, ApiVersion} +import code.api.util.{APIUtil, ApiVersion, VersionedOBPApis} import code.api.v1_3_0.APIMethods130 import code.api.v1_4_0.APIMethods140 import code.api.v2_0_0.APIMethods200 @@ -12,7 +12,7 @@ import code.util.Helper.MdcLoggable import scala.collection.immutable.Nil -object OBPAPI2_2_0 extends OBPRestHelper with APIMethods130 with APIMethods140 with APIMethods200 with APIMethods210 with APIMethods220 with MdcLoggable { +object OBPAPI2_2_0 extends OBPRestHelper with APIMethods130 with APIMethods140 with APIMethods200 with APIMethods210 with APIMethods220 with MdcLoggable with VersionedOBPApis{ val version : ApiVersion = ApiVersion.v2_2_0 // "2.2.0" val versionStatus = "STABLE" diff --git a/obp-api/src/main/scala/code/api/v3_0_0/OBPAPI3_0_0.scala b/obp-api/src/main/scala/code/api/v3_0_0/OBPAPI3_0_0.scala index 7e6c2dd04..d5f487b6c 100644 --- a/obp-api/src/main/scala/code/api/v3_0_0/OBPAPI3_0_0.scala +++ b/obp-api/src/main/scala/code/api/v3_0_0/OBPAPI3_0_0.scala @@ -28,7 +28,7 @@ package code.api.v3_0_0 import code.api.OBPRestHelper import code.api.util.APIUtil.{OBPEndpoint, ResourceDoc, getAllowedEndpoints} -import code.api.util.ApiVersion +import code.api.util.{ApiVersion, VersionedOBPApis} import code.api.v1_3_0.APIMethods130 import code.api.v1_4_0.APIMethods140 import code.api.v2_0_0.APIMethods200 @@ -46,7 +46,7 @@ This file defines which endpoints from all the versions are available in v3.0.0 */ -object OBPAPI3_0_0 extends OBPRestHelper with APIMethods130 with APIMethods140 with APIMethods200 with APIMethods210 with APIMethods220 with APIMethods300 with CustomAPIMethods300 with MdcLoggable { +object OBPAPI3_0_0 extends OBPRestHelper with APIMethods130 with APIMethods140 with APIMethods200 with APIMethods210 with APIMethods220 with APIMethods300 with CustomAPIMethods300 with MdcLoggable with VersionedOBPApis{ diff --git a/obp-api/src/main/scala/code/api/v3_1_0/OBPAPI3_1_0.scala b/obp-api/src/main/scala/code/api/v3_1_0/OBPAPI3_1_0.scala index e6be767d9..c8c2ea7ab 100644 --- a/obp-api/src/main/scala/code/api/v3_1_0/OBPAPI3_1_0.scala +++ b/obp-api/src/main/scala/code/api/v3_1_0/OBPAPI3_1_0.scala @@ -28,7 +28,7 @@ package code.api.v3_1_0 import code.api.OBPRestHelper import code.api.util.APIUtil.{OBPEndpoint, ResourceDoc, getAllowedEndpoints} -import code.api.util.ApiVersion +import code.api.util.{ApiVersion, VersionedOBPApis} import code.api.v1_3_0.APIMethods130 import code.api.v1_4_0.APIMethods140 import code.api.v2_0_0.APIMethods200 @@ -47,7 +47,7 @@ This file defines which endpoints from all the versions are available in v3.0.0 */ -object OBPAPI3_1_0 extends OBPRestHelper with APIMethods130 with APIMethods140 with APIMethods200 with APIMethods210 with APIMethods220 with APIMethods300 with CustomAPIMethods300 with APIMethods310 with MdcLoggable { +object OBPAPI3_1_0 extends OBPRestHelper with APIMethods130 with APIMethods140 with APIMethods200 with APIMethods210 with APIMethods220 with APIMethods300 with CustomAPIMethods300 with APIMethods310 with MdcLoggable with VersionedOBPApis{ val version : ApiVersion = ApiVersion.v3_1_0 diff --git a/obp-api/src/main/scala/code/api/v4_0_0/OBPAPI4_0_0.scala b/obp-api/src/main/scala/code/api/v4_0_0/OBPAPI4_0_0.scala index 430a17028..2c3278e3e 100644 --- a/obp-api/src/main/scala/code/api/v4_0_0/OBPAPI4_0_0.scala +++ b/obp-api/src/main/scala/code/api/v4_0_0/OBPAPI4_0_0.scala @@ -28,8 +28,8 @@ package code.api.v4_0_0 import code.api.OBPRestHelper import code.api.util.APIUtil.{OBPEndpoint, ResourceDoc, getAllowedEndpoints} -import code.api.util.ApiVersion -import code.api.v1_3_0.APIMethods130 +import code.api.util.{ApiVersion, VersionedOBPApis} +import code.api.v1_3_0.{APIMethods130} import code.api.v1_4_0.APIMethods140 import code.api.v2_0_0.APIMethods200 import code.api.v2_1_0.APIMethods210 @@ -48,7 +48,7 @@ This file defines which endpoints from all the versions are available in v4.0.0 */ -object OBPAPI4_0_0 extends OBPRestHelper with APIMethods130 with APIMethods140 with APIMethods200 with APIMethods210 with APIMethods220 with APIMethods300 with CustomAPIMethods300 with APIMethods310 with APIMethods400 with MdcLoggable { +object OBPAPI4_0_0 extends OBPRestHelper with APIMethods130 with APIMethods140 with APIMethods200 with APIMethods210 with APIMethods220 with APIMethods300 with CustomAPIMethods300 with APIMethods310 with APIMethods400 with MdcLoggable with VersionedOBPApis{ val version : ApiVersion = ApiVersion.v4_0_0 diff --git a/obp-api/src/main/scala/code/util/ClassScanUtils.scala b/obp-api/src/main/scala/code/util/ClassScanUtils.scala index 57f0bcc39..c7cc44284 100644 --- a/obp-api/src/main/scala/code/util/ClassScanUtils.scala +++ b/obp-api/src/main/scala/code/util/ClassScanUtils.scala @@ -5,6 +5,7 @@ import java.io.File import com.openbankproject.commons.model.Bank import org.apache.commons.lang3.StringUtils import org.clapper.classutil.{ClassFinder, ClassInfo} +import com.openbankproject.commons.util.ReflectUtils import scala.reflect.runtime.universe.TypeTag @@ -29,11 +30,11 @@ object ClassScanUtils { /** * scan classpath to get all companion objects or singleton objects those implements given trait - * @param clazz a trait type for filter object * @tparam T the trait type parameter * @return all companion objects or singleton object those implements given clazz */ - def getSubTypeObjects[T:TypeTag](clazz: Class[T]) = { + def getSubTypeObjects[T:TypeTag]: List[T] = { + val clazz = ReflectUtils.typeTagToClass[T] finder.getClasses().filter(_.implements(clazz.getName)).map(_.name).map(companion[T](_)).toList } diff --git a/obp-api/src/test/resources/frozen_type_meta_data b/obp-api/src/test/resources/frozen_type_meta_data new file mode 100644 index 000000000..ad3f22a56 Binary files /dev/null and b/obp-api/src/test/resources/frozen_type_meta_data differ diff --git a/obp-api/src/test/scala/RunWebApp.scala b/obp-api/src/test/scala/RunWebApp.scala index a02470c71..ef1248db3 100644 --- a/obp-api/src/test/scala/RunWebApp.scala +++ b/obp-api/src/test/scala/RunWebApp.scala @@ -26,7 +26,6 @@ TESOBE (http://www.tesobe.com/) */ import code.api.util.APIUtil -import net.liftweb.util.Props import org.eclipse.jetty.server.Server import org.eclipse.jetty.webapp.WebAppContext diff --git a/obp-api/src/test/scala/code/util/FrozenClassTest.scala b/obp-api/src/test/scala/code/util/FrozenClassTest.scala new file mode 100644 index 000000000..8161fa690 --- /dev/null +++ b/obp-api/src/test/scala/code/util/FrozenClassTest.scala @@ -0,0 +1,81 @@ +package code.util + +import code.api.util.ApiVersion +import code.setup.ServerSetup +import org.scalatest.Tag + +class FrozenClassTest extends ServerSetup { + + object FrozenClassTag extends Tag("Frozen_Classes") + + val (persistedVersionToEndpointNames, persistedTypeNameToTypeValFields) = FrozenClassUtil.readPersistedFrozenApiInfo + val (versionToEndpointNames, typeNameToTypeValFields) = FrozenClassUtil.getFrozenApiInfo + + feature("Frozen version apis not changed") { + + scenario(s"count of STABLE OBPAPIxxxx should not be reduce, if pretty sure need modify it, please run ${FrozenClassUtil.sourceName}", FrozenClassTag) { + + val persistedStableVersions = persistedVersionToEndpointNames.map(_._1).toSet + val currentStableVersions = versionToEndpointNames.map(_._1).toSet + + val increasedVersions = persistedStableVersions.diff(currentStableVersions) + increasedVersions should equal(Set.empty[ApiVersion]) + } + + scenario(s"count of STABLE OBPAPIxxxx should not be increased, if pretty sure need modify it, please run ${FrozenClassUtil.sourceName}", FrozenClassTag) { + val persistedStableVersions = persistedVersionToEndpointNames.map(_._1).toSet + val currentStableVersions = versionToEndpointNames.map(_._1).toSet + + val reducedVersions = currentStableVersions.diff(persistedStableVersions) + reducedVersions should equal(Set.empty[ApiVersion]) + } + + scenario(s"api count of STABLE value of OBPAPIxxxx#versionStatus should not be reduce, if pretty sure need modify it, please run ${FrozenClassUtil.sourceName}", FrozenClassTag) { + val reducedApis = for { + (pVersion, pEndpointNames) <- persistedVersionToEndpointNames + (version, endpointNames) <- versionToEndpointNames + if (pVersion == version) + reducedApisOfVersion = pEndpointNames.diff(endpointNames).mkString(",") + if (reducedApisOfVersion.size > 0) + } yield { + s"$version reduced apis: $reducedApisOfVersion" + } + reducedApis should equal(Nil) + } + + scenario(s"api count of STABLE value of OBPAPIxxxx#versionStatus should not be increased, if pretty sure need modify it, please run ${FrozenClassUtil.sourceName}", FrozenClassTag) { + val increasedApis = for { + (pVersion, pEndpointNames) <- persistedVersionToEndpointNames + (version, endpointNames) <- versionToEndpointNames + if (pVersion == version) + increasedApis = endpointNames.diff(pEndpointNames).mkString(",") + if (increasedApis.size > 0) + } yield { + s"$version increased apis: $increasedApis" + } + increasedApis should equal(Nil) + } + } + + feature("Frozen type structure not be modified") { + scenario(s"frozen class structure should not be modified, if pretty sure need modify it, please run ${FrozenClassUtil.sourceName}", FrozenClassTag) { + val changedTypes = for { + (pTypeName, pFields) <- persistedTypeNameToTypeValFields.toList + (typeName, fields) <- typeNameToTypeValFields.toList + if(pTypeName == typeName && pFields != fields) + } yield { + val expectStructure = pFields.map(pair => s"${pair._1}:${pair._2}").mkString("(", ", ", ")") + val actualStructure = fields.map(pair => s"${pair._1}:${pair._2}").mkString("(", ", ", ")") + + s""" + |{ + | typeName: $typeName + | expectStructure: $expectStructure + | actualStructure: $actualStructure + |} + |""".stripMargin + } + changedTypes should equal (Nil) + } + } +} diff --git a/obp-api/src/test/scala/code/util/FrozenClassUtil.scala b/obp-api/src/test/scala/code/util/FrozenClassUtil.scala new file mode 100644 index 000000000..077df525b --- /dev/null +++ b/obp-api/src/test/scala/code/util/FrozenClassUtil.scala @@ -0,0 +1,96 @@ +package code.util + +import java.io._ +import java.net.URI + +import code.TestServer +import code.api.util.{ApiVersion, VersionedOBPApis} +import com.openbankproject.commons.util.ReflectUtils +import org.apache.commons.io.IOUtils + +import scala.reflect.runtime.universe._ + +/** + * this util is for persist metadata of frozen type, those frozen type is versionStatus = "STABLE" related example classes, + * after persist the metadata, the FrozenClassTest can check whether there some modify change any frozen type, the test will fail when there are some changes in the frozen type + */ +object FrozenClassUtil { + + val sourceName = s"""${this.getClass.getName.replace("$", "")}.scala""" + // current project absolute path + val basePath = this.getClass.getResource("/").toString .replaceFirst("target[/\\\\].*$", "") + val persistFilePath = new URI(s"${basePath}/src/test/resources/frozen_type_meta_data").getPath + + def main(args: Array[String]): Unit = { + System.setProperty("run.mode", "test") // make sure this Props.mode is the same as unit test Props.mode + val server = TestServer + val out = new ObjectOutputStream(new FileOutputStream(persistFilePath)) + try { + out.writeObject(getFrozenApiInfo) + } finally { + IOUtils.closeQuietly(out) + // there is no graceful way to shutdown jetty server, so here use brutal way to shutdown it. + server.server.stop() + System.exit(0) + } + } + + /** + * get frozen api information by scan classes + * @return frozen api information, include api names of given api version and frozen class metadata + */ + def getFrozenApiInfo: (List[(ApiVersion, Set[String])], Map[String, Map[String, String]]) = { + val versionedOBPApisList: List[VersionedOBPApis] = ClassScanUtils.getSubTypeObjects[VersionedOBPApis] + .filter(_.versionStatus == "STABLE") + + val versionToEndpointNames: List[(ApiVersion, Set[String])] = versionedOBPApisList + .map(it => { + val version = it.version + val currentVersionApis = it.allResourceDocs.filter(version == _.implementedInApiVersion).toSet + (version, currentVersionApis.map(_.partialFunctionName)) + }) + + val allFreezingTypes: Set[Type] = versionedOBPApisList + .flatMap(_.allResourceDocs) + .flatMap(it => it.exampleRequestBody :: it.successResponseBody :: Nil) + .filter(ReflectUtils.isObpObject(_)) + .map(ReflectUtils.getType(_)) + .toSet + .flatMap(getNestedOBPType(_)) + + val typeNameToTypeValFields: Map[String, Map[String, String]] = allFreezingTypes + .map(it => { + val valNameToTypeName = ReflectUtils.getConstructorParamInfo(it).map(pair => (pair._1, pair._2.toString)) + (it.typeSymbol.asClass.fullName, valNameToTypeName) + }) + .toMap + (versionToEndpointNames, typeNameToTypeValFields) + } + + /** + * read persisted frozen api info from persist file + * @return persisted frozen api information, include api names of given api version and frozen class metadata + */ + def readPersistedFrozenApiInfo: (List[(ApiVersion, Set[String])], Map[String, Map[String, String]]) = { + assume(new File(persistFilePath).exists(), s"freeze type not persisted yet, please run ${this.sourceName}") + val input = new ObjectInputStream(new FileInputStream(persistFilePath)) + try { + input.readObject().asInstanceOf[(List[(ApiVersion, Set[String])], Map[String, Map[String, String]])] + } finally { + IOUtils.closeQuietly(input) + } + } + + private def getNestedOBPType(tp: Type): Set[Type] = { + ReflectUtils.getConstructorParamInfo(tp) + .values + .map(it => ReflectUtils.getDeepGenericType(it).head) + .toSet + .filter(ReflectUtils.isObpType) + .filterNot(tp ==) // avoid infinite recursive + match { + case set if(set.size > 0) => set.flatMap(getNestedOBPType) + tp + case _ => Set(tp) + } + } +} diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/util/ReflectUtils.scala b/obp-commons/src/main/scala/com/openbankproject/commons/util/ReflectUtils.scala index 023c6cb60..0b6454dc1 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/util/ReflectUtils.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/util/ReflectUtils.scala @@ -15,7 +15,7 @@ object ReflectUtils { def isObpObject(any: Any): Boolean = any != null && OBP_TYPE_REGEX.findFirstIn(any.getClass.getName).isDefined - def isObpType(tp: Type): Boolean = tp != null && OBP_TYPE_REGEX.findFirstIn(tp.typeSymbol.fullName).isDefined + def isObpType(tp: Type): Boolean = tp != null && tp.typeSymbol.isClass && OBP_TYPE_REGEX.findFirstIn(tp.typeSymbol.fullName).isDefined /** * get all val and var name to values of given object @@ -278,6 +278,11 @@ object ReflectUtils { }) } + def typeTagToClass[T: TypeTag]: Class[_] = { + val tt = implicitly[TypeTag[T]] + tt.mirror.runtimeClass(tt.tpe.typeSymbol.asClass) + } + def classToSymbol(clazz: Class[_]): ru.ClassSymbol = ru.runtimeMirror(clazz.getClassLoader).classSymbol(clazz) /** @@ -285,10 +290,16 @@ object ReflectUtils { * @param tp to do extract type * @return a map of constructor parameter name to type, if tp is abstract, return empty amp */ - def getConstructorParamInfo(tp: ru.Type): Map[String, ru.Type] = tp.typeSymbol.isClass match { + def getConstructorParamInfo(tp: ru.Type): Map[String, ru.Type] = + tp.typeSymbol.isClass && !tp.typeSymbol.asClass.isTrait match { case false => Map.empty[String, ru.Type] case true => { - ReflectUtils.getPrimaryConstructor(tp).paramLists.headOption.getOrElse(Nil).map(it => (it.name.toString, it.info)).toMap + ReflectUtils.getPrimaryConstructor(tp) + .paramLists + .headOption + .getOrElse(Nil) + .map(it => (it.name.toString, it.info)) + .toMap } } /**