Skip to content

Commit

Permalink
Merge pull request #23 from hmrc/API-3790
Browse files Browse the repository at this point in the history
API-3790: added ability to add and remove subscriptions
  • Loading branch information
cjrowe authored May 14, 2019
2 parents 2ea06d9 + ef5ce5e commit 3c1c73f
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,17 @@ class AwsApiGatewayConnector @Inject()(http: HttpClient, config: AwsApiGatewayCo
val awsApiKey: String = config.awsApiKey
val apiKeyHeaderName = "x-api-key"

def createOrUpdateApplication(upsertApplicationRequest: UpsertApplicationRequest)(hc: HeaderCarrier): Future[HasSucceeded] = {
def createOrUpdateApplication(applicationName: String, upsertApplicationRequest: UpsertApplicationRequest)(hc: HeaderCarrier): Future[HasSucceeded] = {
implicit val headersWithoutAuthorization: HeaderCarrier = hc
.copy(authorization = None)
.withExtraHeaders(apiKeyHeaderName -> awsApiKey, CONTENT_TYPE -> JSON)

http.PUT(serviceBaseUrl, upsertApplicationRequest) map { result =>
http.PUT(s"$serviceBaseUrl/$applicationName", upsertApplicationRequest) map { result =>
val requestId = (result.json \ "RequestId").as[String]
Logger.info(s"Successfully created or updated application '${upsertApplicationRequest.applicationName}' in AWS API Gateway with request ID $requestId")
Logger.info(s"Successfully created or updated application '$applicationName' in AWS API Gateway with request ID $requestId")
HasSucceeded
} recover {
awsRecovery(s"Failed to create or update application ${upsertApplicationRequest.applicationName} in AWS API Gateway")
awsRecovery(s"Failed to create or update application '$applicationName' in AWS API Gateway")
}
}

Expand All @@ -60,7 +60,35 @@ class AwsApiGatewayConnector @Inject()(http: HttpClient, config: AwsApiGatewayCo
Logger.info(s"Successfully deleted application '$applicationName' from AWS API Gateway with request ID $requestId")
HasSucceeded
} recover {
awsRecovery(s"Failed to delete application $applicationName from AWS API Gateway")
awsRecovery(s"Failed to delete application '$applicationName' from AWS API Gateway")
}
}

def addSubscription(applicationName: String, apiName: String)(hc: HeaderCarrier): Future[HasSucceeded] = {
implicit val headersWithoutAuthorization: HeaderCarrier = hc
.copy(authorization = None)
.withExtraHeaders(apiKeyHeaderName -> awsApiKey)

http.PUT(s"$serviceBaseUrl/$applicationName/subscription/$apiName", "") map { result =>
val requestId = (result.json \ "RequestId").as[String]
Logger.info(s"Successfully added subscription '$applicationName/$apiName' in AWS API Gateway with request ID $requestId")
HasSucceeded
} recover {
awsRecovery(s"Failed to add subscription '$applicationName/$apiName' in AWS API Gateway")
}
}

def removeSubscription(applicationName: String, apiName: String)(hc: HeaderCarrier): Future[HasSucceeded] = {
implicit val headersWithoutAuthorization: HeaderCarrier = hc
.copy(authorization = None)
.withExtraHeaders(apiKeyHeaderName -> awsApiKey)

http.DELETE(s"$serviceBaseUrl/$applicationName/subscription/$apiName") map { result =>
val requestId = (result.json \ "RequestId").as[String]
Logger.info(s"Successfully deleted subscription '$applicationName/$apiName' from AWS API Gateway with request ID $requestId")
HasSucceeded
} recover {
awsRecovery(s"Failed to delete subscription '$applicationName/$apiName' from AWS API Gateway")
}
}

Expand All @@ -73,4 +101,4 @@ class AwsApiGatewayConnector @Inject()(http: HttpClient, config: AwsApiGatewayCo
}

case class AwsApiGatewayConfig(baseUrl: String, awsApiKey: String)
case class UpsertApplicationRequest(applicationName: String, usagePlan: RateLimitTier, serverToken: String)
case class UpsertApplicationRequest(usagePlan: RateLimitTier, serverToken: String)
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import javax.inject.{Inject, Singleton}
import uk.gov.hmrc.http.HeaderCarrier
import uk.gov.hmrc.thirdpartyapplication.connector.{AwsApiGatewayConnector, UpsertApplicationRequest, Wso2ApiStoreConnector}
import uk.gov.hmrc.thirdpartyapplication.models.RateLimitTier.{BRONZE, RateLimitTier}
import uk.gov.hmrc.thirdpartyapplication.models._
import uk.gov.hmrc.thirdpartyapplication.models.{Wso2Api, _}
import uk.gov.hmrc.thirdpartyapplication.scheduled.Retrying

import scala.collection._
Expand Down Expand Up @@ -80,7 +80,7 @@ class RealApiGatewayStore @Inject()(wso2APIStoreConnector: Wso2ApiStoreConnector
sandboxKeys <- wso2APIStoreConnector.generateApplicationKey(cookie, wso2ApplicationName, Environment.SANDBOX)
prodKeys <- wso2APIStoreConnector.generateApplicationKey(cookie, wso2ApplicationName, Environment.PRODUCTION)
_ <- wso2APIStoreConnector.logout(cookie)
_ <- awsApiGatewayConnector.createOrUpdateApplication(UpsertApplicationRequest(wso2ApplicationName, BRONZE, prodKeys.accessToken))(hc)
_ <- awsApiGatewayConnector.createOrUpdateApplication(wso2ApplicationName, UpsertApplicationRequest(BRONZE, prodKeys.accessToken))(hc)
} yield ApplicationTokens(prodKeys, sandboxKeys)
}

Expand Down Expand Up @@ -111,7 +111,7 @@ class RealApiGatewayStore @Inject()(wso2APIStoreConnector: Wso2ApiStoreConnector
withLogin(app.wso2Username, app.wso2Password) {
wso2APIStoreConnector.updateApplication(_, app.wso2ApplicationName, rateLimitTier)
} flatMap { _ =>
awsApiGatewayConnector.createOrUpdateApplication(UpsertApplicationRequest(app.wso2ApplicationName, rateLimitTier, app.tokens.production.accessToken))(hc)
awsApiGatewayConnector.createOrUpdateApplication(app.wso2ApplicationName, UpsertApplicationRequest(rateLimitTier, app.tokens.production.accessToken))(hc)
}
}

Expand All @@ -130,16 +130,24 @@ class RealApiGatewayStore @Inject()(wso2APIStoreConnector: Wso2ApiStoreConnector
wso2ApplicationName: String,
api: APIIdentifier,
rateLimitTier: Option[RateLimitTier])
(implicit hc: HeaderCarrier): Future[HasSucceeded] =
(implicit hc: HeaderCarrier): Future[HasSucceeded] = {
val wso2Api = Wso2Api.create(api)
withLogin(wso2Username, wso2Password) {
wso2APIStoreConnector.addSubscription(_, wso2ApplicationName, Wso2Api.create(api), rateLimitTier, 0)
wso2APIStoreConnector.addSubscription(_, wso2ApplicationName, wso2Api, rateLimitTier, 0)
} flatMap { _ =>
awsApiGatewayConnector.addSubscription(wso2ApplicationName, wso2Api.name)(hc)
}
}

override def removeSubscription(wso2Username: String, wso2Password: String, wso2ApplicationName: String, api: APIIdentifier)
(implicit hc: HeaderCarrier): Future[HasSucceeded] =
(implicit hc: HeaderCarrier): Future[HasSucceeded] = {
val wso2Api = Wso2Api.create(api)
withLogin(wso2Username: String, wso2Password) {
wso2APIStoreConnector.removeSubscription(_, wso2ApplicationName, Wso2Api.create(api), 0)
wso2APIStoreConnector.removeSubscription(_, wso2ApplicationName, wso2Api, 0)
} flatMap { _ =>
awsApiGatewayConnector.removeSubscription(wso2ApplicationName, wso2Api.name)(hc)
}
}

override def resubscribeApi(originalApis: Seq[APIIdentifier],
wso2Username: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,14 @@ class AwsApiGatewayConnectorSpec extends UnitSpec with WithFakeApplication with
private val wireMockUrl = s"http://$stubHost:$stubPort"
private val wireMockServer = new WireMockServer(wireMockConfig().port(stubPort))
private val applicationName = "api-platform-app"
private val apiName = "hello--1.0"

trait Setup {
SharedMetricRegistries.clear()
WireMock.reset()
implicit val hc: HeaderCarrier = HeaderCarrier(authorization = Some(Authorization("foo")))

val upsertApplicationRequest = UpsertApplicationRequest("foobar-app", SILVER, UUID.randomUUID().toString)
val upsertApplicationRequest = UpsertApplicationRequest(SILVER, UUID.randomUUID().toString)
val http: HttpClient = fakeApplication.injector.instanceOf[HttpClient]
val awsApiKey: String = UUID.randomUUID().toString
val config: AwsApiGatewayConfig = AwsApiGatewayConfig(wireMockUrl, awsApiKey)
Expand All @@ -73,28 +74,28 @@ class AwsApiGatewayConnectorSpec extends UnitSpec with WithFakeApplication with

"createOrUpdateApplication" should {
"send the right body and headers when creating or updating an application" in new Setup {
stubFor(put(urlPathEqualTo("/v1/application"))
stubFor(put(urlPathEqualTo(s"/v1/application/$applicationName"))
.willReturn(
aResponse()
.withStatus(OK)
.withBody(s"""{ "RequestId" : "${UUID.randomUUID().toString}" }""")))

await(underTest.createOrUpdateApplication(upsertApplicationRequest)(hc))
await(underTest.createOrUpdateApplication(applicationName, upsertApplicationRequest)(hc))

wireMockServer.verify(putRequestedFor(urlEqualTo("/v1/application"))
wireMockServer.verify(putRequestedFor(urlEqualTo(s"/v1/application/$applicationName"))
.withHeader(CONTENT_TYPE, equalTo(JSON))
.withHeader("x-api-key", equalTo(awsApiKey))
.withoutHeader(AUTHORIZATION)
.withRequestBody(equalToJson(Json.toJson(upsertApplicationRequest).toString())))
}

"return HasSucceeded when application creation or update fails" in new Setup {
stubFor(put(urlPathEqualTo("/v1/application"))
stubFor(put(urlPathEqualTo(s"/v1/application/$applicationName"))
.willReturn(
aResponse()
.withStatus(INTERNAL_SERVER_ERROR)))

await(underTest.createOrUpdateApplication(upsertApplicationRequest)(hc)) shouldBe HasSucceeded
await(underTest.createOrUpdateApplication(applicationName, upsertApplicationRequest)(hc)) shouldBe HasSucceeded
}
}

Expand Down Expand Up @@ -122,4 +123,55 @@ class AwsApiGatewayConnectorSpec extends UnitSpec with WithFakeApplication with
await(underTest.deleteApplication(applicationName)(hc)) shouldBe HasSucceeded
}
}

"addSubscription" should {
"send the x-api-key header when adding a subscription" in new Setup {
stubFor(put(urlPathEqualTo(s"/v1/application/$applicationName/subscription/$apiName"))
.willReturn(
aResponse()
.withStatus(OK)
.withBody(s"""{ "RequestId" : "${UUID.randomUUID().toString}" }""")))

await(underTest.addSubscription(applicationName, apiName)(hc))

wireMockServer.verify(putRequestedFor(urlEqualTo(s"/v1/application/$applicationName/subscription/$apiName"))
.withHeader("x-api-key", equalTo(awsApiKey))
.withoutHeader(AUTHORIZATION)
.withRequestBody(equalTo("\"\"")))
}

"return HasSucceeded when subscription addition fails" in new Setup {
stubFor(post(urlPathEqualTo(s"/v1/application/$applicationName/subscription/$apiName"))
.willReturn(
aResponse()
.withStatus(INTERNAL_SERVER_ERROR)))

await(underTest.addSubscription(applicationName, apiName)(hc)) shouldBe HasSucceeded
}
}

"removeSubscription" should {
"send the x-api-key header when removing a subscription" in new Setup {
stubFor(delete(urlPathEqualTo(s"/v1/application/$applicationName/subscription/$apiName"))
.willReturn(
aResponse()
.withStatus(OK)
.withBody(s"""{ "RequestId" : "${UUID.randomUUID().toString}" }""")))

await(underTest.removeSubscription(applicationName, apiName)(hc))

wireMockServer.verify(deleteRequestedFor(urlEqualTo(s"/v1/application/$applicationName/subscription/$apiName"))
.withHeader("x-api-key", equalTo(awsApiKey))
.withoutHeader(AUTHORIZATION))
}

"return HasSucceeded when subscription removal fails" in new Setup {
stubFor(delete(urlPathEqualTo(s"/v1/application/$applicationName/subscription/$apiName"))
.willReturn(
aResponse()
.withStatus(INTERNAL_SERVER_ERROR)))

await(underTest.removeSubscription(applicationName, apiName)(hc)) shouldBe HasSucceeded
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class ApiGatewayStoreSpec extends UnitSpec with ScalaFutures with MockitoSugar w
val wso2ApplicationName = "myapplication"
val cookie = "some-cookie-value"
val tokens = ApplicationTokens(EnvironmentToken("aaa", "bbb", "ccc"), EnvironmentToken("111", "222", "333"))
val upsertApplicationRequest = UpsertApplicationRequest(wso2ApplicationName, BRONZE, tokens.production.accessToken)
val upsertApplicationRequest = UpsertApplicationRequest(BRONZE, tokens.production.accessToken)

when(mockWSO2APIStoreConnector.createUser(wso2Username, wso2Password))
.thenReturn(Future.successful(HasSucceeded))
Expand All @@ -71,14 +71,14 @@ class ApiGatewayStoreSpec extends UnitSpec with ScalaFutures with MockitoSugar w
when(mockWSO2APIStoreConnector.generateApplicationKey(cookie, wso2ApplicationName, Environment.PRODUCTION))
.thenReturn(Future.successful(tokens.production))
when(mockWSO2APIStoreConnector.logout(cookie)).thenReturn(Future.successful(HasSucceeded))
when(mockAwsApiGatewayConnector.createOrUpdateApplication(upsertApplicationRequest)(hc)).thenReturn(successful(HasSucceeded))
when(mockAwsApiGatewayConnector.createOrUpdateApplication(wso2ApplicationName, upsertApplicationRequest)(hc)).thenReturn(successful(HasSucceeded))

val result = await(underTest.createApplication(wso2Username, wso2Password, wso2ApplicationName))

result shouldBe tokens

verify(mockWSO2APIStoreConnector).logout(cookie)
verify(mockAwsApiGatewayConnector).createOrUpdateApplication(upsertApplicationRequest)(hc)
verify(mockAwsApiGatewayConnector).createOrUpdateApplication(wso2ApplicationName, upsertApplicationRequest)(hc)
}

}
Expand All @@ -104,19 +104,19 @@ class ApiGatewayStoreSpec extends UnitSpec with ScalaFutures with MockitoSugar w
EnvironmentToken(nextString(2), nextString(2), serverToken),
EnvironmentToken(nextString(2), nextString(2), nextString(2))),
testingState())
val upsertApplicationRequest = UpsertApplicationRequest(wso2ApplicationName, SILVER, serverToken)
val upsertApplicationRequest = UpsertApplicationRequest(SILVER, serverToken)

when(mockWSO2APIStoreConnector.login(wso2Username, wso2Password)).thenReturn(Future.successful(cookie))
when(mockWSO2APIStoreConnector.updateApplication(cookie, wso2ApplicationName, SILVER)).
thenReturn(Future.successful(HasSucceeded))
when(mockWSO2APIStoreConnector.logout(cookie)).thenReturn(Future.successful(HasSucceeded))
when(mockAwsApiGatewayConnector.createOrUpdateApplication(upsertApplicationRequest)(hc)).thenReturn(successful(HasSucceeded))
when(mockAwsApiGatewayConnector.createOrUpdateApplication(wso2ApplicationName, upsertApplicationRequest)(hc)).thenReturn(successful(HasSucceeded))

await(underTest updateApplication(app, SILVER))

verify(mockWSO2APIStoreConnector).updateApplication(cookie, wso2ApplicationName, SILVER)
verify(mockWSO2APIStoreConnector).logout(cookie)
verify(mockAwsApiGatewayConnector).createOrUpdateApplication(upsertApplicationRequest)(hc)
verify(mockAwsApiGatewayConnector).createOrUpdateApplication(wso2ApplicationName, upsertApplicationRequest)(hc)
}

}
Expand Down Expand Up @@ -154,17 +154,19 @@ class ApiGatewayStoreSpec extends UnitSpec with ScalaFutures with MockitoSugar w
val wso2API = Wso2Api("some--context--1.0", "1.0")
val api = APIIdentifier("some/context", "1.0")

"add a subscription to an application in WSO2" in new Setup {
"add a subscription to an application in AWS and WSO2" in new Setup {

when(mockWSO2APIStoreConnector.login(wso2Username, wso2Password)).thenReturn(Future.successful(cookie))
when(mockWSO2APIStoreConnector.addSubscription(cookie, wso2ApplicationName, wso2API, Some(GOLD), 0))
.thenReturn(Future.successful(HasSucceeded))
when(mockWSO2APIStoreConnector.logout(cookie)).thenReturn(Future.successful(HasSucceeded))
when(mockAwsApiGatewayConnector.addSubscription(wso2ApplicationName, wso2API.name)(hc)).thenReturn(successful(HasSucceeded))

await(underTest.addSubscription(wso2Username, wso2Password, wso2ApplicationName, api, Some(GOLD)))

verify(mockWSO2APIStoreConnector).addSubscription(cookie, wso2ApplicationName, wso2API, Some(GOLD), 0)
verify(mockWSO2APIStoreConnector).logout(cookie)
verify(mockAwsApiGatewayConnector).addSubscription(wso2ApplicationName, wso2API.name)(hc)
}

"fail when add subscription fails" in new Setup {
Expand All @@ -182,7 +184,7 @@ class ApiGatewayStoreSpec extends UnitSpec with ScalaFutures with MockitoSugar w

"removeSubscription" should {

"remove a subscription from an application in WSO2" in new Setup {
"remove a subscription from an application in AWS and WSO2" in new Setup {

val wso2Username = "myuser"
val wso2Password = "mypassword"
Expand All @@ -195,11 +197,13 @@ class ApiGatewayStoreSpec extends UnitSpec with ScalaFutures with MockitoSugar w
when(mockWSO2APIStoreConnector.removeSubscription(cookie, wso2ApplicationName, wso2API, 0))
.thenReturn(Future.successful(HasSucceeded))
when(mockWSO2APIStoreConnector.logout(cookie)).thenReturn(Future.successful(HasSucceeded))
when(mockAwsApiGatewayConnector.removeSubscription(wso2ApplicationName, wso2API.name)(hc)).thenReturn(successful(HasSucceeded))

await(underTest.removeSubscription(wso2Username, wso2Password, wso2ApplicationName, api))

verify(mockWSO2APIStoreConnector).removeSubscription(cookie, wso2ApplicationName, wso2API, 0)
verify(mockWSO2APIStoreConnector).logout(cookie)
verify(mockAwsApiGatewayConnector).removeSubscription(wso2ApplicationName, wso2API.name)(hc)
}

}
Expand Down

0 comments on commit 3c1c73f

Please sign in to comment.