From 958ca5c80db747ec2c4f35a832067f1e8e886aa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Thu, 7 Feb 2019 15:03:14 +0100 Subject: [PATCH] Added endpoint /banks/BANK_ID/product-bucket/PRODUCT_CODE v3.1.0 --- .../SwaggerDefinitionsJSON.scala | 41 ++++++++++ src/main/scala/code/api/util/NewStyle.scala | 3 +- .../scala/code/api/v3_1_0/APIMethods310.scala | 60 ++++++++++++++- .../code/api/v3_1_0/JSONFactory3.1.0.scala | 70 +++++++++++++++++ .../scala/code/api/v3_1_0/OBPAPI3_1_0.scala | 1 + .../scala/code/api/v3_1_0/ProductTest.scala | 75 ++++++++++--------- 6 files changed, 213 insertions(+), 37 deletions(-) diff --git a/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala b/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala index 0ca01f060..3ac621254 100644 --- a/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala +++ b/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala @@ -2090,6 +2090,47 @@ object SwaggerDefinitionsJSON { ) val productsJsonV210 = ProductsJsonV210(products = List(productJsonV210)) + + + val grandparentProductBucketJsonV310 = ProductBucketJsonV310( + bank_id="testBank2", + code="GRANDPARENT_CODE", + name="product name", + category="category", + family="family", + super_family="super family", + more_info_url="www.example.com/prod1/more-info.html", + details="Details", + description="Description", + meta = metaJson, + parent_product=None + ) + val parentProductBucketJsonV310 = ProductBucketJsonV310( + bank_id="testBank2", + code="PARENT_CODE", + name="product name", + category="category", + family="family", + super_family="super family", + more_info_url="www.example.com/prod1/more-info.html", + details="Details", + description="Description", + meta = metaJson, + parent_product=Some(grandparentProductBucketJsonV310) + ) + val childProductBucketJsonV310 = ProductBucketJsonV310( + bank_id="testBank2", + code="CHILD_CODE", + name="product name", + category="category", + family="family", + super_family="super family", + more_info_url="www.example.com/prod1/more-info.html", + details="Details", + description="Description", + meta = metaJson, + parent_product=Some(parentProductBucketJsonV310) + ) val postCounterpartyBespokeJson = PostCounterpartyBespokeJson( diff --git a/src/main/scala/code/api/util/NewStyle.scala b/src/main/scala/code/api/util/NewStyle.scala index 2938f39c0..068ebc8e0 100644 --- a/src/main/scala/code/api/util/NewStyle.scala +++ b/src/main/scala/code/api/util/NewStyle.scala @@ -145,7 +145,8 @@ object NewStyle { (nameOf(Implementations3_1_0.createProduct), ApiVersion.v3_1_0.toString), (nameOf(Implementations3_1_0.updateCustomerAddress), ApiVersion.v3_1_0.toString), (nameOf(Implementations3_1_0.getProduct), ApiVersion.v3_1_0.toString), - (nameOf(Implementations3_1_0.getProducts), ApiVersion.v3_1_0.toString) + (nameOf(Implementations3_1_0.getProducts), ApiVersion.v3_1_0.toString), + (nameOf(Implementations3_1_0.getProductBucket), ApiVersion.v3_1_0.toString) ) object HttpCode { diff --git a/src/main/scala/code/api/v3_1_0/APIMethods310.scala b/src/main/scala/code/api/v3_1_0/APIMethods310.scala index c16b3cc03..7f3b967d1 100644 --- a/src/main/scala/code/api/v3_1_0/APIMethods310.scala +++ b/src/main/scala/code/api/v3_1_0/APIMethods310.scala @@ -23,7 +23,7 @@ import code.metrics.APIMetrics import code.model._ import code.model.dataAccess.{AuthUser, BankAccountCreation} import code.productattribute.ProductAttribute.ProductAttributeType -import code.products.Products.ProductCode +import code.products.Products.{Product, ProductCode} import code.users.Users import code.util.Helper import code.webhook.AccountWebhook @@ -2511,6 +2511,64 @@ trait APIMethods310 { } } + resourceDocs += ResourceDoc( + getProductBucket, + implementedInApiVersion, + "getProductBucket", + "GET", + "/banks/BANK_ID/product-bucket/PRODUCT_CODE", + "Get Bank Product", + s"""Returns information about the financial products offered by a bank specified by BANK_ID and PRODUCT_CODE including: + | + |* Name + |* Code + |* Category + |* Family + |* Super Family + |* More info URL + |* Description + |* Terms and Conditions + |* License the data under this endpoint is released under + |${authenticationRequiredMessage(!getProductsIsPublic)}""", + emptyObjectJson, + childProductBucketJsonV310, + List( + UserNotLoggedIn, + ProductNotFoundByProductCode, + UnknownError + ), + Catalogs(notCore, notPSD2, OBWG), + List(apiTagProduct) + ) + + lazy val getProductBucket: OBPEndpoint = { + case "banks" :: BankId(bankId) :: "product-bucket" :: ProductCode(productCode) :: Nil JsonGet _ => { + def getProductBucket(bankId : BankId, productCode : ProductCode): List[Product] = { + Connector.connector.vend.getProduct(bankId, productCode) match { + case Full(p) if p.parentProductCode.value.nonEmpty => p :: getProductBucket(p.bankId, p.parentProductCode) + case Full(p) => List(p) + case _ => List() + } + } + cc => { + for { + (_, callContext) <- + getProductsIsPublic match { + case true => authorizeEndpoint(UserNotLoggedIn, cc) + case false => Future((None, Some(cc))) + } + (_, callContext) <- NewStyle.function.getBank(bankId, callContext) + _ <- Future(Connector.connector.vend.getProduct(bankId, productCode)) map { + unboxFullOrFail(_, callContext, ProductNotFoundByProductCode, 400) + } + product <- Future(getProductBucket(bankId, productCode)) + } yield { + (JSONFactory310.createProductBucketJson(product, productCode.value), HttpCode.`200`(callContext)) + } + } + } + } + resourceDocs += ResourceDoc( getProducts, implementedInApiVersion, diff --git a/src/main/scala/code/api/v3_1_0/JSONFactory3.1.0.scala b/src/main/scala/code/api/v3_1_0/JSONFactory3.1.0.scala index 4a8c5e42c..5bf30f4f4 100644 --- a/src/main/scala/code/api/v3_1_0/JSONFactory3.1.0.scala +++ b/src/main/scala/code/api/v3_1_0/JSONFactory3.1.0.scala @@ -380,6 +380,18 @@ case class ProductJsonV310(bank_id: String, description: String, meta : MetaJsonV140) case class ProductsJsonV310 (products : List[ProductJsonV310]) +case class ProductBucketJsonV310 (bank_id: String, + code : String, + name : String, + category: String, + family : String, + super_family : String, + more_info_url: String, + details: String, + description: String, + meta : MetaJsonV140, + parent_product: Option[ProductBucketJsonV310], + ) object JSONFactory310{ def createCheckbookOrdersJson(checkbookOrders: CheckbookOrdersJson): CheckbookOrdersJson = @@ -675,5 +687,63 @@ object JSONFactory310{ def createProductsJson(productsList: List[Product]) : ProductsJsonV310 = { ProductsJsonV310(productsList.map(createProductJson)) } + + def createProductBucketJson(product: Product, productBucket: Option[ProductBucketJsonV310]): ProductBucketJsonV310 = { + ProductBucketJsonV310( + bank_id = product.bankId.toString, + code = product.code.value, + parent_product = productBucket, + name = product.name, + category = product.category, + family = product.family, + super_family = product.superFamily, + more_info_url = product.moreInfoUrl, + details = product.details, + description = product.description, + meta = createMetaJson(product.meta) + ) + } + + def createProductBucketJson(productsList: List[Product], rootProductCode: String): ProductBucketJsonV310 = { + def getProductBucket(list: List[Product], code: String): Option[ProductBucketJsonV310] = { + productsList.filter(_.code.value == code) match { + case x :: Nil => + Some( + ProductBucketJsonV310( + bank_id = x.bankId.toString, + code = x.code.value, + parent_product = getProductBucket(productsList, x.parentProductCode.value), + name = x.name, + category = x.category, + family = x.family, + super_family = x.superFamily, + more_info_url = x.moreInfoUrl, + details = x.details, + description = x.description, + meta = createMetaJson(x.meta) + ) + ) + case Nil => + None + } + } + + val rootElement = productsList.filter(_.code.value == rootProductCode).head + ProductBucketJsonV310( + bank_id = rootElement.bankId.toString, + code = rootElement.code.value, + parent_product = getProductBucket(productsList, rootElement.parentProductCode.value), + name = rootElement.name, + category = rootElement.category, + family = rootElement.family, + super_family = rootElement.superFamily, + more_info_url = rootElement.moreInfoUrl, + details = rootElement.details, + description = rootElement.description, + meta = createMetaJson(rootElement.meta) + ) + } + + } \ No newline at end of file diff --git a/src/main/scala/code/api/v3_1_0/OBPAPI3_1_0.scala b/src/main/scala/code/api/v3_1_0/OBPAPI3_1_0.scala index 9d733d905..11e065da2 100644 --- a/src/main/scala/code/api/v3_1_0/OBPAPI3_1_0.scala +++ b/src/main/scala/code/api/v3_1_0/OBPAPI3_1_0.scala @@ -327,6 +327,7 @@ object OBPAPI3_1_0 extends OBPRestHelper with APIMethods130 with APIMethods140 w Implementations3_1_0.updateCustomerAddress :: Implementations3_1_0.getProduct :: Implementations3_1_0.getProducts :: + Implementations3_1_0.getProductBucket :: Nil val allResourceDocs = Implementations3_1_0.resourceDocs ++ diff --git a/src/test/scala/code/api/v3_1_0/ProductTest.scala b/src/test/scala/code/api/v3_1_0/ProductTest.scala index 9002a1209..3ef52a08e 100644 --- a/src/test/scala/code/api/v3_1_0/ProductTest.scala +++ b/src/test/scala/code/api/v3_1_0/ProductTest.scala @@ -34,6 +34,7 @@ import code.api.util.ErrorMessages._ import code.api.v3_1_0.OBPAPI3_1_0.Implementations3_1_0 import code.entitlement.Entitlement import com.github.dwickern.macros.NameOf.nameOf +import net.liftweb.json.{Extraction, prettyRender} import net.liftweb.json.Serialization.write import org.scalatest.Tag @@ -60,9 +61,10 @@ class ProductTest extends V310ServerSetup { object ApiEndpoint1 extends Tag(nameOf(Implementations3_1_0.createProduct)) object ApiEndpoint2 extends Tag(nameOf(Implementations3_1_0.getProduct)) object ApiEndpoint3 extends Tag(nameOf(Implementations3_1_0.getProducts)) + object ApiEndpoint4 extends Tag(nameOf(Implementations3_1_0.getProductBucket)) lazy val testBankId = randomBankId - lazy val parentPostPutProductJsonV310 = SwaggerDefinitionsJSON.postPutProductJsonV310.copy(bank_id = testBankId, parent_product_code ="") + lazy val parentPostPutProductJsonV310: PostPutProductJsonV310 = SwaggerDefinitionsJSON.postPutProductJsonV310.copy(bank_id = testBankId, parent_product_code ="") feature("Create Product v3.1.0") { scenario("We will call the Add endpoint without a user credentials", ApiEndpoint1, VersionOfApi) { @@ -86,25 +88,35 @@ class ProductTest extends V310ServerSetup { response310.body.extract[ErrorMessage].message should equal (createProductEntitlementsRequiredText) } - scenario("We will call the Add endpoint with user credentials and role", ApiEndpoint1, ApiEndpoint2, ApiEndpoint3, VersionOfApi) { - // Create - Entitlement.entitlement.vend.addEntitlement(testBankId, resourceUser1.userId, CanCreateProduct.toString) + def createProduct(code: String, json: PostPutProductJsonV310) = { When("We try to create a product v3.1.0") - val request310 = (v3_1_0_Request / "banks" / testBankId / "products" / "PARENT_CODE").PUT <@(user1) - val response310 = makePutRequest(request310, write(parentPostPutProductJsonV310)) + val request310 = (v3_1_0_Request / "banks" / testBankId / "products" / code).PUT <@ (user1) + val response310 = makePutRequest(request310, write(json)) Then("We should get a 201") response310.code should equal(201) val product = response310.body.extract[ProductJsonV310] - product.code shouldBe "PARENT_CODE" - product.parent_product_code shouldBe "" - product.bank_id shouldBe parentPostPutProductJsonV310.bank_id - product.name shouldBe parentPostPutProductJsonV310.name - product.category shouldBe parentPostPutProductJsonV310.category - product.super_family shouldBe parentPostPutProductJsonV310.super_family - product.family shouldBe parentPostPutProductJsonV310.family - product.more_info_url shouldBe parentPostPutProductJsonV310.more_info_url - product.details shouldBe parentPostPutProductJsonV310.details - product.description shouldBe parentPostPutProductJsonV310.description + product.code shouldBe code + product.parent_product_code shouldBe json.parent_product_code + product.bank_id shouldBe json.bank_id + product.name shouldBe json.name + product.category shouldBe json.category + product.super_family shouldBe json.super_family + product.family shouldBe json.family + product.more_info_url shouldBe json.more_info_url + product.details shouldBe json.details + product.description shouldBe json.description + product + } + + scenario("We will call the Add endpoint with user credentials and role", ApiEndpoint1, ApiEndpoint2, ApiEndpoint3, ApiEndpoint4, VersionOfApi) { + + Entitlement.entitlement.vend.addEntitlement(testBankId, resourceUser1.userId, CanCreateProduct.toString) + + // Create an grandparent + val grandparent: ProductJsonV310 = createProduct(code = "GRANDPARENT_CODE", json = parentPostPutProductJsonV310) + + // Create an parent + val product: ProductJsonV310 = createProduct(code = "PARENT_CODE", json = parentPostPutProductJsonV310.copy(parent_product_code = grandparent.code)) // Get val requestGet310 = (v3_1_0_Request / "banks" / product.bank_id / "products" / product.code ).GET <@(user1) @@ -113,25 +125,9 @@ class ProductTest extends V310ServerSetup { responseGet310.code should equal(200) responseGet310.body.extract[ProductJsonV310] - // Create + // Create an child val childPostPutProductJsonV310 = parentPostPutProductJsonV310.copy(parent_product_code = product.code) - When("We try to create a product v3.1.0") - val createChildRequest310 = (v3_1_0_Request / "banks" / testBankId / "products" / "CHILD_CODE").PUT <@(user1) - val createChildResponse310 = makePutRequest(createChildRequest310, write(childPostPutProductJsonV310)) - Then("We should get a 201") - createChildResponse310.code should equal(201) - val childProduct = createChildResponse310.body.extract[ProductJsonV310] - childProduct.code shouldBe "CHILD_CODE" - childProduct.parent_product_code shouldBe "PARENT_CODE" - childProduct.bank_id shouldBe childPostPutProductJsonV310.bank_id - childProduct.name shouldBe childPostPutProductJsonV310.name - childProduct.category shouldBe childPostPutProductJsonV310.category - childProduct.super_family shouldBe childPostPutProductJsonV310.super_family - childProduct.family shouldBe childPostPutProductJsonV310.family - childProduct.more_info_url shouldBe childPostPutProductJsonV310.more_info_url - childProduct.details shouldBe childPostPutProductJsonV310.details - childProduct.description shouldBe childPostPutProductJsonV310.description - + createProduct(code = "CHILD_CODE", json = childPostPutProductJsonV310) // Get val requestGetAll310 = (v3_1_0_Request / "banks" / product.bank_id / "products").GET <@(user1) @@ -139,7 +135,16 @@ class ProductTest extends V310ServerSetup { Then("We should get a 200") responseGetAll310.code should equal(200) val products: ProductsJsonV310 = responseGetAll310.body.extract[ProductsJsonV310] - products.products.size shouldBe 2 + products.products.size shouldBe 3 + + // Get bucket + val requestGetBucket310 = (v3_1_0_Request / "banks" / product.bank_id / "product-bucket" / "CHILD_CODE").GET <@(user1) + val responseGetBucket310 = makeGetRequest(requestGetBucket310) + Then("We should get a 200") + org.scalameta.logger.elem(responseGetBucket310) + responseGetBucket310.code should equal(200) + val productBucket: ProductBucketJsonV310 = responseGetBucket310.body.extract[ProductBucketJsonV310] + org.scalameta.logger.elem(prettyRender(Extraction.decompose(productBucket))) } }