Merge pull request #1398 from hongwei1/develop

methodRouting  and webuis
This commit is contained in:
Simon Redfern 2019-08-22 11:08:35 +02:00 committed by GitHub
commit fc67401a26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 141 additions and 56 deletions

View File

@ -411,7 +411,9 @@ webui_dummy_customer_logins = Customer Logins\
\
Please ask a member of the Open Bank Project team for more logins if you require. You can use this [application](https://sofit.openbankproject.com) which also uses OAuth to browse your transaction data (use the above username/password).\
# when developer register the consumer successfully, it will show this message to developer on the webpage or email.
webui_register_consumer_success_message_webpage = Thanks for registering your consumer with the Open Bank API! Here is your developer information. Please save it in a secure location.
webui_register_consumer_success_message_email = Thank you for registering to use the Open Bank Project API.
## End of webui_ section ########

View File

@ -512,7 +512,7 @@ object SwaggerDefinitionsJSON {
)
val userJSONV121 = UserJSONV121(
id = "5995d6a2-01b3-423c-a173-5481df49bdaf",
provider = "OBP",
provider = providerValueExample.value,
display_name = "OBP"
)
@ -1569,8 +1569,8 @@ object SwaggerDefinitionsJSON {
val userJsonV200 = UserJsonV200(
user_id = ExampleValue.userIdExample.value,
email = ExampleValue.emailExample.value,
provider_id = "OBP",
provider = "OBP",
provider_id = providerIdValueExample.value,
provider = providerValueExample.value,
username = "robert.x.0.gh",
entitlements = entitlementJSONs
)
@ -1690,7 +1690,7 @@ object SwaggerDefinitionsJSON {
)
val createMeetingJson = CreateMeetingJson(
provider_id = "String",
provider_id = providerIdValueExample.value,
purpose_id = "String"
)
@ -1707,7 +1707,7 @@ object SwaggerDefinitionsJSON {
val meetingJson = MeetingJson(
meeting_id = "String",
provider_id = "String",
provider_id = providerIdValueExample.value,
purpose_id = "String",
bank_id = bankIdExample.value,
present = meetingPresentJSON,
@ -1852,9 +1852,9 @@ object SwaggerDefinitionsJSON {
val resourceUserJSON = ResourceUserJSON(
user_id = ExampleValue.userIdExample.value,
email = ExampleValue.emailExample.value,
provider_id = "obp",
provider = "obp",
username = "TESOBE"
provider_id = providerIdValueExample.value,
provider = providerValueExample.value,
username = usernameExample.value
)
val availableRoleJSON = AvailableRoleJSON(
@ -1919,7 +1919,7 @@ object SwaggerDefinitionsJSON {
val userJSONV210 = UserJSONV210(
id = "123",
provider = "OBP",
provider = providerValueExample.value,
username = "OBP"
)
@ -3184,7 +3184,7 @@ object SwaggerDefinitionsJSON {
)
val createMeetingJsonV310 = CreateMeetingJsonV310(
provider_id = "String, eg: tokbox",
provider_id = providerIdValueExample.value,
purpose_id = "String, eg: onboarding",
date = DateWithMsExampleObject,
creator = contactDetailsJson,
@ -3193,7 +3193,7 @@ object SwaggerDefinitionsJSON {
val meetingJsonV310 = MeetingJsonV310(
meeting_id = "UUID-String",
provider_id = "String, eg: tokbox",
provider_id = providerIdValueExample.value,
purpose_id = "String, eg: onboarding",
bank_id = bankIdExample.value,
present = meetingPresentJSON,

View File

@ -2472,15 +2472,16 @@ Returns a string showed to the developer
//eg: List(("webui_get_started_text","Get started building your application using this sandbox now"),
// ("webui_post_consumer_registration_more_info_text"," Please tell us more your Application and / or Startup using this link"))
def getWebUIPropsPairs: List[(String, String)] = {
val filepath = this.getClass.getResource("/props/sample.props.template").getPath
val bufferedSource: BufferedSource = scala.io.Source.fromFile(filepath)
val bufferedSource = scala.io.Source.fromFile("obp-api/src/main/resources/props/sample.props.template")
val proPairs: List[(String, String)] = for{
line <- bufferedSource.getLines.toList if(line.startsWith("webui_"))
webuiProps = line.toString.split("=", 2)
} yield {
val webuiProsKey = webuiProps(0)
val webuiProsValue = if (webuiProps.length > 1) webuiProps(1) else ""
(webuiProsKey, webuiProsValue)
val webuiPropsKey = webuiProps(0).trim //Remove the whitespace
val webuiPropsValue = if (webuiProps.length > 1) webuiProps(1).trim else ""
(webuiPropsKey, webuiPropsValue)
}
bufferedSource.close()
proPairs

View File

@ -208,7 +208,12 @@ object ExampleValue {
val cardAttributeValueExample = ConnectorField("2012-04-23", s"The card attribute values")
glossaryItems += makeGlossaryItem("Adapter.card_attribute_value", cardAttributeValueExample)
val providerValueExample = ConnectorField("http://127.0.0.1:8080", s"The provider for current user.")
glossaryItems += makeGlossaryItem("Adapter.provider", providerValueExample)
val providerIdValueExample = ConnectorField("Chris", s"The provider id of the current user. ")
glossaryItems += makeGlossaryItem("Adapter.provider_id", providerIdValueExample)
val cbsErrorCodeExample = ConnectorField("500-OFFLINE", "An error code returned by the CBS")

View File

@ -20,7 +20,7 @@ import code.api.v2_2_0.{CreateAccountJSONV220, JSONFactory220}
import code.api.v3_0_0.JSONFactory300
import code.api.v3_0_0.JSONFactory300.createAdapterInfoJson
import code.api.v3_1_0.JSONFactory310._
import code.bankconnectors.Connector
import code.bankconnectors.{Connector, LocalMappedConnector}
import code.bankconnectors.rest.RestConnector_vMar2019
import code.consent.{ConsentStatus, Consents}
import code.consumer.Consumers
@ -28,7 +28,7 @@ import code.context.{UserAuthContextUpdateProvider, UserAuthContextUpdateStatus}
import code.entitlement.Entitlement
import code.kafka.KafkaHelper
import code.loginattempts.LoginAttempt
import code.methodrouting.{MethodRoutingCommons, MethodRoutingParam}
import code.methodrouting.{MethodRoutingCommons, MethodRoutingParam, MethodRoutingT}
import code.metrics.APIMetrics
import code.model._
import code.model.dataAccess.{AuthUser, BankAccountCreation}
@ -42,6 +42,7 @@ import com.github.dwickern.macros.NameOf.nameOf
import com.nexmo.client.NexmoClient
import com.nexmo.client.sms.messages.TextMessage
import com.openbankproject.commons.model.{CreditLimit, _}
import com.openbankproject.commons.util.ReflectUtils
import net.liftweb.common.{Box, Empty, Full}
import net.liftweb.http.S
import net.liftweb.http.provider.HTTPParam
@ -3907,15 +3908,20 @@ trait APIMethods310 {
"Get MethodRoutings",
s"""Get the all MethodRoutings.
|
|optional request parameters:
|Query url parameters:
|
|* method_name: filter with method_name, url example: /management/method_routings?method_name=getBank
|* method_name: filter with method_name
|* active: if active = true, it will show all the webui_ props. Even if they are set yet, we will retrun all the default webui_ props
|
|eg:
|${getObpApiRoot}/v3.1.0/management/method_routings?active=true
|${getObpApiRoot}/v3.1.0/management/method_routings?method_name=getBank
|
|""",
emptyObjectJson,
ListResult(
"method_routings",
(List(MethodRoutingCommons("getBanks", "rest_vMar2019", false, Some("some_bank_.*"), Some(List(MethodRoutingParam("url", "http://mydomain.com/xxx"))), Some("method-routing-id"))))
(List(MethodRoutingCommons("getBanks", "rest_vMar2019", false, Some("some_bank_.*"), List(MethodRoutingParam("url", "http://mydomain.com/xxx")), Some("method-routing-id"))))
)
,
List(
@ -3937,12 +3943,42 @@ trait APIMethods310 {
_ <- NewStyle.function.hasEntitlement("", u.userId, ApiRole.canGetMethodRoutings, callContext)
methodRoutings <- NewStyle.function.getMethodRoutingsByMethdName(req.param("method_name"))
} yield {
val listCommons: List[MethodRoutingCommons] = methodRoutings
(ListResult("method_routings", listCommons), HttpCode.`200`(callContext))
val listCommons: List[MethodRoutingCommons] = req.param("active") match {
case Full("true") => methodRoutings ++ getDefaultMethodRountings(methodRoutings )
case _ => methodRoutings
}
(ListResult("method_routings", listCommons.map(_.toJson)), HttpCode.`200`(callContext))
}
}
}
/**
* get all methodRountings exception saved in DB
* @param methodRoutingsInDB saved in DB methodRouting#methodName
* @return all default methodRounting#methodName, those just in mapped connector
*/
private def getDefaultMethodRountings(methodRoutingsInDB: List[MethodRoutingT]) = {
val methodRountingNamesInDB = methodRoutingsInDB.map(_.methodName).toSet
val methodRegex = """method \S+(?<!\$default\$\d{0,10})""".r.pattern
ReflectUtils.getType(LocalMappedConnector)
.decls
.filter(it => methodRegex.matcher(it.toString).matches())
.filter(_.asMethod.isPublic)
.map(_.asMethod)
.filter(it => !methodRountingNamesInDB.contains(it.name.toString) && it.overrides.size > 0)
.map(it => MethodRoutingCommons(
methodName = it.name.toString,
connectorName = "mapped",
isBankIdExactMatch = false,
bankIdPattern = Some("*"),
parameters= List.empty[MethodRoutingParam],
methodRoutingId = Some(""),
))
.toList
}
resourceDocs += ResourceDoc(
createMethodRouting,
implementedInApiVersion,
@ -3967,9 +4003,9 @@ trait APIMethods310 {
|
|* if bank_id_pattern is regex, special characters need to do escape, for example: bank_id_pattern = "some\\-id_pattern_\\d+"
|""",
MethodRoutingCommons("getBank", "rest_vMar2019", false, Some("some_bankId_.*"), Some(List(MethodRoutingParam("url", "http://mydomain.com/xxx")))),
MethodRoutingCommons("getBank", "rest_vMar2019", false, Some("some_bankId_.*"), List(MethodRoutingParam("url", "http://mydomain.com/xxx"))),
MethodRoutingCommons("getBank", "rest_vMar2019", false, Some("some_bankId_.*"),
Some(List(MethodRoutingParam("url", "http://mydomain.com/xxx"))),
List(MethodRoutingParam("url", "http://mydomain.com/xxx")),
Some("this-method-routing-Id")
),
List(
@ -4002,7 +4038,7 @@ trait APIMethods310 {
Full(methodRouting) <- NewStyle.function.createOrUpdateMethodRouting(postedData)
} yield {
val commonsData: MethodRoutingCommons = methodRouting
(commonsData, HttpCode.`201`(callContext))
(commonsData.toJson, HttpCode.`201`(callContext))
}
}
}
@ -4031,8 +4067,8 @@ trait APIMethods310 {
|
|* if bank_id_pattern is regex, special characters need to do escape, for example: bank_id_pattern = "some\\-id_pattern_\\d+"
|""",
MethodRoutingCommons("getBank", "rest_vMar2019", true, Some("some_bankId"), Some(List(MethodRoutingParam("url", "http://mydomain.com/xxx")))),
MethodRoutingCommons("getBank", "rest_vMar2019", true, Some("some_bankId"),Some(List(MethodRoutingParam("url", "http://mydomain.com/xxx"))), Some("this-method-routing-Id")),
MethodRoutingCommons("getBank", "rest_vMar2019", true, Some("some_bankId"), List(MethodRoutingParam("url", "http://mydomain.com/xxx"))),
MethodRoutingCommons("getBank", "rest_vMar2019", true, Some("some_bankId"), List(MethodRoutingParam("url", "http://mydomain.com/xxx")), Some("this-method-routing-Id")),
List(
UserNotLoggedIn,
UserHasMissingRoles,
@ -4068,7 +4104,7 @@ trait APIMethods310 {
Full(methodRouting) <- NewStyle.function.createOrUpdateMethodRouting(putData)
} yield {
val commonsData: MethodRoutingCommons = methodRouting
(commonsData, HttpCode.`200`(callContext))
(commonsData.toJson, HttpCode.`200`(callContext))
}
}
}
@ -5289,8 +5325,14 @@ trait APIMethods310 {
|
|Get the all WebUiProps key values, those props key with "webui_" can be stored in DB, this endpoint get all from DB.
|
|You can use the url query parameter: *active*. It must be a boolean string. and If active == true, it will show
|combination of explicit (inserted) + implicit (default) method_routings.
|url query parameter:
|active: It must be a boolean string. and If active = true, it will show
| combination of explicit (inserted) + implicit (default) method_routings.
|
|eg:
|${getObpApiRoot}/v3.1.0/management/webui_props
|${getObpApiRoot}/v3.1.0/management/webui_props?active=true
|
|""",
emptyObjectJson,
ListResult(
@ -5323,7 +5365,10 @@ trait APIMethods310 {
explicitWebUiProps <- Future{ MappedWebUiPropsProvider.getAll() }
implicitWebUiPropsRemovedDuplicated = if(isActived){
val implicitWebUiProps = getWebUIPropsPairs.map(webUIPropsPairs=>WebUiPropsCommons(webUIPropsPairs._1, webUIPropsPairs._2, webUiPropsId= Some("default")))
explicitWebUiProps.map(abck =>implicitWebUiProps.filterNot(_.name==abck.name)).flatten
if(explicitWebUiProps.nonEmpty)
//remove the depulicated fields in the webui fileds.
explicitWebUiProps.map(webUiProp =>implicitWebUiProps.filterNot(_.name==webUiProp.name)).flatten
else implicitWebUiProps
} else {
List.empty[WebUiPropsCommons]
}

View File

@ -238,7 +238,7 @@ messageDocs += MessageDoc(
var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString)
CacheKeyFromArguments.buildCacheKey {
Caching.memoizeWithProvider(Some(cacheKey.toString()))(banksTTL second){
val url = getUrl("getBankAccountsBalances" , ("bankIdAccountIds", bankIdAccountIds))
val url = getUrl(callContext,"getBankAccountsBalances" , ("bankIdAccountIds", bankIdAccountIds))
sendGetRequest[InBoundGetBankAccountsBalances](url, callContext)
.map { boxedResult =>
boxedResult match {
@ -293,16 +293,24 @@ messageDocs += MessageDoc(
//TODO please modify this baseUrl to your remote api server base url of this connector
private[this] val baseUrl = "http://localhost:8080/restConnector"
private[this] def getUrl(methodName: String, variables: (String, Any)*): String = {
private[this] def getUrl(callContext: Option[CallContext], methodName: String, variables: (String, Any)*): String = {
// rest connector can have url value in the parameters, key is url
//Temporary solution:
val basicUserAuthContext: List[BasicUserAuthContext] = callContext.map(_.toOutboundAdapterCallContext.outboundAdapterAuthInfo.map(_.userAuthContext)).flatten.flatten.getOrElse(List.empty[BasicUserAuthContext])
val bankId = basicUserAuthContext.find(_.key=="bank-id").map(_.value)
val accountId = basicUserAuthContext.find(_.key=="account-id").map(_.value)
val parameterUrl = if (bankId.isDefined &&accountId.isDefined) s"/${bankId.get},${accountId.get}" else ""
//http://127.0.0.1:8080/restConnector/getBankAccountsBalances/bankIdAccountIds
val urlInMethodRouting = NewStyle.function.getMethodRoutings(Some(methodName))
.flatMap(_.parameters).flatten
.flatMap(_.parameters)
.find(_.key == "url")
.map(_.value)
// http://127.0.0.1:8080/restConnector/getBankAccountsBalances/bankIdAccountIds/dmo.02.de.de,60e65f3f-0743-41f5-9efd-3c6f0438aa42
if(urlInMethodRouting.isDefined) {
return urlInMethodRouting.get
return urlInMethodRouting.get + parameterUrl
}
// convert any type value to string, to fill in the url

View File

@ -46,7 +46,7 @@ object MappedMethodRoutingProvider extends MethodRoutingProvider with CustomJson
val isExactMatch = if(bankIdPattern.isDefined) methodRouting.isBankIdExactMatch else false
val existsMethodRoutingParameters = methodRouting.parameters match {
case Some(parameters) if (parameters.nonEmpty) => parameters
case parameters if (parameters.nonEmpty) => parameters
case _ => List.empty[MethodRoutingParam]
}
@ -88,7 +88,7 @@ class MethodRouting extends MethodRoutingT with LongKeyedMapper[MethodRouting] w
override def connectorName: String = ConnectorName.get
//Here we store all the key-value paris in one big String filed in database.
override def parameters: Option[List[MethodRoutingParam]] = Option(json.parse(if (Parameters.get != null) Parameters.get else "[]").extract[List[MethodRoutingParam]])
override def parameters: List[MethodRoutingParam] = json.parse(if (Parameters.get != null) Parameters.get else "[]").extract[List[MethodRoutingParam]]
}
object MethodRouting extends MethodRouting with LongKeyedMetaMapper[MethodRouting] {

View File

@ -4,6 +4,7 @@ package code.methodrouting
import com.openbankproject.commons.model.{Converter, JsonFieldReName, ProductCollection, ProductCollectionCommons}
import net.liftweb.common.Box
import net.liftweb.json.JsonAST.{JArray, JBool, JField, JNull, JObject, JString}
import net.liftweb.util.SimpleInjector
object MethodRoutingProvider extends SimpleInjector {
@ -24,16 +25,33 @@ trait MethodRoutingT {
*/
def isBankIdExactMatch: Boolean
def connectorName: String
def parameters: Option[List[MethodRoutingParam]]
def parameters: List[MethodRoutingParam]
}
case class MethodRoutingCommons(methodName: String,
connectorName: String,
isBankIdExactMatch: Boolean,
bankIdPattern: Option[String],
parameters: Option[List[MethodRoutingParam]] = None,
parameters: List[MethodRoutingParam] = Nil,
methodRoutingId: Option[String] = None,
) extends MethodRoutingT with JsonFieldReName
) extends MethodRoutingT with JsonFieldReName {
/**
* when serialized to json, the Option filed will be not shown, this method just generate a full fields json, include all None value fields
* @return JObject include all fields
*/
def toJson = {
val paramsJson: List[JObject] = this.parameters.map(param => JObject(List(JField("key", JString(param.key)), JField("value", JString(param.value)))))
JObject(List(
JField("method_name", JString(this.methodName)),
JField("connector_name", JString(this.connectorName)),
JField("is_bank_id_exact_match", JBool(this.isBankIdExactMatch)),
JField("bank_id_pattern", this.bankIdPattern.map(JString(_)).getOrElse(JString("*"))),
JField("parameters", JArray(paramsJson)),
JField("method_routing_id", this.methodRoutingId.map(JString(_)).getOrElse(JNull))
))
}
}
object MethodRoutingCommons extends Converter[MethodRoutingT, MethodRoutingCommons]

View File

@ -89,7 +89,11 @@ class ConsumerRegistration extends MdcLoggable {
val urlOAuthEndpoint = APIUtil.getPropsValue("hostname", "") + "/oauth/initiate"
val urlDirectLoginEndpoint = APIUtil.getPropsValue("hostname", "") + "/my/logins/direct"
val createDirectLoginToken = getWebUiPropsValue("webui_create_directlogin_token_url", "")
val registerConsumerSuccessMessageWebpage = getWebUiPropsValue(
"webui_register_consumer_success_message_webpage",
"Thanks for registering your consumer with the Open Bank API! Here is your developer information. Please save it in a secure location.")
//thanks for registering, here's your key, etc.
"#register-consumer-success-message *" #> registerConsumerSuccessMessageWebpage &
"#app-consumer_id *" #> consumer.id.get &
"#app-name *" #> consumer.name.get &
"#app-redirect-url *" #> consumer.redirectURL &
@ -228,14 +232,14 @@ class ConsumerRegistration extends MdcLoggable {
val params = PlainMailBodyType(registrationMessage) :: List(To(registered.developerEmail.get))
val subject1 : String = "Thank you for registering to use the Open Bank Project API."
val subject2 : String = if (sendSensitive) "This email contains your API keys." else "This email does NOT contain your API keys."
val subject : String = s"$subject1 $subject2"
val webuiRegisterConsumerSuccessMssageEmail : String = getWebUiPropsValue(
"webui_register_consumer_success_message_email",
"Thank you for registering to use the Open Bank Project API.")
//this is an async call
Mailer.sendMail(
From(from),
Subject(subject1),
Subject(webuiRegisterConsumerSuccessMssageEmail),
params :_*
)
}

View File

@ -79,7 +79,7 @@ Berlin 13359, Germany
</div>
<div id="register-consumer-success">
<p>Thanks for registering your consumer with the Open Bank API! Here is your developer information. Please save it in a secure location.</p>
<p id="register-consumer-success-message">Thanks for registering your consumer with the Open Bank API! Here is your developer information. Please save it in a secure location.</p>
<div class="row">
<div class="col-xs-12 col-sm-4">Consumer ID</div>
<div class="col-xs-12 col-sm-8"><span id="app-consumer_id">123</span></div>

View File

@ -54,7 +54,7 @@ class MethodRoutingTest extends V310ServerSetup {
object ApiEndpoint3 extends Tag(nameOf(Implementations3_1_0.getMethodRoutings))
object ApiEndpoint4 extends Tag(nameOf(Implementations3_1_0.deleteMethodRouting))
val rightEntity = MethodRoutingCommons("getBank", "rest_vMar2019", false, Some("some_bankId_.*"), Some(List(MethodRoutingParam("url", "http://mydomain.com/xxx"))))
val rightEntity = MethodRoutingCommons("getBank", "rest_vMar2019", false, Some("some_bankId_.*"), List(MethodRoutingParam("url", "http://mydomain.com/xxx")))
val wrongEntity = MethodRoutingCommons("getBank", "rest_vMar2019", false, Some("some_bankId_([")) // wrong regex

View File

@ -131,13 +131,13 @@ class WebUiPropsTest extends V310ServerSetup {
webUiPropssGetJson.size should be (1)
// val requestGet310AddedQueryParameter = requestGet310.addQueryParameter("active", "true")
// val responseGet310AddedQueryParameter = makeGetRequest(requestGet310AddedQueryParameter)
// Then("We should get a 200")
// responseGet310AddedQueryParameter.code should equal(200)
// val responseJson = responseGet310AddedQueryParameter.body \ "webui_props"
// val responseGet310AddedQueryParameterJson = responseJson.extract[List[WebUiPropsCommons]]
// responseGet310AddedQueryParameterJson.size >1 should be (true)
val requestGet310AddedQueryParameter = requestGet310.addQueryParameter("active", "true")
val responseGet310AddedQueryParameter = makeGetRequest(requestGet310AddedQueryParameter)
Then("We should get a 200")
responseGet310AddedQueryParameter.code should equal(200)
val responseJson = responseGet310AddedQueryParameter.body \ "webui_props"
val responseGet310AddedQueryParameterJson = responseJson.extract[List[WebUiPropsCommons]]
responseGet310AddedQueryParameterJson.size >1 should be (true)
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanDeleteWebUiProps.toString)
When("We make a request v3.1.0 with the Role " + canDeleteWebUiProps)

View File

@ -3,6 +3,8 @@
### Most recent changes at top of file
```
Date Commit Action
21/08/2019 4ac93f1c Added props: webui_register_consumer_success_message_webpage and webui_register_consumer_success_message_email.
These messages will be shown to developers on the webpage or email, when they register the consumer successfully.
05/07/2019 7032ce3 Added props: webui_sandbox_introduction, To display the introduction page for sandbox.
It supports the markdown format.It will show the introduction OBP-API home page `INTRODUCTION`
page and also for Glossary `Sandbox Introduction`.