Merge pull request #1092 from constantine2nd/develop

Added getCallsLimit endpoint
This commit is contained in:
Simon Redfern 2018-09-24 13:53:12 +02:00 committed by GitHub
commit b8384ea8d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 138 additions and 28 deletions

View File

@ -642,7 +642,7 @@ def filterResourceDocs(allResources: List[ResourceDoc], showCore: Option[Boolean
cc =>
val apiDetails: JValue = {
val hostedBy = new HostedBy("Dummy Org", "contact@example.com", "12345", "http://www.example.com")
val apiInfoJSON = new APIInfoJSON(apiVersion, apiVersionStatus, gitCommit, "dummy-connector", hostedBy, Akka(APIUtil.akkaSanityCheck()))
val apiInfoJSON = new APIInfoJSON(apiVersion, apiVersionStatus, gitCommit, "dummy-connector", hostedBy, Akka(APIUtil.akkaSanityCheck()), None)
Extraction.decompose(apiInfoJSON)
}

View File

@ -441,6 +441,8 @@ object SwaggerDefinitionsJSON {
val akka = Akka(
remote_data_secret_matched = Option(true)
)
val rateLimiting = Option(RateLimiting(true, Option(true)))
val apiInfoJSON = APIInfoJSON(
version = "String",
@ -448,7 +450,8 @@ object SwaggerDefinitionsJSON {
git_commit = "String",
connector = "String",
hosted_by = hostedBy,
akka = akka
akka = akka,
rate_limiting = rateLimiting
)
/* val aggregateMetricsJSON = AggregateMetricJSON(

View File

@ -169,8 +169,11 @@ object ApiRole {
case class CanReadUserLockedStatus(requiresBankId: Boolean = false) extends ApiRole
lazy val canReadUserLockedStatus = CanReadUserLockedStatus()
case class CanSetCallLimit (requiresBankId: Boolean = false) extends ApiRole
lazy val canSetCallLimit = CanSetCallLimit()
case class CanSetCallLimits(requiresBankId: Boolean = false) extends ApiRole
lazy val canSetCallLimits = CanSetCallLimits()
case class CanReadCallLimits(requiresBankId: Boolean = false) extends ApiRole
lazy val canReadCallLimits = CanReadCallLimits()
case class CanCheckFundsAvailable (requiresBankId: Boolean = false) extends ApiRole
lazy val canCheckFundsAvailable = CanCheckFundsAvailable()
@ -235,7 +238,8 @@ object ApiRole {
canDeleteScopeAtAnyBank ::
canDeleteScopeAtOneBank ::
canUnlockUser ::
canSetCallLimit ::
canSetCallLimits ::
canReadCallLimits ::
canReadUserLockedStatus ::
canCheckFundsAvailable ::
canCreateWebHook ::

View File

@ -1,5 +1,7 @@
package code.api.util
import java.util.UUID
import code.api.util.LimitCallPeriod.LimitCallPeriod
import code.util.Helper.MdcLoggable
import net.liftweb.util.Props
@ -60,6 +62,17 @@ object LimitCallsUtil extends MdcLoggable {
new Jedis(url, port)
}
def isRedisAvailable() = {
try {
val uuid = UUID.randomUUID().toString()
jedis.connect()
jedis.set(uuid, "10")
jedis.exists(uuid) == true
} catch {
case e : Throwable => false
}
}
private def createUniqueKey(consumerKey: String, period: LimitCallPeriod) = consumerKey + LimitCallPeriod.toString(period)
def underConsumerLimits(consumerKey: String, period: LimitCallPeriod, limit: Long): Boolean = {

View File

@ -68,6 +68,7 @@ object NewStyle {
(nameOf(Implementations3_1_0.getBadLoginStatus), ApiVersion.v3_1_0.toString),
(nameOf(Implementations3_1_0.unlockUser), ApiVersion.v3_1_0.toString),
(nameOf(Implementations3_1_0.callsLimit), ApiVersion.v3_1_0.toString),
(nameOf(Implementations3_1_0.getCallsLimit), ApiVersion.v3_1_0.toString),
(nameOf(Implementations3_1_0.checkFundsAvailable), ApiVersion.v3_1_0.toString),
(nameOf(Implementations3_1_0.getConsumer), ApiVersion.v3_1_0.toString),
(nameOf(Implementations3_1_0.getConsumersForCurrentUser), ApiVersion.v3_1_0.toString),

View File

@ -2,12 +2,13 @@ package code.api.v1_2_1
import java.net.URL
import java.util.UUID.randomUUID
import com.tesobe.CacheKeyFromArguments
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._
import code.api.cache.Caching
import code.api.util.APIUtil._
import code.api.util.ErrorMessages._
import code.api.util.{APIUtil, ApiVersion, CallContext, ErrorMessages}
import code.api.util._
import code.bankconnectors._
import code.metadata.comments.Comments
import code.metadata.counterparties.Counterparties
@ -34,7 +35,7 @@ trait APIMethods121 {
self: RestHelper =>
val apiMethods121GetTransactionsTTL = APIUtil.getPropsValue("connector.cache.ttl.seconds.APIMethods121.getTransactions", "0").toInt * 1000 // Miliseconds
// helper methods begin here
private def privateBankAccountsListToJson(bankAccounts: List[BankAccount], privateViewsUserCanAccess: List[View]): JValue = {
@ -50,7 +51,7 @@ trait APIMethods121 {
val accounts = new AccountsJSON(accJson)
Extraction.decompose(accounts)
}
private def publicBankAccountsListToJson(bankAccounts: List[BankAccount], publicViews: List[View]): JValue = {
val accJson : List[AccountJSON] = bankAccounts.map( account => {
val viewsAvailable : List[ViewJSONV121] =
@ -60,7 +61,7 @@ trait APIMethods121 {
.distinct
JSONFactory.createAccountJSON(account,viewsAvailable)
})
val accounts = new AccountsJSON(accJson)
Extraction.decompose(accounts)
}
@ -90,9 +91,10 @@ trait APIMethods121 {
val organisationWebsite = APIUtil.getPropsValue("organisation_website", "https://www.tesobe.com")
val connector = APIUtil.getPropsValue("connector").openOrThrowException("no connector set")
val rateLimiting = RateLimiting(LimitCallsUtil.useConsumerLimits, Some(LimitCallsUtil.isRedisAvailable()))
val hostedBy = new HostedBy(organisation, email, phone, organisationWebsite)
val apiInfoJSON = new APIInfoJSON(apiVersion.vDottedApiVersion(), apiVersionStatus, gitCommit, connector, hostedBy, Akka(APIUtil.akkaSanityCheck()))
val apiInfoJSON = new APIInfoJSON(apiVersion.vDottedApiVersion(), apiVersionStatus, gitCommit, connector, hostedBy, Akka(APIUtil.akkaSanityCheck()), Some(rateLimiting))
Extraction.decompose(apiInfoJSON)
}
apiDetails

View File

@ -42,7 +42,8 @@ case class APIInfoJSON(
git_commit : String,
connector : String,
hosted_by : HostedBy,
akka: Akka
akka: Akka,
rate_limiting: Option[RateLimiting]
)
case class HostedBy(
organisation : String,
@ -51,6 +52,8 @@ case class HostedBy(
organisation_website: String
)
case class Akka(remote_data_secret_matched: Option[Boolean])
case class RateLimiting(enabled: Boolean, redis_available: Option[Boolean])
case class ErrorMessage(
error : String
)

View File

@ -744,7 +744,7 @@ trait APIMethods140 extends MdcLoggable with APIMethods130 with APIMethods121{
cc =>
val apiDetails: JValue = {
val hostedBy = new HostedBy("Dummy Org", "contact@example.com", "12345", "https://www.example.com")
val apiInfoJSON = new APIInfoJSON(apiVersion.vDottedApiVersion, apiVersionStatus, gitCommit, "DUMMY", hostedBy, Akka(APIUtil.akkaSanityCheck()))
val apiInfoJSON = new APIInfoJSON(apiVersion.vDottedApiVersion, apiVersionStatus, gitCommit, "DUMMY", hostedBy, Akka(APIUtil.akkaSanityCheck()), None)
Extraction.decompose(apiInfoJSON)
}

View File

@ -544,15 +544,15 @@ trait APIMethods310 {
),
Catalogs(notCore, notPSD2, notOBWG),
List(apiTagConsumer),
Some(List(canSetCallLimit)))
Some(List(canSetCallLimits)))
lazy val callsLimit : OBPEndpoint = {
case "management" :: "consumers" :: consumerId :: "consumer" :: "calls_limit" :: Nil JsonPut json -> _ => {
cc =>
for {
(Full(u), callContext) <- extractCallContext(UserNotLoggedIn, cc)
_ <- Helper.booleanToFuture(failMsg = UserHasMissingRoles + CanSetCallLimit) {
hasEntitlement("", u.userId, canSetCallLimit)
_ <- Helper.booleanToFuture(failMsg = UserHasMissingRoles + CanSetCallLimits) {
hasEntitlement("", u.userId, canSetCallLimits)
}
postJson <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the $CallLimitJson ", 400, callContext) {
json.extract[CallLimitJson]
@ -578,6 +578,53 @@ trait APIMethods310 {
}
}
resourceDocs += ResourceDoc(
getCallsLimit,
implementedInApiVersion,
"getCallsLimit",
"GET",
"/management/consumers/CONSUMER_ID/consumer/calls_limit",
"Get Calls' Limit for a Consumer",
s"""
|Get calls' limit per Consumer.
|${authenticationRequiredMessage(true)}
|
|""".stripMargin,
callLimitJson,
callLimitJson,
List(
UserNotLoggedIn,
InvalidJsonFormat,
InvalidConsumerId,
ConsumerNotFoundByConsumerId,
UserHasMissingRoles,
UpdateConsumerError,
UnknownError
),
Catalogs(notCore, notPSD2, notOBWG),
List(apiTagConsumer),
Some(List(canSetCallLimits)))
lazy val getCallsLimit : OBPEndpoint = {
case "management" :: "consumers" :: consumerId :: "consumer" :: "calls_limit" :: Nil JsonGet _ => {
cc =>
for {
(Full(u), callContext) <- extractCallContext(UserNotLoggedIn, cc)
_ <- Helper.booleanToFuture(failMsg = UserHasMissingRoles + CanReadCallLimits) {
hasEntitlement("", u.userId, canReadCallLimits)
}
consumerIdToLong <- NewStyle.function.tryons(s"$InvalidConsumerId", 400, callContext) {
consumerId.toLong
}
consumer <- Consumers.consumers.vend.getConsumerByPrimaryIdFuture(consumerIdToLong) map {
unboxFullOrFail(_, callContext, ConsumerNotFoundByConsumerId, 400)
}
} yield {
(createCallLimitJson(consumer), callContext.map(_.copy(httpCode = Some(200))))
}
}
}

View File

@ -291,6 +291,7 @@ object OBPAPI3_1_0 extends OBPRestHelper with APIMethods130 with APIMethods140 w
Implementations3_1_0.getBadLoginStatus ::
Implementations3_1_0.unlockUser ::
Implementations3_1_0.callsLimit ::
Implementations3_1_0.getCallsLimit ::
Implementations3_1_0.checkFundsAvailable ::
// Implementations3_1_0.getConsumer ::
Implementations3_1_0.getConsumersForCurrentUser ::

View File

@ -2,7 +2,7 @@ package code.api.v3_1_0
import code.api.ErrorMessage
import code.api.util.APIUtil.OAuth._
import code.api.util.ApiRole.CanSetCallLimit
import code.api.util.ApiRole.{CanReadCallLimits, CanSetCallLimits}
import code.api.util.ErrorMessages.{UserHasMissingRoles, UserNotLoggedIn}
import code.api.util.{APIUtil, ApiRole, ApiVersion}
import code.api.v3_1_0.OBPAPI3_1_0.Implementations3_1_0
@ -23,6 +23,7 @@ class RateLimitTest extends V310ServerSetup {
*/
object VersionOfApi extends Tag(ApiVersion.v3_1_0.toString)
object ApiEndpoint extends Tag(nameOf(Implementations3_1_0.callsLimit))
object ApiEndpoint2 extends Tag(nameOf(Implementations3_1_0.getCallsLimit))
val callLimitJson1 = CallLimitJson(
per_minute_call_limit = "-1",
@ -39,7 +40,7 @@ class RateLimitTest extends V310ServerSetup {
per_month_call_limit = "-1"
)
feature("Rate Limit - v3.1.0")
feature("Rate Limit - " + ApiEndpoint + " - " + VersionOfApi)
{
scenario("We will try to set calls limit per minute for a Consumer - unauthorized access", ApiEndpoint, VersionOfApi) {
@ -53,36 +54,34 @@ class RateLimitTest extends V310ServerSetup {
And("error should be " + UserNotLoggedIn)
response310.body.extract[ErrorMessage].error should equal (UserNotLoggedIn)
}
scenario("We will try to set calls limit per minute without a proper Role " + ApiRole.canGetConsumers, ApiEndpoint, VersionOfApi) {
When("We make a request v3.1.0 without a Role " + ApiRole.canSetCallLimit)
scenario("We will try to set calls limit per minute without a proper Role " + ApiRole.canSetCallLimits, ApiEndpoint, VersionOfApi) {
When("We make a request v3.1.0 without a Role " + ApiRole.canSetCallLimits)
val Some((c, _)) = user1
val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.id.get).getOrElse(0)
val request310 = (v3_1_0_Request / "management" / "consumers" / consumerId / "consumer" / "calls_limit").PUT <@(user1)
val response310 = makePutRequest(request310, write(callLimitJson1))
Then("We should get a 403")
org.scalameta.logger.elem(response310.body)
response310.code should equal(403)
And("error should be " + UserHasMissingRoles + CanSetCallLimit)
response310.body.extract[ErrorMessage].error should equal (UserHasMissingRoles + CanSetCallLimit)
And("error should be " + UserHasMissingRoles + CanSetCallLimits)
response310.body.extract[ErrorMessage].error should equal (UserHasMissingRoles + CanSetCallLimits)
}
scenario("We will try to set calls limit per minute with a proper Role " + ApiRole.canGetConsumers, ApiEndpoint, VersionOfApi) {
When("We make a request v3.1.0 with a Role " + ApiRole.canSetCallLimit)
scenario("We will try to set calls limit per minute with a proper Role " + ApiRole.canSetCallLimits, ApiEndpoint, VersionOfApi) {
When("We make a request v3.1.0 with a Role " + ApiRole.canSetCallLimits)
val Some((c, _)) = user1
val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.id.get).getOrElse(0)
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanSetCallLimit.toString)
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanSetCallLimits.toString)
val request310 = (v3_1_0_Request / "management" / "consumers" / consumerId / "consumer" / "calls_limit").PUT <@(user1)
val response310 = makePutRequest(request310, write(callLimitJson1))
Then("We should get a 200")
org.scalameta.logger.elem(response310.body)
response310.code should equal(200)
response310.body.extract[CallLimitJson]
}
scenario("We will set calls limit per minute for a Consumer", ApiEndpoint, VersionOfApi) {
if(APIUtil.getPropsAsBoolValue("use_consumer_limits", false)) {
When("We make a request v3.1.0 with a Role " + ApiRole.canSetCallLimit)
When("We make a request v3.1.0 with a Role " + ApiRole.canSetCallLimits)
val Some((c, _)) = user1
val consumerId: Long = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.id.get).getOrElse(0)
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanSetCallLimit.toString)
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanSetCallLimits.toString)
val request310 = (v3_1_0_Request / "management" / "consumers" / consumerId / "consumer" / "calls_limit").PUT <@(user1)
val response01 = makePutRequest(request310, write(callLimitJson2))
Then("We should get a 200")
@ -104,4 +103,41 @@ class RateLimitTest extends V310ServerSetup {
}
}
feature("Rate Limit - " + ApiEndpoint2 + " - " + VersionOfApi)
{
scenario("We will try to get calls limit per minute for a Consumer - unauthorized access", ApiEndpoint2, VersionOfApi) {
When("We make a request v3.1.0")
val Some((c, _)) = user1
val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.id.get).getOrElse(0)
val request310 = (v3_1_0_Request / "management" / "consumers" / consumerId / "consumer" / "calls_limit").GET
val response310 = makeGetRequest(request310)
Then("We should get a 400")
response310.code should equal(400)
And("error should be " + UserNotLoggedIn)
response310.body.extract[ErrorMessage].error should equal (UserNotLoggedIn)
}
scenario("We will try to get calls limit per minute without a proper Role " + ApiRole.canReadCallLimits, ApiEndpoint2, VersionOfApi) {
When("We make a request v3.1.0 without a Role " + ApiRole.canReadCallLimits)
val Some((c, _)) = user1
val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.id.get).getOrElse(0)
val request310 = (v3_1_0_Request / "management" / "consumers" / consumerId / "consumer" / "calls_limit").GET <@(user1)
val response310 = makeGetRequest(request310)
Then("We should get a 403")
response310.code should equal(403)
And("error should be " + UserHasMissingRoles + CanReadCallLimits)
response310.body.extract[ErrorMessage].error should equal (UserHasMissingRoles + CanReadCallLimits)
}
scenario("We will try to get calls limit per minute with a proper Role " + ApiRole.canReadCallLimits, ApiEndpoint2, VersionOfApi) {
When("We make a request v3.1.0 with a Role " + ApiRole.canReadCallLimits)
val Some((c, _)) = user1
val consumerId = Consumers.consumers.vend.getConsumerByConsumerKey(c.key).map(_.id.get).getOrElse(0)
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, ApiRole.CanReadCallLimits.toString)
val request310 = (v3_1_0_Request / "management" / "consumers" / consumerId / "consumer" / "calls_limit").GET <@(user1)
val response310 = makeGetRequest(request310)
Then("We should get a 200")
response310.code should equal(200)
response310.body.extract[CallLimitJson]
}
}
}