diff --git a/obp-api/pom.xml b/obp-api/pom.xml index 5f62fa3a73..66bf746e62 100644 --- a/obp-api/pom.xml +++ b/obp-api/pom.xml @@ -20,11 +20,6 @@ obp-commons - - com.github.everit-org.json-schema - org.everit.json.schema - 1.6.1 - @@ -41,6 +36,7 @@ net.databinder.dispatch dispatch-core_${scala.version} 0.13.1 + test org.json4s @@ -121,21 +117,20 @@ protobuf-java 3.25.5 - + + org.apache.avro + avro + ${apache.avro.version} + + org.xerial.snappy snappy-java 1.1.10.4 - - - commons-beanutils - commons-beanutils - 1.11.0 - - + com.vladsch.flexmark flexmark-profile-pegdown 0.40.8 - - - com.vladsch.flexmark - flexmark-util-options - 0.64.0 - + org.web3j @@ -422,6 +407,7 @@ org.asynchttpclient async-http-client 2.15.0 + test javax.activation @@ -514,11 +500,12 @@ jackson-databind - + - tools.jackson.dataformat + com.fasterxml.jackson.dataformat jackson-dataformat-yaml - 3.0.4 diff --git a/obp-api/src/main/scala/code/api/dynamic/endpoint/OBPAPIDynamicEndpoint.scala b/obp-api/src/main/scala/code/api/dynamic/endpoint/OBPAPIDynamicEndpoint.scala index d3dd7b4700..cf2ce89f8e 100644 --- a/obp-api/src/main/scala/code/api/dynamic/endpoint/OBPAPIDynamicEndpoint.scala +++ b/obp-api/src/main/scala/code/api/dynamic/endpoint/OBPAPIDynamicEndpoint.scala @@ -28,12 +28,9 @@ package code.api.dynamic.endpoint import APIMethodsDynamicEndpoint.ImplementationsDynamicEndpoint import code.api.OBPRestHelper -import code.api.dynamic.endpoint.helper.DynamicEndpoints -import code.api.util.{APIUtil, VersionedOBPApis} +import code.api.util.VersionedOBPApis import code.util.Helper.MdcLoggable import com.openbankproject.commons.util.{ApiVersion,ApiVersionStatus} -import net.liftweb.common.{Box, Full} -import org.apache.http.HttpStatus /* This file defines which endpoints from all the versions are available in v4.0.0 diff --git a/obp-api/src/main/scala/code/api/dynamic/entity/projection/ProjectionDualWrite.scala b/obp-api/src/main/scala/code/api/dynamic/entity/projection/ProjectionDualWrite.scala index d59fb43ab9..b9420802ed 100644 --- a/obp-api/src/main/scala/code/api/dynamic/entity/projection/ProjectionDualWrite.scala +++ b/obp-api/src/main/scala/code/api/dynamic/entity/projection/ProjectionDualWrite.scala @@ -9,13 +9,13 @@ import org.json4s.JsonAST.JObject /** * Keeps a record's projection row in sync on the write path (DE_indexing, Phase 3). Guarded by * `projectionEnabled` and a no-op unless the entity has a `ready` projection — so it changes nothing - * by default. Uses `DoobieUtil.runQuery`, which reuses Lift's request connection, so the projection - * upsert/delete participates in the SAME transaction as the canonical blob write (commit/rollback - * together). Scalar fields only (spatial dual-write is Phase 4). + * by default. Uses `DoobieUtil.runUpdate` so the INSERT is committed even when called outside an + * explicit request-scope transaction (e.g. dynamic-entity POST handlers that don't wrap in + * `withBusinessDBTransaction`). When a request-scope proxy IS present, `runUpdate` still reuses it + * via `transactorFromConnection`. Scalar fields only (spatial dual-write is Phase 4). */ object ProjectionDualWrite extends MdcLoggable { - /** Upsert the record's ready indexed scalar columns into the projection (called after the blob save). */ def onSave(bankId: Option[String], entityName: String, dataId: String, body: JObject): Unit = withReadyScalarFields(bankId, entityName) { (safeTable, fields) => val cols = fields.map { case (f, spec) => @@ -24,13 +24,12 @@ object ProjectionDualWrite extends MdcLoggable { ProjectionDDL.sqlColumnType(spec.fieldType.toString), ProjectionCoerce.toColumnValue(body \ f, spec.fieldType)) } - DoobieUtil.runQuery(ProjectionStore.upsert(safeTable, dataId, cols)) + DoobieUtil.runUpdate(ProjectionStore.upsert(safeTable, dataId, cols)) } - /** Delete the record's projection row (called after the blob delete; FK cascade is a backstop). */ def onDelete(bankId: Option[String], entityName: String, dataId: String): Unit = withReadyScalarFields(bankId, entityName) { (safeTable, _) => - DoobieUtil.runQuery(ProjectionStore.delete(safeTable, dataId)) + DoobieUtil.runUpdate(ProjectionStore.delete(safeTable, dataId)) } private def withReadyScalarFields(bankId: Option[String], entityName: String) 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 fe93b9a8d1..8daef9ec2d 100644 --- a/obp-api/src/main/scala/code/api/util/APIUtil.scala +++ b/obp-api/src/main/scala/code/api/util/APIUtil.scala @@ -1416,34 +1416,12 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ //ended -- Filtering and Paging relevant methods //////////////////////////// - /** Import this object's methods to add signing operators to dispatch.Request */ object OAuth { - import dispatch.{Req => Request} - - import scala.collection.Map - case class Consumer(key: String, secret: String) case class Token(value: String, secret: String) /** Out-of-band callback code */ val oob = "oob" - - /** Add OAuth operators to dispatch.Request */ - implicit def Request2RequestSigner(r: Request) = new RequestSigner(r) - - class RequestSigner(rb: Request) { - /** sign a request with a consumer and a token, e.g. an OAuth-signed API request */ - def <@ (consumer: Consumer, token: Token): Request = { - rb <:< Map("Authorization" -> s"""DirectLogin token="${token.value}"""") - } - def <@ (consumerAndToken: Option[(Consumer,Token)]): Request = { - consumerAndToken match { - case Some((_, token)) => - rb <:< Map("Authorization" -> s"""DirectLogin token="${token.value}"""") - case None => rb - } - } - } } /* diff --git a/obp-api/src/main/scala/code/api/util/JwtUtil.scala b/obp-api/src/main/scala/code/api/util/JwtUtil.scala index edaa27069d..3aec89156e 100644 --- a/obp-api/src/main/scala/code/api/util/JwtUtil.scala +++ b/obp-api/src/main/scala/code/api/util/JwtUtil.scala @@ -15,7 +15,6 @@ import com.nimbusds.jose.util.{DefaultResourceRetriever, JSONObjectUtils} import com.nimbusds.jwt.proc.{BadJWTException, DefaultJWTProcessor} import com.nimbusds.jwt.{JWTClaimsSet, SignedJWT} import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet -import dispatch.Future import net.liftweb.common.{Box, Empty, Failure, Full} object JwtUtil extends MdcLoggable { diff --git a/obp-api/src/main/scala/code/bankconnectors/AvroSerializer.scala b/obp-api/src/main/scala/code/bankconnectors/AvroSerializer.scala index 8dec75f379..9d75703790 100644 --- a/obp-api/src/main/scala/code/bankconnectors/AvroSerializer.scala +++ b/obp-api/src/main/scala/code/bankconnectors/AvroSerializer.scala @@ -5,37 +5,34 @@ import java.io.{ByteArrayOutputStream, InputStream} import com.sksamuel.avro4s._ import scala.concurrent.{ExecutionContext, Future} -import scala.util.Try +import scala.util.Success -/** - * Provides generic serialization/deserialization - */ trait AvroSerializer { - def serialize[T: SchemaFor : ToRecord](event: T)(implicit executionContext: ExecutionContext): String = { + def serialize[T: Encoder](event: T)(implicit executionContext: ExecutionContext): String = { val baos = new ByteArrayOutputStream() - val output = AvroOutputStream.json[T](baos) + val output = AvroOutputStream.json[T].to(baos).build() output.write(event) output.close() - val r = baos.toString("UTF-8") - r + baos.toString("UTF-8") } - def serializeFuture[T: SchemaFor : ToRecord](event: T)(implicit executionContext: ExecutionContext): Future[String] = + def serializeFuture[T: Encoder](event: T)(implicit executionContext: ExecutionContext): Future[String] = Future(serialize(event)) - def deserializeFuture[T >: Null : SchemaFor : FromRecord](data: String)(implicit executionContext: ExecutionContext): Future[Option[T]] = + def deserializeFuture[T >: Null : Decoder](data: String)(implicit executionContext: ExecutionContext): Future[Option[T]] = Future(deserialize[T](data)) - def deserialize[T >: Null : SchemaFor : FromRecord](data: String)(implicit executionContext: ExecutionContext): Option[T] = { - - val input = AvroInputStream.json[T](new StringInputStream(data)) - val result: Try[T] = input.singleEntity - result.toOption + def deserialize[T >: Null : Decoder](data: String)(implicit executionContext: ExecutionContext): Option[T] = { + val schema = implicitly[Decoder[T]].schema + val input = AvroInputStream.json[T].from(new StringInputStream(data)).build(schema) + val result = input.tryIterator.collectFirst { case Success(v) => v } + input.close() + result } class StringInputStream(s: String) extends InputStream { - private val bytes = s.getBytes + private val bytes = s.getBytes("UTF-8") private var pos = 0 @@ -44,7 +41,7 @@ trait AvroSerializer { } else { val r = bytes(pos) pos += 1 - r.toInt + r.toInt & 0xFF } } -} \ No newline at end of file +} diff --git a/obp-api/src/main/scala/code/bankconnectors/akka/AkkaConnector_vDec2018.scala b/obp-api/src/main/scala/code/bankconnectors/akka/AkkaConnector_vDec2018.scala index 0443a26d75..89c163e8c1 100644 --- a/obp-api/src/main/scala/code/bankconnectors/akka/AkkaConnector_vDec2018.scala +++ b/obp-api/src/main/scala/code/bankconnectors/akka/AkkaConnector_vDec2018.scala @@ -18,7 +18,6 @@ import com.openbankproject.commons.dto._ import com.openbankproject.commons.model._ import com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.SCAStatus import com.openbankproject.commons.model.enums.{AccountAttributeType, CardAttributeType, ChallengeType, CustomerAttributeType, ProductAttributeType, StrongCustomerAuthentication, TransactionAttributeType, TransactionRequestStatus} -import com.sksamuel.avro4s.SchemaFor import net.liftweb.common.{Box, Full} import com.openbankproject.commons.util.JsonAliases.parse @@ -55,8 +54,8 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { inboundStatus, inboundAdapterInfoInternal) ), - outboundAvroSchema = Some(parse(SchemaFor[OutBoundGetAdapterInfo]().toString(true))), - inboundAvroSchema = Some(parse(SchemaFor[InBoundGetAdapterInfo]().toString(true))), + outboundAvroSchema = None, + inboundAvroSchema = None, adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) override def getAdapterInfo(callContext: Option[CallContext]): Future[Box[(InboundAdapterInfoInternal, Option[CallContext])]] = { @@ -81,8 +80,8 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { List(bankCommons) ) ), - outboundAvroSchema = Some(parse(SchemaFor[OutBoundGetBanks]().toString(true))), - inboundAvroSchema = Some(parse(SchemaFor[InBoundGetBanks]().toString(true))), + outboundAvroSchema = None, + inboundAvroSchema = None, adapterImplementation = Some(AdapterImplementation("- Core", 2)) ) @@ -110,8 +109,8 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { bankCommons ) ), - outboundAvroSchema = Some(parse(SchemaFor[OutBoundGetBank]().toString(true))), - inboundAvroSchema = Some(parse(SchemaFor[InBoundGetBank]().toString(true))), + outboundAvroSchema = None, + inboundAvroSchema = None, adapterImplementation = Some(AdapterImplementation("- Core", 5)) ) override def getBank(bankId : BankId, callContext: Option[CallContext]): Future[Box[(Bank, Option[CallContext])]] = { diff --git a/obp-api/src/main/scala/code/search/search.scala b/obp-api/src/main/scala/code/search/search.scala index 6ef39c75a9..6849ac71ae 100644 --- a/obp-api/src/main/scala/code/search/search.scala +++ b/obp-api/src/main/scala/code/search/search.scala @@ -1,7 +1,6 @@ package code.search import org.json4s._ -import java.nio.charset.Charset import java.util.Date import code.api.util.APIUtil @@ -9,18 +8,19 @@ import code.api.util.ErrorMessages._ import code.util.Helper.MdcLoggable import com.sksamuel.elastic4s.http.JavaClient import com.sksamuel.elastic4s.{ElasticClient, ElasticProperties} -import dispatch.Defaults._ -import dispatch.{Http, url, _} import net.liftweb.common.{Box, Empty, Failure, Full} import com.openbankproject.commons.util.json +import okhttp3.{MediaType => OkMediaType, OkHttpClient, Request => OkRequest, RequestBody} import org.json4s.JsonAST -import scala.concurrent.Await +import scala.concurrent.{Await, ExecutionContext, Future, Promise} import scala.concurrent.duration.Duration import scala.util.control.NoStackTrace class elasticsearch extends MdcLoggable { + private implicit val ec: ExecutionContext = ExecutionContext.global + case class APIResponse(code: Int, body: JValue) case class ErrorMessage(error: String) @@ -32,6 +32,8 @@ class elasticsearch extends MdcLoggable { val esType = "" val esIndex = "" + private val httpClient = new OkHttpClient() + private val jsonMediaType = OkMediaType.parse("application/json; charset=UTF-8") def isEnabled(): Boolean = { APIUtil.getPropsAsBoolValue("allow_elasticsearch", false) @@ -39,8 +41,8 @@ class elasticsearch extends MdcLoggable { def searchProxy(userId: String, queryString: String): JValue = { if (APIUtil.getPropsAsBoolValue("allow_elasticsearch", false)) { - val request = constructQuery(userId, getParameters(queryString)) - getAPIResponse(request).body + val esUrl = constructQuery(userId, getParameters(queryString)) + getAPIResponse(esUrl).body } else { json.JsonParser.parse("""{"error":"elasticsearch disabled"}""") } @@ -52,27 +54,22 @@ class elasticsearch extends MdcLoggable { val esUrl = s"${httpHost}${uri.replaceAll("\"", "")}" logger.info(s"searchProxyV300 says esUrl is: $esUrl") logger.info(s"searchProxyV300 says body is: $body") - val request: Req = (url(esUrl).<<(body).GET).setContentType("application/json", Charset.forName("UTF-8")) - val response = getAPIResponse(request) + val response = getAPIResponse(esUrl, body) if (statsOnly) Full(privacyCheckStatistics(response.body)) else Full(response.body) } else { Full(json.JsonParser.parse("""{"error":"elasticsearch disabled"}""")) } } + def searchProxyAsyncV300(userId: String, uri: String, body: String, statsOnly: Boolean = false): Future[APIResponse] = { - val httpHost = ("http://" + esHost + ":" + esPortHTTP) - val esUrl = s"${httpHost}${uri.replaceAll("\"" , "")}" - logger.info(s"searchProxyAsyncV300 says esUrl is: $esUrl") - logger.info(s"searchProxyAsyncV300 says body is: $body") - val request: Req = (url(esUrl).<<(body).GET).setContentType("application/json", Charset.forName("UTF-8")) // Note that WE ONLY do GET - Keep it this way! - logger.info(s"searchProxyAsyncV300 says request I will send to ES is: ${request.toRequest.toString}") - val response = getAPIResponseAsync(request) - logger.info (s"searchProxyAsyncV300 says response follows:") - - response foreach { - msg => logger.info(msg.body) - } + val httpHost = "http://" + esHost + ":" + esPortHTTP + val esUrl = s"${httpHost}${uri.replaceAll("\"", "")}" + logger.info(s"searchProxyAsyncV300 says esUrl is: $esUrl") + logger.info(s"searchProxyAsyncV300 says body is: $body") + val response = getAPIResponseAsync(esUrl, body) + logger.info(s"searchProxyAsyncV300 says response follows:") + response foreach { msg => logger.info(msg.body) } response } @@ -81,21 +78,20 @@ class elasticsearch extends MdcLoggable { else response.body } - def searchProxyStatsV300(userId: String, uriPart: String, bodyPart: String, field: String): Box[JValue] = searchProxyV300(userId, uriPart, addAggregation(bodyPart, field), statsOnly = true) - def searchProxyStatsAsyncV300(userId: String, uriPart: String, bodyPart:String, field: String): Future[APIResponse] = { - searchProxyAsyncV300(userId, uriPart, addAggregation(bodyPart,field), true) - } + + def searchProxyStatsAsyncV300(userId: String, uriPart: String, bodyPart: String, field: String): Future[APIResponse] = + searchProxyAsyncV300(userId, uriPart, addAggregation(bodyPart, field), true) private def addAggregation(bodyPart: String, field: String): String = { - bodyPart.dropRight(1).concat(",\"aggs\":{\"" + field + "\":{\"stats\":{\"field\":\""+ field + "\"}}}}") + bodyPart.dropRight(1).concat(",\"aggs\":{\"" + field + "\":{\"stats\":{\"field\":\"" + field + "\"}}}}") } - + private def extractStatistics(body: JValue): JValue = { - body \ "aggregations" + body \ "aggregations" } - + private def privacyCheckStatistics(body: JValue): JValue = { println("Enter privacyCheckStatistics") logger.debug(body) @@ -104,21 +100,40 @@ class elasticsearch extends MdcLoggable { if (count > 9) result else json.JsonParser.parse("{\"error\": \"" + NotEnoughtSearchStatisticsResults + "\"}") } - - private def getAPIResponse(req: Req): APIResponse = { - Await.result( - getAPIResponseAsync(req) - , Duration.Inf) + + private def getAPIResponse(esUrl: String, body: String = ""): APIResponse = { + Await.result(getAPIResponseAsync(esUrl, body), Duration.Inf) } - private def getAPIResponseAsync(req: Req): Future[APIResponse] = { - for (response <- Http.default(req > as.Response(p => p))) - yield { - val body = if (response.getResponseBody().isEmpty) "{}" else response.getResponseBody() - APIResponse(response.getStatusCode, json.parse(body)) + private def getAPIResponseAsync(esUrl: String, body: String = ""): Future[APIResponse] = { + val promise = Promise[APIResponse]() + val request = buildRequest(esUrl, body) + httpClient.newCall(request).enqueue(new okhttp3.Callback { + override def onFailure(call: okhttp3.Call, e: java.io.IOException): Unit = + promise.failure(e) + override def onResponse(call: okhttp3.Call, response: okhttp3.Response): Unit = { + try { + val bodyStr = Option(response.body()).map(_.string()).filter(_.nonEmpty).getOrElse("{}") + promise.success(APIResponse(response.code(), json.parse(bodyStr))) + } catch { + case e: Throwable => promise.failure(e) + } finally { + response.close() + } } + }) + promise.future } + private def buildRequest(esUrl: String, body: String): OkRequest = + if (body.nonEmpty) + new OkRequest.Builder() + .url(esUrl) + .post(RequestBody.create(jsonMediaType, body)) + .build() + else + new OkRequest.Builder().url(esUrl).get().build() + private def appendParams(url: String, params: Seq[(String, String)]): String = { def encode(s: String) = java.net.URLEncoder.encode(s, "UTF-8") params.toList match { @@ -129,16 +144,14 @@ class elasticsearch extends MdcLoggable { } } - private def constructQuery(userId: String, params: Map[String, String]): Req = { + private def constructQuery(userId: String, params: Map[String, String]): String = { var esScroll = "" val esType = params.getOrElse("esType", "") val q = params.getOrElse("q", "") - val source = params.getOrElse("source","") - //val jsonQuery = Json.encode(filteredParams) - //TODO: Workaround - HTTP and TCP ports differ. Should there be props entry for both? - val httpHost = ("http://" + esHost + ":" + esPortHTTP) + val source = params.getOrElse("source", "") + val httpHost = "http://" + esHost + ":" + esPortHTTP - var parameters = Seq[(String,String)]() + var parameters = Seq[(String, String)]() if (q != "") { parameters = parameters ++ Seq(("q", q)) val size = params.getOrElse("size", "") @@ -148,46 +161,34 @@ class elasticsearch extends MdcLoggable { val scroll = params.getOrElse("scroll", "") val scroll_id = params.getOrElse("scroll_id", "") val search_type = params.getOrElse("search_type", "") - if (size != "") - parameters = parameters ++ Seq(("size", size)) - if (sort != "") - parameters = parameters ++ Seq(("sort", sort)) - if (from != "") - parameters = parameters ++ Seq(("from", from)) - if (df != "") - parameters = parameters ++ Seq(("df", df)) - if (scroll != "") - parameters = parameters ++ Seq(("scroll", scroll)) - if (search_type != "") - parameters = parameters ++ Seq(("search_type", search_type)) - // scroll needs specific URL + if (size != "") parameters = parameters ++ Seq(("size", size)) + if (sort != "") parameters = parameters ++ Seq(("sort", sort)) + if (from != "") parameters = parameters ++ Seq(("from", from)) + if (df != "") parameters = parameters ++ Seq(("df", df)) + if (scroll != "") parameters = parameters ++ Seq(("scroll", scroll)) + if (search_type != "") parameters = parameters ++ Seq(("search_type", search_type)) if (scroll_id != "" && scroll != "") { esScroll = "/scroll" parameters = Seq(("scroll", scroll)) ++ Seq(("scroll_id", scroll_id)) } - } - else if (q == "" && source != "") { + } else if (q == "" && source != "") { parameters = Seq(("source", source)) } - val esUrl = appendParams( s"${httpHost}/${esIndex}/${esType}${if (esType.nonEmpty) "/" else ""}_search${esScroll}", parameters ) - //println("[ES.URL]===> " + esUrl) - // Use this incase we cant log to elastic search + val esUrl = appendParams( + s"${httpHost}/${esIndex}/${esType}${if (esType.nonEmpty) "/" else ""}_search${esScroll}", + parameters + ) logger.info(s"esUrl is $esUrl parameters are $parameters user_id is $userId") - - url(esUrl).GET + esUrl } private def getParameters(queryString: String): Map[String, String] = { - val res = queryString.split('&').map { str => - val pair = str.split('=') - if (pair.length > 1) - (pair(0) -> pair(1)) - else - (pair(0) -> "") + queryString.split('&').map { str => + val pair = str.split('=') + if (pair.length > 1) (pair(0) -> pair(1)) + else (pair(0) -> "") }.toMap - - res } def createElasticSearchUriPart(index: String, topic: String): String = { @@ -195,15 +196,14 @@ class elasticsearch extends MdcLoggable { val realIndex = if (index == "" || index == "ALL") APIUtil.getPropsValue("es.warehouse.allowed.indices").getOrElse(throw new RuntimeException) else index - if (! realIndex.split(",").toSet.subsetOf(validIndices)) throw new RuntimeException() with NoStackTrace + if (!realIndex.split(",").toSet.subsetOf(validIndices)) throw new RuntimeException() with NoStackTrace val addTopic = if (topic == "ALL") "" else "/" + topic "/" + realIndex + addTopic + "/_search" } def getElasticSearchUri(indexString: String): Box[String] = { val validIndices: List[String] = APIUtil.getPropsValue("es.warehouse.allowed.indices").getOrElse( - throw new RuntimeException(NoValidElasticsearchIndicesConfigured) with NoStackTrace).split(",").toList match - { + throw new RuntimeException(NoValidElasticsearchIndicesConfigured) with NoStackTrace).split(",").toList match { case List("ALL") => List("") case x => x } @@ -214,12 +214,12 @@ class elasticsearch extends MdcLoggable { } } - def checkIndicesValidity(indexString: String, validIndices: List[String]): Box[String] ={ + def checkIndicesValidity(indexString: String, validIndices: List[String]): Box[String] = { indexString match { case "ALL" => Empty case x => x match { case y if !y.split(",").toSet.subsetOf(validIndices.toSet) => Failure("") - case y => Full(y) + case y => Full(y) } } } @@ -237,9 +237,8 @@ class elasticsearchMetrics extends elasticsearch { val props = ElasticProperties(s"http://$esHost:${esPortTCP.toInt}") lazy val client = ElasticClient(JavaClient(props)) - // we must import the dsl import com.sksamuel.elastic4s.ElasticDsl._ - + if (APIUtil.getPropsAsBoolValue("allow_elasticsearch", false) && APIUtil.getPropsAsBoolValue("allow_elasticsearch_metrics", false) ) { try { client.execute { @@ -264,7 +263,6 @@ class elasticsearchMetrics extends elasticsearch { def indexMetric(userId: String, url: String, date: Date, duration: Long, userName: String, appName: String, developerEmail: String, correlationId: String, apiInstanceId: String) { if (APIUtil.getPropsAsBoolValue("allow_elasticsearch", false) && APIUtil.getPropsAsBoolValue("allow_elasticsearch_metrics", false) ) { try { - // we must import the dsl import com.sksamuel.elastic4s.ElasticDsl._ client.execute { indexInto(s"$esIndex/request") fields ( @@ -296,10 +294,6 @@ class elasticsearchWarehouse extends elasticsearch { val props = ElasticProperties(s"http://$esHost:${esPortTCP.toInt}") var client: ElasticClient = null if (APIUtil.getPropsAsBoolValue("allow_elasticsearch", false) && APIUtil.getPropsAsBoolValue("allow_elasticsearch_warehouse", false) ) { - //this is not used in the current code, first comment to solve the vulnerability issue - // val settings = Settings.builder().put("cluster.name", APIUtil.getPropsValue("es.cluster.name", "elasticsearch")).build() client = ElasticClient(JavaClient(props)) } } - - diff --git a/obp-api/src/test/scala/code/api/v1_4_0/JSONFactory1_4_0Test.scala b/obp-api/src/test/scala/code/api/v1_4_0/JSONFactory1_4_0Test.scala index a0c44cc42f..c8072481a9 100644 --- a/obp-api/src/test/scala/code/api/v1_4_0/JSONFactory1_4_0Test.scala +++ b/obp-api/src/test/scala/code/api/v1_4_0/JSONFactory1_4_0Test.scala @@ -12,8 +12,8 @@ import code.api.v1_2_1.OBPAPI1_2_1 import org.json4s.Extraction.decompose import org.json4s._ import com.openbankproject.commons.util.JsonAliases._ -import org.everit.json.schema.loader.SchemaLoader -import org.json.JSONObject +import com.fasterxml.jackson.databind.ObjectMapper +import com.networknt.schema.{JsonSchemaFactory, SpecVersion} import scala.collection.mutable; @@ -154,15 +154,17 @@ class JSONFactory1_4_0Test extends code.setup.ServerSetup { scenario("validate all the resourceDocs json schema, no exception is good enough") { val resourceDocsRaw= OBPAPI3_0_0.allResourceDocs val resourceDocs = JSONFactory1_4_0.createResourceDocsJson(resourceDocsRaw.toList,false, None) + val mapper = new ObjectMapper() + val schemaFactory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4) for{ resourceDoc <- resourceDocs.resource_docs if (resourceDoc.request_verb != "DELETE") json <- List(compactRender(decompose(resourceDoc.success_response_body))) jsonSchema <- List(compactRender(resourceDoc.typed_success_response_body)) } yield { - val rawSchema = new JSONObject(jsonSchema) - val schema = SchemaLoader.load(rawSchema) - schema.validate(new JSONObject(json)) + val schema = schemaFactory.getSchema(mapper.readTree(jsonSchema)) + val errors = schema.validate(mapper.readTree(json)) + assert(errors.isEmpty, s"JSON Schema validation failed: ${errors}") } } diff --git a/obp-api/src/test/scala/code/setup/SendServerRequests.scala b/obp-api/src/test/scala/code/setup/SendServerRequests.scala index 1cac46e50e..7b8d5b30c4 100644 --- a/obp-api/src/test/scala/code/setup/SendServerRequests.scala +++ b/obp-api/src/test/scala/code/setup/SendServerRequests.scala @@ -57,7 +57,21 @@ case class APIResponse(code: Int, body: JValue, headers: Option[HttpHeaders]) trait SendServerRequests { TimeZone.setDefault(TimeZone.getTimeZone("UTC")) - + + import code.api.util.APIUtil.OAuth.{Consumer, Token} + + implicit def Request2RequestSigner(r: Req): RequestSigner = new RequestSigner(r) + + class RequestSigner(rb: Req) { + def <@(consumer: Consumer, token: Token): Req = + rb <:< Map("Authorization" -> s"""DirectLogin token="${token.value}"""") + def <@(consumerAndToken: Option[(Consumer, Token)]): Req = + consumerAndToken match { + case Some((_, token)) => rb <:< Map("Authorization" -> s"""DirectLogin token="${token.value}"""") + case None => rb + } + } + case class ReqData ( url: String, method: String, diff --git a/pom.xml b/pom.xml index 56e6a0e231..6ecae876c8 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,8 @@ 2.12.20 1.1.5 1.1.0 - 1.8.2 + 4.1.2 + 1.11.4 v1.0.4 0.23.30 @@ -88,15 +89,13 @@ accessors-smart 2.6.0 - + org.json json 20250107 - + commons-validator commons-validator