From c8c1e786e1a0efe219901bc9edba6e58628862aa Mon Sep 17 00:00:00 2001 From: hongwei Date: Thu, 26 Mar 2020 08:17:04 +0100 Subject: [PATCH 01/13] added the dynamicEndpoints endpoints #support Create, Get and GetAll now. --- .../main/scala/bootstrap/liftweb/Boot.scala | 2 + .../main/scala/code/api/util/ApiRole.scala | 15 ++ .../src/main/scala/code/api/util/ApiTag.scala | 1 + .../scala/code/api/util/ExampleValue.scala | 243 ++++++++++++++++++ .../main/scala/code/api/util/NewStyle.scala | 25 ++ .../scala/code/api/v4_0_0/APIMethods400.scala | 126 ++++++++- .../scala/code/bankconnectors/Connector.scala | 13 + .../bankconnectors/LocalMappedConnector.scala | 18 +- .../DynamicEndpointProvider.scala | 34 +++ .../MapppedDynamicEndpointProvider.scala | 40 +++ 10 files changed, 513 insertions(+), 4 deletions(-) create mode 100644 obp-api/src/main/scala/code/dynamicEndpoint/DynamicEndpointProvider.scala create mode 100644 obp-api/src/main/scala/code/dynamicEndpoint/MapppedDynamicEndpointProvider.scala diff --git a/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala b/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala index d9b8650fb..df6831dad 100644 --- a/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala @@ -31,6 +31,7 @@ import java.util.{Locale, TimeZone} import code.CustomerDependants.MappedCustomerDependant import code.DynamicData.DynamicData +import code.DynamicEndpoint.DynamicEndpoint import code.accountapplication.MappedAccountApplication import code.accountattribute.MappedAccountAttribute import code.accountholders.MapperAccountHolders @@ -706,6 +707,7 @@ object ToSchemify { Authorisation, DynamicEntity, DynamicData, + DynamicEndpoint, AccountIdMapping, DirectDebit, StandingOrder diff --git a/obp-api/src/main/scala/code/api/util/ApiRole.scala b/obp-api/src/main/scala/code/api/util/ApiRole.scala index c8752b6f0..4522b52c1 100644 --- a/obp-api/src/main/scala/code/api/util/ApiRole.scala +++ b/obp-api/src/main/scala/code/api/util/ApiRole.scala @@ -406,6 +406,21 @@ object ApiRole { case class CanDeleteDynamicEntity(requiresBankId: Boolean = false) extends ApiRole lazy val canDeleteDynamicEntity = CanDeleteDynamicEntity() + + case class CanGetDynamicEndpoint(requiresBankId: Boolean = false) extends ApiRole + lazy val canGetDynamicEndpoint = CanGetDynamicEndpoint() + + case class CanGetDynamicEndpoints(requiresBankId: Boolean = false) extends ApiRole + lazy val canGetDynamicEndpoints = CanGetDynamicEndpoints() + + case class CanCreateDynamicEndpoint(requiresBankId: Boolean = false) extends ApiRole + lazy val canCreateDynamicEndpoint = CanCreateDynamicEndpoint() + + case class CanUpdateDynamicEndpoint(requiresBankId: Boolean = false) extends ApiRole + lazy val canUpdateDynamicEndpoint = CanUpdateDynamicEndpoint() + + case class CanDeleteDynamicEndpoint(requiresBankId: Boolean = false) extends ApiRole + lazy val canDeleteDynamicEndpoint = CanDeleteDynamicEndpoint() case class CanCreateResetPasswordUrl(requiresBankId: Boolean = false) extends ApiRole lazy val canCreateResetPasswordUrl = CanCreateResetPasswordUrl() diff --git a/obp-api/src/main/scala/code/api/util/ApiTag.scala b/obp-api/src/main/scala/code/api/util/ApiTag.scala index 4498a2105..38f8fca72 100644 --- a/obp-api/src/main/scala/code/api/util/ApiTag.scala +++ b/obp-api/src/main/scala/code/api/util/ApiTag.scala @@ -68,6 +68,7 @@ object ApiTag { val apiTagMethodRouting = ResourceDocTag("Method-Routing") val apiTagWebUiProps = ResourceDocTag("WebUi-Props") val apiTagDynamicEntity= ResourceDocTag("Dynamic-Entity") + val apiTagDynamicEndpoint= ResourceDocTag("Dynamic-Endpoint") // To mark the Berlin Group APIs suggested order of implementation val apiTagBerlinGroupM = ResourceDocTag("Berlin-Group-M") diff --git a/obp-api/src/main/scala/code/api/util/ExampleValue.scala b/obp-api/src/main/scala/code/api/util/ExampleValue.scala index d72689ae0..bcaf617e1 100644 --- a/obp-api/src/main/scala/code/api/util/ExampleValue.scala +++ b/obp-api/src/main/scala/code/api/util/ExampleValue.scala @@ -1,6 +1,7 @@ package code.api.util +import code.DynamicEndpoint.DynamicEndpointSwagger import code.api.util.Glossary.{glossaryItems, makeGlossaryItem} import code.dynamicEntity.{DynamicEntityDefinition, DynamicEntityFooBar, DynamicEntityFullBarFields, DynamicEntityIntTypeExample, DynamicEntityStringTypeExample} import com.openbankproject.commons.model.enums.DynamicEntityFieldType @@ -358,6 +359,248 @@ object ExampleValue { ) lazy val dynamicEntityResponseBodyExample = dynamicEntityRequestBodyExample.copy(dynamicEntityId = Some("dynamic-entity-id")) + + lazy val dynamicEndpointRequestBodyExample = DynamicEndpointSwagger(None, """{ + | "swagger": "2.0", + | "info": { + | "version": "0.0.1", + | "title": "Portus EVS sandbox demo API", + | "description": "Portus EVS sandbox demo API", + | "contact": { + | "name": "Digital & FinTech, Grant Thornton", + | "email": "peng.xu@ie.gt.com", + | "url": "https://www.tesobe.com/" + | } + | }, + | "host": "localhost:8080", + | "basePath": "/user", + | "schemes": [ + | "http" + | ], + | "consumes": [ + | "application/json" + | ], + | "produces": [ + | "application/json" + | ], + | "paths": { + | "/save": { + | "post": { + | "parameters": [ + | { + | "name": "body", + | "in": "body", + | "required": true, + | "schema": { + | "$ref": "#/definitions/user" + | } + | } + | ], + | "responses": { + | "201": { + | "description": "create user successful and return created user object", + | "schema": { + | "$ref": "#/definitions/user" + | } + | }, + | "500": { + | "description": "unexpected error", + | "schema": { + | "$ref": "#/responses/unexpectedError" + | } + | } + | } + | } + | }, + | "/getById/{userId}": { + | "get": { + | "description": "get reuested user by user ID", + | "parameters": [ + | { + | "$ref": "#/parameters/userId" + | } + | ], + | "consumes": [], + | "responses": { + | "200": { + | "description": "the successful get requested user by user ID", + | "schema": { + | "$ref": "#/definitions/user" + | } + | }, + | "400": { + | "description": "bad request", + | "schema": { + | "$ref": "#/responses/invalidRequest" + | } + | }, + | "404": { + | "description": "user not found", + | "schema": { + | "$ref": "#/definitions/APIError" + | } + | }, + | "500": { + | "description": "unexpected error", + | "schema": { + | "$ref": "#/responses/unexpectedError" + | } + | } + | } + | } + | }, + | "/listUsers": { + | "get": { + | "description": "get list of users", + | "consumes": [], + | "responses": { + | "200": { + | "description": "get all users", + | "schema": { + | "$ref": "#/definitions/users" + | } + | }, + | "404": { + | "description": "user not found", + | "schema": { + | "$ref": "#/definitions/APIError" + | } + | } + | } + | } + | }, + | "/updateUser": { + | "put": { + | "parameters": [ + | { + | "name": "body", + | "in": "body", + | "required": true, + | "schema": { + | "$ref": "#/definitions/user" + | } + | } + | ], + | "responses": { + | "200": { + | "description": "create user successful and return created user object", + | "schema": { + | "$ref": "#/definitions/user" + | } + | }, + | "500": { + | "description": "unexpected error", + | "schema": { + | "$ref": "#/responses/unexpectedError" + | } + | } + | } + | } + | }, + | "/delete/{userId}": { + | "delete": { + | "description": "delete user by user ID", + | "parameters": [ + | { + | "$ref": "#/parameters/userId" + | } + | ], + | "consumes": [], + | "responses": { + | "204": { + | "description": "the successful delete user by user ID" + | }, + | "400": { + | "description": "bad request", + | "schema": { + | "$ref": "#/responses/invalidRequest" + | } + | }, + | "500": { + | "description": "unexpected error", + | "schema": { + | "$ref": "#/responses/unexpectedError" + | } + | } + | } + | } + | } + | }, + | "definitions": { + | "user": { + | "type": "object", + | "properties": { + | "id": { + | "type": "integer", + | "description": "user ID" + | }, + | "first_name": { + | "type": "string" + | }, + | "last_name": { + | "type": "string" + | }, + | "age": { + | "type": "integer" + | }, + | "career": { + | "type": "string" + | } + | }, + | "required": [ + | "first_name", + | "last_name", + | "age" + | ] + | }, + | "users": { + | "description": "array of users", + | "type": "array", + | "items": { + | "$ref": "#/definitions/user" + | } + | }, + | "APIError": { + | "description": "content any error from API", + | "type": "object", + | "properties": { + | "errorCode": { + | "description": "content error code relate to API", + | "type": "string" + | }, + | "errorMessage": { + | "description": "content user-friendly error message", + | "type": "string" + | } + | } + | } + | }, + | "responses": { + | "unexpectedError": { + | "description": "unexpected error", + | "schema": { + | "$ref": "#/definitions/APIError" + | } + | }, + | "invalidRequest": { + | "description": "invalid request", + | "schema": { + | "$ref": "#/definitions/APIError" + | } + | } + | }, + | "parameters": { + | "userId": { + | "name": "userId", + | "in": "path", + | "required": true, + | "type": "string", + | "description": "user ID" + | } + | } + |} + |""".stripMargin) + lazy val dynamicEndpointResponseBodyExample = dynamicEndpointRequestBodyExample.copy(dynamicEndpointId = Some("dynamic-endpoint-id")) } diff --git a/obp-api/src/main/scala/code/api/util/NewStyle.scala b/obp-api/src/main/scala/code/api/util/NewStyle.scala index aabaecc47..cca431560 100644 --- a/obp-api/src/main/scala/code/api/util/NewStyle.scala +++ b/obp-api/src/main/scala/code/api/util/NewStyle.scala @@ -4,6 +4,7 @@ import java.util.Date import java.util.UUID.randomUUID import code.DynamicData.DynamicDataProvider +import code.DynamicEndpoint.DynamicEndpointT import code.api.APIFailureNewStyle import code.api.cache.Caching import code.api.util.APIUtil.{OBPReturnType, canGrantAccessToViewCommon, canRevokeAccessToViewCommon, connectorEmptyResponse, createHttpParamsByUrlFuture, createQueriesByHttpParamsFuture, fullBoxOrException, generateUUID, unboxFull, unboxFullOrFail} @@ -1996,5 +1997,29 @@ object NewStyle { getConnectorByName(connectorName).flatMap(_.implementedMethods.get(methodName)) } + def createDynamicEndpoint(swaggerString: String, callContext: Option[CallContext]): OBPReturnType[DynamicEndpointT] = { + Connector.connector.vend.createDynamicEndpoint( + swaggerString, + callContext + ) map { + i => (connectorEmptyResponse(i._1, callContext), i._2) + } + } + + def getDynamicEndpoint(dynamicEndpointId: String, callContext: Option[CallContext]): OBPReturnType[DynamicEndpointT] = { + Connector.connector.vend.getDynamicEndpoint( + dynamicEndpointId, + callContext + ) map { + i => (connectorEmptyResponse(i._1, callContext), i._2) + } + } + + def getDynamicEndpoints(callContext: Option[CallContext]): OBPReturnType[List[DynamicEndpointT]] = { + Connector.connector.vend.getDynamicEndpoints( + callContext + ) + } + } } 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 48629ad0d..96890dff2 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 @@ -3,6 +3,7 @@ package code.api.v4_0_0 import java.util.Date import code.DynamicData.DynamicData +import code.DynamicEndpoint.{DynamicEndpointCommons, DynamicEndpointSwagger} import code.accountattribute.AccountAttributeX import code.api.ChargePolicy import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._ @@ -10,7 +11,7 @@ import code.api.util.APIUtil.{fullBoxOrException, _} import code.api.util.ApiRole._ import code.api.util.ApiTag._ import code.api.util.ErrorMessages._ -import code.api.util.ExampleValue.{dynamicEntityRequestBodyExample, dynamicEntityResponseBodyExample} +import code.api.util.ExampleValue.{dynamicEndpointRequestBodyExample, dynamicEndpointResponseBodyExample, dynamicEntityRequestBodyExample, dynamicEntityResponseBodyExample} import code.api.util.NewStyle.HttpCode import code.api.util._ import code.api.v1_2_1.{JSONFactory, PostTransactionTagJSON} @@ -2721,7 +2722,130 @@ trait APIMethods400 { } } } + + resourceDocs += ResourceDoc( + createDynamicEndpoint, + implementedInApiVersion, + nameOf(createDynamicEndpoint), + "POST", + "/management/dynamic_endpoints", + "Create DynamicEndpoint", + s"""Create a DynamicEndpoint. + | + | + |${authenticationRequiredMessage(true)} + | + |Create one DynamicEndpoint, + | + |""", + dynamicEndpointRequestBodyExample, + dynamicEndpointResponseBodyExample, + List( + $UserNotLoggedIn, + UserHasMissingRoles, + InvalidJsonFormat, + UnknownError + ), + Catalogs(notCore, notPSD2, notOBWG), + List(apiTagDynamicEndpoint, apiTagApi, apiTagNewStyle), + Some(List(canCreateDynamicEndpoint))) + + lazy val createDynamicEndpoint: OBPEndpoint = { + case "management" :: "dynamic_endpoints" :: Nil JsonPost json -> _ => { + cc => + for { + postedJson <- NewStyle.function.tryons(InvalidJsonFormat, 400, cc.callContext) { + json.extract[DynamicEndpointSwagger] + } + (dynamicEndpoint, callContext) <- NewStyle.function.createDynamicEndpoint(postedJson.swaggerString, cc.callContext) + } yield { + val commonsData: DynamicEndpointCommons = dynamicEndpoint + (commonsData, HttpCode.`201`(callContext)) + } + } + } + + + resourceDocs += ResourceDoc( + getDynamicEndpoint, + implementedInApiVersion, + nameOf(getDynamicEndpoint), + "GET", + "/management/dynamic_endpoints/DYNAMIC_ENDPOINT_ID", + "Get DynamicEndpoint", + s"""Get a DynamicEndpoint. + | + | + |${authenticationRequiredMessage(true)} + | + |Get one DynamicEndpoint, + | + |""", + emptyObjectJson, + dynamicEndpointResponseBodyExample, + List( + $UserNotLoggedIn, + UserHasMissingRoles, + InvalidJsonFormat, + UnknownError + ), + Catalogs(notCore, notPSD2, notOBWG), + List(apiTagDynamicEndpoint, apiTagApi, apiTagNewStyle), + Some(List(canGetDynamicEndpoint))) + + lazy val getDynamicEndpoint: OBPEndpoint = { + case "management" :: "dynamic_endpoints" :: dynamicEndpointId :: Nil JsonGet req => { + cc => + for { + (dynamicEndpoint, callContext) <- NewStyle.function.getDynamicEndpoint(dynamicEndpointId, cc.callContext) + } yield { + val commonsData: DynamicEndpointCommons = dynamicEndpoint + (commonsData, HttpCode.`201`(callContext)) + } + } + } + resourceDocs += ResourceDoc( + getDynamicEndpoints, + implementedInApiVersion, + nameOf(getDynamicEndpoints), + "GET", + "/management/dynamic_endpoints", + "Get DynamicEndpoints", + s"""Get DynamicEndpoints. + | + | + |${authenticationRequiredMessage(true)} + | + |Get DynamicEndpoints, + | + |""", + emptyObjectJson, + ListResult( + "dynamic_entities", + List(dynamicEndpointResponseBodyExample) + ), + List( + $UserNotLoggedIn, + UserHasMissingRoles, + InvalidJsonFormat, + UnknownError + ), + Catalogs(notCore, notPSD2, notOBWG), + List(apiTagDynamicEndpoint, apiTagApi, apiTagNewStyle), + Some(List(canGetDynamicEndpoints))) + + lazy val getDynamicEndpoints: OBPEndpoint = { + case "management" :: "dynamic_endpoints" :: Nil JsonGet req => { + cc => + for { + (dynamicEndpoints, callContext) <- NewStyle.function.getDynamicEndpoints(cc.callContext) + } yield { + val listCommons: List[DynamicEndpointCommons] = dynamicEndpoints + (ListResult("dynamic_entities", listCommons), HttpCode.`200`(cc.callContext)) + } + } + } } diff --git a/obp-api/src/main/scala/code/bankconnectors/Connector.scala b/obp-api/src/main/scala/code/bankconnectors/Connector.scala index 7a882b720..12a7ce871 100644 --- a/obp-api/src/main/scala/code/bankconnectors/Connector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/Connector.scala @@ -3,6 +3,7 @@ package code.bankconnectors import java.util.Date import java.util.UUID.randomUUID +import code.DynamicEndpoint.DynamicEndpointT import code.accountholders.{AccountHolders, MapperAccountHolders} import code.api.{APIFailure, APIFailureNewStyle} import code.api.cache.Caching @@ -2177,4 +2178,16 @@ trait Connector extends MdcLoggable { callContext: Option[CallContext]): OBPReturnType[Box[StandingOrderTrait]] = Future { (Failure(setUnimplementedError), callContext) } + + def createDynamicEndpoint(swaggerString: String, callContext: Option[CallContext]): OBPReturnType[Box[DynamicEndpointT]] = Future { + (Failure(setUnimplementedError), callContext) + } + + def getDynamicEndpoint(dynamicEndpointId: String, callContext: Option[CallContext]): OBPReturnType[Box[DynamicEndpointT]] = Future { + (Failure(setUnimplementedError), callContext) + } + + def getDynamicEndpoints(callContext: Option[CallContext]): OBPReturnType[List[DynamicEndpointT]] = Future { + (List.empty[DynamicEndpointT], callContext) + } } diff --git a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala index 569a8aef5..576bd181c 100644 --- a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala @@ -4,12 +4,13 @@ import java.util.Date import java.util.UUID.randomUUID import scala.concurrent.duration._ import code.DynamicData.DynamicDataProvider +import code.DynamicEndpoint.{DynamicEndpointProvider, DynamicEndpointT} import code.accountapplication.AccountApplicationX import code.accountattribute.AccountAttributeX import code.accountholders.{AccountHolders, MapperAccountHolders} import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON import code.api.cache.Caching -import code.api.util.APIUtil.{OBPReturnType, DateWithMsFormat, generateUUID, hasEntitlement, isValidCurrencyISOCode, saveConnectorMetric, stringOrNull, unboxFullOrFail} +import code.api.util.APIUtil.{DateWithMsFormat, OBPReturnType, generateUUID, hasEntitlement, isValidCurrencyISOCode, saveConnectorMetric, stringOrNull, unboxFullOrFail} import code.api.util.ApiRole.canCreateAnyTransactionRequest import code.api.util.ErrorMessages._ import code.api.util._ @@ -73,7 +74,7 @@ import net.liftweb.common._ import net.liftweb.json import net.liftweb.json.{JArray, JBool, JObject, JValue} import net.liftweb.mapper.{By, _} -import net.liftweb.util.Helpers.{tryo,now, time, hours} +import net.liftweb.util.Helpers.{hours, now, time, tryo} import net.liftweb.util.Mailer import net.liftweb.util.Mailer.{From, PlainMailBodyType, Subject, To} import org.apache.commons.lang3.StringUtils @@ -84,7 +85,6 @@ import scala.collection.immutable.{List, Nil} import com.openbankproject.commons.ExecutionContext.Implicits.global import scala.concurrent._ - import scala.language.postfixOps import scala.math.{BigDecimal, BigInt} import scala.util.Random @@ -4001,6 +4001,18 @@ object LocalMappedConnector extends Connector with MdcLoggable { } yield { trtc } Full(res) } + + override def createDynamicEndpoint(swaggerString: String, callContext: Option[CallContext]): OBPReturnType[Box[DynamicEndpointT]] = Future { + (DynamicEndpointProvider.connectorMethodProvider.vend.create(swaggerString), callContext) + } + + override def getDynamicEndpoint(dynamicEndpointId: String, callContext: Option[CallContext]): OBPReturnType[Box[DynamicEndpointT]] = Future { + (DynamicEndpointProvider.connectorMethodProvider.vend.get(dynamicEndpointId), callContext) + } + + override def getDynamicEndpoints(callContext: Option[CallContext]): OBPReturnType[List[DynamicEndpointT]] = Future { + (DynamicEndpointProvider.connectorMethodProvider.vend.getAll(), callContext) + } } diff --git a/obp-api/src/main/scala/code/dynamicEndpoint/DynamicEndpointProvider.scala b/obp-api/src/main/scala/code/dynamicEndpoint/DynamicEndpointProvider.scala new file mode 100644 index 000000000..229686706 --- /dev/null +++ b/obp-api/src/main/scala/code/dynamicEndpoint/DynamicEndpointProvider.scala @@ -0,0 +1,34 @@ +package code.DynamicEndpoint + +import com.openbankproject.commons.model.{Converter, JsonFieldReName} +import net.liftweb.common.Box +import net.liftweb.util.SimpleInjector + +object DynamicEndpointProvider extends SimpleInjector { + + val connectorMethodProvider = new Inject(buildOne _) {} + + def buildOne: MappedDynamicEndpointProvider.type = MappedDynamicEndpointProvider +} + +trait DynamicEndpointT { + def dynamicEndpointId: Option[String] + def swaggerString: String +} + +case class DynamicEndpointCommons( + dynamicEndpointId: Option[String] = None, + swaggerString: String + ) extends DynamicEndpointT with JsonFieldReName + +object DynamicEndpointCommons extends Converter[DynamicEndpointT, DynamicEndpointCommons] + +case class DynamicEndpointSwagger(dynamicEndpointId: Option[String] = None, swaggerString: String) + +trait DynamicEndpointProvider { + def create(swaggerString: String): Box[DynamicEndpointT] + def update(dynamicEndpointId: String, swaggerString: String): Box[DynamicEndpointT] + def get(dynamicEndpointId: String): Box[DynamicEndpointT] + def getAll(): List[DynamicEndpointT] + def delete(dynamicEndpointId: String): Boolean +} \ No newline at end of file diff --git a/obp-api/src/main/scala/code/dynamicEndpoint/MapppedDynamicEndpointProvider.scala b/obp-api/src/main/scala/code/dynamicEndpoint/MapppedDynamicEndpointProvider.scala new file mode 100644 index 000000000..e7ccbd6e4 --- /dev/null +++ b/obp-api/src/main/scala/code/dynamicEndpoint/MapppedDynamicEndpointProvider.scala @@ -0,0 +1,40 @@ +package code.DynamicEndpoint + +import code.api.util.CustomJsonFormats +import code.util.MappedUUID +import net.liftweb.common.Box +import net.liftweb.mapper._ +import net.liftweb.util.Helpers.tryo + +object MappedDynamicEndpointProvider extends DynamicEndpointProvider with CustomJsonFormats{ + override def create(swaggerString: String): Box[DynamicEndpointT] = { + tryo{DynamicEndpoint.create.SwaggerString(swaggerString).saveMe()} + } + override def update(dynamicEndpointId: String, swaggerString: String): Box[DynamicEndpointT] = { + DynamicEndpoint.find(By(DynamicEndpoint.DynamicEndpointId, dynamicEndpointId)).map(_.SwaggerString(swaggerString).saveMe()) + } + + override def get(dynamicEndpointId: String): Box[DynamicEndpointT] = DynamicEndpoint.find(By(DynamicEndpoint.DynamicEndpointId, dynamicEndpointId)) + + override def getAll(): List[DynamicEndpointT] = DynamicEndpoint.findAll() + + override def delete(dynamicEndpointId: String): Boolean = DynamicEndpoint.bulkDelete_!!(By(DynamicEndpoint.DynamicEndpointId, dynamicEndpointId)) + +} + +class DynamicEndpoint extends DynamicEndpointT with LongKeyedMapper[DynamicEndpoint] with IdPK { + + override def getSingleton = DynamicEndpoint + + object DynamicEndpointId extends MappedUUID(this) + + object SwaggerString extends MappedText(this) + + override def dynamicEndpointId: Option[String] = Option(DynamicEndpointId.get) + override def swaggerString: String = SwaggerString.get +} + +object DynamicEndpoint extends DynamicEndpoint with LongKeyedMetaMapper[DynamicEndpoint] { + override def dbIndexes = UniqueIndex(DynamicEndpointId) :: super.dbIndexes +} + From cd255de3dad1c787f98e7fe2cadc094e3bfa322c Mon Sep 17 00:00:00 2001 From: hongwei Date: Thu, 26 Mar 2020 12:06:48 +0100 Subject: [PATCH 02/13] remove the account_attributes for the create account endpoint --- .../code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala | 3 +-- obp-api/src/main/scala/code/api/v3_1_0/JSONFactory3.1.0.scala | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala index fbfe7143f..51fb7b727 100644 --- a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala +++ b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala @@ -3631,8 +3631,7 @@ object SwaggerDefinitionsJSON { product_code = accountTypeExample.value, balance = amountOfMoneyJsonV121, branch_id = branchIdExample.value, - account_routing = accountRoutingJsonV121, - account_attributes= List(accountAttributeResponseJson) + account_routing = accountRoutingJsonV121 ) val postAccountAccessJsonV400 = PostAccountAccessJsonV400(userIdExample.value, PostViewJsonV400(ExampleValue.viewIdExample.value, true)) diff --git a/obp-api/src/main/scala/code/api/v3_1_0/JSONFactory3.1.0.scala b/obp-api/src/main/scala/code/api/v3_1_0/JSONFactory3.1.0.scala index 0e4bb2058..fe33545cc 100644 --- a/obp-api/src/main/scala/code/api/v3_1_0/JSONFactory3.1.0.scala +++ b/obp-api/src/main/scala/code/api/v3_1_0/JSONFactory3.1.0.scala @@ -664,8 +664,7 @@ case class CreateAccountRequestJsonV310( product_code : String, balance : AmountOfMoneyJsonV121, branch_id : String, - account_routing: AccountRoutingJsonV121, - account_attributes: List[AccountAttributeResponseJson] + account_routing: AccountRoutingJsonV121 ) case class CreateAccountResponseJsonV310( From 93a346dee533f444b0c5b58a9bd428e4ea7a3372 Mon Sep 17 00:00:00 2001 From: hongwei Date: Thu, 26 Mar 2020 12:17:22 +0100 Subject: [PATCH 03/13] modify dynamic_entities --> dynamic-entities and dynamic_endpoints --> dynamic-endpoints in the URL --- .../scala/code/api/util/ErrorMessages.scala | 2 +- .../scala/code/api/v4_0_0/APIMethods400.scala | 30 +++++++++---------- .../code/api/v4_0_0/DynamicEntityTest.scala | 28 ++++++++--------- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala index f41fc883f..9bfc85698 100644 --- a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala +++ b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala @@ -41,7 +41,7 @@ object ErrorMessages { val WebUiPropsNotFound = "OBP-08002: WebUi props not found. Please specify a valid value for WEB_UI_PROPS_ID." // DynamicEntity Exceptions (OBP-09XXX) - val DynamicEntityNotFoundByDynamicEntityId = "OBP-09001: DynamicEntity not found. Please specify a valid value for dynamic_entity_id." + val DynamicEntityNotFoundByDynamicEntityId = "OBP-09001: DynamicEntity not found. Please specify a valid value for DYNAMIC_ENTITY_ID." val DynamicEntityNameAlreadyExists = "OBP-09002: DynamicEntity's entityName already exists. Please specify a different value for entityName." val DynamicEntityNotExists = "OBP-09003: DynamicEntity not exists. Please check entityName." val DynamicEntityMissArgument = "OBP-09004: DynamicEntity process related argument is missing." 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 96890dff2..379583fa4 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 @@ -812,7 +812,7 @@ trait APIMethods400 { implementedInApiVersion, nameOf(getDynamicEntities), "GET", - "/management/dynamic_entities", + "/management/dynamic-entities", "Get DynamicEntities", s"""Get the all DynamicEntities.""", emptyObjectJson, @@ -832,7 +832,7 @@ trait APIMethods400 { lazy val getDynamicEntities: OBPEndpoint = { - case "management" :: "dynamic_entities" :: Nil JsonGet req => { + case "management" :: "dynamic-entities" :: Nil JsonGet req => { cc => for { dynamicEntities <- Future(NewStyle.function.getDynamicEntities()) @@ -849,7 +849,7 @@ trait APIMethods400 { implementedInApiVersion, nameOf(createDynamicEntity), "POST", - "/management/dynamic_entities", + "/management/dynamic-entities", "Create DynamicEntity", s"""Create a DynamicEntity. | @@ -882,7 +882,7 @@ trait APIMethods400 { Some(List(canCreateDynamicEntity))) lazy val createDynamicEntity: OBPEndpoint = { - case "management" :: "dynamic_entities" :: Nil JsonPost json -> _ => { + case "management" :: "dynamic-entities" :: Nil JsonPost json -> _ => { cc => val dynamicEntity = DynamicEntityCommons(json.asInstanceOf[JObject], None) for { @@ -900,7 +900,7 @@ trait APIMethods400 { implementedInApiVersion, nameOf(updateDynamicEntity), "PUT", - "/management/dynamic_entities/DYNAMIC_ENTITY_ID", + "/management/dynamic-entities/DYNAMIC_ENTITY_ID", "Update DynamicEntity", s"""Update a DynamicEntity. | @@ -933,7 +933,7 @@ trait APIMethods400 { Some(List(canUpdateDynamicEntity))) lazy val updateDynamicEntity: OBPEndpoint = { - case "management" :: "dynamic_entities" :: dynamicEntityId :: Nil JsonPut json -> _ => { + case "management" :: "dynamic-entities" :: dynamicEntityId :: Nil JsonPut json -> _ => { cc => for { // Check whether there are uploaded data, only if no uploaded data allow to update DynamicEntity. @@ -958,7 +958,7 @@ trait APIMethods400 { implementedInApiVersion, nameOf(deleteDynamicEntity), "DELETE", - "/management/dynamic_entities/DYNAMIC_ENTITY_ID", + "/management/dynamic-entities/DYNAMIC_ENTITY_ID", "Delete DynamicEntity", s"""Delete a DynamicEntity specified by DYNAMIC_ENTITY_ID. | @@ -975,7 +975,7 @@ trait APIMethods400 { Some(List(canDeleteDynamicEntity))) lazy val deleteDynamicEntity: OBPEndpoint = { - case "management" :: "dynamic_entities" :: dynamicEntityId :: Nil JsonDelete _ => { + case "management" :: "dynamic-entities" :: dynamicEntityId :: Nil JsonDelete _ => { cc => for { // Check whether there are uploaded data, only if no uploaded data allow to delete DynamicEntity. @@ -2728,7 +2728,7 @@ trait APIMethods400 { implementedInApiVersion, nameOf(createDynamicEndpoint), "POST", - "/management/dynamic_endpoints", + "/management/dynamic-endpoints", "Create DynamicEndpoint", s"""Create a DynamicEndpoint. | @@ -2751,7 +2751,7 @@ trait APIMethods400 { Some(List(canCreateDynamicEndpoint))) lazy val createDynamicEndpoint: OBPEndpoint = { - case "management" :: "dynamic_endpoints" :: Nil JsonPost json -> _ => { + case "management" :: "dynamic-endpoints" :: Nil JsonPost json -> _ => { cc => for { postedJson <- NewStyle.function.tryons(InvalidJsonFormat, 400, cc.callContext) { @@ -2771,7 +2771,7 @@ trait APIMethods400 { implementedInApiVersion, nameOf(getDynamicEndpoint), "GET", - "/management/dynamic_endpoints/DYNAMIC_ENDPOINT_ID", + "/management/dynamic-endpoints/DYNAMIC_ENDPOINT_ID", "Get DynamicEndpoint", s"""Get a DynamicEndpoint. | @@ -2794,7 +2794,7 @@ trait APIMethods400 { Some(List(canGetDynamicEndpoint))) lazy val getDynamicEndpoint: OBPEndpoint = { - case "management" :: "dynamic_endpoints" :: dynamicEndpointId :: Nil JsonGet req => { + case "management" :: "dynamic-endpoints" :: dynamicEndpointId :: Nil JsonGet req => { cc => for { (dynamicEndpoint, callContext) <- NewStyle.function.getDynamicEndpoint(dynamicEndpointId, cc.callContext) @@ -2810,7 +2810,7 @@ trait APIMethods400 { implementedInApiVersion, nameOf(getDynamicEndpoints), "GET", - "/management/dynamic_endpoints", + "/management/dynamic-endpoints", "Get DynamicEndpoints", s"""Get DynamicEndpoints. | @@ -2822,7 +2822,7 @@ trait APIMethods400 { |""", emptyObjectJson, ListResult( - "dynamic_entities", + "dynamic-entities", List(dynamicEndpointResponseBodyExample) ), List( @@ -2836,7 +2836,7 @@ trait APIMethods400 { Some(List(canGetDynamicEndpoints))) lazy val getDynamicEndpoints: OBPEndpoint = { - case "management" :: "dynamic_endpoints" :: Nil JsonGet req => { + case "management" :: "dynamic-endpoints" :: Nil JsonGet req => { cc => for { (dynamicEndpoints, callContext) <- NewStyle.function.getDynamicEndpoints(cc.callContext) diff --git a/obp-api/src/test/scala/code/api/v4_0_0/DynamicEntityTest.scala b/obp-api/src/test/scala/code/api/v4_0_0/DynamicEntityTest.scala index 6cebabc96..a0c13de9f 100644 --- a/obp-api/src/test/scala/code/api/v4_0_0/DynamicEntityTest.scala +++ b/obp-api/src/test/scala/code/api/v4_0_0/DynamicEntityTest.scala @@ -152,7 +152,7 @@ class DynamicEntityTest extends V400ServerSetup { feature("Add a DynamicEntity v4.0.4- Unauthorized access") { scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { When("We make a request v4.0.0") - val request400 = (v4_0_0_Request / "management" / "dynamic_entities").POST + val request400 = (v4_0_0_Request / "management" / "dynamic-entities").POST val response400 = makePostRequest(request400, write(rightEntity)) Then("We should get a 400") response400.code should equal(400) @@ -163,7 +163,7 @@ class DynamicEntityTest extends V400ServerSetup { feature("Update a DynamicEntity v4.0.4- Unauthorized access") { scenario("We will call the endpoint without user credentials", ApiEndpoint2, VersionOfApi) { When("We make a request v4.0.0") - val request400 = (v4_0_0_Request / "management" / "dynamic_entities"/ "some-method-routing-id").PUT + val request400 = (v4_0_0_Request / "management" / "dynamic-entities"/ "some-method-routing-id").PUT val response400 = makePutRequest(request400, write(rightEntity)) Then("We should get a 400") response400.code should equal(400) @@ -174,7 +174,7 @@ class DynamicEntityTest extends V400ServerSetup { feature("Get DynamicEntities v4.0.4- Unauthorized access") { scenario("We will call the endpoint without user credentials", ApiEndpoint3, VersionOfApi) { When("We make a request v4.0.0") - val request400 = (v4_0_0_Request / "management" / "dynamic_entities").GET + val request400 = (v4_0_0_Request / "management" / "dynamic-entities").GET val response400 = makeGetRequest(request400) Then("We should get a 400") response400.code should equal(400) @@ -185,7 +185,7 @@ class DynamicEntityTest extends V400ServerSetup { feature("Delete the DynamicEntity specified by METHOD_ROUTING_ID v4.0.4- Unauthorized access") { scenario("We will call the endpoint without user credentials", ApiEndpoint4, VersionOfApi) { When("We make a request v4.0.0") - val request400 = (v4_0_0_Request / "management" / "dynamic_entities" / "METHOD_ROUTING_ID").DELETE + val request400 = (v4_0_0_Request / "management" / "dynamic-entities" / "METHOD_ROUTING_ID").DELETE val response400 = makeDeleteRequest(request400) Then("We should get a 400") response400.code should equal(400) @@ -198,7 +198,7 @@ class DynamicEntityTest extends V400ServerSetup { feature("Add a DynamicEntity v4.0.4- Unauthorized access - Authorized access") { scenario("We will call the endpoint without the proper Role " + canCreateDynamicEntity, ApiEndpoint1, VersionOfApi) { When("We make a request v4.0.0 without a Role " + canCreateDynamicEntity) - val request400 = (v4_0_0_Request / "management" / "dynamic_entities").POST <@(user1) + val request400 = (v4_0_0_Request / "management" / "dynamic-entities").POST <@(user1) val response400 = makePostRequest(request400, write(rightEntity)) Then("We should get a 403") response400.code should equal(403) @@ -209,7 +209,7 @@ class DynamicEntityTest extends V400ServerSetup { scenario("We will call the endpoint with the proper Role " + canCreateDynamicEntity , ApiEndpoint1, ApiEndpoint2, ApiEndpoint3, ApiEndpoint4, VersionOfApi) { Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanCreateDynamicEntity.toString) When("We make a request v4.0.0") - val request = (v4_0_0_Request / "management" / "dynamic_entities").POST <@(user1) + val request = (v4_0_0_Request / "management" / "dynamic-entities").POST <@(user1) val response = makePostRequest(request, write(rightEntity)) Then("We should get a 201") response.code should equal(201) @@ -244,7 +244,7 @@ class DynamicEntityTest extends V400ServerSetup { { // update success - val request400 = (v4_0_0_Request / "management" / "dynamic_entities" / dynamicEntityId ).PUT <@(user1) + val request400 = (v4_0_0_Request / "management" / "dynamic-entities" / dynamicEntityId ).PUT <@(user1) val response400 = makePutRequest(request400, compactRender(updateRequest)) Then("We should get a 200") response400.code should equal(200) @@ -254,7 +254,7 @@ class DynamicEntityTest extends V400ServerSetup { { // update a not exists DynamicEntity - val request404 = (v4_0_0_Request / "management" / "dynamic_entities" / "not-exists-id" ).PUT <@(user1) + val request404 = (v4_0_0_Request / "management" / "dynamic-entities" / "not-exists-id" ).PUT <@(user1) val response404 = makePutRequest(request404, compactRender(updateRequest)) Then("We should get a 404") response404.code should equal(404) @@ -263,7 +263,7 @@ class DynamicEntityTest extends V400ServerSetup { { // update a DynamicEntity with wrong required field name - val request400 = (v4_0_0_Request / "management" / "dynamic_entities" / dynamicEntityId ).PUT <@(user1) + val request400 = (v4_0_0_Request / "management" / "dynamic-entities" / dynamicEntityId ).PUT <@(user1) val response400 = makePutRequest(request400, compactRender(wrongRequiredEntity)) Then("We should get a 400") @@ -273,7 +273,7 @@ class DynamicEntityTest extends V400ServerSetup { { // update a DynamicEntity with wrong type of description - val request400 = (v4_0_0_Request / "management" / "dynamic_entities" / dynamicEntityId ).PUT <@(user1) + val request400 = (v4_0_0_Request / "management" / "dynamic-entities" / dynamicEntityId ).PUT <@(user1) val response400 = makePutRequest(request400, compactRender(wrongDescriptionEntity)) Then("We should get a 400") @@ -283,7 +283,7 @@ class DynamicEntityTest extends V400ServerSetup { { // update a DynamicEntity with wrong type of property description - val request400 = (v4_0_0_Request / "management" / "dynamic_entities" / dynamicEntityId ).PUT <@(user1) + val request400 = (v4_0_0_Request / "management" / "dynamic-entities" / dynamicEntityId ).PUT <@(user1) val response400 = makePutRequest(request400, compactRender(wrongPropertyDescriptionEntity)) Then("We should get a 400") @@ -293,11 +293,11 @@ class DynamicEntityTest extends V400ServerSetup { Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanGetDynamicEntities.toString) When("We make a request v4.0.0 with the Role " + canGetDynamicEntities) - val requestGet = (v4_0_0_Request / "management" / "dynamic_entities").GET <@(user1) + val requestGet = (v4_0_0_Request / "management" / "dynamic-entities").GET <@(user1) val responseGet = makeGetRequest(requestGet) Then("We should get a 200") responseGet.code should equal(200) - val json = responseGet.body \ "dynamic_entities" + val json = responseGet.body \ "dynamic-entities" val dynamicEntitiesGetJson = json.asInstanceOf[JArray] dynamicEntitiesGetJson.values should have size 1 @@ -308,7 +308,7 @@ class DynamicEntityTest extends V400ServerSetup { Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanDeleteDynamicEntity.toString) When("We make a request v4.0.0 with the Role " + canDeleteDynamicEntity) - val requestDelete400 = (v4_0_0_Request / "management" / "dynamic_entities" / dynamicEntityId).DELETE <@(user1) + val requestDelete400 = (v4_0_0_Request / "management" / "dynamic-entities" / dynamicEntityId).DELETE <@(user1) val responseDelete400 = makeDeleteRequest(requestDelete400) Then("We should get a 200") responseDelete400.code should equal(200) From fe2cbb6c6579779c7a3a709049720470f70593d1 Mon Sep 17 00:00:00 2001 From: shuang Date: Thu, 26 Mar 2020 12:31:11 +0100 Subject: [PATCH 04/13] make upload swagger as string instead of json. --- .../scala/code/api/util/ExampleValue.scala | 2 +- .../scala/code/api/v4_0_0/APIMethods400.scala | 41 ++- .../api/v4_0_0/DynamicEndpointHelper.scala | 245 ++++++++++++++++++ .../DynamicEndpointProvider.scala | 2 +- 4 files changed, 276 insertions(+), 14 deletions(-) create mode 100644 obp-api/src/main/scala/code/api/v4_0_0/DynamicEndpointHelper.scala diff --git a/obp-api/src/main/scala/code/api/util/ExampleValue.scala b/obp-api/src/main/scala/code/api/util/ExampleValue.scala index bcaf617e1..657e26f57 100644 --- a/obp-api/src/main/scala/code/api/util/ExampleValue.scala +++ b/obp-api/src/main/scala/code/api/util/ExampleValue.scala @@ -360,7 +360,7 @@ object ExampleValue { lazy val dynamicEntityResponseBodyExample = dynamicEntityRequestBodyExample.copy(dynamicEntityId = Some("dynamic-entity-id")) - lazy val dynamicEndpointRequestBodyExample = DynamicEndpointSwagger(None, """{ + lazy val dynamicEndpointRequestBodyExample = DynamicEndpointSwagger("""{ | "swagger": "2.0", | "info": { | "version": "0.0.1", 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 379583fa4..0f7004552 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 @@ -59,6 +59,7 @@ import scala.collection.mutable.ArrayBuffer import scala.concurrent.Future import code.model._ import org.apache.commons.lang3.StringUtils +import net.liftweb.json.compactRender trait APIMethods400 { self: RestHelper => @@ -2731,9 +2732,6 @@ trait APIMethods400 { "/management/dynamic-endpoints", "Create DynamicEndpoint", s"""Create a DynamicEndpoint. - | - | - |${authenticationRequiredMessage(true)} | |Create one DynamicEndpoint, | @@ -2750,12 +2748,20 @@ trait APIMethods400 { List(apiTagDynamicEndpoint, apiTagApi, apiTagNewStyle), Some(List(canCreateDynamicEndpoint))) + private val BodyPost = new TestPost[String] with JsonTest { + def body(r: Req): Box[String] = { + val value = r.body.map(it => new String(it)) + value + } + } + lazy val createDynamicEndpoint: OBPEndpoint = { - case "management" :: "dynamic-endpoints" :: Nil JsonPost json -> _ => { + case "management" :: "dynamic-endpoints" :: Nil BodyPost body -> req => { cc => for { postedJson <- NewStyle.function.tryons(InvalidJsonFormat, 400, cc.callContext) { - json.extract[DynamicEndpointSwagger] + DynamicEndpointHelper.parseSwaggerContent(body) + DynamicEndpointSwagger(body) } (dynamicEndpoint, callContext) <- NewStyle.function.createDynamicEndpoint(postedJson.swaggerString, cc.callContext) } yield { @@ -2776,8 +2782,6 @@ trait APIMethods400 { s"""Get a DynamicEndpoint. | | - |${authenticationRequiredMessage(true)} - | |Get one DynamicEndpoint, | |""", @@ -2813,16 +2817,13 @@ trait APIMethods400 { "/management/dynamic-endpoints", "Get DynamicEndpoints", s"""Get DynamicEndpoints. - | - | - |${authenticationRequiredMessage(true)} | |Get DynamicEndpoints, | |""", emptyObjectJson, ListResult( - "dynamic-entities", + "dynamic_entities", List(dynamicEndpointResponseBodyExample) ), List( @@ -2846,7 +2847,23 @@ trait APIMethods400 { } } } - + + lazy val dynamicEndpoint: OBPEndpoint = { + case EntityName(entityName) :: Nil JsonGet req => { cc => + val listName = StringHelpers.snakify(English.plural(entityName)) + for { + (Full(u), callContext) <- authenticatedAccess(cc) + _ <- NewStyle.function.hasEntitlement("", u.userId, DynamicEntityInfo.canGetRole(entityName), callContext) + (box, _) <- NewStyle.function.invokeDynamicConnector(GET_ALL, entityName, None, None, Some(cc)) + resultList: JArray = unboxResult(box.asInstanceOf[Box[JArray]], entityName) + } yield { + import net.liftweb.json.JsonDSL._ + + val jValue: JObject = listName -> filterDynamicObjects(resultList, req) + (jValue, HttpCode.`200`(Some(cc))) + } + } + } } diff --git a/obp-api/src/main/scala/code/api/v4_0_0/DynamicEndpointHelper.scala b/obp-api/src/main/scala/code/api/v4_0_0/DynamicEndpointHelper.scala new file mode 100644 index 000000000..7d16328e0 --- /dev/null +++ b/obp-api/src/main/scala/code/api/v4_0_0/DynamicEndpointHelper.scala @@ -0,0 +1,245 @@ +package code.api.v4_0_0 + +import java.io.File +import java.nio.charset.Charset +import java.util +import java.util.{Date, Objects} + +import code.DynamicEndpoint.{DynamicEndpointProvider, DynamicEndpointT} +import code.api.util.APIUtil.{Catalogs, OBPEndpoint, ResourceDoc, authenticationRequiredMessage, emptyObjectJson, generateUUID, notCore, notOBWG, notPSD2} +import code.api.util.ApiTag.{ResourceDocTag, apiTagApi, apiTagNewStyle} +import code.api.util.ErrorMessages.{InvalidJsonFormat, UnknownError, UserHasMissingRoles, UserNotLoggedIn} +import code.api.util.{APIUtil, ApiRole, ApiTag, CustomJsonFormats, NewStyle} +import code.api.util.ApiRole.getOrCreateDynamicApiRole +import com.openbankproject.commons.model.enums.DynamicEntityFieldType +import com.openbankproject.commons.util.{ApiVersion, Functions} +import io.swagger.v3.oas.models.{OpenAPI, Operation, PathItem} +import io.swagger.v3.oas.models.PathItem.HttpMethod +import io.swagger.v3.oas.models.media.{ArraySchema, BooleanSchema, Content, DateSchema, DateTimeSchema, IntegerSchema, NumberSchema, ObjectSchema, Schema, StringSchema} +import io.swagger.v3.oas.models.parameters.RequestBody +import io.swagger.v3.oas.models.responses.ApiResponses +import io.swagger.v3.parser.OpenAPIV3Parser +import net.liftweb.json.JsonAST.{JArray, JField, JObject} +import net.liftweb.json.JsonDSL._ +import net.liftweb.json +import net.liftweb.json.JValue +import net.liftweb.util.StringHelpers +import org.apache.commons.io.FileUtils +import org.apache.commons.lang3.StringUtils +import org.atteo.evo.inflector.English + +import scala.collection.immutable.{List, Nil} +import scala.collection.mutable +import scala.collection.mutable.ArrayBuffer +import scala.collection.JavaConverters._ + + +object DynamicEndpointHelper { + private implicit val formats = CustomJsonFormats.formats + /** + * dynamic endpoints url prefix + */ + val urlPrefix = APIUtil.getPropsValue("dynamic_endpoints_url_prefix", "dynamic") + + val dynamicEndpointsUrl: Set[(String, HttpMethod)] = Set() + + def swaggerToResourceDocs(content: String): mutable.Iterable[ResourceDoc] = { + val openAPI: OpenAPI = parseSwaggerContent(content) + + val tags: List[ResourceDocTag] = List(ApiTag.apiTagDynamicEndpoint, apiTagApi, apiTagNewStyle) + + val paths: mutable.Map[String, PathItem] = openAPI.getPaths.asScala + def entitlementSuffix(path: String) = Math.abs(path.hashCode).toString.substring(0, 3) // to avoid different swagger have same entitlement + val docs: mutable.Iterable[ResourceDoc] = for { + (path, pathItem) <- paths + (method: HttpMethod, op: Operation) <- pathItem.readOperationsMap.asScala + } yield { + val implementedInApiVersion = ApiVersion.v4_0_0 + val partialFunction: OBPEndpoint = APIMethods400.Implementations4_0_0.genericEndpoint // TODO create real endpoint + val partialFunctionName: String = s"$method-$path".replace('/', '_') + val requestVerb: String = method.name() + val requestUrl: String = s"/$urlPrefix/$path".replace("//", "/") + val summary: String = Option(pathItem.getSummary) + .filter(StringUtils.isNotBlank) + .getOrElse(buildSummary(method, op, path)) + val description: String = Option(pathItem.getDescription) + .filter(StringUtils.isNotBlank) + .orElse(Option(op.getDescription)) + .filter(StringUtils.isNotBlank) + .map(_.capitalize) + .getOrElse(summary) + val exampleRequestBody: Product = getRequestExample(openAPI, op.getRequestBody) + val successResponseBody: Product = getResponseExample(openAPI, op.getResponses) + val errorResponseBodies: List[String] = List( + UserNotLoggedIn, + UserHasMissingRoles, + UnknownError + ) + val catalogs: Catalogs = Catalogs(notCore, notPSD2, notOBWG) + + val roleName = s"Can$summary${entitlementSuffix(path)}" + .replaceFirst("Can(Create|Update|Get|Delete)", "Can$1Dynamic") + .replace(" ", "") + val roles: Option[List[ApiRole]] = Some(List( + ApiRole.getOrCreateDynamicApiRole(roleName) + )) + val connectorMethods = Some(List(s"""dynamicEntityProcess: parameters contains {"key": "entityName", "value": "$summary"}""")) //TODO temp + ResourceDoc( + partialFunction, + implementedInApiVersion, + partialFunctionName, + requestVerb, + requestUrl, + summary, + description, + exampleRequestBody, + successResponseBody, + errorResponseBodies, + catalogs, + tags, + roles, + connectorMethods = connectorMethods + ) + } + docs + } + + def parseSwaggerContent(content: String): OpenAPI = { + val tempSwaggerFile = File.createTempFile("temp", ".swagger") + FileUtils.write(tempSwaggerFile, content, Charset.forName("utf-8")) + val openAPI: OpenAPI = new OpenAPIV3Parser().read(tempSwaggerFile.getAbsolutePath) + // Delete temp file when program exits, only if delete fail. + if(!FileUtils.deleteQuietly(tempSwaggerFile)){ + tempSwaggerFile.deleteOnExit() + } + openAPI + } + + def doc: ArrayBuffer[ResourceDoc] = { + val dynamicEndpoints: List[DynamicEndpointT] = DynamicEndpointProvider.connectorMethodProvider.vend.getAll() + + val docs = dynamicEndpoints.flatMap(it => swaggerToResourceDocs(it.swaggerString)) + ArrayBuffer[ResourceDoc](docs:_*) + } + + private def buildSummary(method: HttpMethod, op: Operation, path: String): String = method match { + case _ if StringUtils.isNotBlank(op.getSummary) => op.getSummary + case HttpMethod.GET | HttpMethod.DELETE => + val opName = if(method == HttpMethod.GET) "Get" else "Delete" + op.getResponses.asScala + .find(_._1.startsWith("20")) + .flatMap(it => getRef(it._2.getContent, it._2.get$ref()) ) + .map(StringUtils.substringAfterLast(_, "/")) + .map(entityName => s"$opName $entityName") + .orElse(Option(op.getDescription)) + .filter(StringUtils.isNotBlank) + .orElse(Option(s"$opName $path")) + .map(_.replaceFirst("(?i)((get|delete)\\s+\\S+).*", "$1")) + .map(capitalize) + .get + + case m@(HttpMethod.POST | HttpMethod.PUT) => + val opName = if(m == HttpMethod.POST) "Create" else "Update" + + getRef(op.getRequestBody.getContent, op.getRequestBody.get$ref()) + .map(StringUtils.substringAfterLast(_, "/")) + .map(entityName => s"$opName $entityName") + .orElse(Option(op.getDescription)) + .filter(StringUtils.isNotBlank) + .orElse(Option(s"$method $path")) + .map(capitalize) + .get + case _ => throw new RuntimeException(s"Support HTTP METHOD: GET, POST, PUT, DELETE, current method is $method") + } + private def capitalize(str: String): String = + StringUtils.split(str, " ").map(_.capitalize).mkString(" ") + + private def getRequestExample(openAPI: OpenAPI, body: RequestBody): Product = { + if(body == null || body.getContent == null) { + "" + } else { + getExample(openAPI, getRef(body.getContent, body.get$ref()).orNull) + } + } + private def getResponseExample(openAPI: OpenAPI, apiResponses: ApiResponses): Product = { + if(apiResponses == null || apiResponses.isEmpty) { + JObject() + } else { + val ref: Option[String] = apiResponses.asScala + .find(_._1.startsWith("20")) + .flatMap(it => getRef(it._2.getContent, it._2.get$ref())) + getExample(openAPI, ref.orNull) + } + } + + private def getRef(content: Content, $ref: String): Option[String] = { + if(StringUtils.isNoneBlank($ref)) { + Option($ref) + } else { + val schemaRef: Option[String] = Option(content.get("application/json")) + .flatMap(it => Option[Schema[_]](it.getSchema)) + .map(_.get$ref()) + .filter(StringUtils.isNoneBlank(_)) + + if(schemaRef.isDefined) { + Option(schemaRef.get) + } else { + val supportMediaTypes = content.values().asScala + supportMediaTypes.collectFirst { + case mediaType if mediaType.getSchema != null && StringUtils.isNotBlank(mediaType.getSchema.get$ref()) => + mediaType.getSchema.get$ref() + } + } + } + + } + private val RegexDefinitions = """(?:#/components/schemas(?:/#definitions)?/)(.+)""".r + private val RegexResponse = """#/responses/(.+)""".r + + private def getExample(openAPI: OpenAPI, ref: String): Product = ref match { + case null => JObject() + + case RegexResponse(refName) => + val response = openAPI.getComponents.getResponses.get(refName) + val ref = getRef(response.getContent, response.get$ref()) + getExample(openAPI, ref.get) + + case RegexDefinitions(refName) => + openAPI.getComponents.getSchemas.get(refName) match { + case o: ObjectSchema => + val properties: util.Map[String, Schema[_]] = o.getProperties + + val jFields: mutable.Iterable[JField] = properties.asScala.map { kv => + val (name, value) = kv + val valueExample = if(value.getClass == classOf[Schema[_]]) getExample(openAPI, value.get$ref()) else getPropertyExample(value) + JField(name, json.Extraction.decompose(valueExample)) + } + JObject(jFields.toList) + + case a: ArraySchema => + Option(a.getExample) + .map(json.Extraction.decompose(_).asInstanceOf[JObject]) + .getOrElse { + val schema: Schema[_] = a.getItems + val singleItem: Any = if(schema.getClass == classOf[Schema[_]]) getExample(openAPI, schema.get$ref()) else getPropertyExample(schema) + val jItem = json.Extraction.decompose(singleItem) + jItem :: Nil + } + } + + } + + private def getPropertyExample(schema: Schema[_]) = schema match { + case b: BooleanSchema => Option(b.getExample).getOrElse(true) + case d: DateSchema => Option(d.getExample).getOrElse { + APIUtil.DateWithDayFormat.format(new Date()) + } + case t: DateTimeSchema => Option(t.getExample).getOrElse { + APIUtil.DateWithSecondsFormat.format(new Date()) + } + case i: IntegerSchema => Option(i.getExample).getOrElse(1) + case n: NumberSchema => Option(n.getExample).getOrElse(1.2) + case s: StringSchema => Option(s.getExample).getOrElse("string") + case _ => throw new RuntimeException(s"Not support type $schema, please support it if necessary.") + } +} \ No newline at end of file diff --git a/obp-api/src/main/scala/code/dynamicEndpoint/DynamicEndpointProvider.scala b/obp-api/src/main/scala/code/dynamicEndpoint/DynamicEndpointProvider.scala index 229686706..f38c239e2 100644 --- a/obp-api/src/main/scala/code/dynamicEndpoint/DynamicEndpointProvider.scala +++ b/obp-api/src/main/scala/code/dynamicEndpoint/DynamicEndpointProvider.scala @@ -23,7 +23,7 @@ case class DynamicEndpointCommons( object DynamicEndpointCommons extends Converter[DynamicEndpointT, DynamicEndpointCommons] -case class DynamicEndpointSwagger(dynamicEndpointId: Option[String] = None, swaggerString: String) +case class DynamicEndpointSwagger(swaggerString: String, dynamicEndpointId: Option[String] = None) trait DynamicEndpointProvider { def create(swaggerString: String): Box[DynamicEndpointT] From 538bfbd415949e2c84dcf931611d10eb220ae95f Mon Sep 17 00:00:00 2001 From: hongwei Date: Thu, 26 Mar 2020 13:31:56 +0100 Subject: [PATCH 05/13] fixed the failed test for Dynamic Entity --- obp-api/src/test/scala/code/api/v4_0_0/DynamicEntityTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/obp-api/src/test/scala/code/api/v4_0_0/DynamicEntityTest.scala b/obp-api/src/test/scala/code/api/v4_0_0/DynamicEntityTest.scala index a0c13de9f..4f4527e87 100644 --- a/obp-api/src/test/scala/code/api/v4_0_0/DynamicEntityTest.scala +++ b/obp-api/src/test/scala/code/api/v4_0_0/DynamicEntityTest.scala @@ -297,7 +297,7 @@ class DynamicEntityTest extends V400ServerSetup { val responseGet = makeGetRequest(requestGet) Then("We should get a 200") responseGet.code should equal(200) - val json = responseGet.body \ "dynamic-entities" + val json = responseGet.body \ "dynamic_entities" val dynamicEntitiesGetJson = json.asInstanceOf[JArray] dynamicEntitiesGetJson.values should have size 1 From 71cc7cd515816e2dd15c7fa48d55999c9876b0c5 Mon Sep 17 00:00:00 2001 From: hongwei Date: Thu, 26 Mar 2020 13:36:30 +0100 Subject: [PATCH 06/13] DynamicEntities -> Dynamic Entities DynamicEndpoints -> Dynamic Endpoints --- .../scala/code/api/v4_0_0/APIMethods400.scala | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) 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 0f7004552..9184a8baa 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 @@ -814,8 +814,8 @@ trait APIMethods400 { nameOf(getDynamicEntities), "GET", "/management/dynamic-entities", - "Get DynamicEntities", - s"""Get the all DynamicEntities.""", + "Get Dynamic Entities", + s"""Get the all Dynamic Entities.""", emptyObjectJson, ListResult( "dynamic_entities", @@ -851,7 +851,7 @@ trait APIMethods400 { nameOf(createDynamicEntity), "POST", "/management/dynamic-entities", - "Create DynamicEntity", + "Create Dynamic Entity", s"""Create a DynamicEntity. | | @@ -902,7 +902,7 @@ trait APIMethods400 { nameOf(updateDynamicEntity), "PUT", "/management/dynamic-entities/DYNAMIC_ENTITY_ID", - "Update DynamicEntity", + "Update Dynamic Entity", s"""Update a DynamicEntity. | | @@ -960,7 +960,7 @@ trait APIMethods400 { nameOf(deleteDynamicEntity), "DELETE", "/management/dynamic-entities/DYNAMIC_ENTITY_ID", - "Delete DynamicEntity", + "Delete Dynamic Entity", s"""Delete a DynamicEntity specified by DYNAMIC_ENTITY_ID. | |""", @@ -2730,10 +2730,10 @@ trait APIMethods400 { nameOf(createDynamicEndpoint), "POST", "/management/dynamic-endpoints", - "Create DynamicEndpoint", - s"""Create a DynamicEndpoint. + "Create Dynamic Endpoint", + s"""Create a Dynamic Endpoint. | - |Create one DynamicEndpoint, + |Create one Dynamic Endpoint, | |""", dynamicEndpointRequestBodyExample, @@ -2778,11 +2778,11 @@ trait APIMethods400 { nameOf(getDynamicEndpoint), "GET", "/management/dynamic-endpoints/DYNAMIC_ENDPOINT_ID", - "Get DynamicEndpoint", - s"""Get a DynamicEndpoint. + "Get Dynamic Endpoint", + s"""Get a Dynamic Endpoint. | | - |Get one DynamicEndpoint, + |Get one Dynamic Endpoint, | |""", emptyObjectJson, @@ -2815,10 +2815,10 @@ trait APIMethods400 { nameOf(getDynamicEndpoints), "GET", "/management/dynamic-endpoints", - "Get DynamicEndpoints", - s"""Get DynamicEndpoints. + "Get Dynamic Endpoints", + s"""Get Dynamic Endpoints. | - |Get DynamicEndpoints, + |Get Dynamic Endpoints, | |""", emptyObjectJson, From 1ad13f307f5642bcc2d81339061a6b07d1e0ccce Mon Sep 17 00:00:00 2001 From: shuang Date: Thu, 26 Mar 2020 15:33:26 +0100 Subject: [PATCH 07/13] feature/add_dynamic_endpoints_with_swagger: request body, response content all not show special character escape --- .../scala/code/api/util/ExampleValue.scala | 489 +++++++++--------- .../scala/code/api/v4_0_0/APIMethods400.scala | 34 +- 2 files changed, 264 insertions(+), 259 deletions(-) diff --git a/obp-api/src/main/scala/code/api/util/ExampleValue.scala b/obp-api/src/main/scala/code/api/util/ExampleValue.scala index 657e26f57..b95acd9df 100644 --- a/obp-api/src/main/scala/code/api/util/ExampleValue.scala +++ b/obp-api/src/main/scala/code/api/util/ExampleValue.scala @@ -1,10 +1,12 @@ package code.api.util -import code.DynamicEndpoint.DynamicEndpointSwagger +import net.liftweb.json.JsonDSL._ import code.api.util.Glossary.{glossaryItems, makeGlossaryItem} import code.dynamicEntity.{DynamicEntityDefinition, DynamicEntityFooBar, DynamicEntityFullBarFields, DynamicEntityIntTypeExample, DynamicEntityStringTypeExample} import com.openbankproject.commons.model.enums.DynamicEntityFieldType +import net.liftweb.json +import net.liftweb.json.JObject case class ConnectorField(value: String, description: String) { @@ -360,247 +362,250 @@ object ExampleValue { lazy val dynamicEntityResponseBodyExample = dynamicEntityRequestBodyExample.copy(dynamicEntityId = Some("dynamic-entity-id")) - lazy val dynamicEndpointRequestBodyExample = DynamicEndpointSwagger("""{ - | "swagger": "2.0", - | "info": { - | "version": "0.0.1", - | "title": "Portus EVS sandbox demo API", - | "description": "Portus EVS sandbox demo API", - | "contact": { - | "name": "Digital & FinTech, Grant Thornton", - | "email": "peng.xu@ie.gt.com", - | "url": "https://www.tesobe.com/" - | } - | }, - | "host": "localhost:8080", - | "basePath": "/user", - | "schemes": [ - | "http" - | ], - | "consumes": [ - | "application/json" - | ], - | "produces": [ - | "application/json" - | ], - | "paths": { - | "/save": { - | "post": { - | "parameters": [ - | { - | "name": "body", - | "in": "body", - | "required": true, - | "schema": { - | "$ref": "#/definitions/user" - | } - | } - | ], - | "responses": { - | "201": { - | "description": "create user successful and return created user object", - | "schema": { - | "$ref": "#/definitions/user" - | } - | }, - | "500": { - | "description": "unexpected error", - | "schema": { - | "$ref": "#/responses/unexpectedError" - | } - | } - | } - | } - | }, - | "/getById/{userId}": { - | "get": { - | "description": "get reuested user by user ID", - | "parameters": [ - | { - | "$ref": "#/parameters/userId" - | } - | ], - | "consumes": [], - | "responses": { - | "200": { - | "description": "the successful get requested user by user ID", - | "schema": { - | "$ref": "#/definitions/user" - | } - | }, - | "400": { - | "description": "bad request", - | "schema": { - | "$ref": "#/responses/invalidRequest" - | } - | }, - | "404": { - | "description": "user not found", - | "schema": { - | "$ref": "#/definitions/APIError" - | } - | }, - | "500": { - | "description": "unexpected error", - | "schema": { - | "$ref": "#/responses/unexpectedError" - | } - | } - | } - | } - | }, - | "/listUsers": { - | "get": { - | "description": "get list of users", - | "consumes": [], - | "responses": { - | "200": { - | "description": "get all users", - | "schema": { - | "$ref": "#/definitions/users" - | } - | }, - | "404": { - | "description": "user not found", - | "schema": { - | "$ref": "#/definitions/APIError" - | } - | } - | } - | } - | }, - | "/updateUser": { - | "put": { - | "parameters": [ - | { - | "name": "body", - | "in": "body", - | "required": true, - | "schema": { - | "$ref": "#/definitions/user" - | } - | } - | ], - | "responses": { - | "200": { - | "description": "create user successful and return created user object", - | "schema": { - | "$ref": "#/definitions/user" - | } - | }, - | "500": { - | "description": "unexpected error", - | "schema": { - | "$ref": "#/responses/unexpectedError" - | } - | } - | } - | } - | }, - | "/delete/{userId}": { - | "delete": { - | "description": "delete user by user ID", - | "parameters": [ - | { - | "$ref": "#/parameters/userId" - | } - | ], - | "consumes": [], - | "responses": { - | "204": { - | "description": "the successful delete user by user ID" - | }, - | "400": { - | "description": "bad request", - | "schema": { - | "$ref": "#/responses/invalidRequest" - | } - | }, - | "500": { - | "description": "unexpected error", - | "schema": { - | "$ref": "#/responses/unexpectedError" - | } - | } - | } - | } - | } - | }, - | "definitions": { - | "user": { - | "type": "object", - | "properties": { - | "id": { - | "type": "integer", - | "description": "user ID" - | }, - | "first_name": { - | "type": "string" - | }, - | "last_name": { - | "type": "string" - | }, - | "age": { - | "type": "integer" - | }, - | "career": { - | "type": "string" - | } - | }, - | "required": [ - | "first_name", - | "last_name", - | "age" - | ] - | }, - | "users": { - | "description": "array of users", - | "type": "array", - | "items": { - | "$ref": "#/definitions/user" - | } - | }, - | "APIError": { - | "description": "content any error from API", - | "type": "object", - | "properties": { - | "errorCode": { - | "description": "content error code relate to API", - | "type": "string" - | }, - | "errorMessage": { - | "description": "content user-friendly error message", - | "type": "string" - | } - | } - | } - | }, - | "responses": { - | "unexpectedError": { - | "description": "unexpected error", - | "schema": { - | "$ref": "#/definitions/APIError" - | } - | }, - | "invalidRequest": { - | "description": "invalid request", - | "schema": { - | "$ref": "#/definitions/APIError" - | } - | } - | }, - | "parameters": { - | "userId": { - | "name": "userId", - | "in": "path", - | "required": true, - | "type": "string", - | "description": "user ID" - | } - | } - |} - |""".stripMargin) - lazy val dynamicEndpointResponseBodyExample = dynamicEndpointRequestBodyExample.copy(dynamicEndpointId = Some("dynamic-endpoint-id")) + private val dynamicEndpointSwagger = + """{ + | "swagger": "2.0", + | "info": { + | "version": "0.0.1", + | "title": "Portus EVS sandbox demo API", + | "description": "Portus EVS sandbox demo API", + | "contact": { + | "name": "Digital & FinTech, Grant Thornton", + | "email": "peng.xu@ie.gt.com", + | "url": "https://www.tesobe.com/" + | } + | }, + | "host": "localhost:8080", + | "basePath": "/user", + | "schemes": [ + | "http" + | ], + | "consumes": [ + | "application/json" + | ], + | "produces": [ + | "application/json" + | ], + | "paths": { + | "/save": { + | "post": { + | "parameters": [ + | { + | "name": "body", + | "in": "body", + | "required": true, + | "schema": { + | "$ref": "#/definitions/user" + | } + | } + | ], + | "responses": { + | "201": { + | "description": "create user successful and return created user object", + | "schema": { + | "$ref": "#/definitions/user" + | } + | }, + | "500": { + | "description": "unexpected error", + | "schema": { + | "$ref": "#/responses/unexpectedError" + | } + | } + | } + | } + | }, + | "/getById/{userId}": { + | "get": { + | "description": "get reuested user by user ID", + | "parameters": [ + | { + | "$ref": "#/parameters/userId" + | } + | ], + | "consumes": [], + | "responses": { + | "200": { + | "description": "the successful get requested user by user ID", + | "schema": { + | "$ref": "#/definitions/user" + | } + | }, + | "400": { + | "description": "bad request", + | "schema": { + | "$ref": "#/responses/invalidRequest" + | } + | }, + | "404": { + | "description": "user not found", + | "schema": { + | "$ref": "#/definitions/APIError" + | } + | }, + | "500": { + | "description": "unexpected error", + | "schema": { + | "$ref": "#/responses/unexpectedError" + | } + | } + | } + | } + | }, + | "/listUsers": { + | "get": { + | "description": "get list of users", + | "consumes": [], + | "responses": { + | "200": { + | "description": "get all users", + | "schema": { + | "$ref": "#/definitions/users" + | } + | }, + | "404": { + | "description": "user not found", + | "schema": { + | "$ref": "#/definitions/APIError" + | } + | } + | } + | } + | }, + | "/updateUser": { + | "put": { + | "parameters": [ + | { + | "name": "body", + | "in": "body", + | "required": true, + | "schema": { + | "$ref": "#/definitions/user" + | } + | } + | ], + | "responses": { + | "200": { + | "description": "create user successful and return created user object", + | "schema": { + | "$ref": "#/definitions/user" + | } + | }, + | "500": { + | "description": "unexpected error", + | "schema": { + | "$ref": "#/responses/unexpectedError" + | } + | } + | } + | } + | }, + | "/delete/{userId}": { + | "delete": { + | "description": "delete user by user ID", + | "parameters": [ + | { + | "$ref": "#/parameters/userId" + | } + | ], + | "consumes": [], + | "responses": { + | "204": { + | "description": "the successful delete user by user ID" + | }, + | "400": { + | "description": "bad request", + | "schema": { + | "$ref": "#/responses/invalidRequest" + | } + | }, + | "500": { + | "description": "unexpected error", + | "schema": { + | "$ref": "#/responses/unexpectedError" + | } + | } + | } + | } + | } + | }, + | "definitions": { + | "user": { + | "type": "object", + | "properties": { + | "id": { + | "type": "integer", + | "description": "user ID" + | }, + | "first_name": { + | "type": "string" + | }, + | "last_name": { + | "type": "string" + | }, + | "age": { + | "type": "integer" + | }, + | "career": { + | "type": "string" + | } + | }, + | "required": [ + | "first_name", + | "last_name", + | "age" + | ] + | }, + | "users": { + | "description": "array of users", + | "type": "array", + | "items": { + | "$ref": "#/definitions/user" + | } + | }, + | "APIError": { + | "description": "content any error from API", + | "type": "object", + | "properties": { + | "errorCode": { + | "description": "content error code relate to API", + | "type": "string" + | }, + | "errorMessage": { + | "description": "content user-friendly error message", + | "type": "string" + | } + | } + | } + | }, + | "responses": { + | "unexpectedError": { + | "description": "unexpected error", + | "schema": { + | "$ref": "#/definitions/APIError" + | } + | }, + | "invalidRequest": { + | "description": "invalid request", + | "schema": { + | "$ref": "#/definitions/APIError" + | } + | } + | }, + | "parameters": { + | "userId": { + | "name": "userId", + | "in": "path", + | "required": true, + | "type": "string", + | "description": "user ID" + | } + | } + |} + |""".stripMargin + lazy val dynamicEndpointRequestBodyExample = json.parse(dynamicEndpointSwagger).asInstanceOf[JObject] + lazy val dynamicEndpointResponseBodyExample = ("dynamicEndpointId", "dynamic-endpoint-id") ~ ("swaggerString", dynamicEndpointRequestBodyExample) + } 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 9184a8baa..60ddff3cd 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 @@ -49,6 +49,7 @@ import net.liftweb.http.rest.RestHelper import net.liftweb.json.JsonAST.JValue import net.liftweb.json.Serialization.write import net.liftweb.json._ +import net.liftweb.json.JsonDSL._ import net.liftweb.mapper.By import net.liftweb.util.Helpers.now import net.liftweb.util.{Helpers, StringHelpers} @@ -2748,25 +2749,20 @@ trait APIMethods400 { List(apiTagDynamicEndpoint, apiTagApi, apiTagNewStyle), Some(List(canCreateDynamicEndpoint))) - private val BodyPost = new TestPost[String] with JsonTest { - def body(r: Req): Box[String] = { - val value = r.body.map(it => new String(it)) - value - } - } - lazy val createDynamicEndpoint: OBPEndpoint = { - case "management" :: "dynamic-endpoints" :: Nil BodyPost body -> req => { + case "management" :: "dynamic-endpoints" :: Nil JsonPost json -> _ => { cc => for { postedJson <- NewStyle.function.tryons(InvalidJsonFormat, 400, cc.callContext) { - DynamicEndpointHelper.parseSwaggerContent(body) - DynamicEndpointSwagger(body) + val swaggerContent = compactRender(json) + DynamicEndpointHelper.parseSwaggerContent(swaggerContent) + DynamicEndpointSwagger(swaggerContent) } (dynamicEndpoint, callContext) <- NewStyle.function.createDynamicEndpoint(postedJson.swaggerString, cc.callContext) } yield { - val commonsData: DynamicEndpointCommons = dynamicEndpoint - (commonsData, HttpCode.`201`(callContext)) + val swaggerJson = parse(dynamicEndpoint.swaggerString) + val responseJson: JObject = ("dynamicEndpointId", dynamicEndpoint.dynamicEndpointId) ~ ("swaggerString", swaggerJson) + (responseJson, HttpCode.`201`(callContext)) } } } @@ -2803,8 +2799,9 @@ trait APIMethods400 { for { (dynamicEndpoint, callContext) <- NewStyle.function.getDynamicEndpoint(dynamicEndpointId, cc.callContext) } yield { - val commonsData: DynamicEndpointCommons = dynamicEndpoint - (commonsData, HttpCode.`201`(callContext)) + val swaggerJson = parse(dynamicEndpoint.swaggerString) + val responseJson: JObject = ("dynamicEndpointId", dynamicEndpoint.dynamicEndpointId) ~ ("swaggerString", swaggerJson) + (responseJson, HttpCode.`201`(callContext)) } } } @@ -2840,10 +2837,13 @@ trait APIMethods400 { case "management" :: "dynamic-endpoints" :: Nil JsonGet req => { cc => for { - (dynamicEndpoints, callContext) <- NewStyle.function.getDynamicEndpoints(cc.callContext) + (dynamicEndpoints, _) <- NewStyle.function.getDynamicEndpoints(cc.callContext) } yield { - val listCommons: List[DynamicEndpointCommons] = dynamicEndpoints - (ListResult("dynamic_entities", listCommons), HttpCode.`200`(cc.callContext)) + val resultList = dynamicEndpoints.map[JObject, List[JObject]] { dynamicEndpoint=> + val swaggerJson = parse(dynamicEndpoint.swaggerString) + ("dynamicEndpointId", dynamicEndpoint.dynamicEndpointId) ~ ("swaggerString", swaggerJson) + } + (ListResult("dynamic_entities", resultList), HttpCode.`200`(cc.callContext)) } } } From afb7319c49971fa3311f788927ae61feec762fd3 Mon Sep 17 00:00:00 2001 From: hongwei Date: Fri, 27 Mar 2020 09:59:55 +0100 Subject: [PATCH 08/13] tweaked the camel case to snake case for the dynamicEndpoint response body --- obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 60ddff3cd..3f66f3898 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 @@ -2800,7 +2800,7 @@ trait APIMethods400 { (dynamicEndpoint, callContext) <- NewStyle.function.getDynamicEndpoint(dynamicEndpointId, cc.callContext) } yield { val swaggerJson = parse(dynamicEndpoint.swaggerString) - val responseJson: JObject = ("dynamicEndpointId", dynamicEndpoint.dynamicEndpointId) ~ ("swaggerString", swaggerJson) + val responseJson: JObject = ("dynamic_endpoint_id", dynamicEndpoint.dynamicEndpointId) ~ ("swagger_string", swaggerJson) (responseJson, HttpCode.`201`(callContext)) } } @@ -2820,7 +2820,7 @@ trait APIMethods400 { |""", emptyObjectJson, ListResult( - "dynamic_entities", + "dynamic_endpoints", List(dynamicEndpointResponseBodyExample) ), List( @@ -2841,7 +2841,7 @@ trait APIMethods400 { } yield { val resultList = dynamicEndpoints.map[JObject, List[JObject]] { dynamicEndpoint=> val swaggerJson = parse(dynamicEndpoint.swaggerString) - ("dynamicEndpointId", dynamicEndpoint.dynamicEndpointId) ~ ("swaggerString", swaggerJson) + ("dynamic_endpoint_id", dynamicEndpoint.dynamicEndpointId) ~ ("swagger_string", swaggerJson) } (ListResult("dynamic_entities", resultList), HttpCode.`200`(cc.callContext)) } From a7884b43ce9131e92959dcc0a2734bbd320fcac5 Mon Sep 17 00:00:00 2001 From: hongwei Date: Fri, 27 Mar 2020 13:35:12 +0100 Subject: [PATCH 09/13] tweaked the camel case to snake case --- obp-api/src/main/scala/code/api/util/ExampleValue.scala | 2 +- obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/obp-api/src/main/scala/code/api/util/ExampleValue.scala b/obp-api/src/main/scala/code/api/util/ExampleValue.scala index b95acd9df..1698abecb 100644 --- a/obp-api/src/main/scala/code/api/util/ExampleValue.scala +++ b/obp-api/src/main/scala/code/api/util/ExampleValue.scala @@ -604,7 +604,7 @@ object ExampleValue { |} |""".stripMargin lazy val dynamicEndpointRequestBodyExample = json.parse(dynamicEndpointSwagger).asInstanceOf[JObject] - lazy val dynamicEndpointResponseBodyExample = ("dynamicEndpointId", "dynamic-endpoint-id") ~ ("swaggerString", dynamicEndpointRequestBodyExample) + lazy val dynamicEndpointResponseBodyExample = ("dynamic_endpoint_id", "dynamic-endpoint-id") ~ ("swagger_string", dynamicEndpointRequestBodyExample) } 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 34055fa24..ce4feff2a 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 @@ -2761,7 +2761,7 @@ trait APIMethods400 { (dynamicEndpoint, callContext) <- NewStyle.function.createDynamicEndpoint(postedJson.swaggerString, cc.callContext) } yield { val swaggerJson = parse(dynamicEndpoint.swaggerString) - val responseJson: JObject = ("dynamicEndpointId", dynamicEndpoint.dynamicEndpointId) ~ ("swaggerString", swaggerJson) + val responseJson: JObject = ("dynamic_endpoint_id", dynamicEndpoint.dynamicEndpointId) ~ ("swagger_string", swaggerJson) (responseJson, HttpCode.`201`(callContext)) } } @@ -2843,7 +2843,7 @@ trait APIMethods400 { val swaggerJson = parse(dynamicEndpoint.swaggerString) ("dynamic_endpoint_id", dynamicEndpoint.dynamicEndpointId) ~ ("swagger_string", swaggerJson) } - (ListResult("dynamic_entities", resultList), HttpCode.`200`(cc.callContext)) + (ListResult("dynamic_endpoints", resultList), HttpCode.`200`(cc.callContext)) } } } From 0686517bd4a2809232a40ff06cf37390fb76f1af Mon Sep 17 00:00:00 2001 From: hongwei Date: Fri, 27 Mar 2020 13:41:28 +0100 Subject: [PATCH 10/13] added the tests for DynamicEndpoints --- .../api/v4_0_0/DynamicendPointsTest.scala | 185 ++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 obp-api/src/test/scala/code/api/v4_0_0/DynamicendPointsTest.scala diff --git a/obp-api/src/test/scala/code/api/v4_0_0/DynamicendPointsTest.scala b/obp-api/src/test/scala/code/api/v4_0_0/DynamicendPointsTest.scala new file mode 100644 index 000000000..f6ae29046 --- /dev/null +++ b/obp-api/src/test/scala/code/api/v4_0_0/DynamicendPointsTest.scala @@ -0,0 +1,185 @@ +package code.api.v4_0_0 + +import code.api.util.APIUtil.OAuth._ +import code.api.util.ApiRole._ +import code.api.util.ErrorMessages.{UserHasMissingRoles, UserNotLoggedIn} +import code.api.util.ExampleValue +import code.api.v4_0_0.OBPAPI4_0_0.Implementations4_0_0 +import code.entitlement.Entitlement +import com.github.dwickern.macros.NameOf.nameOf +import com.openbankproject.commons.model.ErrorMessage +import com.openbankproject.commons.util.ApiVersion +import net.liftweb.json.Serialization.write +import org.scalatest.Tag + +class DynamicEndpointsTest extends V400ServerSetup { + /** + * Test tags + * Example: To run tests with tag "getPermissions": + * mvn test -D tagsToInclude + * + * This is made possible by the scalatest maven plugin + */ + object VersionOfApi extends Tag(ApiVersion.v4_0_0.toString) + object ApiEndpoint1 extends Tag(nameOf(Implementations4_0_0.createDynamicEndpoint)) + object ApiEndpoint2 extends Tag(nameOf(Implementations4_0_0.getDynamicEndpoints)) + object ApiEndpoint3 extends Tag(nameOf(Implementations4_0_0.getDynamicEndpoint)) + + + feature(s"test $ApiEndpoint1 version $VersionOfApi - Unauthorized access") { + scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { + val postDynamicEndpointRequestBodyExample = ExampleValue.dynamicEndpointRequestBodyExample + + When("We make a request v4.0.0") + val request400 = (v4_0_0_Request / "management" / "dynamic-endpoints").POST + val response400 = makePostRequest(request400, write(postDynamicEndpointRequestBodyExample)) + Then("We should get a 400") + response400.code should equal(400) + response400.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) + } + } + + feature(s"test $ApiEndpoint1 version $VersionOfApi - authorized access- missing role") { + scenario("We will call the endpoint with user credentials", ApiEndpoint1, VersionOfApi) { + val postDynamicEndpointRequestBodyExample = ExampleValue.dynamicEndpointRequestBodyExample + + When("We make a request v4.0.0") + val request = (v4_0_0_Request / "management" / "dynamic-endpoints").POST<@ (user1) + val response = makePostRequest(request, write(postDynamicEndpointRequestBodyExample)) + Then("We should get a 403") + response.code should equal(403) + response.body.extract[ErrorMessage].message.toString contains (UserHasMissingRoles) should be (true) + } + } + + feature(s"test $ApiEndpoint1 version $VersionOfApi - authorized access - with role - should be success!") { + scenario("We will call the endpoint with user credentials", ApiEndpoint1, VersionOfApi) { + When("We make a request v4.0.0") + val postDynamicEndpointRequestBodyExample = ExampleValue.dynamicEndpointRequestBodyExample + + When("We make a request v4.0.0") + val request = (v4_0_0_Request / "management" / "dynamic-endpoints").POST<@ (user1) + val response = makePostRequest(request, write(postDynamicEndpointRequestBodyExample)) + Then("We should get a 403") + response.code should equal(403) + response.body.extract[ErrorMessage].message.toString contains (UserHasMissingRoles) should be (true) + + Then("We grant the role to the user1") + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, canCreateDynamicEndpoint.toString) + + val responseWithRole = makePostRequest(request, write(postDynamicEndpointRequestBodyExample)) + Then("We should get a 201") + responseWithRole.code should equal(201) + responseWithRole.body.toString contains("dynamic_endpoint_id") should be (true) + responseWithRole.body.toString contains("Portus EVS sandbox demo API") should be (true) + responseWithRole.body.toString contains("content user-friendly error message") should be (true) + responseWithRole.body.toString contains("create user successful and return created user object") should be (true) + } + } + + feature(s"test $ApiEndpoint2 version $VersionOfApi - Unauthorized access") { + scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { + When("We make a request v4.0.0") + val request400 = (v4_0_0_Request / "management" / "dynamic-endpoints").GET + val response400 = makeGetRequest(request400) + Then("We should get a 400") + response400.code should equal(400) + response400.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) + } + } + + feature(s"test $ApiEndpoint2 version $VersionOfApi - authorized access- missing role") { + scenario("We will call the endpoint with user credentials", ApiEndpoint1, VersionOfApi) { + When("We make a request v4.0.0") + val request = (v4_0_0_Request / "management" / "dynamic-endpoints").GET<@ (user1) + val response = makeGetRequest(request) + Then("We should get a 400") + response.code should equal(403) + response.body.extract[ErrorMessage].message.toString contains (UserHasMissingRoles) should be (true) + } + } + + feature(s"test $ApiEndpoint2 version $VersionOfApi - authorized access - with role - should be success!") { + scenario("We will call the endpoint with user credentials", ApiEndpoint1, VersionOfApi) { + When("We make a request v4.0.0") + val postDynamicEndpointRequestBodyExample = ExampleValue.dynamicEndpointRequestBodyExample + + When("We make a request v4.0.0") + val request = (v4_0_0_Request / "management" / "dynamic-endpoints").POST<@ (user1) + val response = makePostRequest(request, write(postDynamicEndpointRequestBodyExample)) + Then("We should get a 403") + response.code should equal(403) + response.body.extract[ErrorMessage].message.toString contains (UserHasMissingRoles) should be (true) + + Then("We grant the role to the user1") + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanGetDynamicEndpoints.toString) + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanCreateDynamicEndpoint.toString) + + val responseWithRole = makePostRequest(request, write(postDynamicEndpointRequestBodyExample)) + Then("We should get a 201") + responseWithRole.code should equal(201) + + val request400 = (v4_0_0_Request / "management" / "dynamic-endpoints").GET<@ (user1) + val response400 = makeGetRequest(request400) + response400.code should be (200) + response400.body.toString contains("Portus EVS sandbox demo API") should be (true) + response400.body.toString contains("content user-friendly error message") should be (true) + response400.body.toString contains("create user successful and return created user object") should be (true) + + } + } + + feature(s"test $ApiEndpoint3 version $VersionOfApi - Unauthorized access") { + scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { + When("We make a request v4.0.0") + val request400 = (v4_0_0_Request / "management" / "dynamic-endpoints"/ "some-id").GET + val response400 = makeGetRequest(request400) + Then("We should get a 400") + response400.code should equal(400) + response400.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) + } + } + + feature(s"test $ApiEndpoint3 version $VersionOfApi - authorized access- missing role") { + scenario("We will call the endpoint with user credentials", ApiEndpoint1, VersionOfApi) { + When("We make a request v4.0.0") + val request = (v4_0_0_Request / "management" / "dynamic-endpoints" /"some-id").GET<@ (user1) + val response = makeGetRequest(request) + Then("We should get a 400") + response.code should equal(403) + response.body.extract[ErrorMessage].message.toString contains (UserHasMissingRoles) should be (true) + } + } + + feature(s"test $ApiEndpoint3 version $VersionOfApi - authorized access - with role - should be success!") { + scenario("We will call the endpoint with user credentials", ApiEndpoint1, VersionOfApi) { + When("We make a request v4.0.0") + val postDynamicEndpointRequestBodyExample = ExampleValue.dynamicEndpointRequestBodyExample + + When("We make a request v4.0.0") + val request = (v4_0_0_Request / "management" / "dynamic-endpoints").POST<@ (user1) + val response = makePostRequest(request, write(postDynamicEndpointRequestBodyExample)) + Then("We should get a 403") + response.code should equal(403) + response.body.extract[ErrorMessage].message.toString contains (UserHasMissingRoles) should be (true) + + Then("We grant the role to the user1") + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanGetDynamicEndpoint.toString) + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanCreateDynamicEndpoint.toString) + + val responseWithRole = makePostRequest(request, write(postDynamicEndpointRequestBodyExample)) + Then("We should get a 201") + responseWithRole.code should equal(201) + + val id = responseWithRole.body.\\("dynamic_endpoint_id").values.get("dynamic_endpoint_id").head.toString + + val request400 = (v4_0_0_Request / "management" / "dynamic-endpoints" /id).GET<@ (user1) + val response400 = makeGetRequest(request400) + response400.code should be (200) + response400.body.toString contains("Portus EVS sandbox demo API") should be (true) + response400.body.toString contains("content user-friendly error message") should be (true) + response400.body.toString contains("create user successful and return created user object") should be (true) + + } + } +} From 01045f17eeb31a8db1182f4750e03804501adcfe Mon Sep 17 00:00:00 2001 From: hongwei Date: Fri, 27 Mar 2020 13:53:40 +0100 Subject: [PATCH 11/13] fixed the merge issue --- .../main/scala/code/api/util/NewStyle.scala | 24 ------------------- .../scala/code/bankconnectors/Connector.scala | 12 ---------- 2 files changed, 36 deletions(-) diff --git a/obp-api/src/main/scala/code/api/util/NewStyle.scala b/obp-api/src/main/scala/code/api/util/NewStyle.scala index 3267320b1..63d5aaa88 100644 --- a/obp-api/src/main/scala/code/api/util/NewStyle.scala +++ b/obp-api/src/main/scala/code/api/util/NewStyle.scala @@ -2027,29 +2027,5 @@ object NewStyle { } } - def createDynamicEndpoint(swaggerString: String, callContext: Option[CallContext]): OBPReturnType[DynamicEndpointT] = { - Connector.connector.vend.createDynamicEndpoint( - swaggerString, - callContext - ) map { - i => (connectorEmptyResponse(i._1, callContext), i._2) - } - } - - def getDynamicEndpoint(dynamicEndpointId: String, callContext: Option[CallContext]): OBPReturnType[DynamicEndpointT] = { - Connector.connector.vend.getDynamicEndpoint( - dynamicEndpointId, - callContext - ) map { - i => (connectorEmptyResponse(i._1, callContext), i._2) - } - } - - def getDynamicEndpoints(callContext: Option[CallContext]): OBPReturnType[List[DynamicEndpointT]] = { - Connector.connector.vend.getDynamicEndpoints( - callContext - ) - } - } } diff --git a/obp-api/src/main/scala/code/bankconnectors/Connector.scala b/obp-api/src/main/scala/code/bankconnectors/Connector.scala index 924f19be1..4b5ae7051 100644 --- a/obp-api/src/main/scala/code/bankconnectors/Connector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/Connector.scala @@ -2179,18 +2179,6 @@ trait Connector extends MdcLoggable { (Failure(setUnimplementedError), callContext) } - def createDynamicEndpoint(swaggerString: String, callContext: Option[CallContext]): OBPReturnType[Box[DynamicEndpointT]] = Future { - (Failure(setUnimplementedError), callContext) - } - - def getDynamicEndpoint(dynamicEndpointId: String, callContext: Option[CallContext]): OBPReturnType[Box[DynamicEndpointT]] = Future { - (Failure(setUnimplementedError), callContext) - } - - def getDynamicEndpoints(callContext: Option[CallContext]): OBPReturnType[List[DynamicEndpointT]] = Future { - (List.empty[DynamicEndpointT], callContext) - } - def deleteCustomerAttribute(customerAttributeId: String, callContext: Option[CallContext]): OBPReturnType[Box[Boolean]] = Future{(Failure(setUnimplementedError), callContext)} From b805e47c9e3a9d230afb87bed1021308a7ad78f4 Mon Sep 17 00:00:00 2001 From: hongwei Date: Fri, 27 Mar 2020 14:00:26 +0100 Subject: [PATCH 12/13] tweaked the dynamicEndpointId -> dynamic_endpoint_id tweaked the swaggerString -> swagger_string --- obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala | 2 +- .../src/test/scala/code/api/v4_0_0/DynamicendPointsTest.scala | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) 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 58c63267c..0935a968e 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 @@ -2886,7 +2886,7 @@ trait APIMethods400 { } yield { val resultList = dynamicEndpoints.map[JObject, List[JObject]] { dynamicEndpoint=> val swaggerJson = parse(dynamicEndpoint.swaggerString) - ("dynamicEndpointId", dynamicEndpoint.dynamicEndpointId) ~ ("swaggerString", swaggerJson) + ("dynamic_endpoint_id", dynamicEndpoint.dynamicEndpointId) ~ ("swagger_string", swaggerJson) } (ListResult("dynamic_endpoints", resultList), HttpCode.`200`(cc.callContext)) } diff --git a/obp-api/src/test/scala/code/api/v4_0_0/DynamicendPointsTest.scala b/obp-api/src/test/scala/code/api/v4_0_0/DynamicendPointsTest.scala index f6ae29046..b9fca8dd1 100644 --- a/obp-api/src/test/scala/code/api/v4_0_0/DynamicendPointsTest.scala +++ b/obp-api/src/test/scala/code/api/v4_0_0/DynamicendPointsTest.scala @@ -71,6 +71,7 @@ class DynamicEndpointsTest extends V400ServerSetup { Then("We should get a 201") responseWithRole.code should equal(201) responseWithRole.body.toString contains("dynamic_endpoint_id") should be (true) + responseWithRole.body.toString contains("swagger_string") should be (true) responseWithRole.body.toString contains("Portus EVS sandbox demo API") should be (true) responseWithRole.body.toString contains("content user-friendly error message") should be (true) responseWithRole.body.toString contains("create user successful and return created user object") should be (true) @@ -176,6 +177,8 @@ class DynamicEndpointsTest extends V400ServerSetup { val request400 = (v4_0_0_Request / "management" / "dynamic-endpoints" /id).GET<@ (user1) val response400 = makeGetRequest(request400) response400.code should be (200) + response400.body.toString contains("dynamic_endpoint_id") should be (true) + response400.body.toString contains("swagger_string") should be (true) response400.body.toString contains("Portus EVS sandbox demo API") should be (true) response400.body.toString contains("content user-friendly error message") should be (true) response400.body.toString contains("create user successful and return created user object") should be (true) From 26b68d62027495747d83e2db8f28b96c2a3951fe Mon Sep 17 00:00:00 2001 From: shuang Date: Fri, 27 Mar 2020 21:03:31 +0800 Subject: [PATCH 13/13] feature/add_dynamic_endpoints_with_swagger: fix "dynamic-entities" to "dynamic_entities" in ListResult --- obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 0935a968e..d57c3289d 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 @@ -819,7 +819,7 @@ trait APIMethods400 { s"""Get the all Dynamic Entities.""", emptyObjectJson, ListResult( - "dynamic-entities", + "dynamic_entities", List(dynamicEntityResponseBodyExample) ), List(