diff --git a/app/uk/gov/hmrc/controllers/GatekeeperController.scala b/app/uk/gov/hmrc/controllers/GatekeeperController.scala index 879d63de3..956c00809 100644 --- a/app/uk/gov/hmrc/controllers/GatekeeperController.scala +++ b/app/uk/gov/hmrc/controllers/GatekeeperController.scala @@ -17,14 +17,14 @@ package uk.gov.hmrc.controllers import java.util.UUID -import javax.inject.Inject +import javax.inject.Inject import play.api.libs.json.Json import uk.gov.hmrc.connector.AuthConnector import uk.gov.hmrc.controllers.ErrorCode._ -import uk.gov.hmrc.models.{AuthRole, InvalidStateTransition} -import uk.gov.hmrc.services.{ApplicationService, GatekeeperService} import uk.gov.hmrc.models.JsonFormatters._ +import uk.gov.hmrc.models.{AuthRole, Blocked, InvalidStateTransition} +import uk.gov.hmrc.services.{ApplicationService, GatekeeperService} import scala.concurrent.ExecutionContext.Implicits.global @@ -73,6 +73,12 @@ class GatekeeperController @Inject()(val authConnector: AuthConnector, val appli } recover recovery } + def blockApplication(id: UUID) = requiresRole(AuthRole.APIGatekeeper).async { implicit request => + gatekeeperService.blockApplication(id) map { + case Blocked => Ok + } recover recovery + } + def fetchAppsForGatekeeper = requiresRole(AuthRole.APIGatekeeper).async { gatekeeperService.fetchNonTestingAppsWithSubmittedDate() map { apps => Ok(Json.toJson(apps)) diff --git a/app/uk/gov/hmrc/models/Application.scala b/app/uk/gov/hmrc/models/Application.scala index 847280e7c..6c5752d2a 100644 --- a/app/uk/gov/hmrc/models/Application.scala +++ b/app/uk/gov/hmrc/models/Application.scala @@ -101,7 +101,8 @@ case class ApplicationResponse(id: UUID, state: ApplicationState = ApplicationState(name = TESTING), rateLimitTier: RateLimitTier = BRONZE, trusted: Boolean = false, - checkInformation: Option[CheckInformation] = None) + checkInformation: Option[CheckInformation] = None, + blocked: Boolean = false) object ApplicationResponse { private def getEnvironment(data: ApplicationData, clientId: Option[String]): Option[Environment] = { @@ -142,7 +143,8 @@ object ApplicationResponse { data.state, data.rateLimitTier.getOrElse(BRONZE), trusted, - data.checkInformation) + data.checkInformation, + data.blocked) } } @@ -160,7 +162,8 @@ case class ApplicationData(id: UUID, createdOn: DateTime = DateTimeUtils.now, rateLimitTier: Option[RateLimitTier] = Some(BRONZE), environment: String = Environment.PRODUCTION.toString, - checkInformation: Option[CheckInformation] = None) { + checkInformation: Option[CheckInformation] = None, + blocked: Boolean = false) { lazy val admins = collaborators.filter(_.role == Role.ADMINISTRATOR) } @@ -420,13 +423,9 @@ object RateLimitTier extends Enumeration { sealed trait ApplicationStateChange case object UpliftRequested extends ApplicationStateChange - case object UpliftApproved extends ApplicationStateChange - case object UpliftRejected extends ApplicationStateChange - case object UpliftVerified extends ApplicationStateChange - case object VerificationResent extends ApplicationStateChange - -case object Deleted extends ApplicationStateChange \ No newline at end of file +case object Deleted extends ApplicationStateChange +case object Blocked extends ApplicationStateChange \ No newline at end of file diff --git a/app/uk/gov/hmrc/models/JsonFormatters.scala b/app/uk/gov/hmrc/models/JsonFormatters.scala index 4361e46b2..085f07792 100644 --- a/app/uk/gov/hmrc/models/JsonFormatters.scala +++ b/app/uk/gov/hmrc/models/JsonFormatters.scala @@ -16,11 +16,15 @@ package uk.gov.hmrc.models +import java.util.UUID + +import org.joda.time.DateTime import play.api.libs.functional.syntax._ import play.api.libs.json._ import uk.gov.hmrc.controllers._ import uk.gov.hmrc.models.AccessType.{PRIVILEGED, ROPC, STANDARD} import uk.gov.hmrc.models.OverrideType._ +import uk.gov.hmrc.models.RateLimitTier.RateLimitTier import uk.gov.hmrc.mongo.json.ReactiveMongoFormats import uk.gov.hmrc.play.json.Union import uk.gov.hmrc.services.WSO2RestoreData @@ -105,7 +109,30 @@ object JsonFormatters { implicit val formatEnvironmentToken = Json.format[EnvironmentToken] implicit val formatApplicationTokens = Json.format[ApplicationTokens] implicit val formatSubscriptionData = Json.format[SubscriptionData] - implicit val formatApplicationData = Json.format[ApplicationData] + + val applicationDataReads: Reads[ApplicationData] = ( + (JsPath \ "id").read[UUID] and + (JsPath \ "name").read[String] and + (JsPath \ "normalisedName").read[String] and + (JsPath \ "collaborators").read[Set[Collaborator]] and + (JsPath \ "description").readNullable[String] and + (JsPath \ "wso2Username").read[String] and + (JsPath \ "wso2Password").read[String] and + (JsPath \ "wso2ApplicationName").read[String] and + (JsPath \ "tokens").read[ApplicationTokens] and + (JsPath \ "state").read[ApplicationState] and + (JsPath \ "access").read[Access] and + (JsPath \ "createdOn").read[DateTime] and + (JsPath \ "rateLimitTier").readNullable[RateLimitTier] and + (JsPath \ "environment").read[String] and + (JsPath \ "checkInformation").readNullable[CheckInformation] and + ((JsPath \ "blocked").read[Boolean] or Reads.pure(false)) + )(ApplicationData.apply _) + + implicit val formatApplicationData = { + Format(applicationDataReads, Json.writes[ApplicationData]) + } + implicit val formatCreateApplicationRequest = Json.format[CreateApplicationRequest] implicit val formatUpdateApplicationRequest = Json.format[UpdateApplicationRequest] implicit val formatApplicationResponse = Json.format[ApplicationResponse] @@ -168,7 +195,30 @@ object MongoFormat { implicit val formatApplicationTokens = Json.format[ApplicationTokens] implicit val formatApiIdentifier = Json.format[APIIdentifier] implicit val formatSubscriptionData = Json.format[SubscriptionData] - implicit val formatApplicationData = Json.format[ApplicationData] + + val applicationDataReads: Reads[ApplicationData] = ( + (JsPath \ "id").read[UUID] and + (JsPath \ "name").read[String] and + (JsPath \ "normalisedName").read[String] and + (JsPath \ "collaborators").read[Set[Collaborator]] and + (JsPath \ "description").readNullable[String] and + (JsPath \ "wso2Username").read[String] and + (JsPath \ "wso2Password").read[String] and + (JsPath \ "wso2ApplicationName").read[String] and + (JsPath \ "tokens").read[ApplicationTokens] and + (JsPath \ "state").read[ApplicationState] and + (JsPath \ "access").read[Access] and + (JsPath \ "createdOn").read[DateTime] and + (JsPath \ "rateLimitTier").readNullable[RateLimitTier] and + (JsPath \ "environment").read[String] and + (JsPath \ "checkInformation").readNullable[CheckInformation] and + ((JsPath \ "blocked").read[Boolean] or Reads.pure(false)) + )(ApplicationData.apply _) + + implicit val formatApplicationData = { + OFormat(applicationDataReads, Json.writes[ApplicationData]) + } + implicit val formatWSO2RestoreData = Json.format[WSO2RestoreData] } diff --git a/app/uk/gov/hmrc/services/GatekeeperService.scala b/app/uk/gov/hmrc/services/GatekeeperService.scala index 91fe64e8a..79cbece8a 100644 --- a/app/uk/gov/hmrc/services/GatekeeperService.scala +++ b/app/uk/gov/hmrc/services/GatekeeperService.scala @@ -188,6 +188,17 @@ class GatekeeperService @Inject()(applicationRepository: ApplicationRepository, } } + def blockApplication(applicationId: UUID)(implicit hc: HeaderCarrier): Future[ApplicationStateChange] = { + def block(application: ApplicationData): ApplicationData = { + application.copy(blocked = true) + } + + for { + app <- fetchApp(applicationId) + _ <- applicationRepository.save(block(app)) + } yield Blocked + } + private def fetchApp(applicationId: UUID): Future[ApplicationData] = { lazy val notFoundException = new NotFoundException(s"application not found for id: $applicationId") diff --git a/conf/app.routes b/conf/app.routes index 9762d2400..c52ccaa89 100644 --- a/conf/app.routes +++ b/conf/app.routes @@ -36,6 +36,7 @@ POST /application/:id/approve-uplift @uk.gov.hmr POST /application/:id/reject-uplift @uk.gov.hmrc.controllers.GatekeeperController.rejectUplift(id: java.util.UUID) POST /application/:id/resend-verification @uk.gov.hmrc.controllers.GatekeeperController.resendVerification(id: java.util.UUID) POST /application/:id/delete @uk.gov.hmrc.controllers.GatekeeperController.deleteApplication(id: java.util.UUID) +POST /application/:id/block @uk.gov.hmrc.controllers.GatekeeperController.blockApplication(id: java.util.UUID) POST /application/:id/rate-limit-tier @uk.gov.hmrc.controllers.ApplicationController.updateRateLimitTier(id: java.util.UUID) diff --git a/test/unit/controllers/GatekeeperControllerSpec.scala b/test/unit/controllers/GatekeeperControllerSpec.scala index 6bfa9dd90..3985d0bd4 100644 --- a/test/unit/controllers/GatekeeperControllerSpec.scala +++ b/test/unit/controllers/GatekeeperControllerSpec.scala @@ -21,7 +21,7 @@ import java.util.UUID import org.apache.http.HttpStatus._ import org.joda.time.DateTime import org.mockito.ArgumentCaptor -import org.mockito.Matchers.{eq => eqTo, any, anyString} +import org.mockito.Matchers.{any, anyString, eq => eqTo} import org.mockito.Mockito._ import org.scalatest.concurrent.ScalaFutures import org.scalatest.mockito.MockitoSugar @@ -43,7 +43,9 @@ import common.uk.gov.hmrc.common.LogSuppressing import common.uk.gov.hmrc.testutils.ApplicationStateUtil import scala.concurrent.Future.{failed, successful} -import uk.gov.hmrc.http.{ HeaderCarrier, NotFoundException } +import uk.gov.hmrc.http.{HeaderCarrier, NotFoundException} +import uk.gov.hmrc.models.AuthRole.APIGatekeeper +import uk.gov.hmrc.models.RateLimitTier.SILVER class GatekeeperControllerSpec extends UnitSpec with ScalaFutures with MockitoSugar with WithFakeApplication with ApplicationStateUtil with LogSuppressing { @@ -336,6 +338,21 @@ class GatekeeperControllerSpec extends UnitSpec with ScalaFutures with MockitoSu } + "blockApplication" should { + + val applicationId: UUID = UUID.randomUUID() + + "block the application" in new Setup { + + when(mockGatekeeperService.blockApplication(any()) (any[HeaderCarrier]())).thenReturn(successful(Blocked)) + + val result = await(underTest.blockApplication(applicationId)(request)) + + status(result) shouldBe SC_OK + verify(mockGatekeeperService).blockApplication(applicationId) + } + } + private def aHistory(appId: UUID, state: State = PENDING_GATEKEEPER_APPROVAL) = { StateHistoryResponse(appId, state, Actor("anEmail", COLLABORATOR), None, DateTimeUtils.now) } diff --git a/test/unit/services/GatekeeperServiceSpec.scala b/test/unit/services/GatekeeperServiceSpec.scala index 73382aac0..d0359d3ec 100644 --- a/test/unit/services/GatekeeperServiceSpec.scala +++ b/test/unit/services/GatekeeperServiceSpec.scala @@ -23,11 +23,13 @@ import org.joda.time.DateTimeUtils import org.mockito.ArgumentCaptor import org.mockito.Matchers.{any, anyString, eq => eqTo} import org.mockito.Mockito._ +import org.apache.http.HttpStatus._ import org.mockito.invocation.InvocationOnMock import org.mockito.stubbing.Answer import org.scalatest.BeforeAndAfterAll import org.scalatest.concurrent.ScalaFutures import org.scalatest.mockito.MockitoSugar +import play.api.test.FakeRequest import uk.gov.hmrc.config.AppContext import uk.gov.hmrc.connector.{ApiSubscriptionFieldsConnector, EmailConnector, ThirdPartyDelegatedAuthorityConnector} import uk.gov.hmrc.controllers.{DeleteApplicationRequest, RejectUpliftRequest} @@ -464,4 +466,23 @@ class GatekeeperServiceSpec extends UnitSpec with ScalaFutures with MockitoSugar mockSubscriptionRepository, mockAuditService, mockEmailConnector, mockApiSubscriptionFieldsConnector) } } + + "blockApplication" should { + + val applicationId: UUID = UUID.randomUUID() + val applicationData = anApplicationData(applicationId) + val updatedApplication = applicationData.copy(blocked = true) + + "set the block flag to true for an application" in new Setup { + + when(mockApplicationRepository.fetch(any())).thenReturn(successful(Option(applicationData))) + when(mockApplicationRepository.save(any())).thenReturn(successful(updatedApplication)) + + val result = await(underTest.blockApplication(applicationId)) + result shouldBe Blocked + + verify(mockApplicationRepository).fetch(applicationId) + verify(mockApplicationRepository).save(updatedApplication) + } + } }