Feature: Featured API collections endpoints

This commit is contained in:
simonredfern 2026-01-30 13:12:52 +01:00
parent 7cec58749c
commit 5d353c80f6
10 changed files with 449 additions and 10 deletions

View File

@ -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,

View File

@ -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")

View File

@ -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()

View File

@ -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."

View File

@ -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)

View File

@ -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)

View File

@ -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))
}
}
}
}
}

View File

@ -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,

View File

@ -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
}

View File

@ -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_!)
}