diff --git a/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala b/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala index 1fdb2eb0f..c2d0e2777 100644 --- a/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala @@ -49,6 +49,7 @@ import code.api.util._ import code.api.util.migration.Migration import code.api.util.migration.Migration.DbFunction import code.apicollection.ApiCollection +import code.featuredapicollection.FeaturedApiCollection import code.apicollectionendpoint.ApiCollectionEndpoint import code.atmattribute.AtmAttribute import code.atms.MappedAtm @@ -1118,6 +1119,7 @@ object ToSchemify { MappedUserRefreshes, ApiCollection, ApiCollectionEndpoint, + FeaturedApiCollection, JsonSchemaValidation, AuthenticationTypeValidation, ConnectorMethod, 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 6e22b45cc..21a3f613c 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 @@ -5175,6 +5175,12 @@ object SwaggerDefinitionsJSON { lazy val apiCollectionEndpointJson400 = ApiCollectionEndpointJson400(apiCollectionEndpointIdExample.value, apiCollectionIdExample.value, operationIdExample.value) lazy val apiCollectionEndpointsJson400 = ApiCollectionEndpointsJson400(List(apiCollectionEndpointJson400)) + // Featured API Collections (v6.0.0) + lazy val postFeaturedApiCollectionJsonV600 = PostFeaturedApiCollectionJsonV600(apiCollectionIdExample.value, 1) + lazy val putFeaturedApiCollectionJsonV600 = PutFeaturedApiCollectionJsonV600(1) + lazy val featuredApiCollectionJsonV600 = FeaturedApiCollectionJsonV600(featuredApiCollectionIdExample.value, apiCollectionIdExample.value, 1) + lazy val featuredApiCollectionsJsonV600 = FeaturedApiCollectionsJsonV600(List(featuredApiCollectionJsonV600)) + lazy val jsonScalaConnectorMethod = JsonConnectorMethod(Some(connectorMethodIdExample.value),"getBank", connectorMethodBodyScalaExample.value, "Scala") lazy val jsonScalaConnectorMethodMethodBody = JsonConnectorMethodMethodBody(connectorMethodBodyScalaExample.value, "Scala") 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 442e67b4b..07a31d929 100644 --- a/obp-api/src/main/scala/code/api/util/ApiRole.scala +++ b/obp-api/src/main/scala/code/api/util/ApiRole.scala @@ -377,7 +377,10 @@ object ApiRole extends MdcLoggable{ case class CanGetAllApiCollections(requiresBankId: Boolean = false) extends ApiRole lazy val canGetAllApiCollections = CanGetAllApiCollections() - + + case class CanManageFeaturedApiCollections(requiresBankId: Boolean = false) extends ApiRole + lazy val canManageFeaturedApiCollections = CanManageFeaturedApiCollections() + case class CanGetCounterpartyAtAnyBank(requiresBankId: Boolean = false) extends ApiRole lazy val canGetCounterpartyAtAnyBank = CanGetCounterpartyAtAnyBank() 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 44fe84e8e..1fd2dcda1 100644 --- a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala +++ b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala @@ -436,6 +436,12 @@ object ErrorMessages { val ApiCollectionEndpointAlreadyExists = "OBP-30085: The ApiCollectionEndpoint is already exists." val ApiCollectionAlreadyExists = "OBP-30086: The ApiCollection is already exists." + val FeaturedApiCollectionNotFound = "OBP-30400: FeaturedApiCollection not found. Please specify a valid value for API_COLLECTION_ID." + val CreateFeaturedApiCollectionError = "OBP-30401: Could not create FeaturedApiCollection." + val UpdateFeaturedApiCollectionError = "OBP-30402: Could not update FeaturedApiCollection." + val DeleteFeaturedApiCollectionError = "OBP-30403: Could not delete FeaturedApiCollection." + val FeaturedApiCollectionAlreadyExists = "OBP-30404: The ApiCollection is already featured." + val DoubleEntryTransactionNotFound = "OBP-30087: Double Entry Transaction not found." val InvalidAuthContextUpdateRequestKey = "OBP-30088: Invalid Auth Context Update Request Key." 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 a3f2e8aa0..3ad75af34 100644 --- a/obp-api/src/main/scala/code/api/util/ExampleValue.scala +++ b/obp-api/src/main/scala/code/api/util/ExampleValue.scala @@ -285,7 +285,13 @@ object ExampleValue { lazy val apiCollectionEndpointIdExample = ConnectorField("8uy8a7e4-6d02-40e3-a129-0b2bf89de8uh", "A string that MUST uniquely identify the session on this OBP instance, can be used in all cache.") glossaryItems += makeGlossaryItem("ApiCollectionEndpoint.apiCollectionEndpointId", apiCollectionEndpointIdExample) - + + lazy val featuredApiCollectionIdExample = ConnectorField("9uy8a7e4-6d02-40e3-a129-0b2bf89de8uh", "A string that uniquely identifies this featured API collection entry.") + glossaryItems += makeGlossaryItem("FeaturedApiCollection.featuredApiCollectionId", featuredApiCollectionIdExample) + + lazy val sortOrderExample = ConnectorField("1", "The sort order for displaying featured API collections. Lower numbers appear first.") + glossaryItems += makeGlossaryItem("FeaturedApiCollection.sortOrder", sortOrderExample) + lazy val operationIdExample = ConnectorField("OBPv4.0.0-getBanks", "A uniquely identify the obp endpoint on OBP instance, you can get it from Get Resource endpoints.") glossaryItems += makeGlossaryItem("ApiCollectionEndpoint.operationId", operationIdExample) 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 2a684e516..e9b28d6ce 100644 --- a/obp-api/src/main/scala/code/api/util/NewStyle.scala +++ b/obp-api/src/main/scala/code/api/util/NewStyle.scala @@ -13,6 +13,7 @@ import code.api.util.ErrorMessages.{InsufficientAuthorisationToCreateTransaction import code.api.{APIFailureNewStyle, Constant, JsonResponseException} import code.apicollection.{ApiCollectionTrait, MappedApiCollectionsProvider} import code.apicollectionendpoint.{ApiCollectionEndpointTrait, MappedApiCollectionEndpointsProvider} +import code.featuredapicollection.{FeaturedApiCollectionTrait, MappedFeaturedApiCollectionsProvider} import code.atmattribute.AtmAttribute import code.authtypevalidation.{AuthenticationTypeValidationProvider, JsonAuthTypeValidation} import code.bankattribute.BankAttribute @@ -3715,11 +3716,35 @@ object NewStyle extends MdcLoggable{ } def getFeaturedApiCollections(callContext: Option[CallContext]) : OBPReturnType[List[ApiCollectionTrait]] = { - //we get the getFeaturedApiCollectionIds from props, and remove the deplication there. - val featuredApiCollectionIds = APIUtil.getPropsValue("featured_api_collection_ids","").split(",").map(_.trim).toSet.toList - //We filter the isDefined and is isSharable collections. - val apiCollections = featuredApiCollectionIds.map(MappedApiCollectionsProvider.getApiCollectionById).filter(_.isDefined).filter(_.head.isSharable).map(_.head) - Future{(apiCollections.sortBy(_.apiCollectionName), callContext)} + // First get featured collections from database, sorted by sortOrder + val dbFeaturedApiCollections = MappedFeaturedApiCollectionsProvider.getAllFeaturedApiCollections() + val dbApiCollectionIds = dbFeaturedApiCollections.map(_.apiCollectionId).toSet + + // Get actual ApiCollections for database featured entries + val dbApiCollections = dbFeaturedApiCollections + .map(f => MappedApiCollectionsProvider.getApiCollectionById(f.apiCollectionId)) + .filter(_.isDefined) + .filter(_.head.isSharable) + .map(_.head) + + // Get props-based featured IDs that are NOT in database + val propsApiCollectionIds = APIUtil.getPropsValue("featured_api_collection_ids", "") + .split(",") + .map(_.trim) + .filter(_.nonEmpty) + .toList + .filterNot(dbApiCollectionIds.contains) + + // Get actual ApiCollections for props entries and sort them by name + val propsApiCollections = propsApiCollectionIds + .map(MappedApiCollectionsProvider.getApiCollectionById) + .filter(_.isDefined) + .filter(_.head.isSharable) + .map(_.head) + .sortBy(_.apiCollectionName) + + // Merge: database entries first (preserve sortOrder), then props entries (sorted by name) + Future{(dbApiCollections ++ propsApiCollections, callContext)} } def createApiCollection( @@ -3813,6 +3838,69 @@ object NewStyle extends MdcLoggable{ } } + // Featured API Collections functions + def createFeaturedApiCollection( + apiCollectionId: String, + sortOrder: Int, + callContext: Option[CallContext] + ): OBPReturnType[FeaturedApiCollectionTrait] = { + Future(MappedFeaturedApiCollectionsProvider.createFeaturedApiCollection(apiCollectionId, sortOrder)) map { + i => (unboxFullOrFail(i, callContext, CreateFeaturedApiCollectionError), callContext) + } + } + + def getFeaturedApiCollectionByApiCollectionId( + apiCollectionId: String, + callContext: Option[CallContext] + ): OBPReturnType[FeaturedApiCollectionTrait] = { + Future(MappedFeaturedApiCollectionsProvider.getFeaturedApiCollectionByApiCollectionId(apiCollectionId)) map { + i => (unboxFullOrFail(i, callContext, s"$FeaturedApiCollectionNotFound Current API_COLLECTION_ID($apiCollectionId)"), callContext) + } + } + + def getAllFeaturedApiCollectionsAdmin(callContext: Option[CallContext]): OBPReturnType[List[FeaturedApiCollectionTrait]] = { + Future(MappedFeaturedApiCollectionsProvider.getAllFeaturedApiCollections(), callContext) + } + + def updateFeaturedApiCollection( + apiCollectionId: String, + sortOrder: Int, + callContext: Option[CallContext] + ): OBPReturnType[FeaturedApiCollectionTrait] = { + Future { + val featured = MappedFeaturedApiCollectionsProvider.getFeaturedApiCollectionByApiCollectionId(apiCollectionId) + featured.flatMap { f => + MappedFeaturedApiCollectionsProvider.updateFeaturedApiCollection(f.featuredApiCollectionId, sortOrder) + } + } map { + i => (unboxFullOrFail(i, callContext, s"$UpdateFeaturedApiCollectionError Current API_COLLECTION_ID($apiCollectionId)"), callContext) + } + } + + def deleteFeaturedApiCollectionByApiCollectionId( + apiCollectionId: String, + callContext: Option[CallContext] + ): OBPReturnType[Boolean] = { + Future(MappedFeaturedApiCollectionsProvider.deleteFeaturedApiCollectionByApiCollectionId(apiCollectionId)) map { + i => (unboxFullOrFail(i, callContext, s"$DeleteFeaturedApiCollectionError Current API_COLLECTION_ID($apiCollectionId)"), callContext) + } + } + + def checkFeaturedApiCollectionDoesNotExist( + apiCollectionId: String, + callContext: Option[CallContext] + ): OBPReturnType[Boolean] = { + Future { + val existing = MappedFeaturedApiCollectionsProvider.getFeaturedApiCollectionByApiCollectionId(apiCollectionId) + existing match { + case net.liftweb.common.Full(_) => + throw new RuntimeException(FeaturedApiCollectionAlreadyExists) + case _ => + (true, callContext) + } + } + } + def createJsonSchemaValidation(validation: JsonValidation, callContext: Option[CallContext]): OBPReturnType[JsonValidation] = Future { val newValidation = JsonSchemaValidationProvider.validationProvider.vend.create(validation) diff --git a/obp-api/src/main/scala/code/api/v6_0_0/APIMethods600.scala b/obp-api/src/main/scala/code/api/v6_0_0/APIMethods600.scala index 35f98d696..4ea8a69a6 100644 --- a/obp-api/src/main/scala/code/api/v6_0_0/APIMethods600.scala +++ b/obp-api/src/main/scala/code/api/v6_0_0/APIMethods600.scala @@ -30,7 +30,7 @@ import code.api.v5_0_0.JSONFactory500 import code.api.v5_0_0.{ViewJsonV500, ViewsJsonV500} import code.api.v5_1_0.{JSONFactory510, PostCustomerLegalNameJsonV510} import code.api.dynamic.entity.helper.{DynamicEntityHelper, DynamicEntityInfo} -import code.api.v6_0_0.JSONFactory600.{AddUserToGroupResponseJsonV600, DynamicEntityDiagnosticsJsonV600, DynamicEntityIssueJsonV600, GroupEntitlementJsonV600, GroupEntitlementsJsonV600, GroupJsonV600, GroupsJsonV600, PostGroupJsonV600, PostGroupMembershipJsonV600, PostResetPasswordUrlJsonV600, PutGroupJsonV600, ReferenceTypeJsonV600, ReferenceTypesJsonV600, ResetPasswordUrlJsonV600, RoleWithEntitlementCountJsonV600, RolesWithEntitlementCountsJsonV600, ScannedApiVersionJsonV600, UpdateViewJsonV600, UserGroupMembershipJsonV600, UserGroupMembershipsJsonV600, ValidateUserEmailJsonV600, ValidateUserEmailResponseJsonV600, ViewJsonV600, ViewPermissionJsonV600, ViewPermissionsJsonV600, ViewsJsonV600, createAbacRuleJsonV600, createAbacRulesJsonV600, createActiveRateLimitsJsonV600, createCallLimitJsonV600, createRedisCallCountersJson} +import code.api.v6_0_0.JSONFactory600.{AddUserToGroupResponseJsonV600, DynamicEntityDiagnosticsJsonV600, DynamicEntityIssueJsonV600, GroupEntitlementJsonV600, GroupEntitlementsJsonV600, GroupJsonV600, GroupsJsonV600, PostGroupJsonV600, PostGroupMembershipJsonV600, PostResetPasswordUrlJsonV600, PutGroupJsonV600, ReferenceTypeJsonV600, ReferenceTypesJsonV600, ResetPasswordUrlJsonV600, RoleWithEntitlementCountJsonV600, RolesWithEntitlementCountsJsonV600, ScannedApiVersionJsonV600, UpdateViewJsonV600, UserGroupMembershipJsonV600, UserGroupMembershipsJsonV600, ValidateUserEmailJsonV600, ValidateUserEmailResponseJsonV600, ViewJsonV600, ViewPermissionJsonV600, ViewPermissionsJsonV600, ViewsJsonV600, createAbacRuleJsonV600, createAbacRulesJsonV600, createActiveRateLimitsJsonV600, createCallLimitJsonV600, createRedisCallCountersJson, createFeaturedApiCollectionJsonV600, createFeaturedApiCollectionsJsonV600} import code.api.v6_0_0.{AbacRuleJsonV600, AbacRuleResultJsonV600, AbacRulesJsonV600, CacheConfigJsonV600, CacheInfoJsonV600, CacheNamespaceInfoJsonV600, CreateAbacRuleJsonV600, CreateDynamicEntityRequestJsonV600, CurrentConsumerJsonV600, DynamicEntityDefinitionJsonV600, DynamicEntityDefinitionWithCountJsonV600, DynamicEntitiesWithCountJsonV600, DynamicEntityLinksJsonV600, ExecuteAbacRuleJsonV600, GetOidcClientResponseJsonV600, InMemoryCacheStatusJsonV600, MyDynamicEntitiesJsonV600, PostVerifyUserCredentialsJsonV600, RedisCacheStatusJsonV600, RelatedLinkJsonV600, UpdateAbacRuleJsonV600, UpdateDynamicEntityRequestJsonV600, VerifyOidcClientRequestJsonV600, VerifyOidcClientResponseJsonV600} import code.api.v6_0_0.OBPAPI6_0_0 import code.abacrule.{AbacRuleEngine, MappedAbacRuleProvider} @@ -1037,7 +1037,7 @@ trait APIMethods600 { holder = AccountHolderJSON("Jane Smith", false), bank_routing = BankRoutingJsonV121("OBP", "other.bank.uk"), account_routings = List(AccountRoutingJsonV121("OBP", "counterparty-123")), - metadata = null + metadata = otherAccountMetadataJSON ), details = TransactionDetailsJSON( `type` = "SEPA", @@ -1047,7 +1047,7 @@ trait APIMethods600 { new_balance = AmountOfMoneyJsonV121("EUR", "1000.00"), value = AmountOfMoneyJsonV121("EUR", "100.00") ), - metadata = null, + metadata = transactionMetadataJSON, transaction_attributes = Nil ))), List( @@ -7521,6 +7521,184 @@ trait APIMethods600 { } } + // Featured API Collections Management Endpoints + + staticResourceDocs += ResourceDoc( + createFeaturedApiCollection, + implementedInApiVersion, + nameOf(createFeaturedApiCollection), + "POST", + "/management/api-collections/featured", + "Create Featured Api Collection", + s"""Add an API Collection to the featured list. + | + |${userAuthenticationMessage(true)} + | + |""".stripMargin, + postFeaturedApiCollectionJsonV600, + featuredApiCollectionJsonV600, + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + ApiCollectionNotFound, + FeaturedApiCollectionAlreadyExists, + CreateFeaturedApiCollectionError, + UnknownError + ), + List(apiTagApiCollection, apiTagApi), + Some(List(canManageFeaturedApiCollections)) + ) + + lazy val createFeaturedApiCollection: OBPEndpoint = { + case "management" :: "api-collections" :: "featured" :: Nil JsonPost json -> _ => { + cc => implicit val ec = EndpointContext(Some(cc)) + for { + (Full(u), callContext) <- authenticatedAccess(cc) + _ <- NewStyle.function.hasEntitlement("", u.userId, canManageFeaturedApiCollections, callContext) + postJson <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the $PostFeaturedApiCollectionJsonV600", 400, callContext) { + json.extract[PostFeaturedApiCollectionJsonV600] + } + // Verify the API Collection exists and is sharable + (apiCollection, callContext) <- NewStyle.function.getApiCollectionById(postJson.api_collection_id, callContext) + _ <- Helper.booleanToFuture(s"$ApiCollectionNotFound The API Collection must be sharable to be featured.", cc=callContext) { + apiCollection.isSharable + } + // Check it's not already featured + _ <- NewStyle.function.checkFeaturedApiCollectionDoesNotExist(postJson.api_collection_id, callContext) + // Create the featured entry + (featuredApiCollection, callContext) <- NewStyle.function.createFeaturedApiCollection( + postJson.api_collection_id, + postJson.sort_order, + callContext + ) + } yield { + (JSONFactory600.createFeaturedApiCollectionJsonV600(featuredApiCollection), HttpCode.`201`(callContext)) + } + } + } + + staticResourceDocs += ResourceDoc( + getFeaturedApiCollectionsAdmin, + implementedInApiVersion, + nameOf(getFeaturedApiCollectionsAdmin), + "GET", + "/management/api-collections/featured", + "Get Featured Api Collections (Admin)", + s"""Get all featured API collections with their sort order (admin view). + | + |This endpoint returns the featured collections stored in the database with their sort order. + |It is intended for administrators to manage the featured list. + | + |${userAuthenticationMessage(true)} + | + |""".stripMargin, + EmptyBody, + featuredApiCollectionsJsonV600, + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), + List(apiTagApiCollection, apiTagApi), + Some(List(canManageFeaturedApiCollections)) + ) + + lazy val getFeaturedApiCollectionsAdmin: OBPEndpoint = { + case "management" :: "api-collections" :: "featured" :: Nil JsonGet _ => { + cc => implicit val ec = EndpointContext(Some(cc)) + for { + (Full(u), callContext) <- authenticatedAccess(cc) + _ <- NewStyle.function.hasEntitlement("", u.userId, canManageFeaturedApiCollections, callContext) + (featuredApiCollections, callContext) <- NewStyle.function.getAllFeaturedApiCollectionsAdmin(callContext) + } yield { + (JSONFactory600.createFeaturedApiCollectionsJsonV600(featuredApiCollections), HttpCode.`200`(callContext)) + } + } + } + + staticResourceDocs += ResourceDoc( + updateFeaturedApiCollection, + implementedInApiVersion, + nameOf(updateFeaturedApiCollection), + "PUT", + "/management/api-collections/featured/API_COLLECTION_ID", + "Update Featured Api Collection", + s"""Update the sort order of a featured API collection. + | + |${userAuthenticationMessage(true)} + | + |""".stripMargin, + putFeaturedApiCollectionJsonV600, + featuredApiCollectionJsonV600, + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + FeaturedApiCollectionNotFound, + UpdateFeaturedApiCollectionError, + UnknownError + ), + List(apiTagApiCollection, apiTagApi), + Some(List(canManageFeaturedApiCollections)) + ) + + lazy val updateFeaturedApiCollection: OBPEndpoint = { + case "management" :: "api-collections" :: "featured" :: apiCollectionId :: Nil JsonPut json -> _ => { + cc => implicit val ec = EndpointContext(Some(cc)) + for { + (Full(u), callContext) <- authenticatedAccess(cc) + _ <- NewStyle.function.hasEntitlement("", u.userId, canManageFeaturedApiCollections, callContext) + putJson <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the $PutFeaturedApiCollectionJsonV600", 400, callContext) { + json.extract[PutFeaturedApiCollectionJsonV600] + } + (updatedFeaturedApiCollection, callContext) <- NewStyle.function.updateFeaturedApiCollection( + apiCollectionId, + putJson.sort_order, + callContext + ) + } yield { + (JSONFactory600.createFeaturedApiCollectionJsonV600(updatedFeaturedApiCollection), HttpCode.`200`(callContext)) + } + } + } + + staticResourceDocs += ResourceDoc( + deleteFeaturedApiCollection, + implementedInApiVersion, + nameOf(deleteFeaturedApiCollection), + "DELETE", + "/management/api-collections/featured/API_COLLECTION_ID", + "Delete Featured Api Collection", + s"""Remove an API Collection from the featured list. + | + |${userAuthenticationMessage(true)} + | + |""".stripMargin, + EmptyBody, + EmptyBody, + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + FeaturedApiCollectionNotFound, + DeleteFeaturedApiCollectionError, + UnknownError + ), + List(apiTagApiCollection, apiTagApi), + Some(List(canManageFeaturedApiCollections)) + ) + + lazy val deleteFeaturedApiCollection: OBPEndpoint = { + case "management" :: "api-collections" :: "featured" :: apiCollectionId :: Nil JsonDelete _ => { + cc => implicit val ec = EndpointContext(Some(cc)) + for { + (Full(u), callContext) <- authenticatedAccess(cc) + _ <- NewStyle.function.hasEntitlement("", u.userId, canManageFeaturedApiCollections, callContext) + (_, callContext) <- NewStyle.function.deleteFeaturedApiCollectionByApiCollectionId(apiCollectionId, callContext) + } yield { + (Full(true), HttpCode.`204`(callContext)) + } + } + } + } } diff --git a/obp-api/src/main/scala/code/api/v6_0_0/JSONFactory6.0.0.scala b/obp-api/src/main/scala/code/api/v6_0_0/JSONFactory6.0.0.scala index 8b721a83f..107b09b23 100644 --- a/obp-api/src/main/scala/code/api/v6_0_0/JSONFactory6.0.0.scala +++ b/obp-api/src/main/scala/code/api/v6_0_0/JSONFactory6.0.0.scala @@ -31,6 +31,7 @@ import code.api.v3_1_0.{AccountAttributeResponseJson, RateLimit, RedisCallLimitJ import code.api.v4_0_0.TransactionAttributeResponseJson import code.api.v4_0_0.{BankAttributeBankResponseJsonV400, UserAgreementJson} import code.entitlement.Entitlement +import code.featuredapicollection.FeaturedApiCollectionTrait import code.loginattempts.LoginAttempt import code.model.dataAccess.ResourceUser import code.users.UserAgreement @@ -633,6 +634,26 @@ case class UpdateDynamicEntityRequestJsonV600( schema: net.liftweb.json.JsonAST.JObject ) +// Featured API Collections (v6.0.0) +case class PostFeaturedApiCollectionJsonV600( + api_collection_id: String, + sort_order: Int +) + +case class PutFeaturedApiCollectionJsonV600( + sort_order: Int +) + +case class FeaturedApiCollectionJsonV600( + featured_api_collection_id: String, + api_collection_id: String, + sort_order: Int +) + +case class FeaturedApiCollectionsJsonV600( + featured_api_collections: List[FeaturedApiCollectionJsonV600] +) + object JSONFactory600 extends CustomJsonFormats with MdcLoggable { def createRedisCallCountersJson( @@ -1258,6 +1279,24 @@ object JSONFactory600 extends CustomJsonFormats with MdcLoggable { AbacRulesJsonV600(rules.map(createAbacRuleJsonV600)) } + def createFeaturedApiCollectionJsonV600( + featuredApiCollection: FeaturedApiCollectionTrait + ): FeaturedApiCollectionJsonV600 = { + FeaturedApiCollectionJsonV600( + featured_api_collection_id = featuredApiCollection.featuredApiCollectionId, + api_collection_id = featuredApiCollection.apiCollectionId, + sort_order = featuredApiCollection.sortOrder + ) + } + + def createFeaturedApiCollectionsJsonV600( + featuredApiCollections: List[FeaturedApiCollectionTrait] + ): FeaturedApiCollectionsJsonV600 = { + FeaturedApiCollectionsJsonV600( + featuredApiCollections.map(createFeaturedApiCollectionJsonV600) + ) + } + def createCacheNamespaceJsonV600( prefix: String, description: String, diff --git a/obp-api/src/main/scala/code/featuredapicollection/FeaturedApiCollection.scala b/obp-api/src/main/scala/code/featuredapicollection/FeaturedApiCollection.scala new file mode 100644 index 000000000..1c33f2c47 --- /dev/null +++ b/obp-api/src/main/scala/code/featuredapicollection/FeaturedApiCollection.scala @@ -0,0 +1,26 @@ +package code.featuredapicollection + +import code.util.MappedUUID +import net.liftweb.mapper._ + +class FeaturedApiCollection extends FeaturedApiCollectionTrait with LongKeyedMapper[FeaturedApiCollection] with IdPK with CreatedUpdated { + def getSingleton = FeaturedApiCollection + + object FeaturedApiCollectionId extends MappedUUID(this) + object ApiCollectionId extends MappedString(this, 100) + object SortOrder extends MappedInt(this) + + override def featuredApiCollectionId: String = FeaturedApiCollectionId.get + override def apiCollectionId: String = ApiCollectionId.get + override def sortOrder: Int = SortOrder.get +} + +object FeaturedApiCollection extends FeaturedApiCollection with LongKeyedMetaMapper[FeaturedApiCollection] { + override def dbIndexes = UniqueIndex(FeaturedApiCollectionId) :: UniqueIndex(ApiCollectionId) :: super.dbIndexes +} + +trait FeaturedApiCollectionTrait { + def featuredApiCollectionId: String + def apiCollectionId: String + def sortOrder: Int +} diff --git a/obp-api/src/main/scala/code/featuredapicollection/FeaturedApiCollectionsProvider.scala b/obp-api/src/main/scala/code/featuredapicollection/FeaturedApiCollectionsProvider.scala new file mode 100644 index 000000000..f06ee5772 --- /dev/null +++ b/obp-api/src/main/scala/code/featuredapicollection/FeaturedApiCollectionsProvider.scala @@ -0,0 +1,85 @@ +package code.featuredapicollection + +import code.util.Helper.MdcLoggable +import net.liftweb.common.Box +import net.liftweb.mapper.{By, OrderBy, Ascending} +import net.liftweb.util.Helpers.tryo + +trait FeaturedApiCollectionsProvider { + def createFeaturedApiCollection( + apiCollectionId: String, + sortOrder: Int + ): Box[FeaturedApiCollectionTrait] + + def getFeaturedApiCollectionById( + featuredApiCollectionId: String + ): Box[FeaturedApiCollectionTrait] + + def getFeaturedApiCollectionByApiCollectionId( + apiCollectionId: String + ): Box[FeaturedApiCollectionTrait] + + def updateFeaturedApiCollection( + featuredApiCollectionId: String, + sortOrder: Int + ): Box[FeaturedApiCollectionTrait] + + def getAllFeaturedApiCollections(): List[FeaturedApiCollectionTrait] + + def deleteFeaturedApiCollectionById( + featuredApiCollectionId: String + ): Box[Boolean] + + def deleteFeaturedApiCollectionByApiCollectionId( + apiCollectionId: String + ): Box[Boolean] +} + +object MappedFeaturedApiCollectionsProvider extends MdcLoggable with FeaturedApiCollectionsProvider { + + override def createFeaturedApiCollection( + apiCollectionId: String, + sortOrder: Int + ): Box[FeaturedApiCollectionTrait] = + tryo( + FeaturedApiCollection + .create + .ApiCollectionId(apiCollectionId) + .SortOrder(sortOrder) + .saveMe() + ) + + override def getFeaturedApiCollectionById( + featuredApiCollectionId: String + ): Box[FeaturedApiCollectionTrait] = + FeaturedApiCollection.find(By(FeaturedApiCollection.FeaturedApiCollectionId, featuredApiCollectionId)) + + override def getFeaturedApiCollectionByApiCollectionId( + apiCollectionId: String + ): Box[FeaturedApiCollectionTrait] = + FeaturedApiCollection.find(By(FeaturedApiCollection.ApiCollectionId, apiCollectionId)) + + override def updateFeaturedApiCollection( + featuredApiCollectionId: String, + sortOrder: Int + ): Box[FeaturedApiCollectionTrait] = { + FeaturedApiCollection.find(By(FeaturedApiCollection.FeaturedApiCollectionId, featuredApiCollectionId)).map { featured => + featured + .SortOrder(sortOrder) + .saveMe() + } + } + + override def getAllFeaturedApiCollections(): List[FeaturedApiCollectionTrait] = + FeaturedApiCollection.findAll(OrderBy(FeaturedApiCollection.SortOrder, Ascending)) + + override def deleteFeaturedApiCollectionById( + featuredApiCollectionId: String + ): Box[Boolean] = + FeaturedApiCollection.find(By(FeaturedApiCollection.FeaturedApiCollectionId, featuredApiCollectionId)).map(_.delete_!) + + override def deleteFeaturedApiCollectionByApiCollectionId( + apiCollectionId: String + ): Box[Boolean] = + FeaturedApiCollection.find(By(FeaturedApiCollection.ApiCollectionId, apiCollectionId)).map(_.delete_!) +}