OBV 400 added the create Product, get Product and get Products

This commit is contained in:
hongwei 2021-08-30 15:38:44 +02:00
parent 6e92c92619
commit 7a537a5b6e
18 changed files with 470 additions and 31 deletions

View File

@ -4334,6 +4334,33 @@ object SwaggerDefinitionsJSON {
)
val atmsJsonV400 = AtmsJsonV400(List(atmJsonV400))
val productJsonV400 = ProductJsonV400(
bank_id = bankIdExample.value,
code = "product_code",
parent_product_code = "parent",
name = "product name",
more_info_url = moreInfoUrlExample.value,
terms_and_conditions_url = termsAndConditionsUrlExample.value,
details = "Details",
description = "Description",
meta = metaJson,
Some(List(productAttributeResponseJson))
)
val productsJsonV400 = ProductsJsonV400(products = List(productJsonV400))
val putProductJsonV400 = PutProductJsonV400(
bank_id = bankIdExample.value,
parent_product_code = "parent",
name = "product name",
more_info_url = moreInfoUrlExample.value,
terms_and_conditions_url = termsAndConditionsUrlExample.value,
details = "Details",
description = "Description",
meta = metaJson
)
//The common error or success format.
//Just some helper format to use in Json

View File

@ -33,7 +33,6 @@ import java.nio.charset.Charset
import java.text.{ParsePosition, SimpleDateFormat}
import java.util.concurrent.ConcurrentHashMap
import java.util.{Calendar, Date, UUID}
import code.UserRefreshes.UserRefreshes
import code.accountholders.AccountHolders
import code.api.Constant._
@ -42,6 +41,7 @@ import code.api.builder.OBP_APIBuilder
import code.api.oauth1a.Arithmetics
import code.api.oauth1a.OauthParams._
import code.api.util.APIUtil.ResourceDoc.{findPathVariableNames, isPathVariable}
import code.api.util.ApiRole.{canCreateProduct, canCreateProductAtAnyBank}
import code.api.util.ApiTag.{ResourceDocTag, apiTagBank, apiTagNewStyle}
import code.api.util.Glossary.GlossaryItem
import code.api.util.JwsUtil.getJwsHeaderValue
@ -4050,4 +4050,21 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
APIUtil.getPropsValue("email_domain_to_entitlement_mappings").map(extractor).getOrElse(Nil)
}
val getProductsIsPublic = APIUtil.getPropsAsBoolValue("apiOptions.getProductsIsPublic", true)
val createProductEntitlements = canCreateProduct :: canCreateProductAtAnyBank :: Nil
val createProductEntitlementsRequiredText = UserHasMissingRoles + createProductEntitlements.mkString(" or ")
val productHiearchyAndCollectionNote =
"""
|
|Product hiearchy vs Product Collections:
|
|* You can define a hierarchy of products - so that a child Product inherits attributes of its parent Product - using the parent_product_code in Product.
|
|* You can define a collection (also known as baskets or buckets) of products using Product Collections.
|
""".stripMargin
}

View File

@ -1221,8 +1221,8 @@ object ExampleValue {
lazy val roleNameExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("role_name", roleNameExample)
lazy val refundExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("refund", refundExample)
lazy val termsAndConditionsUrlExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("terms_and_conditions_url_example", termsAndConditionsUrlExample)
lazy val canAddUrlExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_add_url", canAddUrlExample)

View File

@ -607,6 +607,7 @@ trait APIMethods220 {
family = product.family,
superFamily = product.super_family,
moreInfoUrl = product.more_info_url,
termsAndConditionsUrl = null,
details = product.details,
description = product.description,
metaLicenceId = product.meta.license.id,

View File

@ -2430,22 +2430,6 @@ trait APIMethods310 {
}
}
val productHiearchyAndCollectionNote =
"""
|
|Product hiearchy vs Product Collections:
|
|* You can define a hierarchy of products - so that a child Product inherits attributes of its parent Product - using the parent_product_code in Product.
|
|* You can define a collection (also known as baskets or buckets) of products using Product Collections.
|
""".stripMargin
val createProductEntitlements = canCreateProduct :: canCreateProductAtAnyBank :: Nil
val createProductEntitlementsRequiredText = UserHasMissingRoles + createProductEntitlements.mkString(" or ")
resourceDocs += ResourceDoc(
createProduct,
implementedInApiVersion,
@ -2511,6 +2495,7 @@ trait APIMethods310 {
family = product.family,
superFamily = product.super_family,
moreInfoUrl = product.more_info_url,
termsAndConditionsUrl = null,
details = product.details,
description = product.description,
metaLicenceId = product.meta.license.id,
@ -2526,8 +2511,6 @@ trait APIMethods310 {
}
val getProductsIsPublic = APIUtil.getPropsAsBoolValue("apiOptions.getProductsIsPublic", true)
resourceDocs += ResourceDoc(
getProduct,
implementedInApiVersion,

View File

@ -59,6 +59,7 @@ import code.views.Views
import code.webuiprops.MappedWebUiPropsProvider.getWebUiPropsValue
import com.github.dwickern.macros.NameOf.nameOf
import com.openbankproject.commons.ExecutionContext.Implicits.global
import com.openbankproject.commons.dto.GetProductsParam
import com.openbankproject.commons.model.enums.DynamicEntityOperation._
import com.openbankproject.commons.model.enums.{TransactionRequestStatus, _}
import com.openbankproject.commons.model.{ListResult, _}
@ -9619,7 +9620,186 @@ trait APIMethods400 {
}
}
}
staticResourceDocs += ResourceDoc(
getProducts,
implementedInApiVersion,
"getProducts",
"GET",
"/banks/BANK_ID/products",
"Get Products",
s"""Returns information about the financial products offered by a bank specified by BANK_ID including:
|
|* Name
|* Code
|* Parent Product Code
|* More info URL
|* Terms And Conditions URL
|* Description
|* Terms and Conditions
|* License the data under this endpoint is released under
|
|Can filter with attributes name and values.
|URL params example: /banks/some-bank-id/products?manager=John&count=8
|
|${authenticationRequiredMessage(!getProductsIsPublic)}""".stripMargin,
emptyObjectJson,
productsJsonV310,
List(
UserNotLoggedIn,
BankNotFound,
UnknownError
),
List(apiTagProduct, apiTagNewStyle)
)
lazy val getProducts : OBPEndpoint = {
case "banks" :: BankId(bankId) :: "products" :: Nil JsonGet req => {
cc => {
for {
(_, callContext) <- getProductsIsPublic match {
case false => authenticatedAccess(cc)
case true => anonymousAccess(cc)
}
(_, callContext) <- NewStyle.function.getBank(bankId, callContext)
params = req.params.toList.map(kv => GetProductsParam(kv._1, kv._2))
products <- Future(Connector.connector.vend.getProducts(bankId, params)) map {
unboxFullOrFail(_, callContext, ProductNotFoundByProductCode)
}
} yield {
(JSONFactory400.createProductsJson(products), HttpCode.`200`(callContext))
}
}
}
}
staticResourceDocs += ResourceDoc(
createProduct,
implementedInApiVersion,
nameOf(createProduct),
"PUT",
"/banks/BANK_ID/products/PRODUCT_CODE",
"Create Product",
s"""Create or Update Product for the Bank.
|
|
|Typical Super Family values / Asset classes are:
|
|Debt
|Equity
|FX
|Commodity
|Derivative
|
|$productHiearchyAndCollectionNote
|
|
|${authenticationRequiredMessage(true) }
|
|
|""",
putProductJsonV400,
productJsonV400,
List(
$UserNotLoggedIn,
$BankNotFound,
UserHasMissingRoles,
UnknownError
),
List(apiTagProduct, apiTagNewStyle),
Some(List(canCreateProduct, canCreateProductAtAnyBank))
)
lazy val createProduct: OBPEndpoint = {
case "banks" :: BankId(bankId) :: "products" :: ProductCode(productCode) :: Nil JsonPut json -> _ => {
cc =>
for {
(Full(u), callContext) <- SS.user
_ <- NewStyle.function.hasAtLeastOneEntitlement(failMsg = createProductEntitlementsRequiredText)(bankId.value, u.userId, createProductEntitlements, callContext)
failMsg = s"$InvalidJsonFormat The Json body should be the $PutProductJsonV400 "
product <- NewStyle.function.tryons(failMsg, 400, callContext) {
json.extract[PutProductJsonV400]
}
parentProductCode <- product.parent_product_code.trim.nonEmpty match {
case false =>
Future(Empty)
case true =>
Future(Connector.connector.vend.getProduct(bankId, ProductCode(product.parent_product_code))) map {
getFullBoxOrFail(_, callContext, ParentProductNotFoundByProductCode + " {" + product.parent_product_code + "}", 400)
}
}
success <- Future(Connector.connector.vend.createOrUpdateProduct(
bankId = bankId.value,
code = productCode.value,
parentProductCode = parentProductCode.map(_.code.value).toOption,
name = product.name,
category = null,
family = null,
superFamily = null,
moreInfoUrl = product.more_info_url,
termsAndConditionsUrl = product.terms_and_conditions_url,
details = product.details,
description = product.description,
metaLicenceId = product.meta.license.id,
metaLicenceName = product.meta.license.name
)) map {
connectorEmptyResponse(_, callContext)
}
} yield {
(JSONFactory400.createProductJson(success), HttpCode.`201`(callContext))
}
}
}
staticResourceDocs += ResourceDoc(
getProduct,
implementedInApiVersion,
nameOf(getProduct),
"GET",
"/banks/BANK_ID/products/PRODUCT_CODE",
"Get Bank Product",
s"""Returns information about a financial Product offered by the bank specified by BANK_ID and PRODUCT_CODE including:
|
|* Name
|* Code
|* Parent Product Code
|* Category
|* Family
|* Super Family
|* More info URL
|* Description
|* Terms and Conditions
|* License the data under this endpoint is released under
|
|${authenticationRequiredMessage(!getProductsIsPublic)}""".stripMargin,
emptyObjectJson,
productJsonV400,
List(
UserNotLoggedIn,
$BankNotFound,
ProductNotFoundByProductCode,
UnknownError
),
List(apiTagProduct, apiTagNewStyle)
)
lazy val getProduct: OBPEndpoint = {
case "banks" :: BankId(bankId) :: "products" :: ProductCode(productCode) :: Nil JsonGet _ => {
cc => {
for {
(_, callContext) <- getProductsIsPublic match {
case false => authenticatedAccess(cc)
case true => anonymousAccess(cc)
}
product <- Future(Connector.connector.vend.getProduct(bankId, productCode)) map {
unboxFullOrFail(_, callContext, ProductNotFoundByProductCode)
}
(productAttributes, callContext) <- NewStyle.function.getProductAttributesByBankAndCode(bankId, productCode, callContext)
} yield {
(JSONFactory400.createProductJson(product, productAttributes), HttpCode.`200`(callContext))
}
}
}
}
}
private def createDynamicEndpointMethod(bankId: Option[String], json: JValue, cc: CallContext) = {

View File

@ -28,7 +28,6 @@ package code.api.v4_0_0
import java.text.SimpleDateFormat
import java.util.Date
import code.api.attributedefinition.AttributeDefinition
import code.api.util.APIUtil
import code.api.util.APIUtil.{DateWithDay, DateWithSeconds, stringOptionOrNull, stringOrNull}
@ -40,8 +39,8 @@ import code.api.v2_1_0.{IbanJson, JSONFactory210, PostCounterpartyBespokeJson, R
import code.api.v2_2_0.CounterpartyMetadataJson
import code.api.v3_0_0.JSONFactory300._
import code.api.v3_0_0._
import code.api.v3_1_0.JSONFactory310.createAccountAttributeJson
import code.api.v3_1_0.{AccountAttributeResponseJson, RedisCallLimitJson}
import code.api.v3_1_0.JSONFactory310.{createAccountAttributeJson, createProductAttributesJson}
import code.api.v3_1_0.{AccountAttributeResponseJson, ProductAttributeResponseWithoutBankIdJson, RedisCallLimitJson}
import code.apicollection.ApiCollectionTrait
import code.apicollectionendpoint.ApiCollectionEndpointTrait
import code.atms.Atms.Atm
@ -567,6 +566,31 @@ case class MySpaces(
bank_ids: List[String],
)
case class ProductJsonV400(
bank_id: String,
code: String,
parent_product_code: String,
name: String,
more_info_url: String,
terms_and_conditions_url: String,
details: String,
description: String,
meta: MetaJsonV140,
product_attributes: Option[List[ProductAttributeResponseWithoutBankIdJson]]
)
case class ProductsJsonV400(products: List[ProductJsonV400])
case class PutProductJsonV400(
bank_id: String,
parent_product_code: String,
name: String,
more_info_url: String,
terms_and_conditions_url: String,
details: String,
description: String,
meta: MetaJsonV140
)
case class CounterpartyJson400(
name: String,
description: String,
@ -1516,6 +1540,38 @@ object JSONFactory400 {
balanceInquiryFee = Some(atmJsonV400.balance_inquiry_fee)
)
}
def createProductJson(product: Product) : ProductJsonV400 = {
ProductJsonV400(
bank_id = product.bankId.toString,
code = product.code.value,
parent_product_code = product.parentProductCode.value,
name = product.name,
more_info_url = product.moreInfoUrl,
terms_and_conditions_url = product.moreInfoUrl,
details = product.details,
description = product.description,
meta = createMetaJson(product.meta),
None
)
}
def createProductsJson(productsList: List[Product]) : ProductsJsonV400 = {
ProductsJsonV400(productsList.map(createProductJson))
}
def createProductJson(product: Product, productAttributes: List[ProductAttribute]) : ProductJsonV400 = {
ProductJsonV400(
bank_id = product.bankId.toString,
code = product.code.value,
parent_product_code = product.parentProductCode.value,
name = product.name,
more_info_url = product.moreInfoUrl,
terms_and_conditions_url = product.termsAndConditionsUrl,
details = product.details,
description = product.description,
meta = createMetaJson(product.meta),
product_attributes = Some(createProductAttributesJson(productAttributes))
)
}
}

View File

@ -1588,6 +1588,7 @@ trait Connector extends MdcLoggable {
family : String,
superFamily : String,
moreInfoUrl : String,
termsAndConditionsUrl : String,
details : String,
description : String,
metaLicenceId : String,

View File

@ -1140,11 +1140,12 @@ object KafkaMappedConnector_JVMcompatible extends Connector with KafkaHelper wit
family : String,
superFamily : String,
moreInfoUrl : String,
termsAndConditionsUrl: String,
details : String,
description : String,
metaLicenceId : String,
metaLicenceName : String): Box[Product] = {
LocalMappedConnector.createOrUpdateProduct(bankId, code, parentProductCode, name, category, family, superFamily, moreInfoUrl, details, description, metaLicenceId, metaLicenceName)
LocalMappedConnector.createOrUpdateProduct(bankId, code, parentProductCode, name, category, family, superFamily, moreInfoUrl, termsAndConditionsUrl, details, description, metaLicenceId, metaLicenceName)
}
override def getProduct(bankId: BankId, productCode: ProductCode): Box[Product] = {

View File

@ -2721,6 +2721,7 @@ object LocalMappedConnector extends Connector with MdcLoggable {
family: String,
superFamily: String,
moreInfoUrl: String,
termsAndConditionsUrl: String,
details: String,
description: String,
metaLicenceId: String,
@ -2742,6 +2743,7 @@ object LocalMappedConnector extends Connector with MdcLoggable {
.mFamily(family)
.mSuperFamily(superFamily)
.mMoreInfoUrl(moreInfoUrl)
.mTermsAndConditionsUrl(termsAndConditionsUrl)
.mDetails(details)
.mDescription(description)
.mLicenseId(metaLicenceId)
@ -2759,6 +2761,7 @@ object LocalMappedConnector extends Connector with MdcLoggable {
.mFamily(family)
.mSuperFamily(superFamily)
.mMoreInfoUrl(moreInfoUrl)
.mTermsAndConditionsUrl(termsAndConditionsUrl)
.mDetails(details)
.mDescription(description)
.mLicenseId(metaLicenceId)

View File

@ -2713,6 +2713,7 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit {
family=familyExample.value,
superFamily=superFamilyExample.value,
moreInfoUrl=moreInfoUrlExample.value,
termsAndConditionsUrl=termsAndConditionsUrlExample.value,
details=detailsExample.value,
description=descriptionExample.value,
meta=Meta( License(id=idExample.value,
@ -2750,6 +2751,7 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit {
family=familyExample.value,
superFamily=superFamilyExample.value,
moreInfoUrl=moreInfoUrlExample.value,
termsAndConditionsUrl=termsAndConditionsUrlExample.value,
details=detailsExample.value,
description=descriptionExample.value,
meta=Meta( License(id=idExample.value,
@ -5523,7 +5525,7 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit {
exampleInboundMessage = (
InBoundGetProductCollectionItemsTree(inboundAdapterCallContext=MessageDocsSwaggerDefinitions.inboundAdapterCallContext,
status=MessageDocsSwaggerDefinitions.inboundStatus,
data=List( ProductCollectionItemsTree(productCollectionItem= ProductCollectionItemCommons(collectionCode=collectionCodeExample.value,
data=List(ProductCollectionItemsTree(productCollectionItem= ProductCollectionItemCommons(collectionCode=collectionCodeExample.value,
memberProductCode=memberProductCodeExample.value),
product= ProductCommons(bankId=BankId(bankIdExample.value),
code=ProductCode(productCodeExample.value),
@ -5533,6 +5535,7 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit {
family=familyExample.value,
superFamily=superFamilyExample.value,
moreInfoUrl=moreInfoUrlExample.value,
termsAndConditionsUrl=termsAndConditionsUrlExample.value,
details=detailsExample.value,
description=descriptionExample.value,
meta=Meta( License(id=idExample.value,

View File

@ -2852,6 +2852,7 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable
family=familyExample.value,
superFamily=superFamilyExample.value,
moreInfoUrl=moreInfoUrlExample.value,
termsAndConditionsUrl=termsAndConditionsUrlExample.value,
details=detailsExample.value,
description=descriptionExample.value,
meta=Meta( License(id=idExample.value,
@ -2889,6 +2890,7 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable
family=familyExample.value,
superFamily=superFamilyExample.value,
moreInfoUrl=moreInfoUrlExample.value,
termsAndConditionsUrl=termsAndConditionsUrlExample.value,
details=detailsExample.value,
description=descriptionExample.value,
meta=Meta( License(id=idExample.value,
@ -5720,6 +5722,7 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable
family=familyExample.value,
superFamily=superFamilyExample.value,
moreInfoUrl=moreInfoUrlExample.value,
termsAndConditionsUrl=termsAndConditionsUrlExample.value,
details=detailsExample.value,
description=descriptionExample.value,
meta=Meta( License(id=idExample.value,

View File

@ -2833,6 +2833,7 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable {
family=familyExample.value,
superFamily=superFamilyExample.value,
moreInfoUrl=moreInfoUrlExample.value,
termsAndConditionsUrl=termsAndConditionsUrlExample.value,
details=detailsExample.value,
description=descriptionExample.value,
meta=Meta( License(id=idExample.value,
@ -2870,6 +2871,7 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable {
family=familyExample.value,
superFamily=superFamilyExample.value,
moreInfoUrl=moreInfoUrlExample.value,
termsAndConditionsUrl=termsAndConditionsUrlExample.value,
details=detailsExample.value,
description=descriptionExample.value,
meta=Meta( License(id=idExample.value,
@ -5701,6 +5703,7 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable {
family=familyExample.value,
superFamily=superFamilyExample.value,
moreInfoUrl=moreInfoUrlExample.value,
termsAndConditionsUrl=termsAndConditionsUrlExample.value,
details=detailsExample.value,
description=descriptionExample.value,
meta=Meta( License(id=idExample.value,

View File

@ -39,6 +39,7 @@ class MappedProduct extends Product with LongKeyedMapper[MappedProduct] with IdP
object mFamily extends MappedString(this, 50)
object mSuperFamily extends MappedString(this, 50)
object mMoreInfoUrl extends MappedString(this, 2000) // use URL field?
object mTermsAndConditionsUrl extends MappedString(this, 2000) // use URL field?
object mDetails extends MappedString(this, 2000)
object mDescription extends MappedString(this, 2000)
@ -57,6 +58,7 @@ class MappedProduct extends Product with LongKeyedMapper[MappedProduct] with IdP
override def family : String = mFamily.get
override def superFamily : String = mSuperFamily.get
override def moreInfoUrl: String = mMoreInfoUrl.get
override def termsAndConditionsUrl: String = mTermsAndConditionsUrl.get
override def details: String = mDetails.get
override def description: String = mDescription.get

View File

@ -21,6 +21,7 @@ class ProductsTest extends ServerSetup with DefaultUsers with V140ServerSetup {
family : String,
superFamily : String,
moreInfoUrl: String,
termsAndConditionsUrl: String,
details: String,
description: String,
meta: Meta) extends Product
@ -40,11 +41,11 @@ class ProductsTest extends ServerSetup with DefaultUsers with V140ServerSetup {
)
val fakeProduct1 = ProductImpl(BankWithLicense, ProductCode("prod1"), ProductCode(""), "name 1", "cat 1", "family 1", "super family 1", "http://www.example.com/moreinfo1.html", "", "", fakeMeta)
val fakeProduct2 = ProductImpl(BankWithLicense, ProductCode("prod2"), ProductCode(""), "name 2", "cat 1", "family 1", "super family 1", "http://www.example.com/moreinfo2.html", "", "", fakeMeta)
val fakeProduct1 = ProductImpl(BankWithLicense, ProductCode("prod1"), ProductCode(""), "name 1", "cat 1", "family 1", "super family 1", "http://www.example.com/moreinfo1.html", "http://www.example.com/termsAndConditionsUrl1.html","", "", fakeMeta)
val fakeProduct2 = ProductImpl(BankWithLicense, ProductCode("prod2"), ProductCode(""), "name 2", "cat 1", "family 1", "super family 1", "http://www.example.com/moreinfo2.html", "http://www.example.com/termsAndConditionsUrl2.html", "","", fakeMeta)
// Should not be returned (no license)
val fakeProduct3 = ProductImpl(BankWithoutLicense, ProductCode("prod3"), ProductCode(""), "name 3", "cat 1", "family 1", "super family 1", "http://www.example.com/moreinfo3.html", "", "", fakeMetaNoLicense)
val fakeProduct3 = ProductImpl(BankWithoutLicense, ProductCode("prod3"), ProductCode(""), "name 3", "cat 1", "family 1", "super family 1", "http://www.example.com/moreinfo3.html", "http://www.example.com/termsAndConditionsUrl3.html", "","", fakeMetaNoLicense)
// This mock provider is returning same branches for the fake banks

View File

@ -0,0 +1,156 @@
/**
Open Bank Project - API
Copyright (C) 2011-2019, TESOBE GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Email: contact@tesobe.com
TESOBE GmbH
Osloerstrasse 16/17
Berlin 13359, Germany
This product includes software developed at
TESOBE (http://www.tesobe.com/)
*/
package code.api.v4_0_0
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON
import code.api.util.ErrorMessages._
import code.api.util.APIUtil.OAuth._
import code.api.util.ApiRole._
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
import scala.collection.immutable.{List, Nil}
class ProductTest extends V400ServerSetup {
override def beforeAll(): Unit = {
super.beforeAll()
}
override def afterAll(): Unit = {
super.afterAll()
}
/**
* 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.createProduct))
object ApiEndpoint2 extends Tag(nameOf(Implementations4_0_0.getProduct))
object ApiEndpoint3 extends Tag(nameOf(Implementations4_0_0.getProducts))
lazy val testBankId = randomBankId
lazy val parentPutProductJsonV400: PutProductJsonV400 = SwaggerDefinitionsJSON.putProductJsonV400.copy(parent_product_code ="")
def createProduct(code: String, json: PutProductJsonV400) = {
When("We try to create a product v4.0.0")
val request400 = (v4_0_0_Request / "banks" / testBankId / "products" / code).PUT <@ (user1)
val response400 = makePutRequest(request400, write(json))
Then("We should get a 201")
response400.code should equal(201)
val product = response400.body.extract[ProductJsonV400]
product.code shouldBe code
product.parent_product_code shouldBe json.parent_product_code
product.bank_id shouldBe testBankId
product.name shouldBe json.name
product.more_info_url shouldBe json.more_info_url
product.terms_and_conditions_url shouldBe json.terms_and_conditions_url
product.details shouldBe json.details
product.description shouldBe json.description
product
}
feature("Create Product v4.0.0") {
scenario("We will call the Add endpoint without a user credentials", ApiEndpoint1, VersionOfApi) {
When("We make a request v4.0.0")
val request400 = (v4_0_0_Request / "banks" / testBankId / "products" / "CODE").PUT
val response400 = makePutRequest(request400, write(parentPutProductJsonV400))
Then("We should get a 401")
response400.code should equal(401)
And("error should be " + UserNotLoggedIn)
response400.body.extract[ErrorMessage].message should equal (UserNotLoggedIn)
}
scenario("We will call the Add endpoint without a proper role", ApiEndpoint1, VersionOfApi) {
When("We make a request v4.0.0")
val request400 = (v4_0_0_Request / "banks" / testBankId / "products" / "CODE").PUT <@(user1)
val response400 = makePutRequest(request400, write(parentPutProductJsonV400))
Then("We should get a 403")
response400.code should equal(403)
val createProductEntitlements = canCreateProduct :: canCreateProductAtAnyBank :: Nil
val createProductEntitlementsRequiredText = UserHasMissingRoles + createProductEntitlements.mkString(" or ")
And("error should be " + createProductEntitlementsRequiredText)
response400.body.extract[ErrorMessage].message contains (createProductEntitlementsRequiredText) should be (true)
}
scenario("We will call the Add endpoint with user credentials and role", ApiEndpoint1, ApiEndpoint2, ApiEndpoint3, VersionOfApi) {
Entitlement.entitlement.vend.addEntitlement(testBankId, resourceUser1.userId, CanCreateProduct.toString)
// Create an grandparent
val grandparent: ProductJsonV400 = createProduct(code = "GRANDPARENT_CODE", json = parentPutProductJsonV400)
// Create an parent
val product: ProductJsonV400 = createProduct(code = "PARENT_CODE", json = parentPutProductJsonV400.copy(parent_product_code = grandparent.code))
// Get
val requestGet400 = (v4_0_0_Request / "banks" / product.bank_id / "products" / product.code ).GET <@(user1)
val responseGet400 = makeGetRequest(requestGet400)
Then("We should get a 200")
responseGet400.code should equal(200)
val product1 = responseGet400.body.extract[ProductJsonV400]
// Create an child
val childPutProductJsonV400 = parentPutProductJsonV400.copy(parent_product_code = product.code)
createProduct(code = "PRODUCT_CODE", json = childPutProductJsonV400)
// Get
val requestGetAll400 = (v4_0_0_Request / "banks" / product.bank_id / "products").GET <@(user1)
val responseGetAll400 = makeGetRequest(requestGetAll400)
Then("We should get a 200")
responseGetAll400.code should equal(200)
val products: ProductsJsonV400 = responseGetAll400.body.extract[ProductsJsonV400]
products.products.size shouldBe 3
}
scenario("Test the getProducts by url parameters", ApiEndpoint3, VersionOfApi) {
When("We need to first create the products ")
Entitlement.entitlement.vend.addEntitlement(testBankId, resourceUser1.userId, CanCreateProduct.toString)
createProduct(code = "PRODUCT_CODE", json = parentPutProductJsonV400)
When("we grant the CanCreateProductAttribute role")
Entitlement.entitlement.vend.addEntitlement(testBankId, resourceUser1.userId, CanCreateProductAttribute.toString)
val requestGetAll400 = (v4_0_0_Request / "banks" / testBankId / "products").GET <@(user1)
val responseGetAll400 = makeGetRequest(requestGetAll400)
Then("We should get a 200")
responseGetAll400.code should equal(200)
val products: ProductsJsonV400 = responseGetAll400.body.extract[ProductsJsonV400]
products.products.head.code should be ("PRODUCT_CODE")
}
}
}

View File

@ -369,6 +369,7 @@ case class ProductCommons(bankId: BankId,
family : String,
superFamily : String,
moreInfoUrl: String,
termsAndConditionsUrl: String,
details: String,
description: String,
meta: Meta) extends Product

View File

@ -380,6 +380,7 @@ trait Product {
def family : String
def superFamily : String
def moreInfoUrl: String
def termsAndConditionsUrl: String
def details :String
def description: String
def meta : Meta