diff --git a/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala b/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala index 07410e948..1b3704f57 100644 --- a/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala @@ -122,7 +122,7 @@ import code.transactionattribute.MappedTransactionAttribute import code.transactionrequests.{MappedTransactionRequest, MappedTransactionRequestTypeCharge, TransactionRequestReasons} import code.usercustomerlinks.MappedUserCustomerLink import code.userlocks.UserLocks -import code.users.{UserAgreement, UserAttribute, UserInitAction, UserInvitation} +import code.users.{UserAgreement, UserAttribute, UserInitAction, UserInvitation, Users} import code.util.Helper.MdcLoggable import code.util.{Helper, HydraUtil} import code.validation.JsonSchemaValidation @@ -130,7 +130,7 @@ import code.views.Views import code.views.system.{AccountAccess, ViewDefinition} import code.webhook.{BankAccountNotificationWebhook, MappedAccountWebhook, SystemAccountNotificationWebhook, WebhookHelperActors} import code.webuiprops.WebUiProps -import com.openbankproject.commons.model.ErrorMessage +import com.openbankproject.commons.model.{ErrorMessage, User} import com.openbankproject.commons.util.Functions.Implicits._ import com.openbankproject.commons.util.{ApiVersion, Functions} import javax.mail.{Authenticator, PasswordAuthentication} @@ -611,7 +611,11 @@ class Boot extends MdcLoggable { // In case it's true we use that value to set up a new cookie value S.param("locale") match { case Full(requestedLocale) if requestedLocale != null => { - val computedLocale = I18NUtil.computeLocale(requestedLocale) + val computedLocale: Locale = I18NUtil.computeLocale(requestedLocale) + val id: Long = AuthUser.getCurrentUser.map(_.user.userPrimaryKey.value).getOrElse(0) + Users.users.vend.getResourceUserByResourceUserId(id).map { + u => u.LastUsedLocale(computedLocale.toString).save + } S.addCookie(HTTPCookie(localeCookieName, requestedLocale)) computedLocale } 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 80fa55a34..3a7178a6d 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 @@ -95,7 +95,7 @@ object SwaggerDefinitionsJSON { accountRoutings = List(accountRouting) ) - val createViewJson = CreateViewJson( + val createViewJsonV300 = CreateViewJsonV300( name = "_test", description = "This view is for family", metadata_view ="_test", @@ -186,9 +186,103 @@ object SwaggerDefinitionsJSON { ) ) - val createSystemViewJson = createViewJson.copy(name = "test", metadata_view = "test", is_public = false) + val createSystemViewJsonV300 = createViewJsonV300.copy(name = "test", metadata_view = "test", is_public = false) - val updateViewJSON = UpdateViewJSON( + val createSystemViewJsonV500 = CreateViewJsonV500( + name = "_test", + description = "This view is for family", + metadata_view ="_test", + is_public = false, + which_alias_to_use = "family", + hide_metadata_if_alias_used = false, + allowed_actions = List( + "can_see_transaction_this_bank_account", + "can_see_transaction_other_bank_account", + "can_see_transaction_metadata", + "can_see_transaction_label", + "can_see_transaction_amount", + "can_see_transaction_type", + "can_see_transaction_currency", + "can_see_transaction_start_date", + "can_see_transaction_finish_date", + "can_see_transaction_balance", + "can_see_comments", + "can_see_narrative", + "can_see_tags", + "can_see_images", + "can_see_bank_account_owners", + "can_see_bank_account_type", + "can_see_bank_account_balance", + "can_see_bank_account_currency", + "can_see_bank_account_label", + "can_see_bank_account_national_identifier", + "can_see_bank_account_swift_bic", + "can_see_bank_account_iban", + "can_see_bank_account_number", + "can_see_bank_account_bank_name", + "can_see_other_account_national_identifier", + "can_see_other_account_swift_bic", + "can_see_other_account_iban", + "can_see_other_account_bank_name", + "can_see_other_account_number", + "can_see_other_account_metadata", + "can_see_other_account_kind", + "can_see_more_info", + "can_see_url", + "can_see_image_url", + "can_see_open_corporates_url", + "can_see_corporate_location", + "can_see_physical_location", + "can_see_public_alias", + "can_see_private_alias", + "can_add_more_info", + "can_add_url", + "can_add_image_url", + "can_add_open_corporates_url", + "can_add_corporate_location", + "can_add_physical_location", + "can_add_public_alias", + "can_add_private_alias", + "can_delete_corporate_location", + "can_delete_physical_location", + "can_edit_narrative", + "can_add_comment", + "can_delete_comment", + "can_add_tag", + "can_delete_tag", + "can_add_image", + "can_delete_image", + "can_add_where_tag", + "can_see_where_tag", + "can_delete_where_tag", + "can_create_counterparty", + //V300 New + "can_see_bank_routing_scheme", + "can_see_bank_routing_address", + "can_see_bank_account_routing_scheme", + "can_see_bank_account_routing_address", + "can_see_other_bank_routing_scheme", + "can_see_other_bank_routing_address", + "can_see_other_account_routing_scheme", + "can_see_other_account_routing_address", + //v310 + "can_query_available_funds", + "can_add_transaction_request_to_own_account", + "can_add_transaction_request_to_any_account", + "can_see_bank_account_credit_limit", + //v400 + "can_create_direct_debit", + "can_create_standing_order", + + //payments + "can_add_transaction_request_to_any_account" + ), + // Version 5.0.0 + can_grant_access_to_views = Some(List("owner")), + can_revoke_access_to_views = Some(List("owner")) + ) + + val updateViewJsonV300 = UpdateViewJsonV300( description = "this is for family", is_public = true, metadata_view = SYSTEM_OWNER_VIEW_ID, @@ -267,8 +361,91 @@ object SwaggerDefinitionsJSON { "can_query_available_funds" ) ) + lazy val updateSystemViewJson310 = updateViewJsonV300.copy(is_public = false, is_firehose = Some(false)) - lazy val updateSystemViewJson310 = updateViewJSON.copy(is_public = false, is_firehose = Some(false)) + val updateViewJsonV500 = UpdateViewJsonV500( + description = "this is for family", + is_public = true, + metadata_view = SYSTEM_OWNER_VIEW_ID, + which_alias_to_use = "family", + hide_metadata_if_alias_used = true, + allowed_actions = List( + "can_see_transaction_this_bank_account", + "can_see_transaction_other_bank_account", + "can_see_transaction_metadata", + "can_see_transaction_label", + "can_see_transaction_amount", + "can_see_transaction_type", + "can_see_transaction_currency", + "can_see_transaction_start_date", + "can_see_transaction_finish_date", + "can_see_transaction_balance", + "can_see_comments", + "can_see_narrative", "can_see_tags", + "can_see_images", + "can_see_bank_account_owners", + "can_see_bank_account_type", + "can_see_bank_account_balance", + "can_see_bank_account_currency", + "can_see_bank_account_label", + "can_see_bank_account_national_identifier", + "can_see_bank_account_swift_bic", + "can_see_bank_account_iban", + "can_see_bank_account_number", + "can_see_bank_account_bank_name", + "can_see_other_account_national_identifier", + "can_see_other_account_swift_bic", + "can_see_other_account_iban", + "can_see_other_account_bank_name", + "can_see_other_account_number", + "can_see_other_account_metadata", + "can_see_other_account_kind", + "can_see_more_info", + "can_see_url", + "can_see_image_url", + "can_see_open_corporates_url", + "can_see_corporate_location", + "can_see_physical_location", + "can_see_public_alias", + "can_see_private_alias", + "can_add_more_info", + "can_add_url", + "can_add_image_url", + "can_add_open_corporates_url", + "can_add_corporate_location", + "can_add_physical_location", + "can_add_public_alias", + "can_add_private_alias", + "can_delete_corporate_location", + "can_delete_physical_location", + "can_edit_narrative", + "can_add_comment", + "can_delete_comment", + "can_add_tag", + "can_delete_tag", + "can_add_image", + "can_delete_image", + "can_add_where_tag", + "can_see_where_tag", + "can_delete_where_tag", + "can_create_counterparty", + //V300 New + "can_see_bank_routing_scheme", + "can_see_bank_routing_address", + "can_see_bank_account_routing_scheme", + "can_see_bank_account_routing_address", + "can_see_other_bank_routing_scheme", + "can_see_other_bank_routing_address", + "can_see_other_account_routing_scheme", + "can_see_other_account_routing_address", + //v310 + "can_query_available_funds" + ), + // Version 5.0.0 + can_grant_access_to_views = Some(List("owner")), + can_revoke_access_to_views = Some(List("owner")) + ) + lazy val updateSystemViewJson500 = updateViewJsonV500.copy(is_public = false, is_firehose = Some(false)) val transactionTypeIdSwagger = TransactionTypeId(value = "123") @@ -834,8 +1011,8 @@ object SwaggerDefinitionsJSON { ) val bankRoutingJsonV121 = BankRoutingJsonV121( - scheme = "Bank_ID", - address = "gh.29.uk" + scheme = schemeExample.value, + address = addressExample.value ) val bankJSON = BankJSON( @@ -859,6 +1036,14 @@ object SwaggerDefinitionsJSON { list = List(bankAttributeBankResponseJsonV400) ) + val postBankJson400 = PostBankJson400( + id = "gh.29.uk", + short_name = "short_name ", + full_name = "full_name", + logo = "logo", + website = "www.openbankproject.com", + bank_routings = List(bankRoutingJsonV121) + ) val bankJson400 = BankJson400( id = "gh.29.uk", short_name = "short_name ", @@ -869,23 +1054,22 @@ object SwaggerDefinitionsJSON { attributes = Some(List(bankAttributeBankResponseJsonV400)) ) val bankJson500 = BankJson500( - id = "gh.29.uk", - bank_code = "bank_code ", - full_name = "full_name", - logo = "logo", - website = "www.openbankproject.com", + id = bankIdExample.value, + bank_code = bankCodeExample.value, + full_name = bankFullNameExample.value, + logo = bankLogoUrlExample.value, + website = bankLogoUrlExample.value, bank_routings = List(bankRoutingJsonV121), attributes = Some(List(bankAttributeBankResponseJsonV400)) ) val postBankJson500 = PostBankJson500( - id = Some("gh.29.uk"), - bank_code = "bank_code", - full_name = Some("full_name"), - logo = Some("logo"), - website = Some("www.openbankproject.com"), - bank_routings = Some(List(bankRoutingJsonV121)), - attributes = Some(List(bankAttributeBankResponseJsonV400)) + id = Some(idExample.value), + bank_code = bankCodeExample.value, + full_name = Some(fullNameExample.value), + logo = Some(logoExample.value), + website = Some(websiteExample.value), + bank_routings = Some(List(bankRoutingJsonV121)) ) val banksJSON400 = BanksJson400( @@ -1281,7 +1465,7 @@ object SwaggerDefinitionsJSON { face_image = customerFaceImageJson, date_of_birth = DateWithDayExampleObject, relationship_status = ExampleValue.relationshipStatusExample.value, - dependants = ExampleValue.dependentsExample.value.toInt, + dependants = ExampleValue.dependantsExample.value.toInt, dob_of_dependants = List(DateWithDayExampleObject), highest_education_attained = ExampleValue.highestEducationAttainedExample.value, employment_status = ExampleValue.employmentStatusExample.value, @@ -1298,7 +1482,7 @@ object SwaggerDefinitionsJSON { face_image = customerFaceImageJson, date_of_birth = DateWithDayExampleObject, relationship_status = ExampleValue.relationshipStatusExample.value, - dependants = ExampleValue.dependentsExample.value.toInt, + dependants = ExampleValue.dependantsExample.value.toInt, dob_of_dependants = List(DateWithDayExampleObject), highest_education_attained = ExampleValue.highestEducationAttainedExample.value, employment_status = ExampleValue.employmentStatusExample.value, @@ -2002,7 +2186,7 @@ object SwaggerDefinitionsJSON { face_image = customerFaceImageJson, date_of_birth = DateWithDayExampleObject, relationship_status = ExampleValue.relationshipStatusExample.value, - dependants = ExampleValue.dependentsExample.value.toInt, + dependants = ExampleValue.dependantsExample.value.toInt, dob_of_dependants = List(DateWithDayExampleObject), highest_education_attained = ExampleValue.highestEducationAttainedExample.value, employment_status = ExampleValue.employmentStatusExample.value, @@ -2136,7 +2320,7 @@ object SwaggerDefinitionsJSON { face_image = customerFaceImageJson, date_of_birth = DateWithDayExampleObject, relationship_status = ExampleValue.relationshipStatusExample.value, - dependants = ExampleValue.dependentsExample.value.toInt, + dependants = ExampleValue.dependantsExample.value.toInt, dob_of_dependants = List(DateWithDayExampleObject), credit_rating = Option(customerCreditRatingJSON), credit_limit = Option(amountOfMoneyJsonV121), @@ -2172,7 +2356,7 @@ object SwaggerDefinitionsJSON { face_image = customerFaceImageJson, date_of_birth = DateWithDayExampleObject, relationship_status = ExampleValue.relationshipStatusExample.value, - dependants = ExampleValue.dependentsExample.value.toInt, + dependants = ExampleValue.dependantsExample.value.toInt, dob_of_dependants = List(DateWithDayExampleObject), credit_rating = customerCreditRatingJSON, credit_limit = amountOfMoneyJsonV121, @@ -2192,7 +2376,7 @@ object SwaggerDefinitionsJSON { face_image = customerFaceImageJson, date_of_birth = "19900101", relationship_status = ExampleValue.relationshipStatusExample.value, - dependants = ExampleValue.dependentsExample.value.toInt, + dependants = ExampleValue.dependantsExample.value.toInt, dob_of_dependants = List("19900101"), credit_rating = Option(customerCreditRatingJSON), credit_limit = Option(amountOfMoneyJsonV121), @@ -2221,7 +2405,7 @@ object SwaggerDefinitionsJSON { face_image = customerFaceImageJson, date_of_birth = DateWithDayExampleObject, relationship_status = ExampleValue.relationshipStatusExample.value, - dependants = ExampleValue.dependentsExample.value.toInt, + dependants = ExampleValue.dependantsExample.value.toInt, dob_of_dependants = List(DateWithDayExampleObject), credit_rating = customerCreditRatingJSON, credit_limit = amountOfMoneyJsonV121, @@ -2236,12 +2420,13 @@ object SwaggerDefinitionsJSON { val postCustomerJsonV500 = PostCustomerJsonV500( legal_name = ExampleValue.legalNameExample.value, - mobile_phone_number = ExampleValue.mobileNumberExample.value, + customer_number = Some(ExampleValue.customerNumberExample.value), + mobile_phone_number = ExampleValue.mobilePhoneNumberExample.value, email = Some(ExampleValue.emailExample.value), face_image = Some(customerFaceImageJson), date_of_birth = Some(DateWithDayExampleObject), relationship_status = Some(ExampleValue.relationshipStatusExample.value), - dependants = Some(ExampleValue.dependentsExample.value.toInt), + dependants = Some(ExampleValue.dependantsExample.value.toInt), dob_of_dependants = Some(List(DateWithDayExampleObject)), credit_rating = Some(customerCreditRatingJSON), credit_limit = Some(amountOfMoneyJsonV121), @@ -2264,7 +2449,7 @@ object SwaggerDefinitionsJSON { face_image = customerFaceImageJson, date_of_birth = DateWithDayExampleObject, relationship_status = ExampleValue.relationshipStatusExample.value, - dependants = ExampleValue.dependentsExample.value.toInt, + dependants = ExampleValue.dependantsExample.value.toInt, dob_of_dependants = List(DateWithDayExampleObject), credit_rating = Option(customerCreditRatingJSON), credit_limit = Option(amountOfMoneyJsonV121), @@ -2341,7 +2526,7 @@ object SwaggerDefinitionsJSON { face_image = customerFaceImageJson, date_of_birth = DateWithDayExampleObject, relationship_status = ExampleValue.relationshipStatusExample.value, - dependants = ExampleValue.dependentsExample.value.toInt, + dependants = ExampleValue.dependantsExample.value.toInt, dob_of_dependants = List(DateWithDayExampleObject), credit_rating = Option(customerCreditRatingJSON), credit_limit = Option(amountOfMoneyJsonV121), @@ -2366,7 +2551,7 @@ object SwaggerDefinitionsJSON { face_image = customerFaceImageJson, date_of_birth = DateWithDayExampleObject, relationship_status = ExampleValue.relationshipStatusExample.value, - dependants = ExampleValue.dependentsExample.value.toInt, + dependants = ExampleValue.dependantsExample.value.toInt, dob_of_dependants = List(DateWithDayExampleObject), credit_rating = Option(customerCreditRatingJSON), credit_limit = Option(amountOfMoneyJsonV121), @@ -2390,7 +2575,7 @@ object SwaggerDefinitionsJSON { face_image = customerFaceImageJson, date_of_birth = "19900101", relationship_status = ExampleValue.relationshipStatusExample.value, - dependants = ExampleValue.dependentsExample.value.toInt, + dependants = ExampleValue.dependantsExample.value.toInt, dob_of_dependants = List("19900101"), credit_rating = Option(customerCreditRatingJSON), credit_limit = Option(amountOfMoneyJsonV121), @@ -2409,7 +2594,7 @@ object SwaggerDefinitionsJSON { val putUpdateCustomerDataJsonV310 = PutUpdateCustomerDataJsonV310( face_image = customerFaceImageJson, relationship_status = ExampleValue.relationshipStatusExample.value, - dependants = ExampleValue.dependentsExample.value.toInt, + dependants = ExampleValue.dependantsExample.value.toInt, highest_education_attained = ExampleValue.highestEducationAttainedExample.value, employment_status = ExampleValue.employmentStatusExample.value ) @@ -2735,6 +2920,104 @@ object SwaggerDefinitionsJSON { views = List(viewJSONV220) ) + + val viewJsonV500 = ViewJsonV500( + id = "1234", + short_name = "short_name", + description = "description", + metadata_view = SYSTEM_OWNER_VIEW_ID, + is_public = true, + is_system = true, + alias = "No", + hide_metadata_if_alias_used = true, + can_add_comment = true, + can_add_corporate_location = true, + can_add_image = true, + can_add_image_url = true, + can_add_more_info = true, + can_add_open_corporates_url = true, + can_add_physical_location = true, + can_add_private_alias = true, + can_add_public_alias = true, + can_add_tag = true, + can_add_url = true, + can_add_where_tag = true, + can_delete_comment = true, + can_add_counterparty = true, + can_delete_corporate_location = true, + can_delete_image = true, + can_delete_physical_location = true, + can_delete_tag = true, + can_delete_where_tag = true, + can_edit_owner_comment = true, + can_see_bank_account_balance = true, + can_query_available_funds = true, + can_see_bank_account_bank_name = true, + can_see_bank_account_currency = true, + can_see_bank_account_iban = true, + can_see_bank_account_label = true, + can_see_bank_account_national_identifier = true, + can_see_bank_account_number = true, + can_see_bank_account_owners = true, + can_see_bank_account_swift_bic = true, + can_see_bank_account_type = true, + can_see_comments = true, + can_see_corporate_location = true, + can_see_image_url = true, + can_see_images = true, + can_see_more_info = true, + can_see_open_corporates_url = true, + can_see_other_account_bank_name = true, + can_see_other_account_iban = true, + can_see_other_account_kind = true, + can_see_other_account_metadata = true, + can_see_other_account_national_identifier = true, + can_see_other_account_number = true, + can_see_other_account_swift_bic = true, + can_see_owner_comment = true, + can_see_physical_location = true, + can_see_private_alias = true, + can_see_public_alias = true, + can_see_tags = true, + can_see_transaction_amount = true, + can_see_transaction_balance = true, + can_see_transaction_currency = true, + can_see_transaction_description = true, + can_see_transaction_finish_date = true, + can_see_transaction_metadata = true, + can_see_transaction_other_bank_account = true, + can_see_transaction_start_date = true, + can_see_transaction_this_bank_account = true, + can_see_transaction_type = true, + can_see_url = true, + can_see_where_tag = true, + //V300 new + can_see_bank_routing_scheme = true, + can_see_bank_routing_address = true, + can_see_bank_account_routing_scheme = true, + can_see_bank_account_routing_address = true, + can_see_other_bank_routing_scheme = true, + can_see_other_bank_routing_address = true, + can_see_other_account_routing_scheme = true, + can_see_other_account_routing_address = true, + can_add_transaction_request_to_own_account = true, //added following two for payments + can_add_transaction_request_to_any_account = true, + can_see_bank_account_credit_limit = true, + can_create_direct_debit = true, + can_create_standing_order = true, + can_grant_access_to_views = List("Owner"), + can_revoke_access_to_views = List("Owner") + ) + + val viewsJsonV500 = ViewsJsonV500( + views = List(viewJsonV500) + ) + + val viewIdJsonV500 = ViewIdJsonV500(id = "owner") + val viewIdsJsonV500 = ViewsIdsJsonV500( + views = List(viewIdJsonV500) + ) + val fXRateJSON = FXRateJsonV220( bank_id = bankIdExample.value, from_currency_code = "EUR", @@ -3334,12 +3617,12 @@ object SwaggerDefinitionsJSON { bank_id = bankIdExample.value, label = labelExample.value, number = numberExample.value, - owners = "user_id:b27327a2-a822-41e5-a909-0150da688939,provider:https://finx22openplatform.fintech-galaxy.com,user_name:synth_user_1_54891", + owners = List(FastFirehoseOwners(user_id="b27327a2-a822-41e5-a909-0150da688939",provider="https://finx22openplatform.fintech-galaxy.com,user_name:synth_user_1_54891", user_name="")), product_code = productCodeExample.value, balance = amountOfMoneyJsonV121, - account_routings = "bank_id:bisb.com,account_id:c590e38e-847c-466f-9a62-f2ad67daf106", - account_attributes= "type:INTEGER,code:Loan1,value:0," + - "type:STRING,code:Loan1,value:4421.783" + account_routings = List(FastFirehoseRoutings(bank_id="bisb.com",account_id="c590e38e-847c-466f-9a62-f2ad67daf106")), + account_attributes= List(FastFirehoseAttributes(`type`="INTEGER",code="Loan1",value="0"), + FastFirehoseAttributes(`type`="STRING",code="Loan1",value="4421.783")) ) diff --git a/obp-api/src/main/scala/code/api/util/APIUtil.scala b/obp-api/src/main/scala/code/api/util/APIUtil.scala index db9ac975c..e5891fcaa 100644 --- a/obp-api/src/main/scala/code/api/util/APIUtil.scala +++ b/obp-api/src/main/scala/code/api/util/APIUtil.scala @@ -806,6 +806,12 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ null else text + + def nullToString(text : String) = + if(text == null) + null + else + text def stringOptionOrNull(text : Option[String]) = text match { diff --git a/obp-api/src/main/scala/code/api/util/ApiRole.scala b/obp-api/src/main/scala/code/api/util/ApiRole.scala index 653b2d60c..ab72e1180 100644 --- a/obp-api/src/main/scala/code/api/util/ApiRole.scala +++ b/obp-api/src/main/scala/code/api/util/ApiRole.scala @@ -351,6 +351,9 @@ object ApiRole { case class CanReadMetrics (requiresBankId: Boolean = false) extends ApiRole lazy val canReadMetrics = CanReadMetrics() + + case class CanGetMetricsAtOneBank(requiresBankId: Boolean = true) extends ApiRole + lazy val canGetMetricsAtOneBank = CanGetMetricsAtOneBank() case class CanGetConfig(requiresBankId: Boolean = false) extends ApiRole lazy val canGetConfig = CanGetConfig() diff --git a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala index dcd1ea231..aafce8994 100644 --- a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala +++ b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala @@ -499,7 +499,7 @@ object ErrorMessages { val ConsentUserCannotBeAdded = "OBP-35026: The Consent's User cannot be added." val ConsentUserAuthContextCannotBeAdded = "OBP-35027: The Consent's User Auth Context cannot be added." val ConsentRequestNotFound = "OBP-35028: Consent Request not found by CONSENT_REQUEST_ID. " - val ConsentRequestAlreadyUsed = "OBP-35029: The CONSENT_REQUEST_ID is used to create Consent. " + val ConsentRequestIsInvalid = "OBP-35029: The CONSENT_REQUEST_ID is invalid. " //Authorisations val AuthorisationNotFound = "OBP-36001: Authorisation not found. Please specify valid values for PAYMENT_ID and AUTHORISATION_ID. " 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 6e153f99b..04afb8fda 100644 --- a/obp-api/src/main/scala/code/api/util/ExampleValue.scala +++ b/obp-api/src/main/scala/code/api/util/ExampleValue.scala @@ -1,7 +1,7 @@ package code.api.util -import code.api.util.APIUtil.{DateWithMs, DateWithMsExampleString, parseDate} +import code.api.util.APIUtil.{DateWithMs, DateWithMsExampleString, oneYearAgoDate, formatDate, oneYearAgo, parseDate} import code.api.util.ErrorMessages.{InvalidJsonFormat, UnknownError, UserHasMissingRoles, UserNotLoggedIn} import net.liftweb.json.JsonDSL._ import code.api.util.Glossary.{glossaryItems, makeGlossaryItem} @@ -23,13 +23,14 @@ case class ConnectorField(value: String, description: String) { object ExampleValue { val NoDescriptionProvided = "no-description-provided" - val NoExampleProvided = "no-example-provided" + val NoExampleProvided = "" val booleanTrue = "true" lazy val bankIdGlossary = glossaryItems.find(_.title == "Bank.bank_id").map(_.textDescription) lazy val bankIdExample = ConnectorField("gh.29.uk", s"A string that MUST uniquely identify the bank on this OBP instance. It COULD be a UUID but is generally a short string that easily identifies the bank / brand it represents.") lazy val bank_idExample = bankIdExample + glossaryItems += makeGlossaryItem("Bank.bank_id", bankIdExample) lazy val accountIdExample = ConnectorField("8ca8a7e4-6d02-40e3-a129-0b2bf89de9f0", s"A string that, in combination with the bankId MUST uniquely identify the account on this OBP instance. SHOULD be a UUID. MUST NOT be able to guess accountNumber from accountId. OBP-API or Adapter keeps a mapping between accountId and accountNumber. AccountId is a non reversible hash of the human readable account number.") lazy val account_idExample = accountIdExample @@ -96,7 +97,10 @@ object ExampleValue { lazy val relationshipStatusExample = ConnectorField("single", s"relationship status") glossaryItems += makeGlossaryItem("Customer.relationshipStatus", relationshipStatusExample) - lazy val dependentsExample = ConnectorField("1", s"the number of dependents") + lazy val dependantsExample = ConnectorField("1", s"the number of dependants") + glossaryItems += makeGlossaryItem("Customer.dependants", dependantsExample) + + lazy val dependentsExample = ConnectorField("2", s"the number of dependents") // Dominant form in American English glossaryItems += makeGlossaryItem("Customer.dependents", dependentsExample) lazy val kycStatusExample = ConnectorField(booleanTrue, s"This is boolean to indicate if the cusomter's KYC has been checked.") @@ -785,7 +789,7 @@ object ExampleValue { lazy val customerUserIdExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("customer_user_id", customerUserIdExample) - lazy val bankCodeExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) + lazy val bankCodeExample = ConnectorField("CGHZ",NoDescriptionProvided) glossaryItems += makeGlossaryItem("bank_code", bankCodeExample) lazy val averageResponseTimeExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -848,7 +852,7 @@ object ExampleValue { lazy val canAddTransactionRequestToAnyAccountExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("can_add_transaction_request_to_any_account", canAddTransactionRequestToAnyAccountExample) - lazy val websiteExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) + lazy val websiteExample = ConnectorField("www.openbankproject.com",NoDescriptionProvided) glossaryItems += makeGlossaryItem("website", websiteExample) lazy val atmIdExample = ConnectorField("atme0352a-9a0f-4bfa-b30b-9003aa467f51","A string that MUST uniquely identify the ATM on this OBP instance.") @@ -962,7 +966,7 @@ object ExampleValue { lazy val accountExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("account", accountExample) - lazy val idExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) + lazy val idExample = ConnectorField("d8839721-ad8f-45dd-9f78-2080414b93f9",NoDescriptionProvided) glossaryItems += makeGlossaryItem("id", idExample) lazy val canAddCorporateLocationExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -1031,7 +1035,7 @@ object ExampleValue { lazy val toDateExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("to_date", toDateExample) - lazy val bankRoutingsExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) + lazy val bankRoutingsExample = ConnectorField("bank routing in form of (scheme, address)",NoDescriptionProvided) glossaryItems += makeGlossaryItem("bank_routings", bankRoutingsExample) lazy val canSeeOpenCorporatesUrlExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -1085,9 +1089,6 @@ object ExampleValue { lazy val temporaryCreditDocumentationExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("temporary_credit_documentation", temporaryCreditDocumentationExample) - lazy val dependantsExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) - glossaryItems += makeGlossaryItem("dependants", dependantsExample) - lazy val locationExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("location", locationExample) @@ -1275,7 +1276,7 @@ object ExampleValue { lazy val wednesdayExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("wednesday", wednesdayExample) - lazy val lastOkDateExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) + lazy val lastOkDateExample = ConnectorField(formatDate(oneYearAgoDate),NoDescriptionProvided) glossaryItems += makeGlossaryItem("last_ok_date", lastOkDateExample) lazy val transactionTypesExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -1591,7 +1592,7 @@ object ExampleValue { lazy val dateInsertedExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("date_inserted", dateInsertedExample) - lazy val schemeExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) + lazy val schemeExample = ConnectorField("scheme value",NoDescriptionProvided) glossaryItems += makeGlossaryItem("scheme", schemeExample) lazy val customerAddressIdExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -1762,7 +1763,7 @@ object ExampleValue { lazy val branchTypeExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("branch_type", branchTypeExample) - lazy val fullNameExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) + lazy val fullNameExample = ConnectorField("full name string",NoDescriptionProvided) glossaryItems += makeGlossaryItem("full_name", fullNameExample) lazy val canCreateDirectDebitExample = ConnectorField(booleanTrue,NoDescriptionProvided) @@ -1861,7 +1862,7 @@ object ExampleValue { lazy val postedExample = ConnectorField("2020-01-27",NoDescriptionProvided) glossaryItems += makeGlossaryItem("posted", postedExample) - lazy val logoExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) + lazy val logoExample = ConnectorField("logo url",NoDescriptionProvided) glossaryItems += makeGlossaryItem("logo", logoExample) lazy val topApisExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -1987,7 +1988,7 @@ object ExampleValue { lazy val toCurrencyCodeExample = ConnectorField("EUR",NoDescriptionProvided) glossaryItems += makeGlossaryItem("to_currency_code", toCurrencyCodeExample) - lazy val dobOfDependantsExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) + lazy val dobOfDependantsExample = ConnectorField("[2019-09-08, 2017-07-12]",NoDescriptionProvided) glossaryItems += makeGlossaryItem("dob_of_dependants", dobOfDependantsExample) lazy val settlementAccountsExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -2059,7 +2060,7 @@ object ExampleValue { lazy val createdByUserIdExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("created_by_user_id", createdByUserIdExample) - lazy val attributesExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) + lazy val attributesExample = ConnectorField("attribute value in form of (name, value)",NoDescriptionProvided) glossaryItems += makeGlossaryItem("attributes", attributesExample) lazy val revokedExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) @@ -2071,7 +2072,7 @@ object ExampleValue { lazy val currentCreditDocumentationExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) glossaryItems += makeGlossaryItem("current_credit_documentation", currentCreditDocumentationExample) - lazy val mobilePhoneNumberExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) + lazy val mobilePhoneNumberExample = ConnectorField("+49 30 901820",NoDescriptionProvided) glossaryItems += makeGlossaryItem("mobile_phone_number", mobilePhoneNumberExample) lazy val saturdayExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) 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 c2c1333b5..27c7c8f28 100644 --- a/obp-api/src/main/scala/code/api/util/NewStyle.scala +++ b/obp-api/src/main/scala/code/api/util/NewStyle.scala @@ -654,6 +654,9 @@ object NewStyle extends MdcLoggable{ Views.views.vend.systemViewFuture(viewId) map { unboxFullOrFail(_, callContext, s"$SystemViewNotFound. Current ViewId is $viewId") } + } + def systemViews(): Future[List[View]] = { + Views.views.vend.getSystemViews() } def grantAccessToCustomView(view : View, user: User, callContext: Option[CallContext]) : Future[View] = { view.isSystem match { @@ -2522,6 +2525,53 @@ object NewStyle extends MdcLoggable{ ) map { i => (unboxFullOrFail(i._1, callContext, CreateCustomerError), i._2) } + def createCustomerC2( + bankId: BankId, + legalName: String, + customerNumber: String, + mobileNumber: String, + email: String, + faceImage: + CustomerFaceImageTrait, + dateOfBirth: Date, + relationshipStatus: String, + dependents: Int, + dobOfDependents: List[Date], + highestEducationAttained: String, + employmentStatus: String, + kycStatus: Boolean, + lastOkDate: Date, + creditRating: Option[CreditRatingTrait], + creditLimit: Option[AmountOfMoneyTrait], + title: String, + branchId: String, + nameSuffix: String, + callContext: Option[CallContext]): OBPReturnType[Customer] = + Connector.connector.vend.createCustomerC2( + bankId: BankId, + legalName: String, + customerNumber: String, + mobileNumber: String, + email: String, + faceImage: + CustomerFaceImageTrait, + dateOfBirth: Date, + relationshipStatus: String, + dependents: Int, + dobOfDependents: List[Date], + highestEducationAttained: String, + employmentStatus: String, + kycStatus: Boolean, + lastOkDate: Date, + creditRating: Option[CreditRatingTrait], + creditLimit: Option[AmountOfMoneyTrait], + title: String, + branchId: String, + nameSuffix: String, + callContext: Option[CallContext] + ) map { + i => (unboxFullOrFail(i._1, callContext, CreateCustomerError), i._2) + } def updateCustomerScaData(customerId: String, mobileNumber: Option[String], diff --git a/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala b/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala index bf6b74580..ef6011af0 100644 --- a/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala +++ b/obp-api/src/main/scala/code/api/v1_4_0/JSONFactory1_4_0.scala @@ -366,7 +366,31 @@ object JSONFactory1_4_0 extends MdcLoggable{ * @return Bank.bank_id */ def getGlossaryItemTitle(parameter: String): String = { - glossaryItems.find(_.title.toLowerCase.contains(s"${parameter.toLowerCase}")).map(_.title).getOrElse("").replaceAll(" ","-") + def isUrlParameter(): Boolean = { + List( + "BANK_ID", + "ACCOUNT_ID", + "CUSTOMER_ID", + "TRANSACTION_ID", + "ATTRIBUTE_ID", + "VIEW_ID", + "USER_ID", + "PRODUCT_CODE", + "PRODUCT_ID", + "OPERATION_ID", + "ENDPOINT_TAG_ID", + ).exists(_ == parameter) + } + parameter match { + case _ if isUrlParameter() => + glossaryItems + .find(_.title.toLowerCase.contains(s"${parameter.toLowerCase}")) + .map(_.title).getOrElse("").replaceAll(" ","-") + case _ => + glossaryItems + .find(_.title.toLowerCase.equals(s"${parameter.toLowerCase}")) + .map(_.title).getOrElse("").replaceAll(" ","-") + } } /** @@ -420,31 +444,31 @@ object JSONFactory1_4_0 extends MdcLoggable{ * @param parameter BANK_ID * @return [BANK_ID](/glossary#Bank.bank_id):gh.29.uk */ - def prepareDescription(parameter: String, types: List[(String, Boolean)]): String = { + def prepareDescription(parameter: String, optionalTypeFields: List[(String, Boolean)]): String = { val glossaryItemTitle = getGlossaryItemTitle(parameter) val exampleFieldValue = getExampleFieldValue(parameter) def boldIfMandatory() = { - types.exists(i => i._1 == parameter && i._2 == false) match { + optionalTypeFields.exists(i => i._1 == parameter && i._2 == false) match { case true => - s"**$parameter**" + s"***$parameter**" case false => s"$parameter" } } - if(exampleFieldValue.contains(ExampleValue.NoExampleProvided)){ + if(glossaryItemTitle.contains("jsonstring")){ "" } else { - s""" - | - |* [${boldIfMandatory()}](/glossary#$glossaryItemTitle): $exampleFieldValue - | - |""".stripMargin - } + s""" + | + |* [${boldIfMandatory()}](/glossary#$glossaryItemTitle): $exampleFieldValue + | + |""".stripMargin + } } def prepareJsonFieldDescription(jsonBody: scala.Product, jsonType: String): String = { jsonBody.productIterator - val (jsonBodyJValue: json.JValue, types) = jsonBody match { + val (jsonBodyJValue: json.JValue, optionalTypeFields) = jsonBody match { case JvalueCaseClass(jValue) => val types = Nil (jValue, types) @@ -455,9 +479,14 @@ object JSONFactory1_4_0 extends MdcLoggable{ (decompose(jsonBody), types) } - val jsonBodyFields =JsonUtils.collectFieldNames(jsonBodyJValue).keySet.toList.sorted + // Group by is mandatory criteria and sort those 2 groups by name of the field + val jsonBodyFieldsOptional = JsonUtils.collectFieldNames(jsonBodyJValue).keySet.toList + .filter(x => optionalTypeFields.exists(i => i._1 == x && i._2 == true)).sorted + val jsonBodyFieldsMandatory = JsonUtils.collectFieldNames(jsonBodyJValue).keySet.toList + .filter(x => optionalTypeFields.exists(i => i._1 == x && i._2 == false)).sorted + val jsonBodyFields = jsonBodyFieldsMandatory ::: jsonBodyFieldsOptional - val jsonFieldsDescription = jsonBodyFields.map(i => prepareDescription(i, types)) + val jsonFieldsDescription = jsonBodyFields.map(i => prepareDescription(i, optionalTypeFields)) val jsonTitleType = if (jsonType.contains("request")) "\n\n\n**JSON request body fields:**\n\n" else "\n\n\n**JSON response body fields:**\n\n" diff --git a/obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala b/obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala index 37a3c27a7..d94cab176 100644 --- a/obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala +++ b/obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala @@ -148,7 +148,7 @@ trait APIMethods300 { | | You MUST use a leading _ (underscore) in the view name because other view names are reserved for OBP [system views](/index#group-View-System). | """, - SwaggerDefinitionsJSON.createViewJson, + SwaggerDefinitionsJSON.createViewJsonV300, viewJsonV300, List( UserNotLoggedIn, @@ -233,7 +233,7 @@ trait APIMethods300 { | |The json sent is the same as during view creation (above), with one difference: the 'name' field |of a view is not editable (it is only set when a view is created)""", - updateViewJSON, + updateViewJsonV300, viewJsonV300, List( InvalidJsonFormat, @@ -251,7 +251,7 @@ trait APIMethods300 { val res = for { (Full(u), callContext) <- authenticatedAccess(cc) - updateJson <- Future { tryo{json.extract[UpdateViewJSON]} } map { + updateJson <- Future { tryo{json.extract[UpdateViewJsonV300]} } map { val msg = s"$InvalidJsonFormat The Json body should be the $UpdateViewJSON " x => unboxFullOrFail(x, callContext, msg) } @@ -270,7 +270,7 @@ trait APIMethods300 { (account, callContext) <- NewStyle.function.getBankAccount(bankId, accountId, callContext) } yield { for { - updatedView <- account.updateView(u, viewId, updateJson) + updatedView <- account.updateView(u, viewId, updateJson.toUpdateViewJson) } yield { (JSONFactory300.createViewJSON(updatedView), HttpCode.`200`(callContext)) } diff --git a/obp-api/src/main/scala/code/api/v3_0_0/JSONFactory3.0.0.scala b/obp-api/src/main/scala/code/api/v3_0_0/JSONFactory3.0.0.scala index 4489abe34..330543c9f 100644 --- a/obp-api/src/main/scala/code/api/v3_0_0/JSONFactory3.0.0.scala +++ b/obp-api/src/main/scala/code/api/v3_0_0/JSONFactory3.0.0.scala @@ -71,7 +71,44 @@ import scala.util.Try //started - view relevant case classes - +case class CreateViewJsonV300( + name: String, + description: String, + metadata_view: String, + is_public: Boolean, + which_alias_to_use: String, + hide_metadata_if_alias_used: Boolean, + allowed_actions : List[String] +) { + def toCreateViewJson = CreateViewJson( + name = this.name, + description = this.description, + metadata_view = this.metadata_view, + is_public = this.is_public, + which_alias_to_use = this.which_alias_to_use, + hide_metadata_if_alias_used = this.hide_metadata_if_alias_used, + allowed_actions = this.allowed_actions + ) +} +case class UpdateViewJsonV300( + description: String, + metadata_view: String, + is_public: Boolean, + is_firehose: Option[Boolean] = None, + which_alias_to_use: String, + hide_metadata_if_alias_used: Boolean, + allowed_actions: List[String] +) { + def toUpdateViewJson = UpdateViewJSON( + description = this.description, + metadata_view = this.metadata_view, + is_public = this.is_public, + is_firehose = this.is_firehose, + which_alias_to_use = this.which_alias_to_use, + hide_metadata_if_alias_used = this.hide_metadata_if_alias_used, + allowed_actions = this.allowed_actions + ) +} case class ViewsJsonV300( views : List[ViewJsonV300] ) diff --git a/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala b/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala index 7a4673397..a7f17f7a7 100644 --- a/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala +++ b/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala @@ -3,6 +3,7 @@ package code.api.v3_1_0 import java.text.SimpleDateFormat import java.util.UUID import java.util.regex.Pattern + import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._ import code.api.ResourceDocs1_4_0.{MessageDocsSwaggerDefinitions, ResourceDocsAPIMethodsUtil, SwaggerDefinitionsJSON, SwaggerJSONFactory} import code.api.util.APIUtil.{getWebUIPropsPairs, _} @@ -16,7 +17,7 @@ import code.api.v1_2_1.{JSONFactory, RateLimiting} import code.api.v2_0_0.CreateMeetingJson import code.api.v2_1_0._ import code.api.v2_2_0.{CreateAccountJSONV220, JSONFactory220} -import code.api.v3_0_0.JSONFactory300 +import code.api.v3_0_0.{CreateViewJsonV300, JSONFactory300} import code.api.v3_0_0.JSONFactory300.createAdapterInfoJson import code.api.v3_1_0.JSONFactory310._ import code.bankconnectors.rest.RestConnector_vMar2019 @@ -3880,7 +3881,7 @@ trait APIMethods310 { | | Please note that system views cannot be public. In case you try to set it you will get the error $SystemViewCannotBePublicError | """, - SwaggerDefinitionsJSON.createSystemViewJson, + SwaggerDefinitionsJSON.createSystemViewJsonV300, viewJsonV300, List( UserNotLoggedIn, @@ -3900,12 +3901,12 @@ trait APIMethods310 { _ <- NewStyle.function.hasEntitlement("", user.userId, canCreateSystemView, callContext) failMsg = s"$InvalidJsonFormat The Json body should be the $CreateViewJson " createViewJson <- NewStyle.function.tryons(failMsg, 400, callContext) { - json.extract[CreateViewJson] + json.extract[CreateViewJsonV300] } _ <- Helper.booleanToFuture(SystemViewCannotBePublicError, failCode=400, cc=callContext) { createViewJson.is_public == false } - view <- NewStyle.function.createSystemView(createViewJson, callContext) + view <- NewStyle.function.createSystemView(createViewJson.toCreateViewJson, callContext) } yield { (JSONFactory310.createViewJSON(view), HttpCode.`201`(callContext)) } @@ -3929,7 +3930,7 @@ trait APIMethods310 { "user does not have owner access" ), List(apiTagSystemView, apiTagNewStyle), - Some(List(canCreateSystemView)) + Some(List(canDeleteSystemView)) ) lazy val deleteSystemView: OBPEndpoint = { 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 b4d4460b6..8d3ac5a7b 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 @@ -3969,7 +3969,7 @@ trait APIMethods400 { | - Outgoing account (name: Default outgoing settlement account, Account ID: OBP_DEFAULT_OUTGOING_ACCOUNT_ID, currency: EUR) | |""", - bankJson400, + postBankJson400, bankJson400, List( InvalidJsonFormat, diff --git a/obp-api/src/main/scala/code/api/v4_0_0/JSONFactory4.0.0.scala b/obp-api/src/main/scala/code/api/v4_0_0/JSONFactory4.0.0.scala index 1d918551b..d848a9545 100644 --- a/obp-api/src/main/scala/code/api/v4_0_0/JSONFactory4.0.0.scala +++ b/obp-api/src/main/scala/code/api/v4_0_0/JSONFactory4.0.0.scala @@ -106,6 +106,14 @@ case class BankJson400( bank_routings: List[BankRoutingJsonV121], attributes: Option[List[BankAttributeBankResponseJsonV400]] ) +case class PostBankJson400( + id: String, + short_name: String, + full_name: String, + logo: String, + website: String, + bank_routings: List[BankRoutingJsonV121] +) case class BanksJson400(banks: List[BankJson400]) @@ -236,11 +244,11 @@ case class FastFirehoseAccountJsonV400( bank_id: String, label: String, number: String, - owners: String, + owners: List[FastFirehoseOwners], product_code: String, balance: AmountOfMoneyJsonV121, - account_routings: String , - account_attributes: String + account_routings: List[FastFirehoseRoutings] , + account_attributes: List[FastFirehoseAttributes] ) case class FastFirehoseAccountsJsonV400( diff --git a/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala b/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala index 2dd674f7c..a843299b3 100644 --- a/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala +++ b/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala @@ -1,6 +1,9 @@ package code.api.v5_0_0 -import java.util.Date +import java.util.concurrent.ThreadLocalRandom + +import code.accountattribute.AccountAttributeX +import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._ import code.api.util.APIUtil._ import code.api.util.ApiRole._ @@ -14,20 +17,23 @@ import code.api.v3_0_0.JSONFactory300 import code.api.v3_1_0._ import code.api.v4_0_0.JSONFactory400.createCustomersMinimalJson import code.api.v4_0_0.{JSONFactory400, PutProductJsonV400} -import code.api.v5_0_0.JSONFactory500.createPhysicalCardJson +import code.api.v5_0_0.JSONFactory500.{createPhysicalCardJson, createViewJsonV500, createViewsJsonV500, createViewsIdsJsonV500} import code.bankconnectors.Connector import code.consent.{ConsentRequests, Consents} import code.entitlement.Entitlement +import code.metrics.APIMetrics +import code.model._ import code.model.dataAccess.BankAccountCreation import code.transactionrequests.TransactionRequests.TransactionRequestTypes.{apply => _} import code.util.Helper +import code.util.Helper.booleanToFuture import code.views.Views import com.github.dwickern.macros.NameOf.nameOf import com.openbankproject.commons.ExecutionContext.Implicits.global import com.openbankproject.commons.model.enums.StrongCustomerAuthentication -import com.openbankproject.commons.model.{AccountAttribute, AccountId, AccountRouting, BankAccount, BankId, BankIdAccountId, CardAction, CardAttributeCommons, CardCollectionInfo, CardPostedInfo, CardReplacementInfo, CardReplacementReason, CreditLimit, CreditRating, CustomerFaceImage, CustomerId, PinResetInfo, PinResetReason, ProductCode, TransactionRequestType, UserAuthContextUpdateStatus, View, ViewId} +import com.openbankproject.commons.model._ import com.openbankproject.commons.util.ApiVersion -import net.liftweb.common.{Box, Empty, Full} +import net.liftweb.common.{Empty, Full} import net.liftweb.http.Req import net.liftweb.http.rest.RestHelper import net.liftweb.json @@ -41,7 +47,6 @@ import code.util.Helper.booleanToFuture import scala.collection.immutable.{List, Nil} import scala.collection.mutable.ArrayBuffer -import scala.concurrent import scala.concurrent.Future import scala.util.Random @@ -763,6 +768,7 @@ trait APIMethods500 { UserNotLoggedIn, $BankNotFound, InvalidJsonFormat, + ConsentRequestIsInvalid, ConsentAllowedScaMethods, RolesAllowedInConsent, ViewsAllowedInConsent, @@ -788,7 +794,7 @@ trait APIMethods500 { )) map { i => unboxFullOrFail(i,callContext, ConsentRequestNotFound) } - _ <- Helper.booleanToFuture(ConsentRequestAlreadyUsed, cc=callContext){ + _ <- Helper.booleanToFuture(ConsentRequestIsInvalid, cc=callContext){ Consents.consentProvider.vend.getConsentByConsentRequestId(consentRequestId).isEmpty } _ <- Helper.booleanToFuture(ConsentAllowedScaMethods, cc=callContext){ @@ -988,9 +994,12 @@ trait APIMethods500 { _ <- Helper.booleanToFuture(failMsg = InvalidJsonContent + s" The field dependants(${postedData.dependants.getOrElse(0)}) not equal the length(${postedData.dob_of_dependants.getOrElse(Nil).length }) of dob_of_dependants array", 400, cc.callContext) { postedData.dependants.getOrElse(0) == postedData.dob_of_dependants.getOrElse(Nil).length } - (customer, callContext) <- NewStyle.function.createCustomer( + customerNumber = postedData.customer_number.getOrElse(Random.nextInt(Integer.MAX_VALUE).toString) + (_, callContext) <- NewStyle.function.checkCustomerNumberAvailable(bankId, customerNumber, cc.callContext) + (customer, callContext) <- NewStyle.function.createCustomerC2( bankId, postedData.legal_name, + customerNumber, postedData.mobile_phone_number, postedData.email.getOrElse(""), CustomerFaceImage( @@ -1010,7 +1019,7 @@ trait APIMethods500 { postedData.title.getOrElse(""), postedData.branch_id.getOrElse(""), postedData.name_suffix.getOrElse(""), - cc.callContext, + callContext, ) } yield { (JSONFactory310.createCustomerJson(customer), HttpCode.`201`(callContext)) @@ -1443,6 +1452,354 @@ trait APIMethods500 { } } + + staticResourceDocs += ResourceDoc( + getViewsForBankAccount, + implementedInApiVersion, + nameOf(getViewsForBankAccount), + "GET", + "/banks/BANK_ID/accounts/ACCOUNT_ID/views", + "Get Views for Account", + s"""#Views + | + | + |Views in Open Bank Project provide a mechanism for fine grained access control and delegation to Accounts and Transactions. Account holders use the 'owner' view by default. Delegated access is made through other views for example 'accountants', 'share-holders' or 'tagging-application'. Views can be created via the API and each view has a list of entitlements. + | + |Views on accounts and transactions filter the underlying data to redact certain fields for certain users. For instance the balance on an account may be hidden from the public. The way to know what is possible on a view is determined in the following JSON. + | + |**Data:** When a view moderates a set of data, some fields my contain the value `null` rather than the original value. This indicates either that the user is not allowed to see the original data or the field is empty. + | + |There is currently one exception to this rule; the 'holder' field in the JSON contains always a value which is either an alias or the real name - indicated by the 'is_alias' field. + | + |**Action:** When a user performs an action like trying to post a comment (with POST API call), if he is not allowed, the body response will contain an error message. + | + |**Metadata:** + |Transaction metadata (like images, tags, comments, etc.) will appears *ONLY* on the view where they have been created e.g. comments posted to the public view only appear on the public view. + | + |The other account metadata fields (like image_URL, more_info, etc.) are unique through all the views. Example, if a user edits the 'more_info' field in the 'team' view, then the view 'authorities' will show the new value (if it is allowed to do it). + | + |# All + |*Optional* + | + |Returns the list of the views created for account ACCOUNT_ID at BANK_ID. + | + |${authenticationRequiredMessage(true)} and the user needs to have access to the owner view.""", + emptyObjectJson, + viewsJsonV500, + List( + $UserNotLoggedIn, + $BankAccountNotFound, + UnknownError + ), + List(apiTagView, apiTagAccount, apiTagNewStyle)) + + lazy val getViewsForBankAccount : OBPEndpoint = { + //get the available views on an bank account + case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "views" :: Nil JsonGet req => { + cc => + val res = + for { + _ <- Helper.booleanToFuture(failMsg = UserNoOwnerView +"userId : " + cc.userId + ". account : " + accountId, cc=cc.callContext){ + cc.loggedInUser.hasOwnerViewAccess(BankIdAccountId(bankId, accountId)) + } + } yield { + for { + views <- Full(Views.views.vend.availableViewsForAccount(BankIdAccountId(bankId, accountId))) + } yield { + (createViewsJsonV500(views), HttpCode.`200`(cc.callContext)) + } + } + res map { fullBoxOrException(_) } map { unboxFull(_) } + } + } + + staticResourceDocs += ResourceDoc( + deleteSystemView, + implementedInApiVersion, + "deleteSystemView", + "DELETE", + "/system-views/VIEW_ID", + "Delete System View", + "Deletes the system view specified by VIEW_ID", + emptyObjectJson, + emptyObjectJson, + List( + UserNotLoggedIn, + BankAccountNotFound, + UnknownError, + "user does not have owner access" + ), + List(apiTagSystemView, apiTagNewStyle), + Some(List(canDeleteSystemView)) + ) + + lazy val deleteSystemView: OBPEndpoint = { + case "system-views" :: viewId :: Nil JsonDelete req => { + cc => + for { + _ <- NewStyle.function.systemView(ViewId(viewId), cc.callContext) + view <- NewStyle.function.deleteSystemView(ViewId(viewId), cc.callContext) + } yield { + (Full(view), HttpCode.`200`(cc.callContext)) + } + } + } + + staticResourceDocs += ResourceDoc( + getMetricsAtBank, + implementedInApiVersion, + nameOf(getMetricsAtBank), + "GET", + "/management/metrics/banks/BANK_ID", + "Get Metrics at Bank", + s"""Get the all metrics at the Bank specified by BANK_ID + | + |require CanReadMetrics role + | + |Filters Part 1.*filtering* (no wilde cards etc.) parameters to GET /management/metrics + | + |Should be able to filter on the following metrics fields + | + |eg: /management/metrics?from_date=$DateWithMsExampleString&to_date=$DateWithMsExampleString&limit=50&offset=2 + | + |1 from_date (defaults to one week before current date): eg:from_date=$DateWithMsExampleString + | + |2 to_date (defaults to current date) eg:to_date=$DateWithMsExampleString + | + |3 limit (for pagination: defaults to 50) eg:limit=200 + | + |4 offset (for pagination: zero index, defaults to 0) eg: offset=10 + | + |5 sort_by (defaults to date field) eg: sort_by=date + | possible values: + | "url", + | "date", + | "user_name", + | "app_name", + | "developer_email", + | "implemented_by_partial_function", + | "implemented_in_version", + | "consumer_id", + | "verb" + | + |6 direction (defaults to date desc) eg: direction=desc + | + |eg: /management/metrics?from_date=$DateWithMsExampleString&to_date=$DateWithMsExampleString&limit=10000&offset=0&anon=false&app_name=TeatApp&implemented_in_version=v2.1.0&verb=POST&user_id=c7b6cb47-cb96-4441-8801-35b57456753a&user_name=susan.uk.29@example.com&consumer_id=78 + | + |Other filters: + | + |7 consumer_id (if null ignore) + | + |8 user_id (if null ignore) + | + |9 anon (if null ignore) only support two value : true (return where user_id is null.) or false (return where user_id is not null.) + | + |10 url (if null ignore), note: can not contain '&'. + | + |11 app_name (if null ignore) + | + |12 implemented_by_partial_function (if null ignore), + | + |13 implemented_in_version (if null ignore) + | + |14 verb (if null ignore) + | + |15 correlation_id (if null ignore) + | + |16 duration (if null ignore) non digit chars will be silently omitted + | + """.stripMargin, + emptyObjectJson, + metricsJson, + List( + $UserNotLoggedIn, + UserHasMissingRoles, + UnknownError + ), + List(apiTagMetric, apiTagApi, apiTagNewStyle), + Some(List(canGetMetricsAtOneBank))) + + lazy val getMetricsAtBank : OBPEndpoint = { + case "management" :: "metrics" :: "banks" :: bankId :: Nil JsonGet _ => { + cc => { + for { + httpParams <- NewStyle.function.extractHttpParamsFromUrl(cc.url) + (obpQueryParams, callContext) <- createQueriesByHttpParamsFuture(httpParams, cc.callContext) + metrics <- Future(APIMetrics.apiMetrics.vend.getAllMetrics(obpQueryParams ::: List(OBPBankId(bankId)))) + } yield { + (JSONFactory210.createMetricsJson(metrics), HttpCode.`200`(callContext)) + } + } + } + } + + staticResourceDocs += ResourceDoc( + getSystemView, + implementedInApiVersion, + "getSystemView", + "GET", + "/system-views/VIEW_ID", + "Get System View", + s"""Get System View + | + |${authenticationRequiredMessage(true)} + | + """.stripMargin, + emptyObjectJson, + viewJsonV500, + List( + $UserNotLoggedIn, + $BankNotFound, + UnknownError + ), + List(apiTagSystemView, apiTagNewStyle), + Some(List(canGetSystemView)) + ) + + lazy val getSystemView: OBPEndpoint = { + case "system-views" :: viewId :: Nil JsonGet _ => { + cc => + for { + view <- NewStyle.function.systemView(ViewId(viewId), cc.callContext) + } yield { + (createViewJsonV500(view), HttpCode.`200`(cc.callContext)) + } + } + } + + staticResourceDocs += ResourceDoc( + getSystemViewsIds, + implementedInApiVersion, + nameOf(getSystemViewsIds), + "GET", + "/system-views-ids", + "Get Ids of System Views", + s"""Get Ids of System Views + | + |${authenticationRequiredMessage(true)} + | + """.stripMargin, + emptyObjectJson, + viewIdsJsonV500, + List( + $UserNotLoggedIn, + $BankNotFound, + UnknownError + ), + List(apiTagSystemView, apiTagNewStyle), + Some(List(canGetSystemView)) + ) + + lazy val getSystemViewsIds: OBPEndpoint = { + case "system-views-ids" :: Nil JsonGet _ => { + cc => + for { + views <- NewStyle.function.systemViews() + } yield { + (createViewsIdsJsonV500(views), HttpCode.`200`(cc.callContext)) + } + } + } + + + staticResourceDocs += ResourceDoc( + createSystemView, + implementedInApiVersion, + nameOf(createSystemView), + "POST", + "/system-views", + "Create System View", + s"""Create a system view + | + | ${authenticationRequiredMessage(true)} and the user needs to have access to the $canCreateSystemView entitlement. + | The 'alias' field in the JSON can take one of two values: + | + | * _public_: to use the public alias if there is one specified for the other account. + | * _private_: to use the public alias if there is one specified for the other account. + | + | * _''(empty string)_: to use no alias; the view shows the real name of the other account. + | + | The 'hide_metadata_if_alias_used' field in the JSON can take boolean values. If it is set to `true` and there is an alias on the other account then the other accounts' metadata (like more_info, url, image_url, open_corporates_url, etc.) will be hidden. Otherwise the metadata will be shown. + | + | The 'allowed_actions' field is a list containing the name of the actions allowed on this view, all the actions contained will be set to `true` on the view creation, the rest will be set to `false`. + | + | Please note that system views cannot be public. In case you try to set it you will get the error $SystemViewCannotBePublicError + | """, + createSystemViewJsonV500, + viewJsonV500, + List( + $UserNotLoggedIn, + InvalidJsonFormat, + UnknownError + ), + List(apiTagSystemView, apiTagNewStyle), + Some(List(canCreateSystemView)) + ) + + lazy val createSystemView : OBPEndpoint = { + //creates a system view + case "system-views" :: Nil JsonPost json -> _ => { + cc => + for { + createViewJson <- NewStyle.function.tryons(failMsg = s"$InvalidJsonFormat The Json body should be the $CreateViewJson ", 400, cc.callContext) { + json.extract[CreateViewJsonV500] + } + _ <- Helper.booleanToFuture(SystemViewCannotBePublicError, failCode=400, cc=cc.callContext) { + createViewJson.is_public == false + } + view <- NewStyle.function.createSystemView(createViewJson.toCreateViewJson, cc.callContext) + } yield { + (createViewJsonV500(view), HttpCode.`201`(cc.callContext)) + } + } + } + + + staticResourceDocs += ResourceDoc( + updateSystemView, + implementedInApiVersion, + nameOf(updateSystemView), + "PUT", + "/system-views/VIEW_ID", + "Update System View", + s"""Update an existing view on a bank account + | + |${authenticationRequiredMessage(true)} and the user needs to have access to the owner view. + | + |The json sent is the same as during view creation (above), with one difference: the 'name' field + |of a view is not editable (it is only set when a view is created)""", + updateSystemViewJson500, + viewJsonV500, + List( + InvalidJsonFormat, + $UserNotLoggedIn, + BankAccountNotFound, + UnknownError + ), + List(apiTagSystemView, apiTagNewStyle), + Some(List(canUpdateSystemView)) + ) + + lazy val updateSystemView : OBPEndpoint = { + //updates a view on a bank account + case "system-views" :: viewId :: Nil JsonPut json -> _ => { + cc => + for { + updateJson <- Future { tryo{json.extract[UpdateViewJsonV500]} } map { + val msg = s"$InvalidJsonFormat The Json body should be the $UpdateViewJSON " + x => unboxFullOrFail(x, cc.callContext, msg) + } + _ <- Helper.booleanToFuture(SystemViewCannotBePublicError, failCode=400, cc=cc.callContext) { + updateJson.is_public == false + } + _ <- NewStyle.function.systemView(ViewId(viewId), cc.callContext) + updatedView <- NewStyle.function.updateSystemView(ViewId(viewId), updateJson.toUpdateViewJson, cc.callContext) + } yield { + (JSONFactory310.createViewJSON(updatedView), HttpCode.`200`(cc.callContext)) + } + } + } + staticResourceDocs += ResourceDoc( createCustomerAccountLink, implementedInApiVersion, diff --git a/obp-api/src/main/scala/code/api/v5_0_0/JSONFactory5.0.0.scala b/obp-api/src/main/scala/code/api/v5_0_0/JSONFactory5.0.0.scala index 8279312df..c47f679b6 100644 --- a/obp-api/src/main/scala/code/api/v5_0_0/JSONFactory5.0.0.scala +++ b/obp-api/src/main/scala/code/api/v5_0_0/JSONFactory5.0.0.scala @@ -28,23 +28,22 @@ package code.api.v5_0_0 import java.lang import java.util.Date -import code.api.util.APIUtil.{stringOptionOrNull, stringOrNull} + +import code.api.util.APIUtil.{nullToString, stringOptionOrNull, stringOrNull} import code.api.v1_2_1.BankRoutingJsonV121 -import code.api.v1_4_0.JSONFactory1_4_0.{CustomerFaceImageJson, MetaJsonV140} import code.api.v1_3_0.JSONFactory1_3_0.{cardActionsToString, createAccountJson, createPinResetJson, createReplacementJson} import code.api.v1_3_0.{PinResetJSON, ReplacementJSON} -import code.api.v1_4_0.JSONFactory1_4_0.CustomerFaceImageJson +import code.api.v1_4_0.JSONFactory1_4_0.{CustomerFaceImageJson, MetaJsonV140} import code.api.v2_1_0.CustomerCreditRatingJSON import code.api.v3_0_0.{AdapterInfoJsonV300, CustomerAttributeResponseJsonV300, JSONFactory300} import code.api.v3_1_0.{AccountAttributeResponseJson, AccountBasicV310, CustomerWithAttributesJsonV310, PhysicalCardWithAttributesJsonV310, PostConsentEntitlementJsonV310} import code.api.v4_0_0.BankAttributeBankResponseJsonV400 import code.bankattribute.BankAttribute import code.customeraccountlinks.CustomerAccountLinkTrait -import com.openbankproject.commons.model.{AccountAttribute, AccountRouting, AccountRoutingJsonV121, AmountOfMoneyJsonV121, Bank, BankAccount, CardAttribute, Customer, CustomerAttribute, InboundAdapterInfoInternal, InboundStatusMessage, PhysicalCardTrait, User, UserAuthContext, UserAuthContextUpdate, View, ViewBasic} +import com.openbankproject.commons.model.{AccountAttribute, AccountRouting, AccountRoutingJsonV121, AmountOfMoneyJsonV121, Bank, BankAccount, CardAttribute, CreateViewJson, Customer, CustomerAttribute, InboundAdapterInfoInternal, InboundStatusMessage, PhysicalCardTrait, UpdateViewJSON, User, UserAuthContext, UserAuthContextUpdate, View, ViewBasic} import net.liftweb.json.JsonAST.JValue import net.liftweb.util.Helpers -import scala.collection.immutable import scala.collection.immutable.List case class PostBankJson500( @@ -53,8 +52,7 @@ case class PostBankJson500( full_name: Option[String], logo: Option[String], website: Option[String], - bank_routings: Option[List[BankRoutingJsonV121]], - attributes: Option[List[BankAttributeBankResponseJsonV400]] + bank_routings: Option[List[BankRoutingJsonV121]] ) case class BankJson500( @@ -78,6 +76,7 @@ case class CreateAccountRequestJsonV500( case class PostCustomerJsonV500( legal_name: String, + customer_number: Option[String] = None, mobile_phone_number: String, email: Option[String] = None, face_image: Option[CustomerFaceImageJson] = None, @@ -381,6 +380,147 @@ case class AdapterInfoJsonV500( ) +case class CreateViewJsonV500( + name: String, + description: String, + metadata_view: String, + is_public: Boolean, + which_alias_to_use: String, + hide_metadata_if_alias_used: Boolean, + allowed_actions : List[String], + can_grant_access_to_views : Option[List[String]] = None, + can_revoke_access_to_views : Option[List[String]] = None + ) { + def toCreateViewJson = CreateViewJson( + name = this.name, + description = this.description, + metadata_view = this.metadata_view, + is_public = this.is_public, + which_alias_to_use = this.which_alias_to_use, + hide_metadata_if_alias_used = this.hide_metadata_if_alias_used, + allowed_actions = this.allowed_actions, + can_grant_access_to_views = this.can_grant_access_to_views, + can_revoke_access_to_views = this.can_revoke_access_to_views + ) +} +case class UpdateViewJsonV500( + description: String, + metadata_view: String, + is_public: Boolean, + is_firehose: Option[Boolean] = None, + which_alias_to_use: String, + hide_metadata_if_alias_used: Boolean, + allowed_actions: List[String], + can_grant_access_to_views : Option[List[String]] = None, + can_revoke_access_to_views : Option[List[String]] = None + ) { + def toUpdateViewJson = UpdateViewJSON( + description = this.description, + metadata_view = this.metadata_view, + is_public = this.is_public, + is_firehose = this.is_firehose, + which_alias_to_use = this.which_alias_to_use, + hide_metadata_if_alias_used = this.hide_metadata_if_alias_used, + allowed_actions = this.allowed_actions, + can_grant_access_to_views = this.can_grant_access_to_views, + can_revoke_access_to_views = this.can_revoke_access_to_views + ) +} +case class ViewsJsonV500(views : List[ViewJsonV500]) + +case class ViewIdJsonV500(id: String) +case class ViewsIdsJsonV500(views : List[ViewIdJsonV500]) + +case class ViewJsonV500( + val id: String, + val short_name: String, + val description: String, + val metadata_view: String, + val is_public: Boolean, + val is_system: Boolean, + val is_firehose: Option[Boolean] = None, + val alias: String, + val hide_metadata_if_alias_used: Boolean, + val can_grant_access_to_views : List[String], + val can_revoke_access_to_views : List[String], + val can_add_comment : Boolean, + val can_add_corporate_location : Boolean, + val can_add_image : Boolean, + val can_add_image_url: Boolean, + val can_add_more_info: Boolean, + val can_add_open_corporates_url : Boolean, + val can_add_physical_location : Boolean, + val can_add_private_alias : Boolean, + val can_add_public_alias : Boolean, + val can_add_tag : Boolean, + val can_add_url: Boolean, + val can_add_where_tag : Boolean, + val can_delete_comment: Boolean, + val can_add_counterparty : Boolean, + val can_delete_corporate_location : Boolean, + val can_delete_image : Boolean, + val can_delete_physical_location : Boolean, + val can_delete_tag : Boolean, + val can_delete_where_tag : Boolean, + val can_edit_owner_comment: Boolean, + val can_see_bank_account_balance: Boolean, + val can_query_available_funds: Boolean, + val can_see_bank_account_bank_name: Boolean, + val can_see_bank_account_currency: Boolean, + val can_see_bank_account_iban: Boolean, + val can_see_bank_account_label: Boolean, + val can_see_bank_account_national_identifier: Boolean, + val can_see_bank_account_number: Boolean, + val can_see_bank_account_owners: Boolean, + val can_see_bank_account_swift_bic: Boolean, + val can_see_bank_account_type: Boolean, + val can_see_comments: Boolean, + val can_see_corporate_location: Boolean, + val can_see_image_url: Boolean, + val can_see_images: Boolean, + val can_see_more_info: Boolean, + val can_see_open_corporates_url: Boolean, + val can_see_other_account_bank_name: Boolean, + val can_see_other_account_iban: Boolean, + val can_see_other_account_kind: Boolean, + val can_see_other_account_metadata: Boolean, + val can_see_other_account_national_identifier: Boolean, + val can_see_other_account_number: Boolean, + val can_see_other_account_swift_bic: Boolean, + val can_see_owner_comment: Boolean, + val can_see_physical_location: Boolean, + val can_see_private_alias: Boolean, + val can_see_public_alias: Boolean, + val can_see_tags: Boolean, + val can_see_transaction_amount: Boolean, + val can_see_transaction_balance: Boolean, + val can_see_transaction_currency: Boolean, + val can_see_transaction_description: Boolean, + val can_see_transaction_finish_date: Boolean, + val can_see_transaction_metadata: Boolean, + val can_see_transaction_other_bank_account: Boolean, + val can_see_transaction_start_date: Boolean, + val can_see_transaction_this_bank_account: Boolean, + val can_see_transaction_type: Boolean, + val can_see_url: Boolean, + val can_see_where_tag: Boolean, + //V300 new + val can_see_bank_routing_scheme: Boolean, + val can_see_bank_routing_address: Boolean, + val can_see_bank_account_routing_scheme: Boolean, + val can_see_bank_account_routing_address: Boolean, + val can_see_other_bank_routing_scheme: Boolean, + val can_see_other_bank_routing_address: Boolean, + val can_see_other_account_routing_scheme: Boolean, + val can_see_other_account_routing_address: Boolean, + val can_add_transaction_request_to_own_account: Boolean, //added following two for payments + val can_add_transaction_request_to_any_account: Boolean, + val can_see_bank_account_credit_limit: Boolean, + val can_create_direct_debit: Boolean, + val can_create_standing_order: Boolean + ) + + object JSONFactory500 { def createUserAuthContextJson(userAuthContext: UserAuthContext): UserAuthContextJsonV500 = { @@ -579,7 +719,7 @@ object JSONFactory500 { enabled = card.enabled, cancelled = card.cancelled, on_hot_list = card.onHotList, - technology = stringOrNull(card.technology), + technology = nullToString(card.technology), networks = card.networks, allows = card.allows.map(cardActionsToString).toList, account = createAccountJson(card.account, user), @@ -604,7 +744,116 @@ object JSONFactory500 { def createCustomerAccountLinksJon(customerAccountLinks: List[CustomerAccountLinkTrait]): CustomerAccountLinksJson = { CustomerAccountLinksJson(customerAccountLinks.map(createCustomerAccountLinkJson)) - } + } + + + + def createViewJsonV500(view : View) : ViewJsonV500 = { + val alias = + if(view.usePublicAliasIfOneExists) + "public" + else if(view.usePrivateAliasIfOneExists) + "private" + else + "" + + ViewJsonV500( + id = view.viewId.value, + short_name = stringOrNull(view.name), + description = stringOrNull(view.description), + metadata_view= view.metadataView, + is_public = view.isPublic, + is_system = view.isSystem, + alias = alias, + hide_metadata_if_alias_used = view.hideOtherAccountMetadataIfAlias, + can_add_comment = view.canAddComment, + can_add_corporate_location = view.canAddCorporateLocation, + can_add_image = view.canAddImage, + can_add_image_url = view.canAddImageURL, + can_add_more_info = view.canAddMoreInfo, + can_add_open_corporates_url = view.canAddOpenCorporatesUrl, + can_add_physical_location = view.canAddPhysicalLocation, + can_add_private_alias = view.canAddPrivateAlias, + can_add_public_alias = view.canAddPublicAlias, + can_add_tag = view.canAddTag, + can_add_url = view.canAddURL, + can_add_where_tag = view.canAddWhereTag, + can_delete_comment = view.canDeleteComment, + can_add_counterparty = view.canAddCounterparty, + can_delete_corporate_location = view.canDeleteCorporateLocation, + can_delete_image = view.canDeleteImage, + can_delete_physical_location = view.canDeletePhysicalLocation, + can_delete_tag = view.canDeleteTag, + can_delete_where_tag = view.canDeleteWhereTag, + can_edit_owner_comment = view.canEditOwnerComment, + can_see_bank_account_balance = view.canSeeBankAccountBalance, + can_query_available_funds = view.canQueryAvailableFunds, + can_see_bank_account_bank_name = view.canSeeBankAccountBankName, + can_see_bank_account_currency = view.canSeeBankAccountCurrency, + can_see_bank_account_iban = view.canSeeBankAccountIban, + can_see_bank_account_label = view.canSeeBankAccountLabel, + can_see_bank_account_national_identifier = view.canSeeBankAccountNationalIdentifier, + can_see_bank_account_number = view.canSeeBankAccountNumber, + can_see_bank_account_owners = view.canSeeBankAccountOwners, + can_see_bank_account_swift_bic = view.canSeeBankAccountSwift_bic, + can_see_bank_account_type = view.canSeeBankAccountType, + can_see_comments = view.canSeeComments, + can_see_corporate_location = view.canSeeCorporateLocation, + can_see_image_url = view.canSeeImageUrl, + can_see_images = view.canSeeImages, + can_see_more_info = view.canSeeMoreInfo, + can_see_open_corporates_url = view.canSeeOpenCorporatesUrl, + can_see_other_account_bank_name = view.canSeeOtherAccountBankName, + can_see_other_account_iban = view.canSeeOtherAccountIBAN, + can_see_other_account_kind = view.canSeeOtherAccountKind, + can_see_other_account_metadata = view.canSeeOtherAccountMetadata, + can_see_other_account_national_identifier = view.canSeeOtherAccountNationalIdentifier, + can_see_other_account_number = view.canSeeOtherAccountNumber, + can_see_other_account_swift_bic = view.canSeeOtherAccountSWIFT_BIC, + can_see_owner_comment = view.canSeeOwnerComment, + can_see_physical_location = view.canSeePhysicalLocation, + can_see_private_alias = view.canSeePrivateAlias, + can_see_public_alias = view.canSeePublicAlias, + can_see_tags = view.canSeeTags, + can_see_transaction_amount = view.canSeeTransactionAmount, + can_see_transaction_balance = view.canSeeTransactionBalance, + can_see_transaction_currency = view.canSeeTransactionCurrency, + can_see_transaction_description = view.canSeeTransactionDescription, + can_see_transaction_finish_date = view.canSeeTransactionFinishDate, + can_see_transaction_metadata = view.canSeeTransactionMetadata, + can_see_transaction_other_bank_account = view.canSeeTransactionOtherBankAccount, + can_see_transaction_start_date = view.canSeeTransactionStartDate, + can_see_transaction_this_bank_account = view.canSeeTransactionThisBankAccount, + can_see_transaction_type = view.canSeeTransactionType, + can_see_url = view.canSeeUrl, + can_see_where_tag = view.canSeeWhereTag, + //V300 new + can_see_bank_routing_scheme = view.canSeeBankRoutingScheme, + can_see_bank_routing_address = view.canSeeBankRoutingAddress, + can_see_bank_account_routing_scheme = view.canSeeBankAccountRoutingScheme, + can_see_bank_account_routing_address = view.canSeeBankAccountRoutingAddress, + can_see_other_bank_routing_scheme = view.canSeeOtherBankRoutingScheme, + can_see_other_bank_routing_address = view.canSeeOtherBankRoutingAddress, + can_see_other_account_routing_scheme = view.canSeeOtherAccountRoutingScheme, + can_see_other_account_routing_address= view.canSeeOtherAccountRoutingAddress, + can_add_transaction_request_to_own_account = view.canAddTransactionRequestToOwnAccount, //added following two for payments + can_add_transaction_request_to_any_account = view.canAddTransactionRequestToAnyAccount, + can_see_bank_account_credit_limit = view.canSeeBankAccountCreditLimit, + can_create_direct_debit = view.canCreateDirectDebit, + can_create_standing_order = view.canCreateStandingOrder, + // Version 5.0.0 + can_grant_access_to_views = view.canGrantAccessToViews.getOrElse(Nil), + can_revoke_access_to_views = view.canRevokeAccessToViews.getOrElse(Nil), + ) + } + def createViewsJsonV500(views : List[View]) : ViewsJsonV500 = { + ViewsJsonV500(views.map(createViewJsonV500)) + } + + + def createViewsIdsJsonV500(views : List[View]) : ViewsIdsJsonV500 = { + ViewsIdsJsonV500(views.map(i => ViewIdJsonV500(i.viewId.value))) + } def createAdapterInfoJson(inboundAdapterInfoInternal: InboundAdapterInfoInternal, startTime: Long): AdapterInfoJsonV500 = { AdapterInfoJsonV500( diff --git a/obp-api/src/main/scala/code/bankconnectors/Connector.scala b/obp-api/src/main/scala/code/bankconnectors/Connector.scala index 9e44598b7..7812c087f 100644 --- a/obp-api/src/main/scala/code/bankconnectors/Connector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/Connector.scala @@ -1945,7 +1945,31 @@ trait Connector extends MdcLoggable { nameSuffix: String, callContext: Option[CallContext], ): OBPReturnType[Box[Customer]] = Future{(Failure(setUnimplementedError), callContext)} - + + def createCustomerC2( + bankId: BankId, + legalName: String, + customerNumber: String, + mobileNumber: String, + email: String, + faceImage: + CustomerFaceImageTrait, + dateOfBirth: Date, + relationshipStatus: String, + dependents: Int, + dobOfDependents: List[Date], + highestEducationAttained: String, + employmentStatus: String, + kycStatus: Boolean, + lastOkDate: Date, + creditRating: Option[CreditRatingTrait], + creditLimit: Option[AmountOfMoneyTrait], + title: String, + branchId: String, + nameSuffix: String, + callContext: Option[CallContext], + ): OBPReturnType[Box[Customer]] = Future{(Failure(setUnimplementedError), callContext)} + def updateCustomerScaData(customerId: String, mobileNumber: Option[String], email: Option[String], diff --git a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala index c0b64a85a..f301ae241 100644 --- a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala @@ -873,6 +873,60 @@ object LocalMappedConnector extends Connector with MdcLoggable { } private def findFirehoseAccounts(bankId: BankId, ordering: SQLSyntax, limit: Int, offset: Int)(implicit session: DBSession = AutoSession) = { + def parseOwners(owners: String): List[FastFirehoseOwners] = { + if(!owners.isEmpty) { + transformString(owners).map { + i => + FastFirehoseOwners( + user_id = i("user_id").mkString(""), + provider = i("provider").mkString(""), + user_name = i("user_name").mkString("") + ) + } + } else { + List() + } + } + def parseRoutings(owners: String): List[FastFirehoseRoutings] = { + if(!owners.isEmpty) { + transformString(owners).map { + i => + FastFirehoseRoutings( + bank_id = i("bank_id").mkString(""), + account_id = i("account_id").mkString("") + ) + } + } else { + List() + } + } + def parseAttributes(owners: String): List[FastFirehoseAttributes] = { + if(!owners.isEmpty) { + transformString(owners).map { + i => + FastFirehoseAttributes( + `type` = i("type").mkString(""), + code = i("code").mkString(""), + value = i("value").mkString("") + ) + } + } else { + List() + } + } + def transformString(owners: String): List[Map[String, List[String]]] = { + val splitToRows: List[String] = owners.split("::").toList + val keyValuePairs: List[List[(String, String)]] = splitToRows.map { i=> + i.split(",").toList.map { + x => + val keyValue: Array[String] = x.split(":") + if(keyValue.size == 2) (keyValue(0), keyValue(1)) else (keyValue(0), "") + } + } + val maps: List[Map[String, List[String]]] = keyValuePairs.map(_.groupBy(_._1).map { case (k,v) => (k,v.map(_._2))}) + maps + } + val sqlResult = sql""" |select | mappedbankaccount.theaccountid as account_id, @@ -887,7 +941,7 @@ object LocalMappedConnector extends Connector with MdcLoggable { | ||resourceuser.provider_ | ||',user_name:' | ||resourceuser.name_, - | ',') as owners + | '::') as owners | from resourceuser | where | resourceuser.id = mapperaccountholders.user_c @@ -901,7 +955,7 @@ object LocalMappedConnector extends Connector with MdcLoggable { | ||bankaccountrouting.bankid | ||',account_id:' | ||bankaccountrouting.accountid, - | ',' + | '::' | ) as account_routings | from bankaccountrouting | where @@ -915,7 +969,7 @@ object LocalMappedConnector extends Connector with MdcLoggable { | ||mappedaccountattribute.mcode | ||',value:' | ||mappedaccountattribute.mvalue, - | ',') as account_attributes + | '::') as account_attributes | from mappedaccountattribute | where | mappedaccountattribute.maccountid = mappedbankaccount.theaccountid @@ -930,28 +984,31 @@ object LocalMappedConnector extends Connector with MdcLoggable { | | |""".stripMargin - .map( + .map { rs => // Map result to case class + val owners = parseOwners(rs.stringOpt(5).map(_.toString).getOrElse("")) + val routings = parseRoutings(rs.stringOpt(9).map(_.toString).getOrElse("")) + val attributes = parseAttributes(rs.stringOpt(10).map(_.toString).getOrElse("")) FastFirehoseAccount( id = rs.stringOpt(1).map(_.toString).getOrElse(null), - bankId= rs.stringOpt(2).map(_.toString).getOrElse(null), - label= rs.stringOpt(3).map(_.toString).getOrElse(null), + bankId = rs.stringOpt(2).map(_.toString).getOrElse(null), + label = rs.stringOpt(3).map(_.toString).getOrElse(null), number = rs.stringOpt(4).map(_.toString).getOrElse(null), - owners = rs.stringOpt(5).map(_.toString).getOrElse(null), - productCode = rs.stringOpt(6).map(_.toString).getOrElse(null), + owners = owners, + productCode = rs.stringOpt(6).map(_.toString).getOrElse(null), balance = AmountOfMoney( currency = rs.stringOpt(7).map(_.toString).getOrElse(null), - amount = rs.bigIntOpt(8).map( a => + amount = rs.bigIntOpt(8).map(a => Helper.smallestCurrencyUnitToBigDecimal( a.longValue(), rs.stringOpt(7).getOrElse("EUR") ).toString() ).getOrElse(null) ), - accountRoutings = rs.stringOpt(9).map(_.toString).getOrElse(null), - accountAttributes = rs.stringOpt(10).map(_.toString).getOrElse(null) + accountRoutings = routings, + accountAttributes = attributes ) - ).list().apply() + }.list().apply() sqlResult } @@ -3434,6 +3491,52 @@ object LocalMappedConnector extends Connector with MdcLoggable { ), callContext) } + override def createCustomerC2( + bankId: BankId, + legalName: String, + customerNumber: String, + mobileNumber: String, + email: String, + faceImage: + CustomerFaceImageTrait, + dateOfBirth: Date, + relationshipStatus: String, + dependents: Int, + dobOfDependents: List[Date], + highestEducationAttained: String, + employmentStatus: String, + kycStatus: Boolean, + lastOkDate: Date, + creditRating: Option[CreditRatingTrait], + creditLimit: Option[AmountOfMoneyTrait], + title: String, + branchId: String, + nameSuffix: String, + callContext: Option[CallContext] + ): OBPReturnType[Box[Customer]] = Future { + (CustomerX.customerProvider.vend.addCustomer( + bankId, + customerNumber, + legalName, + mobileNumber, + email, + faceImage, + dateOfBirth, + relationshipStatus, + dependents, + dobOfDependents, + highestEducationAttained, + employmentStatus, + kycStatus, + lastOkDate, + creditRating, + creditLimit, + title, + branchId, + nameSuffix + ), callContext) + } + override def updateCustomerScaData(customerId: String, mobileNumber: Option[String], email: Option[String], diff --git a/obp-api/src/main/scala/code/metrics/MappedMetrics.scala b/obp-api/src/main/scala/code/metrics/MappedMetrics.scala index e644ad523..e1654d828 100644 --- a/obp-api/src/main/scala/code/metrics/MappedMetrics.scala +++ b/obp-api/src/main/scala/code/metrics/MappedMetrics.scala @@ -125,6 +125,7 @@ object MappedMetrics extends APIMetrics with MdcLoggable{ .flatMap(consumerIdToPrimaryKey) .map(By(MappedMetric.consumerId, _) ) + val bankId = queryParams.collect { case OBPBankId(value) => Like(MappedMetric.url, s"%banks/$value%") }.headOption val userId = queryParams.collect { case OBPUserId(value) => By(MappedMetric.userId, value) }.headOption val url = queryParams.collect { case OBPUrl(value) => By(MappedMetric.url, value) }.headOption val appName = queryParams.collect { case OBPAppName(value) => By(MappedMetric.appName, value) }.headOption @@ -149,6 +150,7 @@ object MappedMetrics extends APIMetrics with MdcLoggable{ ordering, consumerId.toSeq, userId.toSeq, + bankId.toSeq, url.toSeq, appName.toSeq, implementedInVersion.toSeq, diff --git a/obp-api/src/main/scala/code/model/dataAccess/ResourceUser.scala b/obp-api/src/main/scala/code/model/dataAccess/ResourceUser.scala index fc53e3a85..5e3145c46 100644 --- a/obp-api/src/main/scala/code/model/dataAccess/ResourceUser.scala +++ b/obp-api/src/main/scala/code/model/dataAccess/ResourceUser.scala @@ -85,6 +85,9 @@ class ResourceUser extends LongKeyedMapper[ResourceUser] with User with ManyToMa override def defaultValue = false } object LastMarketingAgreementSignedDate extends MappedDate(this) + object LastUsedLocale extends MappedString(this, 10) { + override def defaultValue = null + } def emailAddress = { val e = email.get @@ -114,6 +117,7 @@ class ResourceUser extends LongKeyedMapper[ResourceUser] with User with ManyToMa override def createdByUserInvitationId = if(CreatedByUserInvitationId.get == null) None else if (CreatedByUserInvitationId.get.isEmpty) None else Some(CreatedByUserInvitationId.get) //null --> None override def isDeleted: Option[Boolean] = if(IsDeleted.jdbcFriendly(IsDeleted.calcFieldName) == null) None else Some(IsDeleted.get) // null --> None override def lastMarketingAgreementSignedDate: Option[Date] = if(IsDeleted.jdbcFriendly(LastMarketingAgreementSignedDate.calcFieldName) == null) None else Some(LastMarketingAgreementSignedDate.get) // null --> None + override def lastUsedLocale: Option[String] = if(LastUsedLocale.get == null) None else Some(LastUsedLocale.get) // null --> None } object ResourceUser extends ResourceUser with LongKeyedMetaMapper[ResourceUser]{ diff --git a/obp-api/src/main/scala/code/remotedata/RemotedataViews.scala b/obp-api/src/main/scala/code/remotedata/RemotedataViews.scala index 0273b4900..6b904b47d 100644 --- a/obp-api/src/main/scala/code/remotedata/RemotedataViews.scala +++ b/obp-api/src/main/scala/code/remotedata/RemotedataViews.scala @@ -69,6 +69,10 @@ object RemotedataViews extends ObpActorInit with Views { def systemViewFuture(viewId : ViewId) : Future[Box[View]] = (actor ? cc.systemViewFuture(viewId)).mapTo[Box[View]] + def getSystemViews() : Future[List[View]] = + (actor ? cc.getSystemViews()).mapTo[List[View]] + + def createView(bankAccountId: BankIdAccountId, view: CreateViewJson): Box[View] = getValueFromFuture( (actor ? cc.createView(bankAccountId, view)).mapTo[Box[View]] ) diff --git a/obp-api/src/main/scala/code/remotedata/RemotedataViewsActor.scala b/obp-api/src/main/scala/code/remotedata/RemotedataViewsActor.scala index 4b49b6941..3923b2a33 100644 --- a/obp-api/src/main/scala/code/remotedata/RemotedataViewsActor.scala +++ b/obp-api/src/main/scala/code/remotedata/RemotedataViewsActor.scala @@ -64,6 +64,10 @@ class RemotedataViewsActor extends Actor with ObpActorHelper with MdcLoggable { case cc.systemView(viewId: ViewId) => logger.debug("view(" + viewId + ")") sender ! (mapper.systemView(viewId)) + + case cc.getSystemViews() => + logger.debug("getSystemViews()") + sender ! (mapper.getSystemViews()) case cc.customViewFuture(viewId: ViewId, bankAccountId: BankIdAccountId) => logger.debug("customViewFuture(" + viewId +", "+ bankAccountId + ")") diff --git a/obp-api/src/main/scala/code/views/MapperViews.scala b/obp-api/src/main/scala/code/views/MapperViews.scala index 0f606b142..8f0895c01 100644 --- a/obp-api/src/main/scala/code/views/MapperViews.scala +++ b/obp-api/src/main/scala/code/views/MapperViews.scala @@ -340,6 +340,15 @@ object MapperViews extends Views with MdcLoggable { def systemView(viewId : ViewId) : Box[View] = { ViewDefinition.findSystemView(viewId.value) } + def getSystemViews() : Future[List[View]] = { + Future { + ViewDefinition.findAll( + NullRef(ViewDefinition.bank_id), + NullRef(ViewDefinition.account_id), + By(ViewDefinition.isSystem_, true) + ) + } + } def systemViewFuture(viewId : ViewId) : Future[Box[View]] = { Future { systemView(viewId) diff --git a/obp-api/src/main/scala/code/views/Views.scala b/obp-api/src/main/scala/code/views/Views.scala index 1793b371e..6776966d1 100644 --- a/obp-api/src/main/scala/code/views/Views.scala +++ b/obp-api/src/main/scala/code/views/Views.scala @@ -55,6 +55,7 @@ trait Views { def systemView(viewId : ViewId) : Box[View] def customViewFuture(viewId : ViewId, bankAccountId: BankIdAccountId) : Future[Box[View]] def systemViewFuture(viewId : ViewId) : Future[Box[View]] + def getSystemViews(): Future[List[View]] //always return a view id String, not error here. def getMetadataViewId(bankAccountId: BankIdAccountId, viewId : ViewId) = Views.views.vend.customView(viewId, bankAccountId).map(_.metadataView).openOr(viewId.value) @@ -154,6 +155,7 @@ class RemotedataViewsCaseClasses { def apply(viewId: ViewId, bankAccountId: BankIdAccountId): Box[View] = this (viewId, bankAccountId) } case class systemView(viewId : ViewId) + case class getSystemViews() case class customViewFuture(viewId : ViewId, bankAccountId: BankIdAccountId) case class systemViewFuture(viewId : ViewId) case class getOrCreateAccountView(account: BankIdAccountId, viewName: String) diff --git a/obp-api/src/main/scala/code/views/system/ViewDefinition.scala b/obp-api/src/main/scala/code/views/system/ViewDefinition.scala index 87f50fc2d..21e62737e 100644 --- a/obp-api/src/main/scala/code/views/system/ViewDefinition.scala +++ b/obp-api/src/main/scala/code/views/system/ViewDefinition.scala @@ -49,6 +49,12 @@ class ViewDefinition extends View with LongKeyedMapper[ViewDefinition] with Many object hideOtherAccountMetadataIfAlias_ extends MappedBoolean(this){ override def defaultValue = false } + object canGrantAccessToViews_ extends MappedText(this){ + override def defaultValue = "" + } + object canRevokeAccessToViews_ extends MappedText(this){ + override def defaultValue = "" + } object canSeeTransactionThisBankAccount_ extends MappedBoolean(this){ override def defaultValue = false } @@ -303,6 +309,9 @@ class ViewDefinition extends View with LongKeyedMapper[ViewDefinition] with Many isPublic_(viewData.is_public) isFirehose_(viewData.is_firehose.getOrElse(false)) metadataView_(viewData.metadata_view) + + canGrantAccessToViews_(viewData.can_grant_access_to_views.getOrElse(Nil).mkString(",")) + canRevokeAccessToViews_(viewData.can_revoke_access_to_views.getOrElse(Nil).mkString(",")) val actions = viewData.allowed_actions @@ -408,6 +417,19 @@ class ViewDefinition extends View with LongKeyedMapper[ViewDefinition] with Many def usePublicAliasIfOneExists: Boolean = usePublicAliasIfOneExists_.get def hideOtherAccountMetadataIfAlias: Boolean = hideOtherAccountMetadataIfAlias_.get + override def canGrantAccessToViews : Option[List[String]] = { + canGrantAccessToViews_.get == null || canGrantAccessToViews_.get.isEmpty() match { + case true => None + case _ => Some(canGrantAccessToViews_.get.split(",").toList.map(_.trim)) + } + } + override def canRevokeAccessToViews : Option[List[String]] = { + canRevokeAccessToViews_.get == null || canRevokeAccessToViews_.get.isEmpty() match { + case true => None + case _ => Some(canRevokeAccessToViews_.get.split(",").toList.map(_.trim)) + } + } + //reading access //transaction fields diff --git a/obp-api/src/main/scala/code/webuiprops/MappedWebUiPropsProvider.scala b/obp-api/src/main/scala/code/webuiprops/MappedWebUiPropsProvider.scala index 61ed5b330..992dbdc3e 100644 --- a/obp-api/src/main/scala/code/webuiprops/MappedWebUiPropsProvider.scala +++ b/obp-api/src/main/scala/code/webuiprops/MappedWebUiPropsProvider.scala @@ -23,7 +23,7 @@ object MappedWebUiPropsProvider extends WebUiPropsProvider { override def createOrUpdate(webUiProps: WebUiPropsT): Box[WebUiPropsT] = { WebUiProps.find(By(WebUiProps.Name, webUiProps.name)) .or(Full(WebUiProps.create)) - .map(_.Name(webUiProps.name).Value(webUiProps.value).saveMe()) + .map(_.Name(webUiProps.name.trim()).Value(webUiProps.value).saveMe()) } override def delete(webUiPropsId: String):Box[Boolean] = WebUiProps.find(By(WebUiProps.WebUiPropsId, webUiPropsId)) match { @@ -81,6 +81,6 @@ class WebUiProps extends WebUiPropsT with LongKeyedMapper[WebUiProps] with IdPK } object WebUiProps extends WebUiProps with LongKeyedMetaMapper[WebUiProps] { - override def dbIndexes = UniqueIndex(WebUiPropsId) :: super.dbIndexes + override def dbIndexes = UniqueIndex(WebUiPropsId) :: UniqueIndex(Name) :: super.dbIndexes } diff --git a/obp-api/src/test/resources/frozen_type_meta_data b/obp-api/src/test/resources/frozen_type_meta_data index 16644ec28..e88e048f6 100644 Binary files a/obp-api/src/test/resources/frozen_type_meta_data and b/obp-api/src/test/resources/frozen_type_meta_data differ diff --git a/obp-api/src/test/scala/code/api/v2_2_0/API2_2_0Test.scala b/obp-api/src/test/scala/code/api/v2_2_0/API2_2_0Test.scala index f70decf91..25a7fe76c 100644 --- a/obp-api/src/test/scala/code/api/v2_2_0/API2_2_0Test.scala +++ b/obp-api/src/test/scala/code/api/v2_2_0/API2_2_0Test.scala @@ -29,7 +29,7 @@ package code.api.v2_2_0 import code.api.Constant._ import _root_.net.liftweb.json.Serialization.write import com.openbankproject.commons.model.ErrorMessage -import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.createViewJson +import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.createViewJsonV300 import code.api.util.APIUtil.OAuth._ import code.api.v1_2._ import code.api.v1_2_1.UpdateViewJsonV121 @@ -67,7 +67,7 @@ class API2_2_0Test extends V220ServerSetup with DefaultUsers { /********************* API test methods ********************/ //System view, owner - val postBodySystemViewJson = createViewJson.copy(name=SYSTEM_OWNER_VIEW_ID) + val postBodySystemViewJson = createViewJsonV300.copy(name=SYSTEM_OWNER_VIEW_ID).toCreateViewJson def randomBank : String = { val banksJson = getBanksInfo.body.extract[BanksJSON] @@ -90,7 +90,7 @@ class API2_2_0Test extends V220ServerSetup with DefaultUsers { viewsIdsToGrant } - def randomView(isPublic: Boolean, alias: String) = createViewJson + def randomView(isPublic: Boolean, alias: String) = createViewJsonV300.toCreateViewJson def getBanksInfo : APIResponse = { diff --git a/obp-api/src/test/scala/code/api/v3_0_0/ViewsTests.scala b/obp-api/src/test/scala/code/api/v3_0_0/ViewsTests.scala index 2a11ce5ca..1d6df40d9 100644 --- a/obp-api/src/test/scala/code/api/v3_0_0/ViewsTests.scala +++ b/obp-api/src/test/scala/code/api/v3_0_0/ViewsTests.scala @@ -53,9 +53,9 @@ class ViewsTests extends V300ServerSetup { //Custom view, name starts from `_` - val postBodyViewJson = createViewJson + val postBodyViewJson = createViewJsonV300.toCreateViewJson //System view, owner - val postBodySystemViewJson = createViewJson.copy(name=SYSTEM_OWNER_VIEW_ID).copy(metadata_view = SYSTEM_OWNER_VIEW_ID) + val postBodySystemViewJson = createViewJsonV300.copy(name=SYSTEM_OWNER_VIEW_ID).copy(metadata_view = SYSTEM_OWNER_VIEW_ID).toCreateViewJson def getAccountViews(bankId : String, accountId : String, consumerAndToken: Option[(Consumer, Token)]): APIResponse = { val request = v3_0Request / "banks" / bankId / "accounts" / accountId / "views" <@(consumerAndToken) diff --git a/obp-api/src/test/scala/code/api/v3_1_0/SystemViewsTests.scala b/obp-api/src/test/scala/code/api/v3_1_0/SystemViewsTests.scala index 14315bc7d..3f5a853f0 100644 --- a/obp-api/src/test/scala/code/api/v3_1_0/SystemViewsTests.scala +++ b/obp-api/src/test/scala/code/api/v3_1_0/SystemViewsTests.scala @@ -72,7 +72,7 @@ class SystemViewsTests extends V310ServerSetup { // Custom view, name starts from `_` // System view, owner val randomSystemViewId = APIUtil.generateUUID() - val postBodySystemViewJson = createSystemViewJson.copy(name=randomSystemViewId).copy(metadata_view = randomSystemViewId) + val postBodySystemViewJson = createSystemViewJsonV300.copy(name=randomSystemViewId).copy(metadata_view = randomSystemViewId).toCreateViewJson val systemViewId = MapperViews.getNewViewPermalink(postBodySystemViewJson.name) def getSystemView(viewId : String, consumerAndToken: Option[(Consumer, Token)]): APIResponse = { diff --git a/obp-api/src/test/scala/code/api/v4_0_0/AccountAccessTest.scala b/obp-api/src/test/scala/code/api/v4_0_0/AccountAccessTest.scala index 232c3eac5..324b7dc2f 100644 --- a/obp-api/src/test/scala/code/api/v4_0_0/AccountAccessTest.scala +++ b/obp-api/src/test/scala/code/api/v4_0_0/AccountAccessTest.scala @@ -2,7 +2,7 @@ package code.api.v4_0_0 import com.openbankproject.commons.model.ErrorMessage import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON -import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.createViewJson +import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.createViewJsonV300 import code.api.util.APIUtil.OAuth._ import code.api.util.ApiRole import com.openbankproject.commons.util.ApiVersion @@ -37,7 +37,7 @@ class AccountAccessTest extends V400ServerSetup { lazy val bankAccount = randomPrivateAccountViaEndpoint(bankId) lazy val ownerView = randomOwnerViewPermalinkViaEndpoint(bankId, bankAccount) lazy val postAccountAccessJson = PostAccountAccessJsonV400(resourceUser2.userId, PostViewJsonV400("_test_view", false)) - lazy val postBodyViewJson = createViewJson + lazy val postBodyViewJson = createViewJsonV300.toCreateViewJson def createAnAccount(bankId: String, user: Option[(Consumer,Token)]): CreateAccountResponseJsonV310 = { val addAccountJson = SwaggerDefinitionsJSON.createAccountRequestJsonV310.copy(user_id = resourceUser1.userId, balance = AmountOfMoneyJsonV121("EUR","0")) diff --git a/obp-api/src/test/scala/code/api/v4_0_0/DeleteAccountCascadeTest.scala b/obp-api/src/test/scala/code/api/v4_0_0/DeleteAccountCascadeTest.scala index 78fc1d02c..bf77ecf97 100644 --- a/obp-api/src/test/scala/code/api/v4_0_0/DeleteAccountCascadeTest.scala +++ b/obp-api/src/test/scala/code/api/v4_0_0/DeleteAccountCascadeTest.scala @@ -1,7 +1,7 @@ package code.api.v4_0_0 import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON -import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.createViewJson +import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.createViewJsonV300 import code.api.util.APIUtil.OAuth._ import code.api.util.ApiRole import code.api.util.ApiRole.CanDeleteAccountCascade @@ -74,7 +74,7 @@ class DeleteAccountCascadeTest extends V400ServerSetup { //it is an asynchronous process, need some time to be done. TimeUnit.SECONDS.sleep(3) - val postBodyView = createViewJson.copy(name = "_cascade_delete", metadata_view = "_cascade_delete", is_public = false) + val postBodyView = createViewJsonV300.copy(name = "_cascade_delete", metadata_view = "_cascade_delete", is_public = false).toCreateViewJson createViewViaEndpoint(bankId, account.account_id, postBodyView, user1) createAccountAttributeViaEndpoint( diff --git a/obp-api/src/test/scala/code/api/v4_0_0/DeleteBankCascadeTest.scala b/obp-api/src/test/scala/code/api/v4_0_0/DeleteBankCascadeTest.scala index 3a555758d..42bd389da 100644 --- a/obp-api/src/test/scala/code/api/v4_0_0/DeleteBankCascadeTest.scala +++ b/obp-api/src/test/scala/code/api/v4_0_0/DeleteBankCascadeTest.scala @@ -1,7 +1,7 @@ package code.api.v4_0_0 import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON -import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.createViewJson +import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.createViewJsonV300 import code.api.util.APIUtil.OAuth._ import code.api.util.{APIUtil, ApiRole} import code.api.util.ApiRole.{CanDeleteAccountCascade, CanDeleteBankCascade} @@ -76,7 +76,7 @@ class DeleteBankCascadeTest extends V400ServerSetup { val account = response400.body.extract[CreateAccountResponseJsonV310] account.account_id should not be empty - val postBodyView = createViewJson.copy(name = "_cascade_delete", metadata_view = "_cascade_delete", is_public = false) + val postBodyView = createViewJsonV300.copy(name = "_cascade_delete", metadata_view = "_cascade_delete", is_public = false).toCreateViewJson createViewViaEndpoint(bankId, account.account_id, postBodyView, user1) createAccountAttributeViaEndpoint( diff --git a/obp-api/src/test/scala/code/api/v4_0_0/V400ServerSetup.scala b/obp-api/src/test/scala/code/api/v4_0_0/V400ServerSetup.scala index 63484a769..8a5be8e5f 100644 --- a/obp-api/src/test/scala/code/api/v4_0_0/V400ServerSetup.scala +++ b/obp-api/src/test/scala/code/api/v4_0_0/V400ServerSetup.scala @@ -4,7 +4,7 @@ import java.time.LocalDateTime import java.time.format.DateTimeFormatter import code.api.Constant._ import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON -import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.createViewJson +import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.createViewJsonV300 import code.api.util.APIUtil.OAuth.{Consumer, Token, _} import code.api.util.ApiRole.{CanCreateAccountAttributeAtOneBank, CanCreateCustomer, CanCreateProduct, _} import code.api.util.{APIUtil, ApiRole} @@ -359,7 +359,7 @@ trait V400ServerSetup extends ServerSetupWithTestData with DefaultUsers { // Create to account val toAccount = createAccountViaEndpoint(bank.bankId.value, addAccountJson2, user1) // Create a custom view - val customViewJson = createViewJson.copy(name = "_cascade_delete", metadata_view = "_cascade_delete", is_public = false) + val customViewJson = createViewJsonV300.copy(name = "_cascade_delete", metadata_view = "_cascade_delete", is_public = false).toCreateViewJson val customView = createViewViaEndpoint(bank.bankId.value, fromAccount.account_id, customViewJson, user1) // Grant access to the view grantUserAccessToViewViaEndpoint( diff --git a/obp-api/src/test/scala/code/api/v5_0_0/MetricsTest.scala b/obp-api/src/test/scala/code/api/v5_0_0/MetricsTest.scala new file mode 100644 index 000000000..25c2bcdf3 --- /dev/null +++ b/obp-api/src/test/scala/code/api/v5_0_0/MetricsTest.scala @@ -0,0 +1,96 @@ +/** +Open Bank Project - API +Copyright (C) 2011-2019, TESOBE GmbH. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + +Email: contact@tesobe.com +TESOBE GmbH. +Osloer Strasse 16/17 +Berlin 13359, Germany + +This product includes software developed at +TESOBE (http://www.tesobe.com/) + + */ +package code.api.v5_0_0 + +import code.api.util.APIUtil.OAuth._ +import code.api.util.ApiRole.CanGetMetricsAtOneBank +import code.api.util.ErrorMessages.{UserHasMissingRoles, UserNotLoggedIn} +import code.api.v2_1_0.MetricsJson +import code.api.v5_0_0.APIMethods500.Implementations5_0_0 +import code.entitlement.Entitlement +import code.setup.APIResponse +import com.github.dwickern.macros.NameOf.nameOf +import com.openbankproject.commons.model.ErrorMessage +import com.openbankproject.commons.util.ApiVersion +import org.scalatest.Tag + +class MetricsTest extends V500ServerSetup { + override def beforeAll(): Unit = { + super.beforeAll() + } + + override def afterAll(): Unit = { + super.afterAll() + } + + /** + * Test tags + * Example: To run tests with tag "getPermissions": + * mvn test -D tagsToInclude + * + * This is made possible by the scalatest maven plugin + */ + object VersionOfApi extends Tag(ApiVersion.v5_0_0.toString) + object ApiEndpoint1 extends Tag(nameOf(Implementations5_0_0.getMetricsAtBank)) + + lazy val bankId = testBankId1.value + + def getMetrics(consumerAndToken: Option[(Consumer, Token)], bankId: String): APIResponse = { + val request = v5_0_0_Request / "management" / "metrics" / "banks" / bankId <@(consumerAndToken) + makeGetRequest(request) + } + + feature(s"test $ApiEndpoint1 version $VersionOfApi - Unauthorized access") { + scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { + When(s"We make a request $ApiEndpoint1") + val response400 = getMetrics(None, bankId) + Then("We should get a 401") + response400.code should equal(401) + response400.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) + } + } + feature(s"test $ApiEndpoint1 version $VersionOfApi - Authorized access") { + scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { + When(s"We make a request $ApiEndpoint1") + val response400 = getMetrics(user1, bankId) + Then("We should get a 403") + response400.code should equal(403) + response400.body.extract[ErrorMessage].message contains (UserHasMissingRoles + CanGetMetricsAtOneBank) should be (true) + } + } + feature(s"test $ApiEndpoint1 version $VersionOfApi - Authorized access with proper Role") { + scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { + When(s"We make a request $ApiEndpoint1") + Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, CanGetMetricsAtOneBank.toString) + val response400 = getMetrics(user1, bankId) + Then("We should get a 200") + response400.code should equal(200) + response400.body.extract[MetricsJson] + } + } + +} diff --git a/obp-api/src/test/scala/code/api/v5_0_0/SystemViewsTests.scala b/obp-api/src/test/scala/code/api/v5_0_0/SystemViewsTests.scala new file mode 100644 index 000000000..4397ffa3f --- /dev/null +++ b/obp-api/src/test/scala/code/api/v5_0_0/SystemViewsTests.scala @@ -0,0 +1,314 @@ +/** +Open Bank Project - API +Copyright (C) 2011-2019, TESOBE GmbH. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + +Email: contact@tesobe.com +TESOBE GmbH. +Osloer Strasse 16/17 +Berlin 13359, Germany + +This product includes software developed at +TESOBE (http://www.tesobe.com/) + + */ +package code.api.v5_0_0 + +import _root_.net.liftweb.json.Serialization.write +import code.api.Constant._ +import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._ +import code.api.util.APIUtil +import code.api.util.APIUtil.OAuth._ +import code.api.util.ApiRole.{CanCreateSystemView, CanDeleteSystemView, CanGetSystemView, CanUpdateSystemView} +import code.api.util.ErrorMessages.{UserHasMissingRoles, UserNotLoggedIn} +import code.api.v5_0_0.APIMethods500.Implementations5_0_0 +import code.entitlement.Entitlement +import code.setup.APIResponse +import code.views.MapperViews +import code.views.system.AccountAccess +import com.github.dwickern.macros.NameOf.nameOf +import com.openbankproject.commons.model.{CreateViewJson, ErrorMessage, UpdateViewJSON} +import com.openbankproject.commons.util.ApiVersion +import net.liftweb.mapper.By +import org.scalatest.Tag + +import scala.collection.immutable.List + +class SystemViewsTests extends V500ServerSetup { + override def beforeAll(): Unit = { + super.beforeAll() + } + + override def afterAll(): Unit = { + super.afterAll() + } + + /** + * Test tags + * Example: To run tests with tag "getPermissions": + * mvn test -D tagsToInclude + * + * This is made possible by the scalatest maven plugin + */ + object VersionOfApi extends Tag(ApiVersion.v5_0_0.toString) + object ApiEndpoint1 extends Tag(nameOf(Implementations5_0_0.getSystemView)) + object ApiEndpoint2 extends Tag(nameOf(Implementations5_0_0.createSystemView)) + object ApiEndpoint3 extends Tag(nameOf(Implementations5_0_0.updateSystemView)) + object ApiEndpoint4 extends Tag(nameOf(Implementations5_0_0.deleteSystemView)) + object ApiEndpoint5 extends Tag(nameOf(Implementations5_0_0.getSystemViewsIds)) + + // Custom view, name starts from `_` + // System view, owner + val randomSystemViewId = APIUtil.generateUUID() + val postBodySystemViewJson = createSystemViewJsonV500 + .copy(name=randomSystemViewId) + .copy(metadata_view = randomSystemViewId).toCreateViewJson + val systemViewId = MapperViews.getNewViewPermalink(postBodySystemViewJson.name) + + def getSystemView(viewId : String, consumerAndToken: Option[(Consumer, Token)]): APIResponse = { + val request = v5_0_0_Request / "system-views" / viewId <@(consumerAndToken) + makeGetRequest(request) + } + def getSystemViewsIds(consumerAndToken: Option[(Consumer, Token)]): APIResponse = { + val request = v5_0_0_Request / "system-views-ids" <@(consumerAndToken) + makeGetRequest(request) + } + def postSystemView(view: CreateViewJson, consumerAndToken: Option[(Consumer, Token)]): APIResponse = { + val request = (v5_0_0_Request / "system-views").POST <@(consumerAndToken) + makePostRequest(request, write(view)) + } + def putSystemView(viewId : String, view: UpdateViewJSON, consumerAndToken: Option[(Consumer, Token)]): APIResponse = { + val request = (v5_0_0_Request / "system-views" / viewId).PUT <@(consumerAndToken) + makePutRequest(request, write(view)) + } + def deleteSystemView(viewId : String, consumerAndToken: Option[(Consumer, Token)]): APIResponse = { + val request = (v5_0_0_Request / "system-views" / viewId).DELETE <@(consumerAndToken) + makeDeleteRequest(request) + } + def createSystemView(viewId: String): Boolean = { + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanCreateSystemView.toString) + val postBody = postBodySystemViewJson.copy(name=viewId).copy(metadata_view = viewId) + val response400 = postSystemView(postBody, user1) + response400.code == 201 + } + + + + feature(s"test $ApiEndpoint2 version $VersionOfApi - Unauthorized access") { + scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { + When(s"We make a request $ApiEndpoint2") + val response400 = postSystemView(postBodySystemViewJson, None) + Then("We should get a 401") + response400.code should equal(401) + response400.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) + } + } + feature(s"test $ApiEndpoint2 version $VersionOfApi - Authorized access") { + scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { + When(s"We make a request $ApiEndpoint2") + val response400 = postSystemView(postBodySystemViewJson, user1) + Then("We should get a 403") + response400.code should equal(403) + response400.body.extract[ErrorMessage].message should equal(UserHasMissingRoles + CanCreateSystemView) + } + } + feature(s"test $ApiEndpoint2 version $VersionOfApi - Authorized access with proper Role") { + scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { + When(s"We make a request $ApiEndpoint2") + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanCreateSystemView.toString) + val response400 = postSystemView(postBodySystemViewJson, user1) + Then("We should get a 201") + response400.code should equal(201) + response400.body.extract[ViewJsonV500] + } + } + + + feature(s"test $ApiEndpoint1 version $VersionOfApi - Unauthorized access") { + scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { + When(s"We make a request $ApiEndpoint1") + val response400 = getSystemView("", None) + Then("We should get a 401") + response400.code should equal(401) + response400.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) + } + } + feature(s"test $ApiEndpoint1 version $VersionOfApi - Authorized access") { + scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { + When(s"We make a request $ApiEndpoint1") + val response400 = getSystemView("", user1) + Then("We should get a 403") + response400.code should equal(403) + response400.body.extract[ErrorMessage].message should equal(UserHasMissingRoles + CanGetSystemView) + } + } + feature(s"test $ApiEndpoint1 version $VersionOfApi - Authorized access with proper Role") { + scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { + val viewId = APIUtil.generateUUID() + createSystemView(viewId) + When(s"We make a request $ApiEndpoint1") + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanGetSystemView.toString) + val response400 = getSystemView(viewId, user1) + Then("We should get a 200") + response400.code should equal(200) + response400.body.extract[ViewJsonV500] + } + } + + + feature(s"test $ApiEndpoint3 version $VersionOfApi - Unauthorized access") { + scenario("We will call the endpoint without user credentials", ApiEndpoint3, VersionOfApi) { + When(s"We make a request $ApiEndpoint3") + val response400 = getSystemView("", None) + Then("We should get a 401") + response400.code should equal(401) + response400.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) + } + } + feature(s"test $ApiEndpoint3 version $VersionOfApi - Authorized access") { + scenario("We will call the endpoint without user credentials", ApiEndpoint3, VersionOfApi) { + When(s"We make a request $ApiEndpoint3") + val response400 = getSystemView("", user1) + Then("We should get a 403") + response400.code should equal(403) + response400.body.extract[ErrorMessage].message should equal(UserHasMissingRoles + CanGetSystemView) + } + } + feature(s"test $ApiEndpoint3 version $VersionOfApi - Authorized access with proper Role") { + scenario("we will update a view on a bank account", ApiEndpoint3, VersionOfApi) { + val updatedViewDescription = "aloha" + val updatedAliasToUse = "public" + val allowedActions = List("can_see_images", "can_delete_comment") + + def viewUpdateJson(originalView : ViewJsonV500) = { + //it's not perfect, assumes too much about originalView (i.e. randomView(true, "")) + UpdateViewJSON( + description = updatedViewDescription, + metadata_view = originalView.metadata_view, + is_public = originalView.is_public, + is_firehose = Some(true), + which_alias_to_use = updatedAliasToUse, + hide_metadata_if_alias_used = !originalView.hide_metadata_if_alias_used, + allowed_actions = allowedActions, + can_grant_access_to_views = Some(originalView.can_grant_access_to_views), + can_revoke_access_to_views = Some(originalView.can_revoke_access_to_views) + ) + } + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanCreateSystemView.toString) + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanUpdateSystemView.toString) + + Given("A view exists") + val creationReply = postSystemView(postBodySystemViewJson, user1) + creationReply.code should equal (201) + val createdView : ViewJsonV500 = creationReply.body.extract[ViewJsonV500] + createdView.id should not startWith("_") + createdView.can_see_images should equal(true) + createdView.can_delete_comment should equal(true) + createdView.can_delete_physical_location should equal(true) + createdView.can_edit_owner_comment should equal(true) + createdView.description should not equal(updatedViewDescription) + createdView.hide_metadata_if_alias_used should equal(false) + + When("We use a valid access token and valid put json") + val reply = putSystemView(createdView.id, viewUpdateJson(createdView), user1) + Then("We should get back the updated view") + reply.code should equal (200) + val updatedView = reply.body.extract[ViewJsonV500] + updatedView.can_see_images should equal(true) + updatedView.can_delete_comment should equal(true) + updatedView.can_delete_physical_location should equal(false) + updatedView.can_edit_owner_comment should equal(false) + updatedView.description should equal(updatedViewDescription) + updatedView.hide_metadata_if_alias_used should equal(true) + updatedView.is_firehose should equal(Some(true)) + } + } + + + feature(s"test $ApiEndpoint4 version $VersionOfApi - Unauthorized access") { + scenario("We will call the endpoint without user credentials", ApiEndpoint4, VersionOfApi) { + When(s"We make a request $ApiEndpoint4") + val response400 = deleteSystemView("", None) + Then("We should get a 401") + response400.code should equal(401) + response400.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) + } + } + feature(s"test $ApiEndpoint4 version $VersionOfApi - Authorized access") { + scenario("We will call the endpoint without user credentials", ApiEndpoint4, VersionOfApi) { + When(s"We make a request $ApiEndpoint4") + val response400 = deleteSystemView("", user1) + Then("We should get a 403") + response400.code should equal(403) + response400.body.extract[ErrorMessage].message should equal(UserHasMissingRoles + CanDeleteSystemView) + } + } + feature(s"test $ApiEndpoint4 version $VersionOfApi - Authorized access with proper Role") { + scenario("We will call the endpoint without user credentials", ApiEndpoint4, VersionOfApi) { + val viewId = APIUtil.generateUUID() + createSystemView(viewId) + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanDeleteSystemView.toString) + When(s"We make a request $ApiEndpoint4") + val response400 = deleteSystemView(viewId, user1) + Then("We should get a 200") + response400.code should equal(200) + } + } + feature(s"test $ApiEndpoint4 version $VersionOfApi - Authorized access with proper Role in order to delete owner view") { + scenario("We will call the endpoint without user credentials", ApiEndpoint4, VersionOfApi) { + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanDeleteSystemView.toString) + When(s"We make a request $ApiEndpoint4") + AccountAccess.findAll( + By(AccountAccess.view_id, SYSTEM_OWNER_VIEW_ID), + By(AccountAccess.user_fk, resourceUser1.id.get) + ).forall(_.delete_!) // Remove all rows assigned to the system owner view in order to delete it + val response400 = deleteSystemView(SYSTEM_OWNER_VIEW_ID, user1) + Then("We should get a 200") + response400.code should equal(200) + } + } + + + feature(s"test $ApiEndpoint5 version $VersionOfApi - Unauthorized access") { + scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { + When(s"We make a request $ApiEndpoint5") + val response400 = getSystemViewsIds(None) + Then("We should get a 401") + response400.code should equal(401) + response400.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) + } + } + feature(s"test $ApiEndpoint5 version $VersionOfApi - Authorized access") { + scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { + When(s"We make a request $ApiEndpoint2") + val response400 = getSystemViewsIds(user1) + Then("We should get a 403") + response400.code should equal(403) + response400.body.extract[ErrorMessage].message should equal(UserHasMissingRoles + CanGetSystemView) + } + } + feature(s"test $ApiEndpoint5 version $VersionOfApi - Authorized access with proper Role") { + scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) { + When(s"We make a request $ApiEndpoint2") + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanGetSystemView.toString) + val response400 = getSystemViewsIds(user1) + Then("We should get a 200") + response400.code should equal(200) + response400.body.extract[ViewsIdsJsonV500] + } + } + + +} diff --git a/obp-api/src/test/scala/code/api/v5_0_0/V500ServerSetup.scala b/obp-api/src/test/scala/code/api/v5_0_0/V500ServerSetup.scala index 7caba12fb..5b770b3b9 100644 --- a/obp-api/src/test/scala/code/api/v5_0_0/V500ServerSetup.scala +++ b/obp-api/src/test/scala/code/api/v5_0_0/V500ServerSetup.scala @@ -12,6 +12,7 @@ import code.setup.{APIResponse, DefaultUsers, ServerSetupWithTestData} import com.openbankproject.commons.util.ApiShortVersions import dispatch.Req import code.api.util.APIUtil.OAuth._ +import code.api.v2_0_0.BasicAccountsJSON import net.liftweb.json.Serialization.write import scala.util.Random.nextInt @@ -33,6 +34,16 @@ trait V500ServerSetup extends ServerSetupWithTestData with DefaultUsers { bank.id } + def getPrivateAccounts(bankId : String, consumerAndToken: Option[(Consumer, Token)]) : APIResponse = { + val request = v5_0_0_Request / "banks" / bankId / "accounts" / "private" <@(consumerAndToken) //TODO, how can we know which endpoint it called? Although it is V300, but this endpoint called V200-privateAccountsAtOneBank + makeGetRequest(request) + } + def randomPrivateAccountId(bankId : String) : String = { + val accountsJson = getPrivateAccounts(bankId, user1).body.extract[BasicAccountsJSON].accounts //TODO, how to make this map automatically. + val randomPosition = nextInt(accountsJson.size) + accountsJson(randomPosition).id + } + // This will call create customer ,then return the customerId def createAndGetCustomerIdViaEndpoint(bankId:String, consumerAndToken: Option[(Consumer, Token)]) = { val postCustomerJson = SwaggerDefinitionsJSON.postCustomerJsonV310 diff --git a/obp-api/src/test/scala/code/api/v5_0_0/ViewsTests.scala b/obp-api/src/test/scala/code/api/v5_0_0/ViewsTests.scala new file mode 100644 index 000000000..c2adc72c6 --- /dev/null +++ b/obp-api/src/test/scala/code/api/v5_0_0/ViewsTests.scala @@ -0,0 +1,93 @@ +/** +Open Bank Project - API +Copyright (C) 2011-2019, TESOBE GmbH. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + +Email: contact@tesobe.com +TESOBE GmbH. +Osloer Strasse 16/17 +Berlin 13359, Germany + +This product includes software developed at +TESOBE (http://www.tesobe.com/) + + */ +package code.api.v5_0_0 + +import code.api.Constant._ +import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._ +import code.api.util.APIUtil.OAuth._ +import code.api.v1_2_1.{PermissionJSON, PermissionsJSON} +import code.api.v3_0_0.OBPAPI3_0_0.Implementations3_0_0 +import code.setup.APIResponse +import com.github.dwickern.macros.NameOf.nameOf +import com.openbankproject.commons.util.ApiVersion +import org.scalatest.Tag + +import scala.util.Random.nextInt + +class ViewsTests extends V500ServerSetup { + + object VersionOfApi extends Tag(ApiVersion.v5_0_0.toString) + object ApiEndpoint1 extends Tag(nameOf(Implementations3_0_0.getPermissionForUserForBankAccount)) + + + //Custom view, name starts from `_` + val postBodyViewJson = createViewJsonV300 + //System view, owner + val postBodySystemViewJson = createViewJsonV300.copy(name=SYSTEM_OWNER_VIEW_ID).copy(metadata_view = SYSTEM_OWNER_VIEW_ID) + + + def getAccountAccessForUser(bankId: String, accountId: String, provider : String, providerId : String, consumerAndToken: Option[(Consumer, Token)]): APIResponse = { + val request = (v5_0_0_Request / "banks" / bankId / "accounts" / accountId / "permissions" / provider / providerId).GET <@(consumerAndToken) + makeGetRequest(request) + } + + //This will call v1_2_1Request and we need it here to prepare the data for further tests + //BK need to check this endpoint.... + def getAccountPermissions(bankId : String, accountId : String, consumerAndToken: Option[(Consumer, Token)]): APIResponse = { + val request = v5_0_0_Request / "banks" / bankId / "accounts" / accountId / "permissions" <@(consumerAndToken) + makeGetRequest(request) + } + //This is a helper method, used to prepare the test parameters + def randomAccountPermission(bankId : String, accountId : String) : PermissionJSON = { + val persmissionsInfo = getAccountPermissions(bankId, accountId, user1).body.extract[PermissionsJSON] + val randomPermission = nextInt(persmissionsInfo.permissions.size) + persmissionsInfo.permissions(randomPermission) + } + + feature(s"$ApiEndpoint1 - Get Account access for User. - $VersionOfApi") { + scenario("we will Get Account access for User.") { + Given("Prepare all the parameters:") + val bankId = randomBankId + val bankAccountId = randomPrivateAccountId(bankId) + val provider = defaultProvider + val permission = randomAccountPermission(bankId, bankAccountId) + val providerId = permission.user.id + + When("We use a valid access token and valid put json") + val reply = getAccountAccessForUser(bankId, bankAccountId, provider, providerId, user1) + Then("We should get back the updated view") + reply.code should equal(200) + val response = reply.body.extract[ViewsJsonV500] + response.views.length should not equal (0) + + //Note: as to new system view stuff, now the default account should both have some system view accesses and custom view accesses. + response.views.filter(_.is_system).length > 0 should be (true) + response.views.filter(!_.is_system).length > 0 should be (true) + } + } + +} diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/model/BankingModel.scala b/obp-commons/src/main/scala/com/openbankproject/commons/model/BankingModel.scala index b8da25b20..113de64f9 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/model/BankingModel.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/model/BankingModel.scala @@ -393,16 +393,19 @@ case class FirehoseAccountUser( displayName : String ) +case class FastFirehoseOwners(user_id: String, provider: String, user_name: String) +case class FastFirehoseRoutings(bank_id: String, account_id: String) +case class FastFirehoseAttributes(`type`: String, code: String, value: String) case class FastFirehoseAccount( id: String, bankId: String, label: String, number: String, - owners: String, + owners: List[FastFirehoseOwners], productCode: String, balance: AmountOfMoney, - accountRoutings: String, - accountAttributes: String + accountRoutings: List[FastFirehoseRoutings], + accountAttributes: List[FastFirehoseAttributes], ) case class FastFirehoseAccounts( diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/model/UserModel.scala b/obp-commons/src/main/scala/com/openbankproject/commons/model/UserModel.scala index 1839a356f..20fd1e898 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/model/UserModel.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/model/UserModel.scala @@ -69,6 +69,7 @@ trait User { def isConsentUser = createdByConsentId.nonEmpty def isDeleted: Option[Boolean] def lastMarketingAgreementSignedDate: Option[Date] + def lastUsedLocale: Option[String] = None } case class UserPrimaryKey(val value : Long) { diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/model/ViewModel.scala b/obp-commons/src/main/scala/com/openbankproject/commons/model/ViewModel.scala index f84ccde23..057b97bb5 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/model/ViewModel.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/model/ViewModel.scala @@ -52,6 +52,8 @@ trait ViewSpecification { def which_alias_to_use: String def hide_metadata_if_alias_used: Boolean def allowed_actions : List[String] + def can_grant_access_to_views : Option[List[String]] = None + def can_revoke_access_to_views : Option[List[String]] = None } /* @@ -64,7 +66,9 @@ case class CreateViewJson( is_public: Boolean, which_alias_to_use: String, hide_metadata_if_alias_used: Boolean, - allowed_actions : List[String] + allowed_actions : List[String], + override val can_grant_access_to_views : Option[List[String]] = None, + override val can_revoke_access_to_views : Option[List[String]] = None ) extends ViewSpecification @@ -78,7 +82,9 @@ case class UpdateViewJSON( override val is_firehose: Option[Boolean] = None, which_alias_to_use: String, hide_metadata_if_alias_used: Boolean, - allowed_actions: List[String]) extends ViewSpecification + allowed_actions: List[String], + override val can_grant_access_to_views : Option[List[String]] = None, + override val can_revoke_access_to_views : Option[List[String]] = None) extends ViewSpecification @@ -245,6 +251,10 @@ trait View { def hideOtherAccountMetadataIfAlias: Boolean + // Introduced in version 5.0.0 + def canGrantAccessToViews : Option[List[String]] = None + def canRevokeAccessToViews : Option[List[String]] = None + //reading access //transaction fields