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 2c9081f74..9362a200c 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 @@ -5322,6 +5322,8 @@ object SwaggerDefinitionsJSON { balance_inquiry_fee = balanceInquiryFeeExample.value, atm_type = atmTypeExample.value, phone = phoneExample.value, + + attributes = Some(List(atmAttributeResponseJsonV510)) ) val atmsJsonV510 = AtmsJsonV510( 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 558a62168..bb4a7f996 100644 --- a/obp-api/src/main/scala/code/api/util/NewStyle.scala +++ b/obp-api/src/main/scala/code/api/util/NewStyle.scala @@ -3917,9 +3917,9 @@ object NewStyle extends MdcLoggable{ } map { unboxFull(_) } map { - branch => + atm => // Before we slice we need to sort in order to keep consistent results - (branch.sortWith(_.atmId.value < _.atmId.value) + (atm.sortWith(_.atmId.value < _.atmId.value) // Slice the result in next way: from=offset and until=offset + limit .slice(offset.getOrElse("0").toInt, offset.getOrElse("0").toInt + limit.getOrElse("100").toInt) , callContext) diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index c547370bd..3c1573bd0 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -14,6 +14,7 @@ import code.api.v3_0_0.JSONFactory300.createAggregateMetricJson import code.api.v3_1_0.ConsentJsonV310 import code.api.v3_1_0.JSONFactory310.createBadLoginStatusJson import code.api.v4_0_0.{JSONFactory400, PostApiCollectionJson400} +import code.atmattribute.AtmAttribute import code.consent.Consents import code.loginattempts.LoginAttempt import code.metrics.APIMetrics @@ -25,7 +26,7 @@ import code.util.Helper import code.views.system.{AccountAccess, ViewDefinition} import com.github.dwickern.macros.NameOf.nameOf import com.openbankproject.commons.ExecutionContext.Implicits.global -import com.openbankproject.commons.model.{AtmId, BankId} +import com.openbankproject.commons.model.{AtmId, AtmT, BankId} import com.openbankproject.commons.model.enums.AtmAttributeType import com.openbankproject.commons.util.{ApiVersion, ScannedApiVersion} import net.liftweb.common.Full @@ -984,8 +985,9 @@ trait APIMethods510 { JSONFactory510.transformToAtmFromV510(atmJsonV510) } (atm, callContext) <- NewStyle.function.createOrUpdateAtm(atm, cc.callContext) + (atmAttributes, callContext) <- NewStyle.function.getAtmAttributesByAtm(bankId, atm.atmId, callContext) } yield { - (JSONFactory510.createAtmJsonV510(atm), HttpCode.`201`(callContext)) + (JSONFactory510.createAtmJsonV510(atm, atmAttributes), HttpCode.`201`(callContext)) } } } @@ -998,7 +1000,7 @@ trait APIMethods510 { "/banks/BANK_ID/atms/ATM_ID", "UPDATE ATM", s"""Update ATM.""", - atmJsonV510.copy(id = None), + atmJsonV510.copy(id = None, attributes = None), atmJsonV510, List( $UserNotLoggedIn, @@ -1023,8 +1025,9 @@ trait APIMethods510 { JSONFactory510.transformToAtmFromV510(atmJsonV510.copy(id = Some(atmId.value))) } (atm, callContext) <- NewStyle.function.createOrUpdateAtm(atm, callContext) + (atmAttributes, callContext) <- NewStyle.function.getAtmAttributesByAtm(bankId, atm.atmId, callContext) } yield { - (JSONFactory510.createAtmJsonV510(atm), HttpCode.`201`(callContext)) + (JSONFactory510.createAtmJsonV510(atm, atmAttributes), HttpCode.`201`(callContext)) } } } @@ -1068,12 +1071,61 @@ trait APIMethods510 { } } (atms, callContext) <- NewStyle.function.getAtmsByBankId(bankId, offset, limit, callContext) + + atmAndAttributesTupleList: List[(AtmT, List[AtmAttribute])] <- Future.sequence(atms.map( + atm => NewStyle.function.getAtmAttributesByAtm(bankId, atm.atmId, callContext).map(_._1).map( + attributes =>{ + (atm-> attributes) + } + ))) + } yield { - (JSONFactory510.createAtmsJsonV510(atms), HttpCode.`200`(callContext)) + (JSONFactory510.createAtmsJsonV510(atmAndAttributesTupleList), HttpCode.`200`(callContext)) } } } + + resourceDocs += ResourceDoc( + getAtm, + implementedInApiVersion, + nameOf(getAtm), + "GET", + "/banks/BANK_ID/atms/ATM_ID", + "Get Bank ATM", + s"""Returns information about ATM for a single bank specified by BANK_ID and ATM_ID including: + | + |* Address + |* Geo Location + |* License the data under this endpoint is released under + |* ATM Attributes + | + | + | + |${authenticationRequiredMessage(!getAtmsIsPublic)}""".stripMargin, + EmptyBody, + atmJsonV510, + List(UserNotLoggedIn, BankNotFound, AtmNotFoundByAtmId, UnknownError), + List(apiTagATM, apiTagNewStyle) + ) + lazy val getAtm: OBPEndpoint = { + case "banks" :: BankId(bankId) :: "atms" :: AtmId(atmId) :: Nil JsonGet req => { + cc => + for { + (_, callContext) <- getAtmsIsPublic match { + case false => authenticatedAccess(cc) + case true => anonymousAccess(cc) + } + (_, callContext) <- NewStyle.function.getBank(bankId, callContext) + (atm, callContext) <- NewStyle.function.getAtm(bankId, atmId, callContext) + (atmAttributes, callContext) <- NewStyle.function.getAtmAttributesByAtm(bankId, atmId, callContext) + } yield { + (JSONFactory510.createAtmJsonV510(atm, atmAttributes), HttpCode.`200`(callContext)) + } + } + } + + } } diff --git a/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala b/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala index c63339605..79e7c6857 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala @@ -108,7 +108,8 @@ case class AtmJsonV510 ( cash_withdrawal_international_fee: String, balance_inquiry_fee: String, atm_type: String, - phone: String + phone: String, + attributes: Option[List[AtmAttributeResponseJsonV510]] ) case class AtmsJsonV510(atms : List[AtmJsonV510]) @@ -153,19 +154,18 @@ case class AtmAttributeResponseJsonV510( is_active: Option[Boolean] ) case class AtmAttributesResponseJsonV510(atm_attributes: List[AtmAttributeResponseJsonV510]) -case class AtmAttributeBankResponseJsonV510(name: String, - value: String) -case class AtmAttributesResponseJson(list: List[AtmAttributeBankResponseJsonV510]) - object JSONFactory510 { - def createAtmsJsonV510(atmList: List[AtmT]): AtmsJsonV510 = { - AtmsJsonV510(atmList.map(createAtmJsonV510)) + def createAtmsJsonV510(atmAndAttributesTupleList: List[(AtmT, List[AtmAttribute])] ): AtmsJsonV510 = { + AtmsJsonV510(atmAndAttributesTupleList.map( + atmAndAttributesTuple => + createAtmJsonV510(atmAndAttributesTuple._1,atmAndAttributesTuple._2) + )) } - def createAtmJsonV510(atm: AtmT): AtmJsonV510 = { + def createAtmJsonV510(atm: AtmT, atmAttributes:List[AtmAttribute]): AtmJsonV510 = { AtmJsonV510( id = Some(atm.atmId.value), bank_id = atm.bankId.value, @@ -220,6 +220,7 @@ object JSONFactory510 { balance_inquiry_fee = atm.balanceInquiryFee.getOrElse(""), atm_type = atm.atmType.getOrElse(""), phone = atm.phone.getOrElse(""), + attributes = Some(atmAttributes.map(createAtmAttributeJson)) ) } diff --git a/obp-api/src/test/scala/code/api/v5_1_0/AtmTest.scala b/obp-api/src/test/scala/code/api/v5_1_0/AtmTest.scala index 19d10d10f..52e815a8b 100644 --- a/obp-api/src/test/scala/code/api/v5_1_0/AtmTest.scala +++ b/obp-api/src/test/scala/code/api/v5_1_0/AtmTest.scala @@ -36,80 +36,81 @@ class AtmTest extends V510ServerSetup with DefaultUsers { object ApiEndpoint1 extends Tag(nameOf(Implementations5_1_0.createAtm)) object ApiEndpoint2 extends Tag(nameOf(Implementations5_1_0.updateAtm)) object ApiEndpoint3 extends Tag(nameOf(Implementations5_1_0.getAtms)) + object ApiEndpoint4 extends Tag(nameOf(Implementations5_1_0.getAtm)) lazy val bankId = randomBankId -// feature(s"Test$ApiEndpoint1 test the error cases - $VersionOfApi") { -// scenario(s"We try to consume endpoint $ApiEndpoint1 - Anonymous access", ApiEndpoint1, VersionOfApi) { -// When("We make the request") -// val requestGet = (v5_1_0_Request / "banks" / bankId / "atms").POST -// val responseGet = makePostRequest(requestGet, write(atmJsonV510)) -// Then("We should get a 401") -// And("We should get a message: " + ErrorMessages.UserNotLoggedIn) -// responseGet.code should equal(401) -// responseGet.body.extract[ErrorMessage].message should equal(ErrorMessages.UserNotLoggedIn) -// } -// -// scenario(s"We try to consume endpoint $ApiEndpoint1 without proper role - Authorized access", ApiEndpoint1, VersionOfApi) { -// When("We make the request") -// val requestGet = (v5_1_0_Request / "banks" / bankId / "atms").POST <@ (user1) -// val responseGet = makePostRequest(requestGet, write(atmJsonV510)) -// Then("We should get a 403") -// And("We should get a message: " + s"$canCreateAtmAtAnyBank or $canCreateAtm entitlement required") -// responseGet.code should equal(403) -// responseGet.body.extract[ErrorMessage].message should startWith(UserHasMissingRoles) -// responseGet.body.extract[ErrorMessage].message contains (canCreateAtmAtAnyBank.toString()) shouldBe (true) -// responseGet.body.extract[ErrorMessage].message contains (canCreateAtm.toString()) shouldBe (true) -// } -// } -// -// -// feature(s"Test$ApiEndpoint2 test the error cases - $VersionOfApi") { -// scenario(s"We try to consume endpoint $ApiEndpoint2 - Anonymous access", ApiEndpoint2, VersionOfApi) { -// When("We make the request") -// val requestGet = (v5_1_0_Request / "banks" / bankId / "atms" / "atmId" ).PUT -// val responseGet = makePutRequest(requestGet, write(atmJsonV510)) -// Then("We should get a 401") -// And("We should get a message: " + ErrorMessages.UserNotLoggedIn) -// responseGet.code should equal(401) -// responseGet.body.extract[ErrorMessage].message should equal(ErrorMessages.UserNotLoggedIn) -// } -// scenario(s"We try to consume endpoint $ApiEndpoint2 without proper role - Authorized access", ApiEndpoint2, VersionOfApi) { -// When("We make the request") -// val requestGet = (v5_1_0_Request / "banks" / bankId / "atms" / "atmId" ).PUT <@ (user1) -// val responseGet = makePutRequest(requestGet, write(atmJsonV510)) -// Then("We should get a 403") -// And("We should get a message: " + s"$canCreateAtmAtAnyBank or $canCreateAtm entitlement required") -// responseGet.code should equal(403) -// responseGet.body.extract[ErrorMessage].message should startWith(UserHasMissingRoles) -// responseGet.body.extract[ErrorMessage].message contains (canUpdateAtmAtAnyBank.toString()) shouldBe (true) -// responseGet.body.extract[ErrorMessage].message contains (canUpdateAtm.toString()) shouldBe (true) -// } -// scenario(s"We try to consume endpoint $ApiEndpoint2 with proper role but invalid ATM - Authorized access", ApiEndpoint2, VersionOfApi) { -// When("We make the request") -// val entitlement = Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanUpdateAtmAtAnyBank.toString) -// val requestGet = (v5_1_0_Request / "banks" / bankId / "atms" / "atmId-invalid" ).PUT <@ (user1) -// val responseGet = makePutRequest(requestGet, write(atmJsonV510)) -// Then("We should get a 404") -// And("We should get a message: " + s"$AtmNotFoundByAtmId") -// responseGet.code should equal(404) -// responseGet.body.extract[ErrorMessage].message should startWith(AtmNotFoundByAtmId) -// responseGet.body.extract[ErrorMessage].message contains (AtmNotFoundByAtmId) shouldBe (true) -// Entitlement.entitlement.vend.deleteEntitlement(entitlement) -// } -// } -// -// feature(s"Test$ApiEndpoint3 test the error cases - $VersionOfApi") { -// scenario(s"We try to consume endpoint $ApiEndpoint3 - Anonymous access", ApiEndpoint3, VersionOfApi) { -// When("We make the request") -// val request = (v5_1_0_Request / "banks" / bankId / "atms").GET -// val response = makeGetRequest(request) -// Then("We should get a 200") -// response.code should equal(200) -// } -// } + feature(s"Test$ApiEndpoint1 test the error cases - $VersionOfApi") { + scenario(s"We try to consume endpoint $ApiEndpoint1 - Anonymous access", ApiEndpoint1, VersionOfApi) { + When("We make the request") + val requestGet = (v5_1_0_Request / "banks" / bankId / "atms").POST + val responseGet = makePostRequest(requestGet, write(atmJsonV510)) + Then("We should get a 401") + And("We should get a message: " + ErrorMessages.UserNotLoggedIn) + responseGet.code should equal(401) + responseGet.body.extract[ErrorMessage].message should equal(ErrorMessages.UserNotLoggedIn) + } + + scenario(s"We try to consume endpoint $ApiEndpoint1 without proper role - Authorized access", ApiEndpoint1, VersionOfApi) { + When("We make the request") + val requestGet = (v5_1_0_Request / "banks" / bankId / "atms").POST <@ (user1) + val responseGet = makePostRequest(requestGet, write(atmJsonV510)) + Then("We should get a 403") + And("We should get a message: " + s"$canCreateAtmAtAnyBank or $canCreateAtm entitlement required") + responseGet.code should equal(403) + responseGet.body.extract[ErrorMessage].message should startWith(UserHasMissingRoles) + responseGet.body.extract[ErrorMessage].message contains (canCreateAtmAtAnyBank.toString()) shouldBe (true) + responseGet.body.extract[ErrorMessage].message contains (canCreateAtm.toString()) shouldBe (true) + } + } + + + feature(s"Test$ApiEndpoint2 test the error cases - $VersionOfApi") { + scenario(s"We try to consume endpoint $ApiEndpoint2 - Anonymous access", ApiEndpoint2, VersionOfApi) { + When("We make the request") + val requestGet = (v5_1_0_Request / "banks" / bankId / "atms" / "atmId" ).PUT + val responseGet = makePutRequest(requestGet, write(atmJsonV510)) + Then("We should get a 401") + And("We should get a message: " + ErrorMessages.UserNotLoggedIn) + responseGet.code should equal(401) + responseGet.body.extract[ErrorMessage].message should equal(ErrorMessages.UserNotLoggedIn) + } + scenario(s"We try to consume endpoint $ApiEndpoint2 without proper role - Authorized access", ApiEndpoint2, VersionOfApi) { + When("We make the request") + val requestGet = (v5_1_0_Request / "banks" / bankId / "atms" / "atmId" ).PUT <@ (user1) + val responseGet = makePutRequest(requestGet, write(atmJsonV510)) + Then("We should get a 403") + And("We should get a message: " + s"$canCreateAtmAtAnyBank or $canCreateAtm entitlement required") + responseGet.code should equal(403) + responseGet.body.extract[ErrorMessage].message should startWith(UserHasMissingRoles) + responseGet.body.extract[ErrorMessage].message contains (canUpdateAtmAtAnyBank.toString()) shouldBe (true) + responseGet.body.extract[ErrorMessage].message contains (canUpdateAtm.toString()) shouldBe (true) + } + scenario(s"We try to consume endpoint $ApiEndpoint2 with proper role but invalid ATM - Authorized access", ApiEndpoint2, VersionOfApi) { + When("We make the request") + val entitlement = Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanUpdateAtmAtAnyBank.toString) + val requestGet = (v5_1_0_Request / "banks" / bankId / "atms" / "atmId-invalid" ).PUT <@ (user1) + val responseGet = makePutRequest(requestGet, write(atmJsonV510)) + Then("We should get a 404") + And("We should get a message: " + s"$AtmNotFoundByAtmId") + responseGet.code should equal(404) + responseGet.body.extract[ErrorMessage].message should startWith(AtmNotFoundByAtmId) + responseGet.body.extract[ErrorMessage].message contains (AtmNotFoundByAtmId) shouldBe (true) + Entitlement.entitlement.vend.deleteEntitlement(entitlement) + } + } + + feature(s"Test$ApiEndpoint3 test the error cases - $VersionOfApi") { + scenario(s"We try to consume endpoint $ApiEndpoint3 - Anonymous access", ApiEndpoint3, VersionOfApi) { + When("We make the request") + val request = (v5_1_0_Request / "banks" / bankId / "atms").GET + val response = makeGetRequest(request) + Then("We should get a 200") + response.code should equal(200) + } + } - feature(s"Test$ApiEndpoint1 $ApiEndpoint2 $ApiEndpoint3 - $VersionOfApi") { + feature(s"Test$ApiEndpoint1 $ApiEndpoint2 $ApiEndpoint3 $ApiEndpoint4 - $VersionOfApi") { scenario(s"Test the CUR methods", ApiEndpoint1, VersionOfApi) { When("We make the CREATE ATMs") Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanCreateAtmAtAnyBank.toString) @@ -123,6 +124,13 @@ class AtmTest extends V510ServerSetup with DefaultUsers { responseCreate.body.extract[AtmJsonV510].atm_type shouldBe("atm_type1") responseCreate.body.extract[AtmJsonV510].phone shouldBe("12345") val atmId = responseCreate.body.extract[AtmJsonV510].id.getOrElse("") + + Then("We create three ATM attributes") + Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, ApiRole.CanCreateAtmAttribute.toString) + val requestGet = (v5_1_0_Request / "banks" / bankId / "atms" / atmId / "attributes").POST <@ (user1) + makePostRequest(requestGet, write(atmAttributeJsonV510.copy(name = "1"))) + makePostRequest(requestGet, write(atmAttributeJsonV510.copy(name = "2"))) + makePostRequest(requestGet, write(atmAttributeJsonV510.copy(name = "3"))) Then("We Update the ATMs") @@ -152,12 +160,34 @@ class AtmTest extends V510ServerSetup with DefaultUsers { Then("We Get the ATMs") val request = (v5_1_0_Request / "banks" / bankId / "atms").GET Then("We should get a 200") - makeGetRequest(request).code should equal(200) - val atms = makeGetRequest(request).body.extract[AtmsJsonV510].atms + val responseGet = makeGetRequest(request) + responseGet.code should equal(200) + val atms = responseGet.body.extract[AtmsJsonV510].atms atms.length should be (3) atms(0).atm_type equals ("atm_type_111") atms(1).atm_type equals ("atm_type2") atms(2).atm_type equals ("atm_type3") + val attibutes = atms.find(_.id == Some(atmId)).get.attributes.get + attibutes.length shouldBe(3) + attibutes(0).name equals ("1") + attibutes(1).name equals ("2") + attibutes(2).name equals ("3") + + + Then("We Get the ATM") + val requestOne = (v5_1_0_Request / "banks" / bankId / "atms" /atmId ).GET + Then("We should get a 200") + val responseOne = makeGetRequest(requestOne) + + responseOne.code should equal(200) + val atm = responseOne.body.extract[AtmJsonV510] + atm.atm_type equals ("atm_type_111") + val atmAttributes = atm.attributes.get + atmAttributes.length shouldBe(3) + atmAttributes(0).name equals ("1") + atmAttributes(1).name equals ("2") + atmAttributes(2).name equals ("3") + } } } \ No newline at end of file