From f480802b5d23c812c4aba58930eeeceeb077158c Mon Sep 17 00:00:00 2001 From: hongwei Date: Fri, 31 Mar 2023 11:24:11 +0200 Subject: [PATCH 1/8] refactor/added two new fields for Atm Model --- .../main/scala/code/atms/MappedAtmsProvider.scala | 12 ++++++++++++ .../openbankproject/commons/model/CommonModel.scala | 2 ++ .../commons/model/CommonModelTrait.scala | 2 ++ 3 files changed, 16 insertions(+) diff --git a/obp-api/src/main/scala/code/atms/MappedAtmsProvider.scala b/obp-api/src/main/scala/code/atms/MappedAtmsProvider.scala index dae3b2b88..03dd717ee 100644 --- a/obp-api/src/main/scala/code/atms/MappedAtmsProvider.scala +++ b/obp-api/src/main/scala/code/atms/MappedAtmsProvider.scala @@ -231,6 +231,8 @@ class MappedAtm extends AtmT with LongKeyedMapper[MappedAtm] with IdPK { object mCashWithdrawalNationalFee extends MappedString(this, 255) object mCashWithdrawalInternationalFee extends MappedString(this, 255) object mBalanceInquiryFee extends MappedString(this, 255) + object mAtmType extends MappedString(this, 255) + object mPhone extends MappedString(this, 255) override def atmId: AtmId = AtmId(mAtmId.get) @@ -360,6 +362,16 @@ class MappedAtm extends AtmT with LongKeyedMapper[MappedAtm] with IdPK { case _ => None } + override def atmType: Option[String] = mAtmType.get match { + case value: String => Some(value) + case _ => None + } + + override def phone: Option[String] = mPhone.get match { + case value: String => Some(value) + case _ => None + } + } // diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModel.scala b/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModel.scala index 7e540954d..31844b7af 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModel.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModel.scala @@ -269,6 +269,8 @@ case class AtmTCommons( cashWithdrawalNationalFee: Option[String] = None, cashWithdrawalInternationalFee: Option[String] = None, balanceInquiryFee: Option[String] = None, + atmType: Option[String] = None, + phone: Option[String] = None, ) extends AtmT object AtmTCommons extends Converter[AtmT, AtmTCommons] diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModelTrait.scala b/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModelTrait.scala index 1425fe19b..62487fa52 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModelTrait.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModelTrait.scala @@ -182,6 +182,8 @@ trait AtmT { def cashWithdrawalNationalFee: Option[String] def cashWithdrawalInternationalFee: Option[String] def balanceInquiryFee: Option[String] + def atmType: Option[String] + def phone: Option[String] } // MappedBranch will implement this. From 224224c8cc313d1c217493a22b738e095d8784c0 Mon Sep 17 00:00:00 2001 From: hongwei Date: Tue, 4 Apr 2023 10:29:49 +0200 Subject: [PATCH 2/8] refactor/typo --- obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala index f9962d81b..18c4e517f 100644 --- a/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala +++ b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala @@ -11695,9 +11695,9 @@ trait APIMethods400 { case Failure(msg, _, _) => fullBoxOrException(Empty ?~! msg) case ParamFailure(msg,_,_,_) => fullBoxOrException(Empty ?~! msg) } 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) From eeca451f1e4701587707ccaa433e9daf34502724 Mon Sep 17 00:00:00 2001 From: hongwei Date: Tue, 4 Apr 2023 11:11:39 +0200 Subject: [PATCH 3/8] feature/OBPV510 added new create/update/get Atms --- .../SwaggerDefinitionsJSON.scala | 41 ++++- .../scala/code/api/util/ExampleValue.scala | 3 + .../main/scala/code/api/util/NewStyle.scala | 20 +++ .../scala/code/api/v4_0_0/APIMethods400.scala | 17 +- .../scala/code/api/v5_1_0/APIMethods510.scala | 126 +++++++++++++ .../code/api/v5_1_0/JSONFactory5.1.0.scala | 165 ++++++++++++++++++ obp-api/src/main/scala/code/atms/Atms.scala | 4 +- .../scala/code/atms/MappedAtmsProvider.scala | 5 + .../KafkaJsonFactory_vSept2018.scala | 2 + 9 files changed, 365 insertions(+), 18 deletions(-) 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 1e7a319b4..60cba3593 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 @@ -36,7 +36,7 @@ import com.openbankproject.commons.util.{ApiVersion, FieldNameApiVersions, Refle import net.liftweb.json import java.net.URLEncoder -import code.api.v5_1_0.{AtmAttributeJsonV510, AtmAttributeResponseJsonV510, CertificateInfoJsonV510, CurrenciesJsonV510, CurrencyJsonV510} +import code.api.v5_1_0.{AtmsJsonV510, _} import code.endpointMapping.EndpointMappingCommons import scala.collection.immutable.List @@ -5284,7 +5284,46 @@ object SwaggerDefinitionsJSON { total_duration = BigDecimal(durationExample.value), backend_messages= List(inboundStatusMessage), ) + + + val atmJsonV510 = AtmJsonV510( + id = Some(atmIdExample.value), + bank_id = bankIdExample.value, + name = atmNameExample.value, + address = addressJsonV300, + location = locationJson, + meta = metaJson, + monday = openingTimesV300, + tuesday = openingTimesV300, + wednesday = openingTimesV300, + thursday = openingTimesV300, + friday = openingTimesV300, + saturday = openingTimesV300, + sunday = openingTimesV300, + is_accessible = isAccessibleExample.value, + located_at = locatedAtExample.value, + more_info = moreInfoExample.value, + has_deposit_capability = hasDepositCapabilityExample.value, + supported_languages = supportedLanguagesJson.supported_languages, + services = atmServicesJson.services, + accessibility_features = accessibilityFeaturesJson.accessibility_features, + supported_currencies = supportedCurrenciesJson.supported_currencies, + notes = atmNotesJson.notes, + location_categories = atmLocationCategoriesJsonV400.location_categories, + minimum_withdrawal = atmMinimumWithdrawalExample.value, + branch_identification = atmBranchIdentificationExample.value, + site_identification = siteIdentification.value, + site_name = atmSiteNameExample.value, + cash_withdrawal_national_fee = cashWithdrawalNationalFeeExample.value, + cash_withdrawal_international_fee = cashWithdrawalInternationalFeeExample.value, + balance_inquiry_fee = balanceInquiryFeeExample.value, + atm_type = atmTypeExample.value, + phone = phoneExample.value, + ) + val atmsJsonV510 = AtmsJsonV510( + atms = List(atmJsonV510) + ) //The common error or success format. //Just some helper format to use in Json case class NotSupportedYet() 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 9195baad5..43d9e1b70 100644 --- a/obp-api/src/main/scala/code/api/util/ExampleValue.scala +++ b/obp-api/src/main/scala/code/api/util/ExampleValue.scala @@ -897,6 +897,9 @@ object ExampleValue { lazy val balanceInquiryFeeExample = ConnectorField(NoExampleProvided, NoDescriptionProvided) glossaryItems += makeGlossaryItem("ATM.balance_inquiry_fee", balanceInquiryFeeExample) + + lazy val atmTypeExample = ConnectorField(NoExampleProvided, NoDescriptionProvided) + glossaryItems += makeGlossaryItem("ATM.atm_type", atmTypeExample) lazy val accessibilityFeaturesExample = ConnectorField("""["ATAC","ATAD"]""", NoDescriptionProvided) glossaryItems += makeGlossaryItem("accessibility_features", accessibilityFeaturesExample) 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 0ea36cbd9..558a62168 100644 --- a/obp-api/src/main/scala/code/api/util/NewStyle.scala +++ b/obp-api/src/main/scala/code/api/util/NewStyle.scala @@ -3904,6 +3904,26 @@ object NewStyle extends MdcLoggable{ Connector.connector.vend.updateCustomerAccountLinkById(customerAccountLinkId: String, relationshipType: String, callContext: Option[CallContext]) map { i => (unboxFullOrFail(i._1, callContext, UpdateCustomerAccountLinkError), i._2) } + + def getAtmsByBankId(bankId: BankId, offset: Box[String], limit: Box[String], callContext: Option[CallContext]): OBPReturnType[List[AtmT]] = + Connector.connector.vend.getAtms(bankId, callContext) map { + case Empty => + fullBoxOrException(Empty ?~! atmsNotFound) + case Full((List(), callContext)) => + Full(List()) + case Full((list, _)) => Full(list) + case Failure(msg, _, _) => fullBoxOrException(Empty ?~! msg) + case ParamFailure(msg, _, _, _) => fullBoxOrException(Empty ?~! msg) + } map { + unboxFull(_) + } map { + branch => + // Before we slice we need to sort in order to keep consistent results + (branch.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/v4_0_0/APIMethods400.scala b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala index 18c4e517f..2976109da 100644 --- a/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala +++ b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala @@ -11686,22 +11686,7 @@ trait APIMethods400 { case _ => true } } - (atms, callContext) <- Connector.connector.vend.getAtms(bankId, callContext) map { - case Empty => - fullBoxOrException(Empty ?~! atmsNotFound) - case Full((List(), callContext)) => - Full(List()) - case Full((list, _)) =>Full(list) - case Failure(msg, _, _) => fullBoxOrException(Empty ?~! msg) - case ParamFailure(msg,_,_,_) => fullBoxOrException(Empty ?~! msg) - } map { unboxFull(_) } map { - atm => - // Before we slice we need to sort in order to keep consistent results - (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) - } + (atms, callContext) <- NewStyle.function.getAtmsByBankId(bankId, offset, limit, cc.callContext) } yield { (JSONFactory400.createAtmsJsonV400(atms), HttpCode.`200`(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 a00a9b0cc..dfb9017d0 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 @@ -28,6 +28,7 @@ import com.openbankproject.commons.model.{AtmId, BankId} import com.openbankproject.commons.model.enums.AtmAttributeType import com.openbankproject.commons.util.{ApiVersion, ScannedApiVersion} import net.liftweb.common.Full +import net.liftweb.http.S import net.liftweb.http.rest.RestHelper import scala.collection.immutable.{List, Nil} @@ -947,6 +948,131 @@ trait APIMethods510 { } } + staticResourceDocs += ResourceDoc( + createAtm, + implementedInApiVersion, + nameOf(createAtm), + "POST", + "/banks/BANK_ID/atms", + "Create ATM", + s"""Create ATM.""", + atmJsonV510, + atmJsonV510, + List( + $UserNotLoggedIn, + InvalidJsonFormat, + UnknownError + ), + List(apiTagATM, apiTagNewStyle), + Some(List(canCreateAtm, canCreateAtmAtAnyBank)) + ) + lazy val createAtm: OBPEndpoint = { + case "banks" :: BankId(bankId) :: "atms" :: Nil JsonPost json -> _ => { + cc => + for { + atmJsonV510 <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the ${classOf[AtmJsonV510]}", 400, cc.callContext) { + val atm = json.extract[AtmJsonV510] + //Make sure the Create contains proper ATM ID + atm.id.get + atm + } + _ <- Helper.booleanToFuture(s"$InvalidJsonValue BANK_ID has to be the same in the URL and Body", 400, cc.callContext) { + atmJsonV510.bank_id == bankId.value + } + atm <- NewStyle.function.tryons(CouldNotTransformJsonToInternalModel + " Atm", 400, cc.callContext) { + JSONFactory510.transformToAtmFromV510(atmJsonV510) + } + (atm, callContext) <- NewStyle.function.createOrUpdateAtm(atm, cc.callContext) + } yield { + (JSONFactory510.createAtmJsonV510(atm), HttpCode.`201`(callContext)) + } + } + } + + staticResourceDocs += ResourceDoc( + updateAtm, + implementedInApiVersion, + nameOf(updateAtm), + "PUT", + "/banks/BANK_ID/atms/ATM_ID", + "UPDATE ATM", + s"""Update ATM.""", + atmJsonV510.copy(id = None), + atmJsonV510, + List( + $UserNotLoggedIn, + InvalidJsonFormat, + UnknownError + ), + List(apiTagATM, apiTagNewStyle), + Some(List(canUpdateAtm, canUpdateAtmAtAnyBank)) + ) + lazy val updateAtm: OBPEndpoint = { + case "banks" :: BankId(bankId) :: "atms" :: AtmId(atmId) :: Nil JsonPut json -> _ => { + cc => + for { + (atm, callContext) <- NewStyle.function.getAtm(bankId, atmId, cc.callContext) + atmJsonV510 <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the ${classOf[AtmJsonV510]}", 400, callContext) { + json.extract[AtmJsonV510] + } + _ <- Helper.booleanToFuture(s"$InvalidJsonValue BANK_ID has to be the same in the URL and Body", 400, callContext) { + atmJsonV510.bank_id == bankId.value + } + atm <- NewStyle.function.tryons(CouldNotTransformJsonToInternalModel + " Atm", 400, callContext) { + JSONFactory510.transformToAtmFromV510(atmJsonV510.copy(id = Some(atmId.value))) + } + (atm, callContext) <- NewStyle.function.createOrUpdateAtm(atm, callContext) + } yield { + (JSONFactory510.createAtmJsonV510(atm), HttpCode.`201`(callContext)) + } + } + } + + staticResourceDocs += ResourceDoc( + getAtms, + implementedInApiVersion, + nameOf(getAtms), + "GET", + "/banks/BANK_ID/atms", + "Get Bank ATMS", + s"""Get Bank ATMS.""", + EmptyBody, + atmsJsonV510, + List( + $BankNotFound, + UnknownError + ), + List(apiTagATM, apiTagNewStyle) + ) + lazy val getAtms: OBPEndpoint = { + case "banks" :: BankId(bankId) :: "atms" :: Nil JsonGet _ => { + cc => + val limit = S.param("limit") + val offset = S.param("offset") + for { + (_, callContext) <- getAtmsIsPublic match { + case false => authenticatedAccess(cc) + case true => anonymousAccess(cc) + } + _ <- Helper.booleanToFuture(failMsg = s"${InvalidNumber} limit:${limit.getOrElse("")}", cc = callContext) { + limit match { + case Full(i) => i.toList.forall(c => Character.isDigit(c) == true) + case _ => true + } + } + _ <- Helper.booleanToFuture(failMsg = maximumLimitExceeded, cc = callContext) { + limit match { + case Full(i) if i.toInt > 10000 => false + case _ => true + } + } + (atms, callContext) <- NewStyle.function.getAtmsByBankId(bankId, offset, limit, callContext) + } yield { + (JSONFactory510.createAtmsJsonV510(atms), 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 ee9919a1a..20732086e 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 @@ -29,12 +29,18 @@ package code.api.v5_1_0 import code.api.Constant import code.api.util.APIUtil import code.api.util.APIUtil.gitCommit +import code.api.v1_4_0.JSONFactory1_4_0.{LocationJsonV140, MetaJsonV140, transformToLocationFromV140, transformToMetaFromV140} +import code.api.v3_0_0.JSONFactory300.{createLocationJson, createMetaJson, transformToAddressFromV300} +import code.api.v3_0_0.{AddressJsonV300, OpeningTimesV300} import code.api.v4_0_0.{EnergySource400, HostedAt400, HostedBy400} import code.atmattribute.AtmAttribute +import code.atms.Atms.Atm import code.views.system.{AccountAccess, ViewDefinition} +import com.openbankproject.commons.model.{Address, AtmId, AtmT, BankId, Location, Meta} import com.openbankproject.commons.util.{ApiVersion, ScannedApiVersion} import scala.collection.immutable.List +import scala.util.Try case class APIInfoJsonV510( @@ -67,7 +73,45 @@ case class CheckSystemIntegrityJsonV510( case class CurrencyJsonV510(alphanumeric_code: String) case class CurrenciesJsonV510(currencies: List[CurrencyJsonV510]) +case class AtmJsonV510 ( + id : Option[String], + bank_id : String, + name : String, + address: AddressJsonV300, + location: LocationJsonV140, + meta: MetaJsonV140, + monday: OpeningTimesV300, + tuesday: OpeningTimesV300, + wednesday: OpeningTimesV300, + thursday: OpeningTimesV300, + friday: OpeningTimesV300, + saturday: OpeningTimesV300, + sunday: OpeningTimesV300, + + is_accessible : String, + located_at : String, + more_info : String, + has_deposit_capability : String, + + supported_languages: List[String], + services: List[String], + accessibility_features: List[String], + supported_currencies: List[String], + notes: List[String], + location_categories: List[String], + minimum_withdrawal: String, + branch_identification: String, + site_identification: String, + site_name: String, + cash_withdrawal_national_fee: String, + cash_withdrawal_international_fee: String, + balance_inquiry_fee: String, + atm_type: String, + phone: String +) + +case class AtmsJsonV510(atms : List[AtmJsonV510]) case class ProductAttributeJsonV510( name: String, @@ -116,6 +160,127 @@ case class AtmAttributesResponseJson(list: List[AtmAttributeBankResponseJsonV510 object JSONFactory510 { + + def createAtmsJsonV510(atmList: List[AtmT]): AtmsJsonV510 = { + AtmsJsonV510(atmList.map(createAtmJsonV510)) + } + + def createAtmJsonV510(atm: AtmT): AtmJsonV510 = { + AtmJsonV510( + id = Some(atm.atmId.value), + bank_id = atm.bankId.value, + name = atm.name, + AddressJsonV300(atm.address.line1, + atm.address.line2, + atm.address.line3, + atm.address.city, + atm.address.county.getOrElse(""), + atm.address.state, + atm.address.postCode, + atm.address.countryCode), + createLocationJson(atm.location), + createMetaJson(atm.meta), + monday = OpeningTimesV300( + opening_time = atm.OpeningTimeOnMonday.getOrElse(""), + closing_time = atm.ClosingTimeOnMonday.getOrElse("")), + tuesday = OpeningTimesV300( + opening_time = atm.OpeningTimeOnTuesday.getOrElse(""), + closing_time = atm.ClosingTimeOnTuesday.getOrElse("")), + wednesday = OpeningTimesV300( + opening_time = atm.OpeningTimeOnWednesday.getOrElse(""), + closing_time = atm.ClosingTimeOnWednesday.getOrElse("")), + thursday = OpeningTimesV300( + opening_time = atm.OpeningTimeOnThursday.getOrElse(""), + closing_time = atm.ClosingTimeOnThursday.getOrElse("")), + friday = OpeningTimesV300( + opening_time = atm.OpeningTimeOnFriday.getOrElse(""), + closing_time = atm.ClosingTimeOnFriday.getOrElse("")), + saturday = OpeningTimesV300( + opening_time = atm.OpeningTimeOnSaturday.getOrElse(""), + closing_time = atm.ClosingTimeOnSaturday.getOrElse("")), + sunday = OpeningTimesV300( + opening_time = atm.OpeningTimeOnSunday.getOrElse(""), + closing_time = atm.ClosingTimeOnSunday.getOrElse("")), + is_accessible = atm.isAccessible.map(_.toString).getOrElse(""), + located_at = atm.locatedAt.getOrElse(""), + more_info = atm.moreInfo.getOrElse(""), + has_deposit_capability = atm.hasDepositCapability.map(_.toString).getOrElse(""), + supported_languages = atm.supportedLanguages.getOrElse(Nil), + services = atm.services.getOrElse(Nil), + accessibility_features = atm.accessibilityFeatures.getOrElse(Nil), + supported_currencies = atm.supportedCurrencies.getOrElse(Nil), + notes = atm.notes.getOrElse(Nil), + location_categories = atm.locationCategories.getOrElse(Nil), + minimum_withdrawal = atm.minimumWithdrawal.getOrElse(""), + branch_identification = atm.branchIdentification.getOrElse(""), + site_identification = atm.siteIdentification.getOrElse(""), + site_name = atm.siteName.getOrElse(""), + cash_withdrawal_national_fee = atm.cashWithdrawalNationalFee.getOrElse(""), + cash_withdrawal_international_fee = atm.cashWithdrawalInternationalFee.getOrElse(""), + balance_inquiry_fee = atm.balanceInquiryFee.getOrElse(""), + atm_type = atm.atmType.getOrElse(""), + phone = atm.phone.getOrElse(""), + ) + } + + + def transformToAtmFromV510(atmJsonV510: AtmJsonV510): Atm = { + val address: Address = transformToAddressFromV300(atmJsonV510.address) // Note the address in V220 is V140 + val location: Location = transformToLocationFromV140(atmJsonV510.location) // Note the location is V140 + val meta: Meta = transformToMetaFromV140(atmJsonV510.meta) // Note the meta is V140 + val isAccessible: Boolean = Try(atmJsonV510.is_accessible.toBoolean).getOrElse(false) + val hdc: Boolean = Try(atmJsonV510.has_deposit_capability.toBoolean).getOrElse(false) + + Atm( + atmId = AtmId(atmJsonV510.id.getOrElse("")), + bankId = BankId(atmJsonV510.bank_id), + name = atmJsonV510.name, + address = address, + location = location, + meta = meta, + OpeningTimeOnMonday = Some(atmJsonV510.monday.opening_time), + ClosingTimeOnMonday = Some(atmJsonV510.monday.closing_time), + + OpeningTimeOnTuesday = Some(atmJsonV510.tuesday.opening_time), + ClosingTimeOnTuesday = Some(atmJsonV510.tuesday.closing_time), + + OpeningTimeOnWednesday = Some(atmJsonV510.wednesday.opening_time), + ClosingTimeOnWednesday = Some(atmJsonV510.wednesday.closing_time), + + OpeningTimeOnThursday = Some(atmJsonV510.thursday.opening_time), + ClosingTimeOnThursday = Some(atmJsonV510.thursday.closing_time), + + OpeningTimeOnFriday = Some(atmJsonV510.friday.opening_time), + ClosingTimeOnFriday = Some(atmJsonV510.friday.closing_time), + + OpeningTimeOnSaturday = Some(atmJsonV510.saturday.opening_time), + ClosingTimeOnSaturday = Some(atmJsonV510.saturday.closing_time), + + OpeningTimeOnSunday = Some(atmJsonV510.sunday.opening_time), + ClosingTimeOnSunday = Some(atmJsonV510.sunday.closing_time), + // Easy access for people who use wheelchairs etc. true or false ""=Unknown + isAccessible = Some(isAccessible), + locatedAt = Some(atmJsonV510.located_at), + moreInfo = Some(atmJsonV510.more_info), + hasDepositCapability = Some(hdc), + + supportedLanguages = Some(atmJsonV510.supported_languages), + services = Some(atmJsonV510.services), + accessibilityFeatures = Some(atmJsonV510.accessibility_features), + supportedCurrencies = Some(atmJsonV510.supported_currencies), + notes = Some(atmJsonV510.notes), + minimumWithdrawal = Some(atmJsonV510.minimum_withdrawal), + branchIdentification = Some(atmJsonV510.branch_identification), + locationCategories = Some(atmJsonV510.location_categories), + siteIdentification = Some(atmJsonV510.site_identification), + siteName = Some(atmJsonV510.site_name), + cashWithdrawalNationalFee = Some(atmJsonV510.cash_withdrawal_national_fee), + cashWithdrawalInternationalFee = Some(atmJsonV510.cash_withdrawal_international_fee), + balanceInquiryFee = Some(atmJsonV510.balance_inquiry_fee), + atmType = Some(atmJsonV510.atm_type), + phone = Some(atmJsonV510.phone) + ) + } def getCustomViewNamesCheck(views: List[ViewDefinition]): CheckSystemIntegrityJsonV510 = { val success = views.size == 0 diff --git a/obp-api/src/main/scala/code/atms/Atms.scala b/obp-api/src/main/scala/code/atms/Atms.scala index 44559482e..6913e21c3 100644 --- a/obp-api/src/main/scala/code/atms/Atms.scala +++ b/obp-api/src/main/scala/code/atms/Atms.scala @@ -60,7 +60,9 @@ object Atms extends SimpleInjector { cashWithdrawalNationalFee: Option[String] = None, cashWithdrawalInternationalFee: Option[String] = None, balanceInquiryFee: Option[String] = None, - + atmType: Option[String] = None, + phone: Option[String] = None, + ) extends AtmT val atmsProvider = new Inject(buildOne _) {} diff --git a/obp-api/src/main/scala/code/atms/MappedAtmsProvider.scala b/obp-api/src/main/scala/code/atms/MappedAtmsProvider.scala index 03dd717ee..331d9dfdb 100644 --- a/obp-api/src/main/scala/code/atms/MappedAtmsProvider.scala +++ b/obp-api/src/main/scala/code/atms/MappedAtmsProvider.scala @@ -92,6 +92,8 @@ object MappedAtmsProvider extends AtmsProvider { .mCashWithdrawalNationalFee(atm.cashWithdrawalNationalFee.orNull) .mCashWithdrawalInternationalFee(atm.cashWithdrawalInternationalFee.orNull) .mBalanceInquiryFee(atm.balanceInquiryFee.orNull) + .mAtmType(atm.atmType.orNull) + .mPhone(atm.phone.orNull) .saveMe() } case _ => @@ -149,6 +151,9 @@ object MappedAtmsProvider extends AtmsProvider { .mCashWithdrawalNationalFee(atm.cashWithdrawalNationalFee.orNull) .mCashWithdrawalInternationalFee(atm.cashWithdrawalInternationalFee.orNull) .mBalanceInquiryFee(atm.balanceInquiryFee.orNull) + + .mAtmType(atm.atmType.orNull) + .mPhone(atm.phone.orNull) .saveMe() } } diff --git a/obp-api/src/main/scala/code/bankconnectors/vSept2018/KafkaJsonFactory_vSept2018.scala b/obp-api/src/main/scala/code/bankconnectors/vSept2018/KafkaJsonFactory_vSept2018.scala index 69db999ac..51ddb8265 100644 --- a/obp-api/src/main/scala/code/bankconnectors/vSept2018/KafkaJsonFactory_vSept2018.scala +++ b/obp-api/src/main/scala/code/bankconnectors/vSept2018/KafkaJsonFactory_vSept2018.scala @@ -366,6 +366,8 @@ case class InboundAtmSept2018( cashWithdrawalNationalFee: Option[String] = None, cashWithdrawalInternationalFee: Option[String] = None, balanceInquiryFee: Option[String] = None, + atmType: Option[String] = None, + phone: Option[String] = None, ) extends AtmT case class InternalTransaction_vSept2018( From b9717be09d5a158eaa422f00d22ecebf0341831f Mon Sep 17 00:00:00 2001 From: hongwei Date: Tue, 4 Apr 2023 12:31:33 +0200 Subject: [PATCH 4/8] test/added the tests for OBPV510 atms --- .../test/scala/code/api/v5_1_0/AtmTest.scala | 163 ++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 obp-api/src/test/scala/code/api/v5_1_0/AtmTest.scala 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 new file mode 100644 index 000000000..19d10d10f --- /dev/null +++ b/obp-api/src/test/scala/code/api/v5_1_0/AtmTest.scala @@ -0,0 +1,163 @@ +package code.api.v5_1_0 + +import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._ +import code.api.util.APIUtil.OAuth._ +import code.api.util.ApiRole._ +import code.api.util.ErrorMessages.{AtmNotFoundByAtmId, UserHasMissingRoles} +import code.api.util.ExampleValue.atmTypeExample +import code.api.util.{ApiRole, ErrorMessages} +import code.api.v5_1_0.APIMethods510.Implementations5_1_0 +import code.entitlement.Entitlement +import code.setup.DefaultUsers +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 + +class AtmTest extends V510ServerSetup with DefaultUsers { + + override def beforeAll() { + super.beforeAll() + } + + override def afterAll() { + 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.v5_1_0.toString) + 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)) + + 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 $ApiEndpoint2 $ApiEndpoint3 - $VersionOfApi") { + scenario(s"Test the CUR methods", ApiEndpoint1, VersionOfApi) { + When("We make the CREATE ATMs") + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanCreateAtmAtAnyBank.toString) + val requestCreate = (v5_1_0_Request / "banks" / bankId / "atms").POST <@ (user1) + val responseCreate = makePostRequest(requestCreate, write(atmJsonV510.copy( + bank_id = bankId, + atm_type = "atm_type1", + phone = "12345"))) + Then("We should get a 201") + responseCreate.code should equal(201) + 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 Update the ATMs") + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanUpdateAtmAtAnyBank.toString) + val requestUpdate = (v5_1_0_Request / "banks" / bankId / "atms" / atmId).PUT <@ (user1) + val responseUpdate = makePutRequest(requestUpdate, write(atmJsonV510.copy( + bank_id = bankId, + atm_type = "atm_type_111", + phone = "123456"))) + Then("We should get a 201") + responseUpdate.code should equal(201) + responseUpdate.body.extract[AtmJsonV510].atm_type shouldBe ("atm_type_111") + responseUpdate.body.extract[AtmJsonV510].phone shouldBe ("123456") + + Then("We create 2 more ATMs") + makePostRequest(requestCreate, write(atmJsonV510.copy( + bank_id = bankId, + id = Some("id2"), + atm_type = "atm_type2", + phone = "12345-2"))) + makePostRequest(requestCreate, write(atmJsonV510.copy( + bank_id = bankId, + id = Some("id3"), + atm_type = "atm_type3", + phone = "12345-3"))) + + 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 + 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") + } + } +} \ No newline at end of file From 0b68b2e5598f2763cecefe085ff653f3c6b47485 Mon Sep 17 00:00:00 2001 From: hongwei Date: Tue, 4 Apr 2023 13:32:05 +0200 Subject: [PATCH 5/8] test/fixed the frozen tests --- .../RestConnector_vMar2019_frozen_meta_data | Bin 113165 -> 113208 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/obp-api/src/test/scala/code/connector/RestConnector_vMar2019_frozen_meta_data b/obp-api/src/test/scala/code/connector/RestConnector_vMar2019_frozen_meta_data index de70249c348058077eacb7985393d203276fc140..5a1f839fd6b5a443eee23752d2b4ab336d09de37 100644 GIT binary patch delta 258 zcmeDE!nWfJ+lB@gM)l2&E_y61?1?40lND9OH?Q~bVPs`3$jHx|{LxKha=9npW=^jw zTC9Z(bWxQ*}w}_ z!0XN^Ir+Vr=46FhzRCQle4C?o3GyJBGpW>k`m$Aw;hSZS>xcridEO~!15yVcon~f? znSAh3)n@kR(Nf4JZ$HAwc+>%A^seoS(?3Koif+H@#AwXI7_%K{VZ1Wqz3sVKjQeGQ beBBPlYrJ5iq_@jYWBe%$v3xtw6L0weZ-rl9 delta 267 zcmdn-g{}7s+lB@gM%B%YE_y61ta-PaomQYmT{3-@S@a zclw4lMuExZ#~U}Rl`(5E-k!cuk5P8=jTz>f>lV!62J$B|GA3@WS+jwc@%CojUHLp9 z-J91OJ0;2(xw-sKIU8f-WI;KR$>*P?P1j$_sJyx7u^ux-Z1eeN2c;NqZwH#7=E!(^ zvhFU)>3@jK!+S?;@f^d wi?Lc3sOUq7$oBjW#zVYtE7>RS>r|Ou;K0bcy>1%g31O(u1h%KGV!Xr;0G~i==>Px# From 66fe397b58c56c5d5d96bbbaf5ab39bc9b53416d Mon Sep 17 00:00:00 2001 From: hongwei Date: Tue, 4 Apr 2023 14:36:57 +0200 Subject: [PATCH 6/8] refactor/fixed the compiling error --- obp-api/src/test/scala/code/api/v1_4_0/AtmsTest.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/obp-api/src/test/scala/code/api/v1_4_0/AtmsTest.scala b/obp-api/src/test/scala/code/api/v1_4_0/AtmsTest.scala index 6ab3f82e6..5d23f3acd 100644 --- a/obp-api/src/test/scala/code/api/v1_4_0/AtmsTest.scala +++ b/obp-api/src/test/scala/code/api/v1_4_0/AtmsTest.scala @@ -62,6 +62,8 @@ class AtmsTest extends V140ServerSetup with DefaultUsers { cashWithdrawalNationalFee: Option[String] = None, cashWithdrawalInternationalFee: Option[String] = None, balanceInquiryFee: Option[String] = None, + atmType: Option[String] = None, + phone: Option[String] = None, ) extends AtmT case class AddressImpl(line1 : String, line2 : String, line3 : String, city : String, county : Option[String], From 9933ebdae3951f027e1c52386e867ce7d2e443e7 Mon Sep 17 00:00:00 2001 From: hongwei Date: Tue, 4 Apr 2023 15:08:52 +0200 Subject: [PATCH 7/8] refactor/tweaked the resourceDocs --- .../SwaggerDefinitionsJSON.scala | 15 +++++++++------ .../main/scala/code/api/util/ExampleValue.scala | 3 +++ .../scala/code/api/v5_1_0/APIMethods510.scala | 3 ++- .../scala/code/api/v5_1_0/JSONFactory5.1.0.scala | 4 ++-- 4 files changed, 16 insertions(+), 9 deletions(-) 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 60cba3593..2c9081f74 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 @@ -4018,13 +4018,16 @@ object SwaggerDefinitionsJSON { val atmAttributeResponseJsonV510 = AtmAttributeResponseJsonV510( bank_id = bankIdExample.value, atm_id = atmIdExample.value, - atm_attribute_id = "613c83ea-80f9-4560-8404-b9cd4ec42a7f", - name = "OVERDRAFT_START_DATE", - `type` = "DATE_WITH_DAY", - value = "2012-04-23", - is_active = Some(true) + atm_attribute_id = atmAttributeIdExample.value, + name = nameExample.value, + `type` = typeExample.value, + value = valueExample.value, + is_active = Some(activeExample.value.toBoolean) + ) + + val atmAttributesResponseJsonV510 = AtmAttributesResponseJsonV510( + List(atmAttributeResponseJsonV510) ) - val accountAttributeJson = AccountAttributeJson( name = "OVERDRAFT_START_DATE", 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 43d9e1b70..4b4213ad2 100644 --- a/obp-api/src/main/scala/code/api/util/ExampleValue.scala +++ b/obp-api/src/main/scala/code/api/util/ExampleValue.scala @@ -858,6 +858,9 @@ object ExampleValue { lazy val atmIdExample = ConnectorField("atme0352a-9a0f-4bfa-b30b-9003aa467f51","A string that MUST uniquely identify the ATM on this OBP instance.") glossaryItems += makeGlossaryItem("atm_id", atmIdExample) + + lazy val atmAttributeIdExample = ConnectorField("xxaf2a-9a0f-4bfa-b30b-9003aa467f51","A string that MUST uniquely identify the ATM Attribute on this OBP instance.") + glossaryItems += makeGlossaryItem("ATM.attribute_id", atmIdExample) lazy val atmNameExample = ConnectorField("Atm by the Lake","The name of the ATM") glossaryItems += makeGlossaryItem("ATM.name", atmNameExample) 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 dfb9017d0..c547370bd 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 @@ -9,6 +9,7 @@ import code.api.util.ApiTag._ import code.api.util.ErrorMessages.{$UserNotLoggedIn, BankNotFound, ConsentNotFound, InvalidJsonFormat, UnknownError, UserNotFoundByUserId, UserNotLoggedIn, _} import code.api.util.{APIUtil, ApiRole, CallContext, CurrencyUtil, NewStyle, X509} import code.api.util.NewStyle.HttpCode +import code.api.v3_0_0.JSONFactory300 import code.api.v3_0_0.JSONFactory300.createAggregateMetricJson import code.api.v3_1_0.ConsentJsonV310 import code.api.v3_1_0.JSONFactory310.createBadLoginStatusJson @@ -374,7 +375,7 @@ trait APIMethods510 { | |""", EmptyBody, - transactionAttributesResponseJson, + atmAttributesResponseJsonV510, List( $UserNotLoggedIn, $BankNotFound, 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 20732086e..c63339605 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 @@ -360,8 +360,8 @@ object JSONFactory510 { is_active = atmAttribute.isActive ) - def createAtmAttributesJson(bankAttributes: List[AtmAttribute]): AtmAttributesResponseJsonV510 = - AtmAttributesResponseJsonV510(bankAttributes.map(createAtmAttributeJson)) + def createAtmAttributesJson(atmAttributes: List[AtmAttribute]): AtmAttributesResponseJsonV510 = + AtmAttributesResponseJsonV510(atmAttributes.map(createAtmAttributeJson)) } From 71bdda4b58b60af3d60e27569c0ce3bc0611cb69 Mon Sep 17 00:00:00 2001 From: hongwei Date: Tue, 4 Apr 2023 16:38:20 +0200 Subject: [PATCH 8/8] feature/OBPV510 added attributes to atm endpoints --- .../SwaggerDefinitionsJSON.scala | 2 + .../main/scala/code/api/util/NewStyle.scala | 4 +- .../scala/code/api/v5_1_0/APIMethods510.scala | 62 ++++++- .../code/api/v5_1_0/JSONFactory5.1.0.scala | 17 +- .../test/scala/code/api/v5_1_0/AtmTest.scala | 174 ++++++++++-------- 5 files changed, 172 insertions(+), 87 deletions(-) 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