Merge pull request #2137 from constantine2nd/develop

Show which fields are optional / required in API Explorer
This commit is contained in:
Simon Redfern 2022-10-13 13:42:08 +02:00 committed by GitHub
commit 9bbf67dbd4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 95 additions and 32 deletions

View File

@ -96,6 +96,12 @@ object ApiRole {
case class CanGetCustomer(requiresBankId: Boolean = true) extends ApiRole
lazy val canGetCustomer = CanGetCustomer()
case class CanGetCustomerOverview(requiresBankId: Boolean = true) extends ApiRole
lazy val canGetCustomerOverview = CanGetCustomerOverview()
case class CanGetCustomerOverviewFlat(requiresBankId: Boolean = true) extends ApiRole
lazy val canGetCustomerOverviewFlat = CanGetCustomerOverviewFlat()
case class CanCreateCustomer(requiresBankId: Boolean = true) extends ApiRole
lazy val canCreateCustomer = CanCreateCustomer()

View File

@ -0,0 +1,50 @@
package code.api.util
import code.api.Constant
import code.entitlement.Entitlement
import code.users.Users
import code.util.Helper.MdcLoggable
import com.openbankproject.commons.model.User
import net.liftweb.common.Box
import net.liftweb.util.Mailer
import net.liftweb.util.Mailer._
import scala.collection.immutable.List
object NotificationUtil extends MdcLoggable {
def sendEmailRegardingAssignedRole(userId : String, entitlement: Entitlement): Unit = {
val user = Users.users.vend.getUserByUserId(userId)
sendEmailRegardingAssignedRole(user, entitlement)
}
def sendEmailRegardingAssignedRole(user: Box[User], entitlement: Entitlement): Unit = {
val mailSent = for {
user <- user
from <- APIUtil.getPropsValue("mail.api.consumer.registered.sender.address") ?~ "Could not send mail: Missing props param for 'from'"
} yield {
val bodyOfMessage : String = s"""Dear ${user.name},
|
|You have been granted the entitlement to use ${entitlement.roleName} on ${Constant.HostName}
|
|Cheers
|""".stripMargin
val params = PlainMailBodyType(bodyOfMessage) :: List(To(user.emailAddress))
val subjectOfMessage = "You have been granted the role"
//this is an async call
Mailer.sendMail(
From(from),
Subject(subjectOfMessage),
params :_*
)
}
if(mailSent.isEmpty) {
val info =
s"""
|Sending email is omitted.
|User: $user
|Props mail.api.consumer.registered.sender.address: ${APIUtil.getPropsValue("mail.api.consumer.registered.sender.address")}
|""".stripMargin
this.logger.warn(info)
}
}
}

View File

@ -408,7 +408,7 @@ object JSONFactory1_4_0 extends MdcLoggable{
if(findMatches.nonEmpty) {
val urlParameters: List[String] = findMatches.toList.sorted
val parametersDescription: List[String] = urlParameters.map(prepareDescription)
val parametersDescription: List[String] = urlParameters.map(i => prepareDescription(i, Nil))
parametersDescription.mkString("\n\n\n**URL Parameters:**", "", "\n")
} else {
""
@ -420,31 +420,44 @@ object JSONFactory1_4_0 extends MdcLoggable{
* @param parameter BANK_ID
* @return [BANK_ID](/glossary#Bank.bank_id):gh.29.uk
*/
def prepareDescription(parameter: String): String = {
def prepareDescription(parameter: String, types: List[(String, Boolean)]): String = {
val glossaryItemTitle = getGlossaryItemTitle(parameter)
val exampleFieldValue = getExampleFieldValue(parameter)
def boldIfMandatory() = {
types.exists(i => i._1 == parameter && i._2 == false) match {
case true =>
s"**$parameter**"
case false =>
s"$parameter"
}
}
if(exampleFieldValue.contains(ExampleValue.NoExampleProvided)){
""
} else {
s"""
|
|* [${parameter}](/glossary#$glossaryItemTitle): $exampleFieldValue
|* [${boldIfMandatory()}](/glossary#$glossaryItemTitle): $exampleFieldValue
|
|""".stripMargin
}
}
def prepareJsonFieldDescription(jsonBody: scala.Product, jsonType: String): String = {
val jsonBodyJValue = jsonBody match {
jsonBody.productIterator
val (jsonBodyJValue: json.JValue, types) = jsonBody match {
case JvalueCaseClass(jValue) =>
jValue
case _ => decompose(jsonBody)
val types = Nil
(jValue, types)
case _ =>
val types = jsonBody.getClass()
.getDeclaredFields().toList
.map(f => (f.getName(), f.getType().getCanonicalName().contains("Option")))
(decompose(jsonBody), types)
}
val jsonBodyFields =JsonUtils.collectFieldNames(jsonBodyJValue).keySet.toList.sorted
val jsonFieldsDescription = jsonBodyFields.map(prepareDescription)
val jsonFieldsDescription = jsonBodyFields.map(i => prepareDescription(i, types))
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"

View File

@ -1040,21 +1040,17 @@ trait APIMethods500 {
UnknownError
),
List(apiTagCustomer, apiTagKyc ,apiTagNewStyle),
Some(List(canGetCustomer))
Some(List(canGetCustomerOverview))
)
lazy val getCustomerOverview : OBPEndpoint = {
case "banks" :: BankId(bankId) :: "customers" :: "customer-number-query" :: "overview" :: Nil JsonPost json -> req => {
cc =>
for {
(Full(u), callContext) <- authenticatedAccess(cc)
(bank, callContext) <- NewStyle.function.getBank(bankId, callContext)
_ <- NewStyle.function.hasEntitlement(bankId.value, u.userId, canGetCustomer, callContext)
failMsg = s"$InvalidJsonFormat The Json body should be the $PostCustomerOverviewJsonV500 "
postedData <- NewStyle.function.tryons(failMsg, 400, callContext) {
postedData <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the $PostCustomerOverviewJsonV500 ", 400, cc.callContext) {
json.extract[PostCustomerOverviewJsonV500]
}
(customer, callContext) <- NewStyle.function.getCustomerByCustomerNumber(postedData.customer_number, bank.bankId, callContext)
(customer, callContext) <- NewStyle.function.getCustomerByCustomerNumber(postedData.customer_number, bankId, cc.callContext)
(customerAttributes, callContext) <- NewStyle.function.getCustomerAttributes(
bankId,
CustomerId(customer.customerId),
@ -1093,21 +1089,17 @@ trait APIMethods500 {
UnknownError
),
List(apiTagCustomer, apiTagKyc ,apiTagNewStyle),
Some(List(canGetCustomer))
Some(List(canGetCustomerOverviewFlat))
)
lazy val getCustomerOverviewFlat : OBPEndpoint = {
case "banks" :: BankId(bankId) :: "customers" :: "customer-number-query" :: "overview-flat" :: Nil JsonPost json -> req => {
cc =>
for {
(Full(u), callContext) <- authenticatedAccess(cc)
(bank, callContext) <- NewStyle.function.getBank(bankId, callContext)
_ <- NewStyle.function.hasEntitlement(bankId.value, u.userId, canGetCustomer, callContext)
failMsg = s"$InvalidJsonFormat The Json body should be the $PostCustomerOverviewJsonV500 "
postedData <- NewStyle.function.tryons(failMsg, 400, callContext) {
postedData <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the $PostCustomerOverviewJsonV500 ", 400, cc.callContext) {
json.extract[PostCustomerOverviewJsonV500]
}
(customer, callContext) <- NewStyle.function.getCustomerByCustomerNumber(postedData.customer_number, bank.bankId, callContext)
(customer, callContext) <- NewStyle.function.getCustomerByCustomerNumber(postedData.customer_number, bankId, cc.callContext)
(customerAttributes, callContext) <- NewStyle.function.getCustomerAttributes(
bankId,
CustomerId(customer.customerId),

View File

@ -2,7 +2,7 @@ package code.entitlement
import code.api.dynamic.endpoint.helper.DynamicEntityInfo
import code.api.util.ApiRole.{CanCreateEntitlementAtAnyBank, CanCreateEntitlementAtOneBank}
import code.api.util.ErrorMessages
import code.api.util.{ErrorMessages, NotificationUtil}
import code.util.{MappedUUID, UUIDString}
import net.liftweb.common.{Box, Failure, Full}
import net.liftweb.mapper._
@ -110,6 +110,8 @@ object MappedEntitlementsProvider extends EntitlementProvider {
val addEntitlement: MappedEntitlement =
MappedEntitlement.create.mBankId(bankId).mUserId(userId).mRoleName(roleName).mCreatedByProcess(createdByProcess)
.saveMe()
// When a role is Granted, we should send an email to the Recipient telling them they have been granted the role.
NotificationUtil.sendEmailRegardingAssignedRole(userId: String, addEntitlement: Entitlement)
Full(addEntitlement)
}
// Return a Box so we can handle errors later.

View File

@ -47,7 +47,7 @@ class JSONFactory1_4_0Test extends V140ServerSetup with DefaultUsers {
feature("Test JSONFactory1_4_0") {
scenario("prepareDescription should work well, extract the parameters from URL") {
val description = JSONFactory1_4_0.prepareDescription("BANK_ID")
val description = JSONFactory1_4_0.prepareDescription("BANK_ID", Nil)
description.contains("[BANK_ID](/glossary#Bank.bank_id): gh.29.uk") should be (true)
}

View File

@ -87,13 +87,13 @@ class CustomerOverviewTest extends V500ServerSetup {
val response = makePostRequest(request, write(getCustomerJson))
Then("We should get a 403")
response.code should equal(403)
And("error should be " + canGetCustomer)
And("error should be " + canGetCustomerOverview)
val errorMessage = response.body.extract[ErrorMessage].message
errorMessage contains (UserHasMissingRoles) should be (true)
errorMessage contains (canGetCustomer.toString()) should be (true)
errorMessage contains (canGetCustomerOverview.toString()) should be (true)
}
scenario(s"We will call the endpoint $ApiEndpoint1 with a user credentials and a proper role", ApiEndpoint1, VersionOfApi) {
Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, CanGetCustomer.toString)
Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, CanGetCustomerOverview.toString)
When(s"We make a request $VersionOfApi")
val request = (v5_0_0_Request / "banks" / bankId / "customers" / "customer-number-query" / "overview").POST <@(user1)
val response = makePostRequest(request, write(getCustomerJson))
@ -106,7 +106,7 @@ class CustomerOverviewTest extends V500ServerSetup {
val legalName = "Evelin Doe"
val mobileNumber = "+44 123 456"
val customer: CustomerJsonV310 = createCustomerEndpointV500(bankId, legalName, mobileNumber)
Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, CanGetCustomer.toString)
Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, CanGetCustomerOverview.toString)
When(s"We make a request $VersionOfApi")
val request = (v5_0_0_Request / "banks" / bankId / "customers" / "customer-number-query" / "overview").POST <@(user1)
val response = makePostRequest(request, write(PostCustomerOverviewJsonV500(customer.customer_number)))
@ -140,13 +140,13 @@ class CustomerOverviewTest extends V500ServerSetup {
val response = makePostRequest(request, write(getCustomerJson))
Then("We should get a 403")
response.code should equal(403)
And("error should be " + canGetCustomer)
And("error should be " + canGetCustomerOverviewFlat)
val errorMessage = response.body.extract[ErrorMessage].message
errorMessage contains (UserHasMissingRoles) should be (true)
errorMessage contains (canGetCustomer.toString()) should be (true)
errorMessage contains (canGetCustomerOverviewFlat.toString()) should be (true)
}
scenario(s"We will call the endpoint $ApiEndpoint2 with a user credentials and a proper role", ApiEndpoint1, VersionOfApi) {
Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, CanGetCustomer.toString)
Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, CanGetCustomerOverviewFlat.toString)
When(s"We make a request $VersionOfApi")
val request = (v5_0_0_Request / "banks" / bankId / "customers" / "customer-number-query" / "overview-flat").POST <@(user1)
val response = makePostRequest(request, write(getCustomerJson))
@ -159,7 +159,7 @@ class CustomerOverviewTest extends V500ServerSetup {
val legalName = "Evelin Doe"
val mobileNumber = "+44 123 456"
val customer: CustomerJsonV310 = createCustomerEndpointV500(bankId, legalName, mobileNumber)
Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, CanGetCustomer.toString)
Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, CanGetCustomerOverviewFlat.toString)
When(s"We make a request $VersionOfApi")
val request = (v5_0_0_Request / "banks" / bankId / "customers" / "customer-number-query" / "overview-flat").POST <@(user1)
val response = makePostRequest(request, write(PostCustomerOverviewJsonV500(customer.customer_number)))