diff --git a/app/uk/gov/hmrc/gatekeeper/connectors/XmlServicesConnector.scala b/app/uk/gov/hmrc/gatekeeper/connectors/XmlServicesConnector.scala index 22e64e67a..b62332fca 100644 --- a/app/uk/gov/hmrc/gatekeeper/connectors/XmlServicesConnector.scala +++ b/app/uk/gov/hmrc/gatekeeper/connectors/XmlServicesConnector.scala @@ -71,6 +71,13 @@ class XmlServicesConnector @Inject() (config: Config, http: HttpClientV2)(implic http.get(url"$baseUrl/organisations?$queryParams").execute[List[XmlOrganisation]] } + def getAllOrganisations()(implicit hc: HeaderCarrier): Future[List[XmlOrganisation]] = { + val sortByParams = Seq("sortBy" -> "VENDOR_ID") + val queryParams = sortByParams + + http.get(url"$baseUrl/organisations?$queryParams").execute[List[XmlOrganisation]] + } + def removeCollaboratorsForUserId(userId: UserId, gatekeeperUser: String)(implicit hc: HeaderCarrier): Future[RemoveAllCollaboratorsForUserIdResult] = { val request = RemoveAllCollaboratorsForUserIdRequest(userId, gatekeeperUser) http.post(url"$baseUrl/organisations/all/remove-collaborators") diff --git a/app/uk/gov/hmrc/gatekeeper/controllers/DevelopersController.scala b/app/uk/gov/hmrc/gatekeeper/controllers/DevelopersController.scala index 599e8bf0b..6e4140937 100644 --- a/app/uk/gov/hmrc/gatekeeper/controllers/DevelopersController.scala +++ b/app/uk/gov/hmrc/gatekeeper/controllers/DevelopersController.scala @@ -35,7 +35,8 @@ import uk.gov.hmrc.apiplatform.modules.tpd.mfa.domain.models.MfaType import uk.gov.hmrc.gatekeeper.config.AppConfig import uk.gov.hmrc.gatekeeper.models.Forms.RemoveEmailPreferencesForm import uk.gov.hmrc.gatekeeper.models._ -import uk.gov.hmrc.gatekeeper.services.{ApiDefinitionService, DeveloperService} +import uk.gov.hmrc.gatekeeper.models.xml.XmlOrganisation +import uk.gov.hmrc.gatekeeper.services.{ApiDefinitionService, DeveloperService, XmlService} import uk.gov.hmrc.gatekeeper.utils.CsvHelper.ColumnDefinition import uk.gov.hmrc.gatekeeper.utils.{CsvHelper, ErrorHelper, UserFunctionsWrapper} import uk.gov.hmrc.gatekeeper.views.html.developers.{DevelopersView, _} @@ -46,6 +47,7 @@ class DevelopersController @Inject() ( val forbiddenView: ForbiddenView, developerService: DeveloperService, val apiDefinitionService: ApiDefinitionService, + xmlService: XmlService, mcc: MessagesControllerComponents, developersView: DevelopersView, removeEmailPreferencesView: RemoveEmailPreferences, @@ -67,22 +69,23 @@ class DevelopersController @Inject() ( def developersCsv() = atLeastSuperUserAction { implicit request => { - def isMfaTypeActive(user: RegisteredUser, mfaType: MfaType): Boolean = { + def isMfaTypeActive(user: RegisteredUser, mfaType: MfaType): Boolean = { user.mfaDetails.exists(mfa => (mfa.verified && mfa.mfaType == mfaType)) } - - def topicSubscribedTo(user: RegisteredUser, topic: EmailTopic): Boolean = { + def getNumberOfXmlOrganisations(user: RegisteredUser, orgs: List[XmlOrganisation]): Int = { + orgs.filter(org => org.collaborators.exists(coll => coll.userId == user.userId)).size + } + def topicSubscribedTo(user: RegisteredUser, topic: EmailTopic): Boolean = { user.emailPreferences.topics.contains(topic) } - - def categoriesSubscribedTo(user: RegisteredUser): Int = { + def categoriesSubscribedTo(user: RegisteredUser): Int = { user.emailPreferences.interests.count(r => r.services.isEmpty) } - def individualApisSubscribedTo(user: RegisteredUser): Int = { + def individualApisSubscribedTo(user: RegisteredUser): Int = { user.emailPreferences.interests.map(r => r.services.size).sum } - val csvColumnDefinitions = Seq[ColumnDefinition[RegisteredUser]]( + def csvColumnDefinitions(orgs: List[XmlOrganisation]) = Seq[ColumnDefinition[RegisteredUser]]( ColumnDefinition("UserId", (dev => dev.userId.toString())), ColumnDefinition("SMS MFA Active", (dev => isMfaTypeActive(dev, MfaType.SMS).toString())), ColumnDefinition("Authenticator MFA Active", (dev => isMfaTypeActive(dev, MfaType.AUTHENTICATOR_APP).toString())), @@ -91,11 +94,18 @@ class DevelopersController @Inject() ( ColumnDefinition("Release Schedules Email", (dev => topicSubscribedTo(dev, RELEASE_SCHEDULES).toString())), ColumnDefinition("Event Invites Email", (dev => topicSubscribedTo(dev, EVENT_INVITES).toString())), ColumnDefinition("Full Category Emails", (dev => categoriesSubscribedTo(dev).toString())), - ColumnDefinition("Individual APIs Emails", (dev => individualApisSubscribedTo(dev).toString())) + ColumnDefinition("Individual APIs Emails", (dev => individualApisSubscribedTo(dev).toString())), + ColumnDefinition("XML Vendors", (dev => getNumberOfXmlOrganisations(dev, orgs).toString())) ) - developerService.fetchUsers - .map(users => CsvHelper.toCsvString(csvColumnDefinitions, users.filter(_.verified))) + ( + for { + allUsers <- developerService.fetchUsers() + users = allUsers.filter(_.verified) + orgs <- xmlService.getAllOrganisations() + } yield (users, orgs) + ) + .map(usersAndOrgs => CsvHelper.toCsvString(csvColumnDefinitions(usersAndOrgs._2), usersAndOrgs._1)) .map(Ok(_).withHeaders(CONTENT_DISPOSITION -> s"attachment; filename=developers-${Instant.now()}.csv").as("text/csv")) } diff --git a/app/uk/gov/hmrc/gatekeeper/controllers/EmailsController.scala b/app/uk/gov/hmrc/gatekeeper/controllers/EmailsController.scala index 0518080c0..c21ff4a42 100644 --- a/app/uk/gov/hmrc/gatekeeper/controllers/EmailsController.scala +++ b/app/uk/gov/hmrc/gatekeeper/controllers/EmailsController.scala @@ -191,7 +191,7 @@ class EmailsController @Inject() ( } def emailAllUsersPage(): Action[AnyContent] = anyStrideUserAction { implicit request => - developerService.fetchUsers + developerService.fetchUsers() .map((users: List[RegisteredUser]) => { val filteredUsers = users.filter(_.verified) Ok(emailsAllUsersView(filteredUsers, usersToEmailCopyText(filteredUsers))) diff --git a/app/uk/gov/hmrc/gatekeeper/models/xml/XmlOrganisation.scala b/app/uk/gov/hmrc/gatekeeper/models/xml/XmlOrganisation.scala index 20f06e2bd..07b3ba67f 100644 --- a/app/uk/gov/hmrc/gatekeeper/models/xml/XmlOrganisation.scala +++ b/app/uk/gov/hmrc/gatekeeper/models/xml/XmlOrganisation.scala @@ -18,6 +18,8 @@ package uk.gov.hmrc.gatekeeper.models.xml import play.api.libs.json.{Format, Json, OFormat} +import uk.gov.hmrc.apiplatform.modules.common.domain.models.{LaxEmailAddress, UserId} + case class OrganisationId(value: java.util.UUID) extends AnyVal object OrganisationId { @@ -30,7 +32,13 @@ object VendorId { implicit val formatVendorId: Format[VendorId] = Json.valueFormat[VendorId] } -case class XmlOrganisation(organisationId: OrganisationId, vendorId: VendorId, name: String) +case class Collaborator(userId: UserId, email: LaxEmailAddress) + +object Collaborator { + implicit val formatCollaborator: OFormat[Collaborator] = Json.format[Collaborator] +} + +case class XmlOrganisation(organisationId: OrganisationId, vendorId: VendorId, name: String, collaborators: List[Collaborator]) object XmlOrganisation { implicit val formatOrganisation: OFormat[XmlOrganisation] = Json.format[XmlOrganisation] diff --git a/app/uk/gov/hmrc/gatekeeper/services/DeveloperService.scala b/app/uk/gov/hmrc/gatekeeper/services/DeveloperService.scala index b65525fec..c76d25118 100644 --- a/app/uk/gov/hmrc/gatekeeper/services/DeveloperService.scala +++ b/app/uk/gov/hmrc/gatekeeper/services/DeveloperService.scala @@ -145,7 +145,7 @@ class DeveloperService @Inject() ( }) } - def fetchUsers(implicit hc: HeaderCarrier): Future[List[RegisteredUser]] = { + def fetchUsers()(implicit hc: HeaderCarrier): Future[List[RegisteredUser]] = { developerConnector.fetchAll().map(_.sortBy(_.sortField)) } diff --git a/app/uk/gov/hmrc/gatekeeper/services/XmlService.scala b/app/uk/gov/hmrc/gatekeeper/services/XmlService.scala index 9cf20637c..f7cd215f7 100644 --- a/app/uk/gov/hmrc/gatekeeper/services/XmlService.scala +++ b/app/uk/gov/hmrc/gatekeeper/services/XmlService.scala @@ -52,6 +52,10 @@ class XmlService @Inject() (xmlServicesConnector: XmlServicesConnector)(implicit xmlServicesConnector.findOrganisationsByUserId(userId) } + def getAllOrganisations()(implicit hc: HeaderCarrier): Future[List[XmlOrganisation]] = { + xmlServicesConnector.getAllOrganisations() + } + def removeCollaboratorsForUserId(userId: UserId, gatekeeperUser: String)(implicit hc: HeaderCarrier): Future[RemoveAllCollaboratorsForUserIdResult] = { xmlServicesConnector.removeCollaboratorsForUserId(userId, gatekeeperUser) } diff --git a/test/uk/gov/hmrc/gatekeeper/connectors/XmlServicesConnectorSpec.scala b/test/uk/gov/hmrc/gatekeeper/connectors/XmlServicesConnectorSpec.scala index 31bd5821a..7f882ff3a 100644 --- a/test/uk/gov/hmrc/gatekeeper/connectors/XmlServicesConnectorSpec.scala +++ b/test/uk/gov/hmrc/gatekeeper/connectors/XmlServicesConnectorSpec.scala @@ -29,7 +29,7 @@ import uk.gov.hmrc.http.{HeaderCarrier, UpstreamErrorResponse} import uk.gov.hmrc.apiplatform.modules.common.domain.models.{ApplicationId, UserId, _} import uk.gov.hmrc.apiplatform.modules.common.utils._ -import uk.gov.hmrc.gatekeeper.models.xml.{OrganisationId, VendorId, XmlApi, XmlOrganisation} +import uk.gov.hmrc.gatekeeper.models.xml.{Collaborator, OrganisationId, VendorId, XmlApi, XmlOrganisation} import uk.gov.hmrc.gatekeeper.models.{RemoveAllCollaboratorsForUserIdFailureResult, RemoveAllCollaboratorsForUserIdRequest, RemoveAllCollaboratorsForUserIdSuccessResult} import uk.gov.hmrc.gatekeeper.utils.UrlEncoding @@ -186,7 +186,7 @@ class XmlServicesConnectorSpec "findOrganisationsByUserId" should { val url = "/api-platform-xml-services/organisations" val userId = UserId.random - val orgOne = XmlOrganisation(name = "Organisation one", vendorId = VendorId(1), organisationId = OrganisationId(UUID.randomUUID())) + val orgOne = XmlOrganisation(name = "Organisation one", vendorId = VendorId(1), organisationId = OrganisationId(UUID.randomUUID()), collaborators = List.empty) "return APIs when userId exists on an organisation" in new Setup { stubFor( @@ -227,6 +227,38 @@ class XmlServicesConnectorSpec } } + "getAllOrganisations" should { + val url = "/api-platform-xml-services/organisations" + val coll = Collaborator(UserId.random, LaxEmailAddress("developer@example.com")) + val orgOne = XmlOrganisation(name = "Organisation one", vendorId = VendorId(1), organisationId = OrganisationId(UUID.randomUUID()), collaborators = List(coll)) + + "return organisations" in new Setup { + stubFor( + get(urlEqualTo(s"$url?sortBy=VENDOR_ID")) + .willReturn( + aResponse() + .withStatus(OK) + .withBody(Json.toJson(List(orgOne)).toString) + ) + ) + await(connector.getAllOrganisations()) shouldBe List(orgOne) + } + + "return UpstreamErrorResponse when backend returns 400" in new Setup { + stubFor( + get(urlEqualTo(s"$url?sortBy=VENDOR_ID")) + .willReturn( + aResponse() + .withStatus(BAD_REQUEST) + ) + ) + intercept[UpstreamErrorResponse](await(connector.getAllOrganisations())) match { + case UpstreamErrorResponse(_, BAD_REQUEST, _, _) => succeed + case _ => fail() + } + } + } + "removeCollaboratorsForUserId" should { val url = "/api-platform-xml-services/organisations/all/remove-collaborators" val userId = UserId.random diff --git a/test/uk/gov/hmrc/gatekeeper/controllers/ControllerSetupBase.scala b/test/uk/gov/hmrc/gatekeeper/controllers/ControllerSetupBase.scala index 916bccf0c..522b4ac2c 100644 --- a/test/uk/gov/hmrc/gatekeeper/controllers/ControllerSetupBase.scala +++ b/test/uk/gov/hmrc/gatekeeper/controllers/ControllerSetupBase.scala @@ -44,6 +44,7 @@ trait ControllerSetupBase with ApiDefinitionServiceMockProvider with DeveloperServiceMockProvider with ApplicationServiceMockProvider + with XmlServiceMockProvider with ApiCataloguePublishConnectorMockProvider with ApmServiceMockProvider with DeploymentApprovalServiceMockProvider diff --git a/test/uk/gov/hmrc/gatekeeper/controllers/DevelopersControllerSpec.scala b/test/uk/gov/hmrc/gatekeeper/controllers/DevelopersControllerSpec.scala index 5c61260bd..0fd532341 100644 --- a/test/uk/gov/hmrc/gatekeeper/controllers/DevelopersControllerSpec.scala +++ b/test/uk/gov/hmrc/gatekeeper/controllers/DevelopersControllerSpec.scala @@ -35,6 +35,7 @@ import uk.gov.hmrc.apiplatform.modules.tpd.emailpreferences.domain.models.EmailT import uk.gov.hmrc.apiplatform.modules.tpd.emailpreferences.domain.models.{EmailPreferences, TaxRegimeInterests} import uk.gov.hmrc.apiplatform.modules.tpd.mfa.domain.models._ import uk.gov.hmrc.gatekeeper.models._ +import uk.gov.hmrc.gatekeeper.models.xml.{Collaborator, OrganisationId, VendorId, XmlOrganisation} import uk.gov.hmrc.gatekeeper.utils.FakeRequestCSRFSupport._ import uk.gov.hmrc.gatekeeper.views.html.developers._ import uk.gov.hmrc.gatekeeper.views.html.{ErrorTemplate, ForbiddenView} @@ -61,6 +62,7 @@ class DevelopersControllerSpec extends ControllerBaseSpec { forbiddenView, mockDeveloperService, mockApiDefinitionService, + mockXmlService, mcc, developersView, removeEmailPref, @@ -182,14 +184,28 @@ class DevelopersControllerSpec extends ControllerBaseSpec { private val user3 = RegisteredUser(LaxEmailAddress("developer3@example.com"), userId3, "first", "last", verified = true, mfaDetails = mfaDetails3, emailPreferences = emailPref3) + private val xmlOrg1 = XmlOrganisation( + OrganisationId(UUID.randomUUID()), + VendorId(1), + "xml org name 1", + List(Collaborator(userId3, LaxEmailAddress("developer3@example.com"))) + ) + private val xmlOrg2 = XmlOrganisation( + OrganisationId(UUID.randomUUID()), + VendorId(2), + "xml org name 2", + List(Collaborator(userId1, LaxEmailAddress("developer1@example.com")), Collaborator(userId3, LaxEmailAddress("developer3@example.com"))) + ) + DeveloperServiceMock.FetchUsers.returns(user1, user2, user3) + XmlServiceMock.GetAllXmlOrganisations.returns(List(xmlOrg1, xmlOrg2)) val result = developersController.developersCsv()(aLoggedInRequest) contentAsString(result) should be( - s"UserId,SMS MFA Active,Authenticator MFA Active,Business And Policy Email,Technical Email,Release Schedules Email,Event Invites Email,Full Category Emails,Individual APIs Emails\n" + - s"${userId1.toString},false,false,false,false,false,false,0,3\n" + - s"${userId2.toString},true,false,false,false,false,false,1,0\n" + - s"${userId3.toString},false,true,true,true,true,true,0,0\n" + s"UserId,SMS MFA Active,Authenticator MFA Active,Business And Policy Email,Technical Email,Release Schedules Email,Event Invites Email,Full Category Emails,Individual APIs Emails,XML Vendors\n" + + s"${userId1.toString},false,false,false,false,false,false,0,3,1\n" + + s"${userId2.toString},true,false,false,false,false,false,1,0,0\n" + + s"${userId3.toString},false,true,true,true,true,true,0,0,2\n" ) } diff --git a/test/uk/gov/hmrc/gatekeeper/mocks/services/DeveloperServiceMockProvider.scala b/test/uk/gov/hmrc/gatekeeper/mocks/services/DeveloperServiceMockProvider.scala index 9b0094c9c..36dfdcd09 100644 --- a/test/uk/gov/hmrc/gatekeeper/mocks/services/DeveloperServiceMockProvider.scala +++ b/test/uk/gov/hmrc/gatekeeper/mocks/services/DeveloperServiceMockProvider.scala @@ -63,7 +63,7 @@ trait DeveloperServiceMockProvider { object FetchUsers { def returns(users: RegisteredUser*) = - when(mockDeveloperService.fetchUsers(*)) + when(mockDeveloperService.fetchUsers()(*)) .thenReturn(successful(users.toList)) } diff --git a/test/uk/gov/hmrc/gatekeeper/mocks/services/XmlServiceMockProvider.scala b/test/uk/gov/hmrc/gatekeeper/mocks/services/XmlServiceMockProvider.scala index f8f260e5d..730bd5f08 100644 --- a/test/uk/gov/hmrc/gatekeeper/mocks/services/XmlServiceMockProvider.scala +++ b/test/uk/gov/hmrc/gatekeeper/mocks/services/XmlServiceMockProvider.scala @@ -51,6 +51,12 @@ trait XmlServiceMockProvider { def returnsError() = when(mockXmlService.findOrganisationsByUserId(*[UserId])(*)).thenThrow(UpstreamErrorResponse("error", 500, 500, Map.empty)) } + + object GetAllXmlOrganisations { + + def returns(xmlOrganisations: List[XmlOrganisation]) = + when(mockXmlService.getAllOrganisations()(*)).thenReturn(successful(xmlOrganisations)) + } } } diff --git a/test/uk/gov/hmrc/gatekeeper/services/DeveloperServiceSpec.scala b/test/uk/gov/hmrc/gatekeeper/services/DeveloperServiceSpec.scala index e32510a32..73935edc6 100644 --- a/test/uk/gov/hmrc/gatekeeper/services/DeveloperServiceSpec.scala +++ b/test/uk/gov/hmrc/gatekeeper/services/DeveloperServiceSpec.scala @@ -121,7 +121,7 @@ class DeveloperServiceSpec extends AsyncHmrcSpec with CollaboratorTracker with A val apiVersion = ApiVersionNbr.random val organisations = List(DeskproOrganisation(uk.gov.hmrc.gatekeeper.models.organisations.OrganisationId("1"), "org name 1", List.empty)) - val orgOne = XmlOrganisation(name = "Organisation one", vendorId = VendorId(1), organisationId = OrganisationId(UUID.randomUUID())) + val orgOne = XmlOrganisation(name = "Organisation one", vendorId = VendorId(1), organisationId = OrganisationId(UUID.randomUUID()), collaborators = List.empty) val xmlServiceNames = Set("XML API one", "XML API two") val offset = 0 diff --git a/test/uk/gov/hmrc/gatekeeper/services/XmlServiceSpec.scala b/test/uk/gov/hmrc/gatekeeper/services/XmlServiceSpec.scala index fed3d4cc8..7778b6f77 100644 --- a/test/uk/gov/hmrc/gatekeeper/services/XmlServiceSpec.scala +++ b/test/uk/gov/hmrc/gatekeeper/services/XmlServiceSpec.scala @@ -124,7 +124,7 @@ class XmlServiceSpec extends AsyncHmrcSpec { } "findOrganisationsByUserId" should { - val orgOne = XmlOrganisation(name = "Organisation one", vendorId = VendorId(1), organisationId = OrganisationId(UUID.randomUUID())) + val orgOne = XmlOrganisation(name = "Organisation one", vendorId = VendorId(1), organisationId = OrganisationId(UUID.randomUUID()), collaborators = List.empty) "Return List of Organisations when call to get xml apis is successful" in new Setup { XmlServicesConnectorMock.GetOrganisations.returnsOrganisations(user.userId, List(orgOne)) diff --git a/test/uk/gov/hmrc/gatekeeper/views/developers/DeveloperDetailsViewSpec.scala b/test/uk/gov/hmrc/gatekeeper/views/developers/DeveloperDetailsViewSpec.scala index 3c6a823e3..5e64e82a6 100644 --- a/test/uk/gov/hmrc/gatekeeper/views/developers/DeveloperDetailsViewSpec.scala +++ b/test/uk/gov/hmrc/gatekeeper/views/developers/DeveloperDetailsViewSpec.scala @@ -44,7 +44,7 @@ class DeveloperDetailsViewSpec extends CommonViewSpec with ApplicationBuilder { val xmlServiceNames = Set("XML Service 1", "XML Service 2", "XML Service 3") - val xmlOrganisations = List(XmlOrganisation(name = "Organisation one", vendorId = VendorId(1), organisationId = OrganisationId(UUID.randomUUID()))) + val xmlOrganisations = List(XmlOrganisation(name = "Organisation one", vendorId = VendorId(1), organisationId = OrganisationId(UUID.randomUUID()), collaborators = List.empty)) val buildXmlServicesFeUrl: (OrganisationId) => String = (organisationId) => s"/api-gatekeeper-xml-services/organisations/${organisationId.value}" diff --git a/testCommon/uk/gov/hmrc/gatekeeper/testdata/MockDataSugar.scala b/testCommon/uk/gov/hmrc/gatekeeper/testdata/MockDataSugar.scala index 236a58e39..afb946325 100644 --- a/testCommon/uk/gov/hmrc/gatekeeper/testdata/MockDataSugar.scala +++ b/testCommon/uk/gov/hmrc/gatekeeper/testdata/MockDataSugar.scala @@ -99,7 +99,7 @@ object MockDataSugar { ) val xmlApis = Json.toJson(Seq(xmlApiOne)).toString - val orgOne = XmlOrganisation(name = "Organisation one", vendorId = VendorId(1), organisationId = OrganisationId(UUID.randomUUID())) + val orgOne = XmlOrganisation(name = "Organisation one", vendorId = VendorId(1), organisationId = OrganisationId(UUID.randomUUID()), collaborators = List.empty) val xmlOrganisations = Json.toJson(List(orgOne)).toString val deskproOrganisationId = uk.gov.hmrc.gatekeeper.models.organisations.OrganisationId("1")