From faaf2da5bf8f7aae78aab0db5cd3a086bf836bf8 Mon Sep 17 00:00:00 2001 From: hongwei Date: Fri, 9 Sep 2022 13:34:51 +0200 Subject: [PATCH] feature/OBPv500 added new endpoints for customers --- .../main/scala/code/api/util/ApiRole.scala | 6 + .../scala/code/api/v4_0_0/APIMethods400.scala | 4 +- .../scala/code/api/v5_0_0/APIMethods500.scala | 159 ++++++++++++- .../bankconnectors/LocalMappedConnector.scala | 4 +- .../scala/code/api/v4_0_0/CustomerTest.scala | 6 +- .../scala/code/api/v5_0_0/CustomerTest.scala | 216 ++++++++++++++++++ 6 files changed, 385 insertions(+), 10 deletions(-) create mode 100644 obp-api/src/test/scala/code/api/v5_0_0/CustomerTest.scala 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 44274fb9e..839f28d30 100644 --- a/obp-api/src/main/scala/code/api/util/ApiRole.scala +++ b/obp-api/src/main/scala/code/api/util/ApiRole.scala @@ -88,6 +88,12 @@ object ApiRole { case class CanGetCustomersMinimalAtAnyBank(requiresBankId: Boolean = false) extends ApiRole lazy val canGetCustomersMinimalAtAnyBank = CanGetCustomersMinimalAtAnyBank() + case class CanGetCustomers(requiresBankId: Boolean = true) extends ApiRole + lazy val canGetCustomers = CanGetCustomers() + + case class CanGetCustomersMinimal(requiresBankId: Boolean = true) extends ApiRole + lazy val canGetCustomersMinimal = CanGetCustomersMinimal() + case class CanGetCustomer(requiresBankId: Boolean = true) extends ApiRole lazy val canGetCustomer = CanGetCustomer() 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 de8415bc9..f17e15394 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 @@ -5144,7 +5144,7 @@ trait APIMethods400 { implementedInApiVersion, nameOf(getCustomersMinimalAtAnyBank), "GET", - "/customers/minimal", + "/customers-minimal", "Get Customers Minimal at Any Bank", s"""Get Customers Minimal at Any Bank. | @@ -5163,7 +5163,7 @@ trait APIMethods400 { Some(List(canGetCustomersMinimalAtAnyBank)) ) lazy val getCustomersMinimalAtAnyBank : OBPEndpoint = { - case "customers" :: "minimal" :: Nil JsonGet _ => { + case "customers-minimal" :: Nil JsonGet _ => { cc => { for { requestParams <- extractQueryParams(cc.url, List("limit","offset","sort_direction"), cc.callContext) 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 1408ce644..4ec2d0ad8 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 @@ -2,12 +2,16 @@ package code.api.v5_0_0 import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._ import code.api.util.APIUtil._ -import code.api.util.ApiRole.{CanCreateUserAuthContextUpdate, canCreateUserAuthContext, canGetUserAuthContext} +import code.api.util.ApiRole.{CanCreateUserAuthContextUpdate, canCreateUserAuthContext, canGetCustomers, canGetCustomersMinimal, canGetUserAuthContext} import code.api.util.ApiTag._ import code.api.util.ErrorMessages._ import code.api.util.{APIUtil, ApiRole, Consent, NewStyle} import code.api.util.NewStyle.HttpCode +import code.api.util.NewStyle.function.extractQueryParams +import code.api.v2_1_0.JSONFactory210 +import code.api.v3_0_0.JSONFactory300 import code.api.v3_1_0.{PostConsentBodyCommonJson, PostConsentEmailJsonV310, PostConsentEntitlementJsonV310, PostConsentPhoneJsonV310, PostConsentViewJsonV310, PostUserAuthContextJson, PostUserAuthContextUpdateJsonV310} +import code.api.v4_0_0.JSONFactory400.createCustomersMinimalJson import code.bankconnectors.Connector import code.consent.{ConsentRequests, Consents} import code.entitlement.Entitlement @@ -19,11 +23,11 @@ import com.openbankproject.commons.ExecutionContext.Implicits.global import com.openbankproject.commons.model.{BankId, UserAuthContextUpdateStatus} import com.openbankproject.commons.model.enums.StrongCustomerAuthentication import com.openbankproject.commons.util.ApiVersion -import net.liftweb.common.{Full} -import net.liftweb.http.{Req} +import net.liftweb.common.Full +import net.liftweb.http.Req import net.liftweb.http.rest.RestHelper import net.liftweb.json -import net.liftweb.json.compactRender +import net.liftweb.json.{compactRender} import net.liftweb.util.Props import scala.collection.immutable.{List, Nil} @@ -615,6 +619,153 @@ trait APIMethods500 { } } + staticResourceDocs += ResourceDoc( + getMyCustomersAtAnyBank, + implementedInApiVersion, + nameOf(getMyCustomersAtAnyBank), + "GET", + "/my/customers", + "Get My Customers", + """Gets all Customers that are linked to me. + | + |Authentication via OAuth is required.""", + emptyObjectJson, + customerJsonV210, + List( + $UserNotLoggedIn, + UserCustomerLinksNotFoundForUser, + UnknownError + ), + List(apiTagCustomer, apiTagUser)) + + lazy val getMyCustomersAtAnyBank : OBPEndpoint = { + case "my" :: "customers" :: Nil JsonGet _ => { + cc => { + for { + (Full(u), callContext) <- SS.user + (customers, callContext) <- Connector.connector.vend.getCustomersByUserId(u.userId, callContext) map { + connectorEmptyResponse(_, callContext) + } + } yield { + (JSONFactory210.createCustomersJson(customers), HttpCode.`200`(callContext)) + } + } + } + } + + staticResourceDocs += ResourceDoc( + getMyCustomersAtBank, + implementedInApiVersion, + nameOf(getMyCustomersAtBank), + "GET", + "/banks/BANK_ID/my/customers", + "Get My Customers at Bank", + s"""Returns a list of Customers at the Bank that are linked to the currently authenticated User. + | + | + |${authenticationRequiredMessage(true)}""".stripMargin, + emptyObjectJson, + customerJSONs, + List( + $UserNotLoggedIn, + $BankNotFound, + UserCustomerLinksNotFoundForUser, + UnknownError + ), + List(apiTagCustomer, apiTagNewStyle) + ) + + lazy val getMyCustomersAtBank : OBPEndpoint = { + case "banks" :: BankId(bankId) :: "my" :: "customers" :: Nil JsonGet _ => { + cc => { + for { + (Full(u), callContext) <- SS.user + (_, callContext) <- NewStyle.function.getBank(bankId, callContext) + (customers, callContext) <- Connector.connector.vend.getCustomersByUserId(u.userId, callContext) map { + connectorEmptyResponse(_, callContext) + } + } yield { + // Filter so we only see the ones for the bank in question + val bankCustomers = customers.filter(_.bankId==bankId.value) + val json = JSONFactory210.createCustomersJson(bankCustomers) + (json, HttpCode.`200`(callContext)) + } + } + } + } + + + staticResourceDocs += ResourceDoc( + getCustomersAtOneBank, + implementedInApiVersion, + nameOf(getCustomersAtOneBank), + "GET", + "/banks/BANK_ID/customers", + "Get Customers at Bank", + s"""Get Customers at Bank. + | + | + |${authenticationRequiredMessage(true)} + | + |""", + emptyObjectJson, + customersJsonV300, + List( + UserNotLoggedIn, + UserCustomerLinksNotFoundForUser, + UnknownError + ), + List(apiTagCustomer, apiTagUser, apiTagNewStyle), + Some(List(canGetCustomers)) + ) + + lazy val getCustomersAtOneBank : OBPEndpoint = { + case "banks" :: BankId(bankId) :: "customers" :: Nil JsonGet _ => { + cc => { + for { + requestParams <- extractQueryParams(cc.url, List("limit","offset","sort_direction"), cc.callContext) + customers <- NewStyle.function.getCustomers(bankId, cc.callContext, requestParams) + } yield { + (JSONFactory300.createCustomersJson(customers.sortBy(_.bankId)), HttpCode.`200`(cc.callContext)) + } + } + } + } + + staticResourceDocs += ResourceDoc( + getCustomersMinimalAtOneBank, + implementedInApiVersion, + nameOf(getCustomersMinimalAtOneBank), + "GET", + "/banks/BANK_ID/customers-minimal", + "Get Customers Minimal at Bank", + s"""Get Customers Minimal at Bank. + | + | + | + |""", + emptyObjectJson, + customersMinimalJsonV300, + List( + UserCustomerLinksNotFoundForUser, + UnknownError + ), + List(apiTagCustomer, apiTagUser, apiTagNewStyle), + Some(List(canGetCustomersMinimal)) + ) + lazy val getCustomersMinimalAtOneBank : OBPEndpoint = { + case "banks" :: BankId(bankId) :: "customers-minimal" :: Nil JsonGet _ => { + cc => { + for { + requestParams <- extractQueryParams(cc.url, List("limit","offset","sort_direction"), cc.callContext) + customers <- NewStyle.function.getCustomers(bankId, cc.callContext, requestParams) + } yield { + (createCustomersMinimalJson(customers.sortBy(_.bankId)), HttpCode.`200`(cc.callContext)) + } + } + } + } + } } diff --git a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala index a0fb1efb0..c7a7e5c87 100644 --- a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala @@ -3491,7 +3491,9 @@ object LocalMappedConnector extends Connector with MdcLoggable { } override def getCustomers(bankId: BankId, callContext: Option[CallContext], queryParams: List[OBPQueryParam]): Future[Box[List[Customer]]] = - CustomerX.customerProvider.vend.getCustomersFuture(bankId, queryParams) + CustomerX.customerProvider.vend.getCustomersFuture(bankId, queryParams)map { + (_, callContext) + } override def getCustomersByCustomerPhoneNumber(bankId: BankId, phoneNumber: String, callContext: Option[CallContext]): OBPReturnType[Box[List[Customer]]] = CustomerX.customerProvider.vend.getCustomersByCustomerPhoneNumber(bankId, phoneNumber) map { diff --git a/obp-api/src/test/scala/code/api/v4_0_0/CustomerTest.scala b/obp-api/src/test/scala/code/api/v4_0_0/CustomerTest.scala index 3350bb103..80958810d 100644 --- a/obp-api/src/test/scala/code/api/v4_0_0/CustomerTest.scala +++ b/obp-api/src/test/scala/code/api/v4_0_0/CustomerTest.scala @@ -109,7 +109,7 @@ class CustomerTest extends V400ServerSetup with PropsReset{ feature(s"Get Customers Minimal at Any Bank $VersionOfApi - Unauthorized access") { scenario("We will call the endpoint without user credentials", ApiEndpoint2, VersionOfApi) { When(s"We make a request $VersionOfApi") - val request = (v4_0_0_Request / "customers" / "minimal").GET + val request = (v4_0_0_Request / "customers-minimal").GET val response = makeGetRequest(request) Then("We should get a 401") response.code should equal(401) @@ -120,7 +120,7 @@ class CustomerTest extends V400ServerSetup with PropsReset{ feature(s"Get Customers Minimal at Any Bank $VersionOfApi - Authorized access") { scenario("We will call the endpoint with user credentials", ApiEndpoint2, VersionOfApi) { When(s"We make a request $VersionOfApi") - val request = (v4_0_0_Request / "customers" / "minimal").GET<@(user1) + val request = (v4_0_0_Request / "customers-minimal").GET<@(user1) val response = makeGetRequest(request) Then("We should get a 403") response.code should equal(403) @@ -133,7 +133,7 @@ class CustomerTest extends V400ServerSetup with PropsReset{ scenario("We will call the endpoint with a user credentials and a proper role", ApiEndpoint1, VersionOfApi) { Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, canGetCustomersMinimalAtAnyBank.toString) When(s"We make a request $VersionOfApi") - val request = (v4_0_0_Request / "customers" / "minimal").GET <@(user1) + val request = (v4_0_0_Request / "customers-minimal").GET <@(user1) val response = makeGetRequest(request) Then("We should get a 200") response.code should equal(200) diff --git a/obp-api/src/test/scala/code/api/v5_0_0/CustomerTest.scala b/obp-api/src/test/scala/code/api/v5_0_0/CustomerTest.scala new file mode 100644 index 000000000..65c4fa821 --- /dev/null +++ b/obp-api/src/test/scala/code/api/v5_0_0/CustomerTest.scala @@ -0,0 +1,216 @@ +/** +Open Bank Project - API +Copyright (C) 2011-2022, 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 +Osloerstrasse 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.ResourceDocs1_4_0.SwaggerDefinitionsJSON +import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.{postUserAuthContextJson, postUserAuthContextUpdateJsonV310} +import code.api.util.APIUtil.OAuth._ +import code.api.util.ApiRole._ +import code.api.util.ErrorMessages._ +import code.api.v2_1_0.CustomerJSONs +import code.api.v3_0_0.CustomerJSONsV300 +import code.api.v3_1_0.CustomerJsonV310 +import code.api.v4_0_0.CustomersMinimalJsonV400 +import code.api.v5_0_0.OBPAPI5_0_0.Implementations5_0_0 +import code.customer.CustomerX +import code.entitlement.Entitlement +import code.usercustomerlinks.UserCustomerLink +import com.github.dwickern.macros.NameOf.nameOf +import com.openbankproject.commons.model.ErrorMessage +import com.openbankproject.commons.util.ApiVersion +import net.liftweb.json.Serialization.write +import org.scalatest.Tag + +import java.util.Date +import scala.language.postfixOps + +class CustomerTest extends V500ServerSetupAsync { + + override def beforeAll(): Unit = { + super.beforeAll() + } + + override def afterAll(): Unit = { + super.afterAll() + CustomerX.customerProvider.vend.bulkDeleteCustomers() + UserCustomerLink.userCustomerLink.vend.bulkDeleteUserCustomerLinks() + } + + /** + * 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.getMyCustomersAtAnyBank)) + object ApiEndpoint2 extends Tag(nameOf(Implementations5_0_0.getMyCustomersAtBank)) + object ApiEndpoint3 extends Tag(nameOf(Implementations5_0_0.getCustomersAtOneBank)) + object ApiEndpoint4 extends Tag(nameOf(Implementations5_0_0.getCustomersMinimalAtOneBank)) + + lazy val bankId = testBankId1.value + val postCustomerJson = SwaggerDefinitionsJSON.postCustomerJsonV310.copy(last_ok_date= new Date()) + + feature(s"$ApiEndpoint1 $ApiEndpoint2 $ApiEndpoint3 $ApiEndpoint4 successful cases") { + + scenario(s"We will call $ApiEndpoint1 with credentials", ApiEndpoint1, VersionOfApi) { + + + + Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, CanCreateCustomer.toString) + When("we prepare the user1's customer") + val postRequestCreateCustomer = (v5_0_0_Request / "banks" / bankId / "customers").POST <@(user1) + val postResponseCreateCustomer = makePostRequest(postRequestCreateCustomer, write(postCustomerJson)) + Then("We should get a 201") + postResponseCreateCustomer.code should equal(201) + val customerIdUser1 = postResponseCreateCustomer.body.extract[CustomerJsonV310].customer_id + + lazy val createUserCustomerLinkJson = SwaggerDefinitionsJSON.createUserCustomerLinkJson + .copy(user_id = resourceUser1.userId, customer_id = customerIdUser1) + + Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, CanCreateUserCustomerLink.toString()) + val createRequestCustomerLinkUser1 = (v5_0_0_Request / "banks" / bankId / "user_customer_links" ).POST <@(user1) + val createResponseCustomerLinkUser1 = makePostRequest(createRequestCustomerLinkUser1, write(createUserCustomerLinkJson)) + Then("We should get a 201") + createResponseCustomerLinkUser1.code should equal(201) + + + Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser2.userId, CanCreateCustomer.toString) + Then("we prepare the user2's customer") + val postRequestCreateCustomerUser2 = (v5_0_0_Request / "banks" / bankId / "customers").POST <@(user2) + val postResponseCreateCustomerUser2 = makePostRequest(postRequestCreateCustomerUser2, write(postCustomerJson)) + Then("We should get a 201") + postResponseCreateCustomerUser2.code should equal(201) + val customerIdUser2 = postResponseCreateCustomerUser2.body.extract[CustomerJsonV310].customer_id + + lazy val createUserCustomerLinkJsonUser2 = SwaggerDefinitionsJSON.createUserCustomerLinkJson + .copy(user_id = resourceUser2.userId, customer_id = customerIdUser2) + + Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser2.userId, CanCreateUserCustomerLink.toString()) + val createRequest = (v5_0_0_Request / "banks" / bankId / "user_customer_links" ).POST <@(user1) + val createResponse = makePostRequest(createRequest, write(createUserCustomerLinkJsonUser2)) + Then("We should get a 201") + createResponse.code should equal(201) + + Then(s"We test $ApiEndpoint1") + val requestApiEndpoint1 = (v5_0_0_Request / "my"/ "customers").GET <@(user1) + val responseApiEndpoint1 = makeGetRequest(requestApiEndpoint1) + Then("We should get a 200") + responseApiEndpoint1.code should equal(200) + responseApiEndpoint1.body.extract[CustomerJSONs].customers.length == 1 should be (true) + + Then(s"We test $ApiEndpoint2") + val requestApiEndpoint2 = (v5_0_0_Request / "banks"/ bankId /"my"/ "customers").GET <@(user1) + val responseApiEndpoint2 = makeGetRequest(requestApiEndpoint2) + Then("We should get a 200") + responseApiEndpoint2.code should equal(200) + responseApiEndpoint2.body.extract[CustomerJSONs].customers.length == 1 should be (true) + + Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, CanGetCustomers.toString) + Then(s"We test $ApiEndpoint3") + val requestApiEndpoint3 = (v5_0_0_Request / "banks"/ bankId /"customers").GET <@(user1) + val responseApiEndpoint3 = makeGetRequest(requestApiEndpoint3) + Then("We should get a 200") + responseApiEndpoint3.code should equal(200) + responseApiEndpoint3.body.extract[CustomerJSONsV300].customers.length == 2 should be (true) + + Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, CanGetCustomersMinimal.toString) + Then(s"We test $ApiEndpoint4") + val requestApiEndpoint4 = (v5_0_0_Request / "banks"/ bankId /"customers-minimal").GET <@(user1) + val responseApiEndpoint4 = makeGetRequest(requestApiEndpoint4) + Then("We should get a 200") + responseApiEndpoint4.code should equal(200) + responseApiEndpoint4.body.extract[CustomersMinimalJsonV400].customers.length == 2 should be (true) + } + } + + feature(s"$ApiEndpoint1 $ApiEndpoint2 $ApiEndpoint3 $ApiEndpoint4 error cases") { + scenario(s"$ApiEndpoint1 without a user credentials", ApiEndpoint1, VersionOfApi) { + When("We make a request v5.0.0") + val requestApiEndpoint1 = (v5_0_0_Request / "my"/ "customers").GET + val responseApiEndpoint1 = makeGetRequest(requestApiEndpoint1) + Then("We should get a 401") + responseApiEndpoint1.code should equal(401) + And("error should be " + UserNotLoggedIn) + responseApiEndpoint1.body.extract[ErrorMessage].message should equal (UserNotLoggedIn) + } + scenario(s"$ApiEndpoint2 without a user credentials", ApiEndpoint2, VersionOfApi) { + When("We make a request v5.0.0") + val requestApiEndpoint2 = (v5_0_0_Request / "my"/ "customers").GET + val responseApiEndpoint2 = makeGetRequest(requestApiEndpoint2) + Then("We should get a 401") + responseApiEndpoint2.code should equal(401) + And("error should be " + UserNotLoggedIn) + responseApiEndpoint2.body.extract[ErrorMessage].message should equal (UserNotLoggedIn) + } + + scenario(s"$ApiEndpoint3 without a user credentials", ApiEndpoint3, VersionOfApi) { + When("We make a request v5.0.0") + val requestApiEndpoint3 = (v5_0_0_Request / "banks"/ bankId /"customers").GET + val responseApiEndpoint3 = makeGetRequest(requestApiEndpoint3) + Then("We should get a 401") + responseApiEndpoint3.code should equal(401) + And("error should be " + UserNotLoggedIn) + responseApiEndpoint3.body.extract[ErrorMessage].message should equal (UserNotLoggedIn) + } + + scenario(s"$ApiEndpoint3 miss role", ApiEndpoint3, VersionOfApi) { + When("We make a request v5.0.0") + val requestApiEndpoint3 = (v5_0_0_Request / "banks"/ bankId /"customers").GET <@(user1) + val responseApiEndpoint3 = makeGetRequest(requestApiEndpoint3) + Then("We should get a 403") + responseApiEndpoint3.code should equal(403) + And("error should be " + UserHasMissingRoles + CanGetUserAuthContext) + responseApiEndpoint3.body.extract[ErrorMessage].message contains (UserHasMissingRoles ) should be (true) + responseApiEndpoint3.body.extract[ErrorMessage].message contains (CanGetCustomers.toString()) should be (true) + } + + scenario(s"$ApiEndpoint4 without a user credentials", ApiEndpoint4, VersionOfApi) { + When("We make a request v5.0.0") + val requestApiEndpoint4 = (v5_0_0_Request / "banks"/ bankId /"customers-minimal").GET + val responseApiEndpoint4 = makeGetRequest(requestApiEndpoint4) + Then("We should get a 401") + responseApiEndpoint4.code should equal(401) + And("error should be " + UserNotLoggedIn) + responseApiEndpoint4.body.extract[ErrorMessage].message should equal (UserNotLoggedIn) + } + + scenario(s"$ApiEndpoint4 miss role", ApiEndpoint4, VersionOfApi) { + When("We make a request v5.0.0") + val requestApiEndpoint4 = (v5_0_0_Request / "banks"/ bankId /"customers-minimal").GET <@(user1) + val responseApiEndpoint4 = makeGetRequest(requestApiEndpoint4) + Then("We should get a 403") + responseApiEndpoint4.code should equal(403) + And("error should be " + UserHasMissingRoles + CanGetUserAuthContext) + responseApiEndpoint4.body.extract[ErrorMessage].message contains (UserHasMissingRoles ) should be (true) + responseApiEndpoint4.body.extract[ErrorMessage].message contains (CanGetCustomersMinimal.toString()) should be (true) + } + + } + + +}