diff --git a/obp-api/pom.xml b/obp-api/pom.xml index 0a1501d849..c11d235330 100644 --- a/obp-api/pom.xml +++ b/obp-api/pom.xml @@ -23,6 +23,52 @@ src/main/resources/web.xml + + http4s-jar + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.6.0 + + false + ${project.artifactId}-http4s + + + bootstrap.http4s.Http4sServer + + + + jar-with-dependencies + + + + / + true + runtime + + + + + ${project.build.outputDirectory} + / + + + + + + http4s-fat-jar + package + + single + + + + + + + @@ -84,6 +130,16 @@ bcpg-jdk15on 1.70 + + org.http4s + http4s-ember-server_${scala.version} + ${http4s.version} + + + org.http4s + http4s-dsl_${scala.version} + ${http4s.version} + org.bouncycastle bcpkix-jdk15on @@ -345,7 +401,7 @@ org.clapper classutil_${scala.version} - 1.4.0 + 1.5.1 com.github.grumlimited @@ -602,6 +658,20 @@ net.alchim31.maven scala-maven-plugin + 4.8.1 + + true + + -Xms4G + -Xmx12G + -XX:MaxMetaspaceSize=4G + -XX:+UseG1GC + + + -deprecation + -feature + + org.apache.maven.plugins @@ -609,6 +679,8 @@ 3.4.0 ${webXmlPath} + true + classes diff --git a/obp-api/src/main/scala/bootstrap/http4s/Http4sBoot.scala b/obp-api/src/main/scala/bootstrap/http4s/Http4sBoot.scala new file mode 100644 index 0000000000..0a867ec4a6 --- /dev/null +++ b/obp-api/src/main/scala/bootstrap/http4s/Http4sBoot.scala @@ -0,0 +1,346 @@ +/** +Open Bank Project - API +Copyright (C) 2011-2019, TESOBE GmbH. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + +Email: contact@tesobe.com +TESOBE GmbH. +Osloer Strasse 16/17 +Berlin 13359, Germany + +This product includes software developed at +TESOBE (http://www.tesobe.com/) + + */ +package bootstrap.http4s + +import bootstrap.liftweb.ToSchemify +import code.api.Constant._ +import code.api.util.ApiRole.CanCreateEntitlementAtAnyBank +import code.api.util.ErrorMessages.MandatoryPropertyIsNotSet +import code.api.util._ +import code.api.util.migration.Migration +import code.api.util.migration.Migration.DbFunction +import code.entitlement.Entitlement +import code.model.dataAccess._ +import code.scheduler._ +import code.users._ +import code.util.Helper.MdcLoggable +import code.views.Views +import com.openbankproject.commons.util.Functions.Implicits._ +import net.liftweb.common.Box.tryo +import net.liftweb.common._ +import net.liftweb.db.{DB, DBLogEntry} +import net.liftweb.mapper.{DefaultConnectionIdentifier => _, _} +import net.liftweb.util._ + +import java.io.{File, FileInputStream} +import java.util.TimeZone + + + + +/** + * Http4s Boot class for initializing OBP-API core components + * This class handles database initialization, migrations, and system setup + * without Lift Web framework dependencies + */ +class Http4sBoot extends MdcLoggable { + + /** + * For the project scope, most early initiate logic should in this method. + */ + override protected def initiate(): Unit = { + val resourceDir = System.getProperty("props.resource.dir") ?: System.getenv("props.resource.dir") + val propsPath = tryo{Box.legacyNullTest(resourceDir)}.toList.flatten + + val propsDir = for { + propsPath <- propsPath + } yield { + Props.toTry.map { + f => { + val name = propsPath + f() + "props" + name -> { () => tryo{new FileInputStream(new File(name))} } + } + } + } + + Props.whereToLook = () => { + propsDir.flatten + } + + if (Props.mode == Props.RunModes.Development) logger.info("OBP-API Props all fields : \n" + Props.props.mkString("\n")) + logger.info("external props folder: " + propsPath) + TimeZone.setDefault(TimeZone.getTimeZone("UTC")) + logger.info("Current Project TimeZone: " + TimeZone.getDefault) + + + // set dynamic_code_sandbox_enable to System.properties, so com.openbankproject.commons.ExecutionContext can read this value + APIUtil.getPropsValue("dynamic_code_sandbox_enable") + .foreach(it => System.setProperty("dynamic_code_sandbox_enable", it)) + } + + + + def boot: Unit = { + implicit val formats = CustomJsonFormats.formats + + logger.info("Http4sBoot says: Hello from the Open Bank Project API. This is Http4sBoot.scala for Http4s runner. The gitCommit is : " + APIUtil.gitCommit) + + logger.debug("Boot says:Using database driver: " + APIUtil.driver) + + DB.defineConnectionManager(net.liftweb.util.DefaultConnectionIdentifier, APIUtil.vendor) + + /** + * Function that determines if foreign key constraints are + * created by Schemifier for the specified connection. + * + * Note: The chosen driver must also support foreign keys for + * creation to happen + * + * In case of PostgreSQL it works + */ + MapperRules.createForeignKeys_? = (_) => APIUtil.getPropsAsBoolValue("mapper_rules.create_foreign_keys", false) + + schemifyAll() + + logger.info("Mapper database info: " + Migration.DbFunction.mapperDatabaseInfo) + + DbFunction.tableExists(ResourceUser) match { + case true => // DB already exist + // Migration Scripts are used to update the model of OBP-API DB to a latest version. + // Please note that migration scripts are executed before Lift Mapper Schemifier + Migration.database.executeScripts(startedBeforeSchemifier = true) + logger.info("The Mapper database already exits. The scripts are executed BEFORE Lift Mapper Schemifier.") + case false => // DB is still not created. The scripts will be executed after Lift Mapper Schemifier + logger.info("The Mapper database is still not created. The scripts are going to be executed AFTER Lift Mapper Schemifier.") + } + + // Migration Scripts are used to update the model of OBP-API DB to a latest version. + + // Please note that migration scripts are executed after Lift Mapper Schemifier + Migration.database.executeScripts(startedBeforeSchemifier = false) + + if (APIUtil.getPropsAsBoolValue("create_system_views_at_boot", true)) { + // Create system views + val owner = Views.views.vend.getOrCreateSystemView(SYSTEM_OWNER_VIEW_ID).isDefined + val auditor = Views.views.vend.getOrCreateSystemView(SYSTEM_AUDITOR_VIEW_ID).isDefined + val accountant = Views.views.vend.getOrCreateSystemView(SYSTEM_ACCOUNTANT_VIEW_ID).isDefined + val standard = Views.views.vend.getOrCreateSystemView(SYSTEM_STANDARD_VIEW_ID).isDefined + val stageOne = Views.views.vend.getOrCreateSystemView(SYSTEM_STAGE_ONE_VIEW_ID).isDefined + val manageCustomViews = Views.views.vend.getOrCreateSystemView(SYSTEM_MANAGE_CUSTOM_VIEWS_VIEW_ID).isDefined + // Only create Firehose view if they are enabled at instance. + val accountFirehose = if (ApiPropsWithAlias.allowAccountFirehose) + Views.views.vend.getOrCreateSystemView(SYSTEM_FIREHOSE_VIEW_ID).isDefined + else Empty.isDefined + + APIUtil.getPropsValue("additional_system_views") match { + case Full(value) => + val additionalSystemViewsFromProps = value.split(",").map(_.trim).toList + val additionalSystemViews = List( + SYSTEM_READ_ACCOUNTS_BASIC_VIEW_ID, + SYSTEM_READ_ACCOUNTS_DETAIL_VIEW_ID, + SYSTEM_READ_BALANCES_VIEW_ID, + SYSTEM_READ_TRANSACTIONS_BASIC_VIEW_ID, + SYSTEM_READ_TRANSACTIONS_DEBITS_VIEW_ID, + SYSTEM_READ_TRANSACTIONS_DETAIL_VIEW_ID, + SYSTEM_READ_ACCOUNTS_BERLIN_GROUP_VIEW_ID, + SYSTEM_READ_BALANCES_BERLIN_GROUP_VIEW_ID, + SYSTEM_READ_TRANSACTIONS_BERLIN_GROUP_VIEW_ID, + SYSTEM_INITIATE_PAYMENTS_BERLIN_GROUP_VIEW_ID + ) + for { + systemView <- additionalSystemViewsFromProps + if additionalSystemViews.exists(_ == systemView) + } { + Views.views.vend.getOrCreateSystemView(systemView) + } + case _ => // Do nothing + } + + } + + ApiWarnings.logWarningsRegardingProperties() + ApiWarnings.customViewNamesCheck() + ApiWarnings.systemViewNamesCheck() + + //see the notes for this method: + createDefaultBankAndDefaultAccountsIfNotExisting() + + createBootstrapSuperUser() + + if (APIUtil.getPropsAsBoolValue("logging.database.queries.enable", false)) { + DB.addLogFunc + { + case (log, duration) => + { + logger.debug("Total query time : %d ms".format(duration)) + log.allEntries.foreach + { + case DBLogEntry(stmt, duration) => + logger.debug("The query : %s in %d ms".format(stmt, duration)) + } + } + } + } + + // start RabbitMq Adapter(using mapped connector as mockded CBS) + if (APIUtil.getPropsAsBoolValue("rabbitmq.adapter.enabled", false)) { + code.bankconnectors.rabbitmq.Adapter.startRabbitMqAdapter.main(Array("")) + } + + // ensure our relational database's tables are created/fit the schema + val connector = code.api.Constant.CONNECTOR.openOrThrowException(s"$MandatoryPropertyIsNotSet. The missing prop is `connector` ") + + logger.info(s"ApiPathZero (the bit before version) is $ApiPathZero") + logger.debug(s"If you can read this, logging level is debug") + + // API Metrics (logs of API calls) + // If set to true we will write each URL with params to a datastore / log file + if (APIUtil.getPropsAsBoolValue("write_metrics", false)) { + logger.info("writeMetrics is true. We will write API metrics") + } else { + logger.info("writeMetrics is false. We will NOT write API metrics") + } + + // API Metrics (logs of Connector calls) + // If set to true we will write each URL with params to a datastore / log file + if (APIUtil.getPropsAsBoolValue("write_connector_metrics", false)) { + logger.info("writeConnectorMetrics is true. We will write connector metrics") + } else { + logger.info("writeConnectorMetrics is false. We will NOT write connector metrics") + } + + + logger.info (s"props_identifier is : ${APIUtil.getPropsValue("props_identifier", "NONE-SET")}") + + val locale = I18NUtil.getDefaultLocale() + logger.info("Default Project Locale is :" + locale) + + } + + def schemifyAll() = { + Schemifier.schemify(true, Schemifier.infoF _, ToSchemify.models: _*) + } + + + /** + * there will be a default bank and two default accounts in obp mapped mode. + * These bank and accounts will be used for the payments. + * when we create transaction request over counterparty and if the counterparty do not link to an existing obp account + * then we will use the default accounts (incoming and outgoing) to keep the money. + */ + private def createDefaultBankAndDefaultAccountsIfNotExisting() ={ + val defaultBankId= APIUtil.defaultBankId + val incomingAccountId= INCOMING_SETTLEMENT_ACCOUNT_ID + val outgoingAccountId= OUTGOING_SETTLEMENT_ACCOUNT_ID + + MappedBank.find(By(MappedBank.permalink, defaultBankId)) match { + case Full(b) => + logger.debug(s"Bank(${defaultBankId}) is found.") + case _ => + MappedBank.create + .permalink(defaultBankId) + .fullBankName("OBP_DEFAULT_BANK") + .shortBankName("OBP") + .national_identifier("OBP") + .mBankRoutingScheme("OBP") + .mBankRoutingAddress("obp1") + .logoURL("") + .websiteURL("") + .saveMe() + logger.debug(s"creating Bank(${defaultBankId})") + } + + MappedBankAccount.find(By(MappedBankAccount.bank, defaultBankId), By(MappedBankAccount.theAccountId, incomingAccountId)) match { + case Full(b) => + logger.debug(s"BankAccount(${defaultBankId}, $incomingAccountId) is found.") + case _ => + MappedBankAccount.create + .bank(defaultBankId) + .theAccountId(incomingAccountId) + .accountCurrency("EUR") + .saveMe() + logger.debug(s"creating BankAccount(${defaultBankId}, $incomingAccountId).") + } + + MappedBankAccount.find(By(MappedBankAccount.bank, defaultBankId), By(MappedBankAccount.theAccountId, outgoingAccountId)) match { + case Full(b) => + logger.debug(s"BankAccount(${defaultBankId}, $outgoingAccountId) is found.") + case _ => + MappedBankAccount.create + .bank(defaultBankId) + .theAccountId(outgoingAccountId) + .accountCurrency("EUR") + .saveMe() + logger.debug(s"creating BankAccount(${defaultBankId}, $outgoingAccountId).") + } + } + + + /** + * Bootstrap Super User + * Given the following credentials, OBP will create a user *if it does not exist already*. + * This user's password will be valid for a limited amount of time. + * This user will be granted ONLY CanCreateEntitlementAtAnyBank + * This feature can also be used in a "Break Glass scenario" + */ + private def createBootstrapSuperUser() ={ + + val superAdminUsername = APIUtil.getPropsValue("super_admin_username","") + val superAdminInitalPassword = APIUtil.getPropsValue("super_admin_inital_password","") + val superAdminEmail = APIUtil.getPropsValue("super_admin_email","") + + val isPropsNotSetProperly = superAdminUsername==""||superAdminInitalPassword ==""||superAdminEmail=="" + + //This is the logic to check if an AuthUser exists for the `create sandbox` endpoint, AfterApiAuth, OpenIdConnect ,,, + val existingAuthUser = AuthUser.find(By(AuthUser.username, superAdminUsername)) + + if(isPropsNotSetProperly) { + //Nothing happens, props is not set + }else if(existingAuthUser.isDefined) { + logger.error(s"createBootstrapSuperUser- Errors: Existing AuthUser with username ${superAdminUsername} detected in data import where no ResourceUser was found") + } else { + val authUser = AuthUser.create + .email(superAdminEmail) + .firstName(superAdminUsername) + .lastName(superAdminUsername) + .username(superAdminUsername) + .password(superAdminInitalPassword) + .passwordShouldBeChanged(true) + .validated(true) + + val validationErrors = authUser.validate + + if(!validationErrors.isEmpty) + logger.error(s"createBootstrapSuperUser- Errors: ${validationErrors.map(_.msg)}") + else { + Full(authUser.save()) //this will create/update the resourceUser. + + val userBox = Users.users.vend.getUserByProviderAndUsername(authUser.getProvider(), authUser.username.get) + + val resultBox = userBox.map(user => Entitlement.entitlement.vend.addEntitlement("", user.userId, CanCreateEntitlementAtAnyBank.toString)) + + if(resultBox.isEmpty){ + logger.error(s"createBootstrapSuperUser- Errors: ${resultBox}") + } + } + + } + + } + + +} diff --git a/obp-api/src/main/scala/bootstrap/http4s/Http4sServer.scala b/obp-api/src/main/scala/bootstrap/http4s/Http4sServer.scala new file mode 100644 index 0000000000..8a8b3366ff --- /dev/null +++ b/obp-api/src/main/scala/bootstrap/http4s/Http4sServer.scala @@ -0,0 +1,35 @@ +package bootstrap.http4s + +import cats.data.{Kleisli, OptionT} +import cats.effect._ +import code.api.util.APIUtil +import com.comcast.ip4s._ +import org.http4s._ +import org.http4s.ember.server._ +import org.http4s.implicits._ + +import scala.language.higherKinds +object Http4sServer extends IOApp { + + val services: Kleisli[({type λ[β$0$] = OptionT[IO, β$0$]})#λ, Request[IO], Response[IO]] = + code.api.v7_0_0.Http4s700.wrappedRoutesV700Services + + val httpApp: Kleisli[IO, Request[IO], Response[IO]] = (services).orNotFound + + //Start OBP relevant objects, and settings + new bootstrap.http4s.Http4sBoot().boot + + val port = APIUtil.getPropsAsIntValue("http4s.port",8181) + val host = APIUtil.getPropsValue("http4s.host","127.0.0.1") + + + override def run(args: List[String]): IO[ExitCode] = EmberServerBuilder + .default[IO] + .withHost(Host.fromString(host).get) + .withPort(Port.fromInt(port).get) + .withHttpApp(httpApp) + .build + .use(_ => IO.never) + .as(ExitCode.Success) +} + diff --git a/obp-api/src/main/scala/code/api/OAuth2.scala b/obp-api/src/main/scala/code/api/OAuth2.scala index d12934d8a0..00800f99f0 100644 --- a/obp-api/src/main/scala/code/api/OAuth2.scala +++ b/obp-api/src/main/scala/code/api/OAuth2.scala @@ -50,7 +50,7 @@ import sh.ory.hydra.model.OAuth2TokenIntrospection import java.net.URI import scala.concurrent.Future -import scala.jdk.CollectionConverters.mapAsJavaMapConverter +import scala.collection.JavaConverters._ /** * This object provides the API calls necessary to third party applications diff --git a/obp-api/src/main/scala/code/api/util/APIUtil.scala b/obp-api/src/main/scala/code/api/util/APIUtil.scala index 2a3c96e128..d6fb5dbb4f 100644 --- a/obp-api/src/main/scala/code/api/util/APIUtil.scala +++ b/obp-api/src/main/scala/code/api/util/APIUtil.scala @@ -2679,8 +2679,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ } def getDisabledVersions() : List[String] = { - val defaultDisabledVersions = "OBPv1.2.1,OBPv1.3.0,OBPv1.4.0,OBPv2.0.0,OBPv2.1.0,OBPv2.2.0,OBPv3.0.0,OBPv3.1.0,OBPv4.0.0,OBPv5.0.0" - val disabledVersions = APIUtil.getPropsValue("api_disabled_versions").getOrElse(defaultDisabledVersions).replace("[", "").replace("]", "").split(",").toList.filter(_.nonEmpty) + val disabledVersions = APIUtil.getPropsValue("api_disabled_versions").getOrElse("").replace("[", "").replace("]", "").split(",").toList.filter(_.nonEmpty) if (disabledVersions.nonEmpty) { logger.info(s"Disabled API versions: ${disabledVersions.mkString(", ")}") } diff --git a/obp-api/src/main/scala/code/api/util/CertificateVerifier.scala b/obp-api/src/main/scala/code/api/util/CertificateVerifier.scala index 4cc0a408fc..66743d4832 100644 --- a/obp-api/src/main/scala/code/api/util/CertificateVerifier.scala +++ b/obp-api/src/main/scala/code/api/util/CertificateVerifier.scala @@ -8,7 +8,7 @@ import java.security.cert._ import java.util.{Base64, Collections} import javax.net.ssl.TrustManagerFactory import scala.io.Source -import scala.jdk.CollectionConverters._ +import scala.collection.JavaConverters._ import scala.util.{Failure, Success, Try} object CertificateVerifier extends MdcLoggable { @@ -69,8 +69,8 @@ object CertificateVerifier extends MdcLoggable { trustManagerFactory.init(trustStore) // Get trusted CAs from the trust store - val trustAnchors = trustStore.aliases().asScala - .filter(trustStore.isCertificateEntry) + val trustAnchors = enumerationAsScalaIterator(trustStore.aliases()) + .filter(trustStore.isCertificateEntry(_)) .map(alias => trustStore.getCertificate(alias).asInstanceOf[X509Certificate]) .map(cert => new TrustAnchor(cert, null)) .toSet diff --git a/obp-api/src/main/scala/code/api/util/JwsUtil.scala b/obp-api/src/main/scala/code/api/util/JwsUtil.scala index fb49658cc7..57df7733c7 100644 --- a/obp-api/src/main/scala/code/api/util/JwsUtil.scala +++ b/obp-api/src/main/scala/code/api/util/JwsUtil.scala @@ -17,7 +17,7 @@ import java.time.format.DateTimeFormatter import java.time.{Duration, ZoneOffset, ZonedDateTime} import java.util import scala.collection.immutable.{HashMap, List} -import scala.jdk.CollectionConverters.seqAsJavaListConverter +import scala.collection.JavaConverters._ object JwsUtil extends MdcLoggable { diff --git a/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala index d85f3d265e..83b9c45e36 100644 --- a/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala +++ b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala @@ -5,16 +5,10 @@ import code.DynamicEndpoint.DynamicEndpointSwagger import code.accountattribute.AccountAttributeX import code.api.Constant._ import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON -import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.{ - jsonDynamicResourceDoc, - _ -} -import code.api.dynamic.endpoint.helper.practise.{ - DynamicEndpointCodeGenerator, - PractiseEndpoint -} +import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.{jsonDynamicResourceDoc, _} +import code.api.dynamic.endpoint.helper.practise.{DynamicEndpointCodeGenerator, PractiseEndpoint} import code.api.dynamic.endpoint.helper.{CompiledObjects, DynamicEndpointHelper} -import code.api.dynamic.entity.helper.{DynamicEntityHelper, DynamicEntityInfo} +import code.api.dynamic.entity.helper.DynamicEntityInfo import code.api.util.APIUtil.{fullBoxOrException, _} import code.api.util.ApiRole._ import code.api.util.ApiTag._ @@ -31,20 +25,11 @@ import code.api.util.migration.Migration import code.api.util.newstyle.AttributeDefinition._ import code.api.util.newstyle.Consumer._ import code.api.util.newstyle.UserCustomerLinkNewStyle.getUserCustomerLinks -import code.api.util.newstyle.{ - BalanceNewStyle, - UserCustomerLinkNewStyle, - ViewNewStyle -} +import code.api.util.newstyle.{BalanceNewStyle, UserCustomerLinkNewStyle, ViewNewStyle} import code.api.v1_2_1.{JSONFactory, PostTransactionTagJSON} import code.api.v1_4_0.JSONFactory1_4_0 import code.api.v2_0_0.OBPAPI2_0_0.Implementations2_0_0 -import code.api.v2_0_0.{ - CreateEntitlementJSON, - CreateUserCustomerLinkJson, - EntitlementJSONs, - JSONFactory200 -} +import code.api.v2_0_0.{CreateEntitlementJSON, CreateUserCustomerLinkJson, EntitlementJSONs, JSONFactory200} import code.api.v2_1_0._ import code.api.v3_0_0.{CreateScopeJson, JSONFactory300} import code.api.v3_1_0._ @@ -54,15 +39,10 @@ import code.apicollection.MappedApiCollectionsProvider import code.apicollectionendpoint.MappedApiCollectionEndpointsProvider import code.authtypevalidation.JsonAuthTypeValidation import code.bankconnectors.LocalMappedConnectorInternal._ -import code.bankconnectors.{ - Connector, - DynamicConnector, - InternalConnector, - LocalMappedConnectorInternal -} +import code.bankconnectors.{Connector, DynamicConnector, InternalConnector, LocalMappedConnectorInternal} import code.connectormethod.{JsonConnectorMethod, JsonConnectorMethodMethodBody} import code.consent.{ConsentStatus, Consents} -import code.dynamicEntity.{DynamicEntityCommons, ReferenceType} +import code.dynamicEntity.DynamicEntityCommons import code.dynamicMessageDoc.JsonDynamicMessageDoc import code.dynamicResourceDoc.JsonDynamicResourceDoc import code.endpointMapping.EndpointMappingCommons @@ -82,10 +62,7 @@ import code.util.Helper.{MdcLoggable, ObpS, SILENCE_IS_GOLDEN, booleanToFuture} import code.util.{Helper, JsonSchemaUtil} import code.validation.JsonValidation import code.views.Views -import code.webhook.{ - BankAccountNotificationWebhookTrait, - SystemAccountNotificationWebhookTrait -} +import code.webhook.{BankAccountNotificationWebhookTrait, SystemAccountNotificationWebhookTrait} import code.webuiprops.MappedWebUiPropsProvider.getWebUiPropsValue import com.github.dwickern.macros.NameOf.nameOf import com.networknt.schema.ValidationMessage @@ -110,10 +87,10 @@ import java.net.URLEncoder import java.text.SimpleDateFormat import java.util import java.util.{Calendar, Date} +import scala.collection.JavaConverters._ import scala.collection.immutable.{List, Nil} import scala.collection.mutable.ArrayBuffer import scala.concurrent.Future -import scala.jdk.CollectionConverters.collectionAsScalaIterableConverter trait APIMethods400 extends MdcLoggable { self: RestHelper => @@ -11796,10 +11773,10 @@ trait APIMethods400 extends MdcLoggable { Future { val versions: List[ScannedApiVersion] = ApiVersion.allScannedApiVersion.asScala.toList.filter { version => - version.urlPrefix.trim.nonEmpty + version.urlPrefix.trim.nonEmpty && APIUtil.versionIsAllowed(version) } ( - ListResult("scanned_api_versions", versions), + ListResult("scanned_api_versions", versions), HttpCode.`200`(cc.callContext) ) } diff --git a/obp-api/src/main/scala/code/api/v6_0_0/APIMethods600.scala b/obp-api/src/main/scala/code/api/v6_0_0/APIMethods600.scala index bb32e82aac..c592809a1e 100644 --- a/obp-api/src/main/scala/code/api/v6_0_0/APIMethods600.scala +++ b/obp-api/src/main/scala/code/api/v6_0_0/APIMethods600.scala @@ -65,7 +65,7 @@ import scala.collection.immutable.{List, Nil} import scala.collection.mutable.ArrayBuffer import scala.concurrent.Future import scala.concurrent.duration._ -import scala.jdk.CollectionConverters._ +import scala.collection.JavaConverters._ import scala.util.Random @@ -3486,7 +3486,7 @@ trait APIMethods600 { | |""", EmptyBody, - WebUiPropsCommons("webui_api_explorer_url", "https://apiexplorer.openbankproject.com", Some("web-ui-props-id"), "database"), + WebUiPropsCommons("webui_api_explorer_url", "https://apiexplorer.openbankproject.com", Some("web-ui-props-id"), Some("config")), List( WebUiPropsNotFoundByName, UnknownError @@ -3509,11 +3509,11 @@ trait APIMethods600 { explicitProp match { case Some(prop) => // Found in database - Future.successful(WebUiPropsCommons(prop.name, prop.value, prop.webUiPropsId, source = "database")) + Future.successful(WebUiPropsCommons(prop.name, prop.value, prop.webUiPropsId, source = Some("database"))) case None if isActived => // Not in database, check implicit props if active=true val implicitWebUiProps = getWebUIPropsPairs.map(webUIPropsPairs => - WebUiPropsCommons(webUIPropsPairs._1, webUIPropsPairs._2, webUiPropsId = None, source = "config") + WebUiPropsCommons(webUIPropsPairs._1, webUIPropsPairs._2, webUiPropsId = Some("default"), source = Some("config")) ) val implicitProp = implicitWebUiProps.find(_.name == webUiPropName) implicitProp match { @@ -3584,7 +3584,7 @@ trait APIMethods600 { EmptyBody, ListResult( "webui_props", - (List(WebUiPropsCommons("webui_api_explorer_url", "https://apiexplorer.openbankproject.com", Some("web-ui-props-id"), "database"))) + (List(WebUiPropsCommons("webui_api_explorer_url", "https://apiexplorer.openbankproject.com", Some("web-ui-props-id"), Some("database")))) ) , List( @@ -3608,8 +3608,8 @@ trait APIMethods600 { } } explicitWebUiProps <- Future{ MappedWebUiPropsProvider.getAll() } - explicitWebUiPropsWithSource = explicitWebUiProps.map(prop => WebUiPropsCommons(prop.name, prop.value, prop.webUiPropsId, source = "database")) - implicitWebUiProps = getWebUIPropsPairs.map(webUIPropsPairs=>WebUiPropsCommons(webUIPropsPairs._1, webUIPropsPairs._2, webUiPropsId = None, source = "config")) + explicitWebUiPropsWithSource = explicitWebUiProps.map(prop => WebUiPropsCommons(prop.name, prop.value, prop.webUiPropsId, source = Some("database"))) + implicitWebUiProps = getWebUIPropsPairs.map(webUIPropsPairs=>WebUiPropsCommons(webUIPropsPairs._1, webUIPropsPairs._2, webUiPropsId = Some("default"), source = Some("config"))) result = what match { case "database" => // Return only database props diff --git a/obp-api/src/main/scala/code/api/v7_0_0/Http4s700.scala b/obp-api/src/main/scala/code/api/v7_0_0/Http4s700.scala new file mode 100644 index 0000000000..877b91b72d --- /dev/null +++ b/obp-api/src/main/scala/code/api/v7_0_0/Http4s700.scala @@ -0,0 +1,72 @@ +package code.api.v7_0_0 + +import cats.data.{Kleisli, OptionT} +import cats.effect._ +import cats.implicits._ +import code.api.util.{APIUtil, CustomJsonFormats} +import code.api.v4_0_0.JSONFactory400 +import code.bankconnectors.Connector +import com.openbankproject.commons.util.{ApiVersion, ScannedApiVersion} +import net.liftweb.json.Formats +import net.liftweb.json.JsonAST.prettyRender +import net.liftweb.json.Extraction +import org.http4s._ +import org.http4s.dsl.io._ +import org.typelevel.vault.Key + +import scala.concurrent.Future +import scala.language.{higherKinds, implicitConversions} + +object Http4s700 { + + type HttpF[A] = OptionT[IO, A] + + implicit val formats: Formats = CustomJsonFormats.formats + implicit def convertAnyToJsonString(any: Any): String = prettyRender(Extraction.decompose(any)) + + val apiVersion: ScannedApiVersion = ApiVersion.v7_0_0 + val apiVersionString: String = apiVersion.toString + + case class CallContext(userId: String, requestId: String) + import cats.effect.unsafe.implicits.global + val callContextKey: Key[CallContext] = Key.newKey[IO, CallContext].unsafeRunSync() + + object CallContextMiddleware { + + def withCallContext(routes: HttpRoutes[IO]): HttpRoutes[IO] = + Kleisli[HttpF, Request[IO], Response[IO]] { req: Request[IO] => + val callContext = CallContext(userId = "example-user", requestId = java.util.UUID.randomUUID().toString) + val updatedAttributes = req.attributes.insert(callContextKey, callContext) + val updatedReq = req.withAttributes(updatedAttributes) + routes(updatedReq) + } + } + + val v700Services: HttpRoutes[IO] = HttpRoutes.of[IO] { + case req @ GET -> Root / "obp" / `apiVersionString` / "root" => + import com.openbankproject.commons.ExecutionContext.Implicits.global + val callContext = req.attributes.lookup(callContextKey).get.asInstanceOf[CallContext] + Ok(IO.fromFuture(IO( + for { + _ <- Future() // Just start async call + } yield { + convertAnyToJsonString( + JSONFactory700.getApiInfoJSON(apiVersion, s"Hello, ${callContext.userId}! Your request ID is ${callContext.requestId}.") + ) + } + ))) + + case req @ GET -> Root / "obp" / `apiVersionString` / "banks" => + import com.openbankproject.commons.ExecutionContext.Implicits.global + Ok(IO.fromFuture(IO( + for { + (banks, callContext) <- code.api.util.NewStyle.function.getBanks(None) + } yield { + convertAnyToJsonString(JSONFactory400.createBanksJson(banks)) + } + ))) + } + + val wrappedRoutesV700Services: HttpRoutes[IO] = CallContextMiddleware.withCallContext(v700Services) +} + diff --git a/obp-api/src/main/scala/code/api/v7_0_0/JSONFactory7.0.0.scala b/obp-api/src/main/scala/code/api/v7_0_0/JSONFactory7.0.0.scala new file mode 100644 index 0000000000..a675842e65 --- /dev/null +++ b/obp-api/src/main/scala/code/api/v7_0_0/JSONFactory7.0.0.scala @@ -0,0 +1,72 @@ +package code.api.v7_0_0 + +import code.api.Constant +import code.api.util.APIUtil +import code.api.util.ErrorMessages.MandatoryPropertyIsNotSet +import code.api.v4_0_0.{EnergySource400, HostedAt400, HostedBy400} +import code.util.Helper.MdcLoggable +import com.openbankproject.commons.util.ApiVersion +import net.liftweb.util.Props + +object JSONFactory700 extends MdcLoggable { + + // Get git commit from build info + lazy val gitCommit: String = { + val commit = try { + Props.get("git.commit.id", "unknown") + } catch { + case _: Throwable => "unknown" + } + commit + } + + case class APIInfoJsonV700( + version: String, + version_status: String, + git_commit: String, + stage: String, + connector: String, + hostname: String, + local_identity_provider: String, + hosted_by: HostedBy400, + hosted_at: HostedAt400, + energy_source: EnergySource400, + resource_docs_requires_role: Boolean, + message: String + ) + + def getApiInfoJSON(apiVersion: ApiVersion, message: String): APIInfoJsonV700 = { + val organisation = APIUtil.getPropsValue("hosted_by.organisation", "TESOBE") + val email = APIUtil.getPropsValue("hosted_by.email", "contact@tesobe.com") + val phone = APIUtil.getPropsValue("hosted_by.phone", "+49 (0)30 8145 3994") + val organisationWebsite = APIUtil.getPropsValue("organisation_website", "https://www.tesobe.com") + val hostedBy = new HostedBy400(organisation, email, phone, organisationWebsite) + + val organisationHostedAt = APIUtil.getPropsValue("hosted_at.organisation", "") + val organisationWebsiteHostedAt = APIUtil.getPropsValue("hosted_at.organisation_website", "") + val hostedAt = HostedAt400(organisationHostedAt, organisationWebsiteHostedAt) + + val organisationEnergySource = APIUtil.getPropsValue("energy_source.organisation", "") + val organisationWebsiteEnergySource = APIUtil.getPropsValue("energy_source.organisation_website", "") + val energySource = EnergySource400(organisationEnergySource, organisationWebsiteEnergySource) + + val connector = code.api.Constant.CONNECTOR.openOrThrowException(s"$MandatoryPropertyIsNotSet. The missing prop is `connector` ") + val resourceDocsRequiresRole = APIUtil.getPropsAsBoolValue("resource_docs_requires_role", false) + + APIInfoJsonV700( + version = apiVersion.vDottedApiVersion, + version_status = "BLEEDING_EDGE", + git_commit = gitCommit, + connector = connector, + hostname = Constant.HostName, + stage = System.getProperty("run.mode"), + local_identity_provider = Constant.localIdentityProvider, + hosted_by = hostedBy, + hosted_at = hostedAt, + energy_source = energySource, + resource_docs_requires_role = resourceDocsRequiresRole, + message = message + ) + } +} + diff --git a/obp-api/src/main/scala/code/bankconnectors/generator/ConnectorBuilderUtil.scala b/obp-api/src/main/scala/code/bankconnectors/generator/ConnectorBuilderUtil.scala index 95d94df174..5a1438be1f 100644 --- a/obp-api/src/main/scala/code/bankconnectors/generator/ConnectorBuilderUtil.scala +++ b/obp-api/src/main/scala/code/bankconnectors/generator/ConnectorBuilderUtil.scala @@ -10,7 +10,7 @@ import org.apache.commons.lang3.StringUtils.uncapitalize import java.io.File import java.net.URL import java.util.Date -import scala.jdk.CollectionConverters.enumerationAsScalaIteratorConverter +import scala.collection.JavaConverters._ import scala.language.postfixOps import scala.reflect.runtime.universe._ import scala.reflect.runtime.{universe => ru} diff --git a/obp-api/src/main/scala/code/snippet/ConsentScreen.scala b/obp-api/src/main/scala/code/snippet/ConsentScreen.scala index 1871517b45..b4be08fecc 100644 --- a/obp-api/src/main/scala/code/snippet/ConsentScreen.scala +++ b/obp-api/src/main/scala/code/snippet/ConsentScreen.scala @@ -45,7 +45,7 @@ import net.liftweb.util.Helpers._ import sh.ory.hydra.api.AdminApi import sh.ory.hydra.model.{AcceptConsentRequest, RejectRequest} -import scala.jdk.CollectionConverters.seqAsJavaListConverter +import scala.collection.JavaConverters._ import scala.xml.NodeSeq diff --git a/obp-api/src/main/scala/code/snippet/ConsumerRegistration.scala b/obp-api/src/main/scala/code/snippet/ConsumerRegistration.scala index b79da7d558..daddd1a293 100644 --- a/obp-api/src/main/scala/code/snippet/ConsumerRegistration.scala +++ b/obp-api/src/main/scala/code/snippet/ConsumerRegistration.scala @@ -43,7 +43,7 @@ import org.apache.commons.lang3.StringUtils import org.codehaus.jackson.map.ObjectMapper import scala.collection.immutable.{List, ListMap} -import scala.jdk.CollectionConverters.seqAsJavaListConverter +import scala.collection.JavaConverters._ import scala.xml.{Text, Unparsed} class ConsumerRegistration extends MdcLoggable { diff --git a/obp-api/src/main/scala/code/util/ClassScanUtils.scala b/obp-api/src/main/scala/code/util/ClassScanUtils.scala index 22c070656f..a57f10f063 100644 --- a/obp-api/src/main/scala/code/util/ClassScanUtils.scala +++ b/obp-api/src/main/scala/code/util/ClassScanUtils.scala @@ -35,7 +35,15 @@ object ClassScanUtils { */ def getSubTypeObjects[T:TypeTag]: List[T] = { val clazz = ReflectUtils.typeTagToClass[T] - finder.getClasses().filter(_.implements(clazz.getName)).map(_.name).map(companion[T](_)).toList + val classes = try { + finder.getClasses().toList + } catch { + case _: UnsupportedOperationException => + // ASM version is too old for some class files (e.g. requires ASM7). In that case, + // skip scanned APIs instead of failing the whole application. + Seq.empty + } + classes.filter(_.implements(clazz.getName)).map(_.name).map(companion[T](_)).toList } /** @@ -43,14 +51,22 @@ object ClassScanUtils { * @param predict check whether include this type in the result * @return all fit type names */ - def findTypes(predict: ClassInfo => Boolean): List[String] = finder.getClasses() - .filter(predict) - .map(it => { - val name = it.name - if(name.endsWith("$")) name.substring(0, name.length - 1) - else name - }) //some companion type name ends with $, it added by scalac, should remove from class name - .toList + def findTypes(predict: ClassInfo => Boolean): List[String] = { + val classes = try { + finder.getClasses().toList + } catch { + case _: UnsupportedOperationException => + Seq.empty + } + classes + .filter(predict) + .map(it => { + val name = it.name + if(name.endsWith("$")) name.substring(0, name.length - 1) + else name + }) //some companion type name ends with $, it added by scalac, should remove from class name + .toList + } /** * get given class exists jar Files @@ -71,7 +87,13 @@ object ClassScanUtils { */ def getMappers(packageName:String = ""): Seq[ClassInfo] = { val mapperInterface = "net.liftweb.mapper.LongKeyedMapper" - val infos = finder.getClasses().filter(it => it.interfaces.contains(mapperInterface)) + val classes = try { + finder.getClasses().toList + } catch { + case _: UnsupportedOperationException => + Seq.empty + } + val infos = classes.filter(it => it.interfaces.contains(mapperInterface)) if(StringUtils.isNoneBlank()) { infos.filter(classInfo => classInfo.name.startsWith(packageName)) } else { diff --git a/obp-api/src/main/scala/code/util/HydraUtil.scala b/obp-api/src/main/scala/code/util/HydraUtil.scala index 1fdf5f6fbb..a6b1627ed1 100644 --- a/obp-api/src/main/scala/code/util/HydraUtil.scala +++ b/obp-api/src/main/scala/code/util/HydraUtil.scala @@ -15,7 +15,7 @@ import sh.ory.hydra.model.OAuth2Client import sh.ory.hydra.{ApiClient, Configuration} import scala.collection.immutable.List -import scala.jdk.CollectionConverters.{mapAsJavaMapConverter, seqAsJavaListConverter} +import scala.collection.JavaConverters._ object HydraUtil extends MdcLoggable{ diff --git a/obp-api/src/main/scala/code/webuiprops/MappedWebUiPropsProvider.scala b/obp-api/src/main/scala/code/webuiprops/MappedWebUiPropsProvider.scala index eb7115ee9d..85e77cb5d9 100644 --- a/obp-api/src/main/scala/code/webuiprops/MappedWebUiPropsProvider.scala +++ b/obp-api/src/main/scala/code/webuiprops/MappedWebUiPropsProvider.scala @@ -77,6 +77,7 @@ class WebUiProps extends WebUiPropsT with LongKeyedMapper[WebUiProps] with IdPK override def webUiPropsId: Option[String] = Option(WebUiPropsId.get) override def name: String = Name.get override def value: String = Value.get + override def source: Option[String] = Some("database") } object WebUiProps extends WebUiProps with LongKeyedMetaMapper[WebUiProps] { diff --git a/obp-api/src/main/scala/code/webuiprops/WebUiProps.scala b/obp-api/src/main/scala/code/webuiprops/WebUiProps.scala index 409be6f4e2..cd1763017f 100644 --- a/obp-api/src/main/scala/code/webuiprops/WebUiProps.scala +++ b/obp-api/src/main/scala/code/webuiprops/WebUiProps.scala @@ -9,12 +9,13 @@ trait WebUiPropsT { def webUiPropsId: Option[String] def name: String def value: String + def source: Option[String] } case class WebUiPropsCommons(name: String, value: String, webUiPropsId: Option[String] = None, - source: String = "database") extends WebUiPropsT with JsonFieldReName + source: Option[String] = None) extends WebUiPropsT with JsonFieldReName object WebUiPropsCommons extends Converter[WebUiPropsT, WebUiPropsCommons] diff --git a/obp-api/src/test/resources/frozen_type_meta_data b/obp-api/src/test/resources/frozen_type_meta_data index 79531b8932..0ddfe92ded 100644 Binary files a/obp-api/src/test/resources/frozen_type_meta_data and b/obp-api/src/test/resources/frozen_type_meta_data differ diff --git a/obp-api/src/test/scala/code/api/v4_0_0/GetScannedApiVersionsTest.scala b/obp-api/src/test/scala/code/api/v4_0_0/GetScannedApiVersionsTest.scala index dadf79cdd3..a87c0e36f0 100644 --- a/obp-api/src/test/scala/code/api/v4_0_0/GetScannedApiVersionsTest.scala +++ b/obp-api/src/test/scala/code/api/v4_0_0/GetScannedApiVersionsTest.scala @@ -25,6 +25,7 @@ TESOBE (http://www.tesobe.com/) */ package code.api.v4_0_0 +import code.api.util.APIUtil import code.api.util.ApiRole._ import code.api.v4_0_0.APIMethods400.Implementations4_0_0 import code.entitlement.Entitlement @@ -33,7 +34,7 @@ import com.openbankproject.commons.model.ListResult import com.openbankproject.commons.util.{ApiVersion, ScannedApiVersion} import org.scalatest.Tag -import scala.jdk.CollectionConverters.collectionAsScalaIterableConverter +import scala.collection.JavaConverters._ class GetScannedApiVersionsTest extends V400ServerSetup { /** @@ -46,8 +47,63 @@ class GetScannedApiVersionsTest extends V400ServerSetup { object VersionOfApi extends Tag(ApiVersion.v4_0_0.toString) object ApiEndpoint extends Tag(nameOf(Implementations4_0_0.getScannedApiVersions)) + feature("test props-api_disabled_versions, Get all scanned API versions should works") { + scenario("We get all the scanned API versions with disabled versions filtered out", ApiEndpoint, VersionOfApi) { + // api_disabled_versions=[OBPv3.0.0,BGv1.3] + setPropsValues("api_disabled_versions"-> "[OBPv3.0.0,BGv1.3]") + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanCreateSystemLevelDynamicEntity.toString) + When("We make a request v4.0.0") + val request = (v4_0_0_Request / "api" / "versions").GET + + val response = makeGetRequest(request) + Then("We should get a 200") + response.code should equal(200) + + val listResult = response.body.extract[ListResult[List[ScannedApiVersion]]] + val responseApiVersions = listResult.results + val scannedApiVersions = ApiVersion.allScannedApiVersion.asScala.toList.filter { version => + version.urlPrefix.trim.nonEmpty && APIUtil.versionIsAllowed(version) + } + + responseApiVersions should equal(scannedApiVersions) + + // Verify that disabled versions are not included + responseApiVersions.exists(_.fullyQualifiedVersion == "OBPv3.0.0") should equal(false) + responseApiVersions.exists(_.fullyQualifiedVersion == "BGv1.3") should equal(false) + + } + } + + feature("test props-api_enabled_versions, Get all scanned API versions should works") { + scenario("We get all the scanned API versions with disabled versions filtered out", ApiEndpoint, VersionOfApi) { + // api_enabled_versions=[OBPv2.2.0,OBPv3.0.0,UKv2.0] + setPropsValues("api_enabled_versions"-> "[OBPv2.2.0,OBPv3.0.0,UKv2.0]") + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanCreateSystemLevelDynamicEntity.toString) + When("We make a request v4.0.0") + val request = (v4_0_0_Request / "api" / "versions").GET + + val response = makeGetRequest(request) + Then("We should get a 200") + response.code should equal(200) + + val listResult = response.body.extract[ListResult[List[ScannedApiVersion]]] + val responseApiVersions = listResult.results + val scannedApiVersions = ApiVersion.allScannedApiVersion.asScala.toList.filter { version => + version.urlPrefix.trim.nonEmpty && APIUtil.versionIsAllowed(version) + } + + responseApiVersions should equal(scannedApiVersions) + // Verify that disabled versions are not included + responseApiVersions.exists(_.fullyQualifiedVersion == "OBPv2.2.0") should equal(true) + responseApiVersions.exists(_.fullyQualifiedVersion == "OBPv3.0.0") should equal(true) + responseApiVersions.exists(_.fullyQualifiedVersion == "UKv2.0") should equal(true) + + } + } + feature("Get all scanned API versions should works") { + scenario("We get all the scanned API versions", ApiEndpoint, VersionOfApi) { Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanCreateSystemLevelDynamicEntity.toString) When("We make a request v4.0.0") diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/util/ApiVersion.scala b/obp-commons/src/main/scala/com/openbankproject/commons/util/ApiVersion.scala index 0a5617d18a..6173ec700c 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/util/ApiVersion.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/util/ApiVersion.scala @@ -23,6 +23,7 @@ object ApiShortVersions extends Enumeration { val `v5.0.0` = Value("v5.0.0") val `v5.1.0` = Value("v5.1.0") val `v6.0.0` = Value("v6.0.0") + val `v7.0.0` = Value("v7.0.0") val `dynamic-endpoint` = Value("dynamic-endpoint") val `dynamic-entity` = Value("dynamic-entity") } @@ -114,6 +115,7 @@ object ApiVersion { val v5_0_0 = ScannedApiVersion(urlPrefix,ApiStandards.obp.toString,ApiShortVersions.`v5.0.0`.toString) val v5_1_0 = ScannedApiVersion(urlPrefix,ApiStandards.obp.toString,ApiShortVersions.`v5.1.0`.toString) val v6_0_0 = ScannedApiVersion(urlPrefix,ApiStandards.obp.toString,ApiShortVersions.`v6.0.0`.toString) + val v7_0_0 = ScannedApiVersion(urlPrefix,ApiStandards.obp.toString,ApiShortVersions.`v7.0.0`.toString) val `dynamic-endpoint` = ScannedApiVersion(urlPrefix,ApiStandards.obp.toString,ApiShortVersions.`dynamic-endpoint`.toString) val `dynamic-entity` = ScannedApiVersion(urlPrefix,ApiStandards.obp.toString,ApiShortVersions.`dynamic-entity`.toString) @@ -131,6 +133,7 @@ object ApiVersion { v5_0_0 :: v5_1_0 :: v6_0_0 :: + v7_0_0 :: `dynamic-endpoint` :: `dynamic-entity`:: Nil diff --git a/obp-http4s-runner/pom.xml b/obp-http4s-runner/pom.xml new file mode 100644 index 0000000000..c1bf115359 --- /dev/null +++ b/obp-http4s-runner/pom.xml @@ -0,0 +1,71 @@ + + 4.0.0 + + + com.tesobe + obp-parent + 1.10.1 + ../pom.xml + + + obp-http4s-runner + jar + OBP Http4s Runner + + + + + com.tesobe + obp-api + ${project.version} + classes + jar + + + + org.clapper + classutil_${scala.version} + + + + + + org.clapper + classutil_${scala.version} + 1.5.1 + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.6.0 + + + + bootstrap.http4s.Http4sServer + + + + jar-with-dependencies + + false + obp-http4s-runner + + + + make-fat-jar + package + + single + + + + + + + + diff --git a/pom.xml b/pom.xml index 2d8cadc37d..da476faed2 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,7 @@ 2.5.32 1.8.2 3.5.0 + 0.23.30 9.4.50.v20221201 2016.11-RC6-SNAPSHOT @@ -34,6 +35,7 @@ obp-commons obp-api + obp-http4s-runner @@ -126,7 +128,7 @@ net.alchim31.maven scala-maven-plugin - 4.3.1 + 4.8.1 ${scala.compiler} ${project.build.sourceEncoding} @@ -143,6 +145,7 @@ -verbose -deprecation --> + -Ypartial-unification