Skip to content

Commit

Permalink
DC-3341 - return message count
Browse files Browse the repository at this point in the history
  • Loading branch information
elabeca authored May 13, 2021
2 parents f843bed + 2c7b6d0 commit cf6f4a3
Show file tree
Hide file tree
Showing 9 changed files with 405 additions and 4 deletions.
13 changes: 11 additions & 2 deletions app/connectors/SecureMessageConnector.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@
package connectors

import controllers.generic.models.{ CustomerEnrolment, Tag }
import models.{ Conversation, ConversationHeader, CustomerMessage, Letter }
import models.{ Conversation, ConversationHeader, Count, CustomerMessage, Letter }
import play.api.Logging
import play.mvc.Http.Status.CREATED
import uk.gov.hmrc.http.HttpReads.Implicits._
import uk.gov.hmrc.http.{ HeaderCarrier, HttpClient, HttpResponse }
import uk.gov.hmrc.play.bootstrap.config.ServicesConfig

import javax.inject.Inject

import scala.concurrent.{ ExecutionContext, Future }

class SecureMessageConnector @Inject()(httpClient: HttpClient, servicesConfig: ServicesConfig) extends Logging {
Expand All @@ -42,6 +42,15 @@ class SecureMessageConnector @Inject()(httpClient: HttpClient, servicesConfig: S
queryParams.getOrElse(List()))
}

def getCount(
enrolmentKeys: Option[List[String]],
customerEnrolments: Option[List[CustomerEnrolment]],
tags: Option[List[Tag]])(implicit ec: ExecutionContext, hc: HeaderCarrier): Future[Count] = {
val queryParams = queryParamsBuilder(enrolmentKeys, customerEnrolments, tags)
httpClient
.GET[Count](s"$secureMessageBaseUrl/secure-messaging/messages/count", queryParams.getOrElse(List()))
}

private def queryParamsBuilder(
enrolmentKeys: Option[List[String]],
customerEnrolments: Option[List[CustomerEnrolment]],
Expand Down
60 changes: 60 additions & 0 deletions app/controllers/ApiController.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright 2021 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package controllers

import config.AppConfig
import connectors.SecureMessageConnector
import controllers.generic.models.{ CustomerEnrolment, Tag }
import controllers.utils.QueryStringValidation
import play.api.i18n.I18nSupport
import play.api.mvc.{ MessagesControllerComponents, _ }
import uk.gov.hmrc.auth.core.{ AuthConnector, AuthorisedFunctions }
import uk.gov.hmrc.http.HeaderCarrier
import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendController
import uk.gov.hmrc.play.http.HeaderCarrierConverter
import javax.inject.{ Inject, Singleton }
import play.api.libs.json.Json

import scala.concurrent.{ ExecutionContext, Future }

@Singleton
class ApiController @Inject()(
appConfig: AppConfig,
controllerComponents: MessagesControllerComponents,
secureMessageConnector: SecureMessageConnector,
val authConnector: AuthConnector)(implicit ec: ExecutionContext)
extends FrontendController(controllerComponents) with I18nSupport with AuthorisedFunctions
with QueryStringValidation {

implicit val config: AppConfig = appConfig

def count(
enrolmentKeys: Option[List[String]],
customerEnrolments: Option[List[CustomerEnrolment]],
tags: Option[List[Tag]]): Action[AnyContent] = Action.async { implicit request =>
implicit val hc: HeaderCarrier = HeaderCarrierConverter.fromRequest(request)
validateQueryParameters(request.queryString, "enrolment", "enrolmentKey", "tag", "sent") match {
case Left(e) => Future.successful(BadRequest(e.getMessage))
case _ =>
authorised() {
secureMessageConnector.getCount(enrolmentKeys, customerEnrolments, tags).flatMap { messageCount =>
Future.successful(Ok(Json.toJson(messageCount)))
}
}
}
}
}
26 changes: 26 additions & 0 deletions app/models/Count.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2021 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package models

import play.api.libs.json.{ Json, OFormat }

final case class Count(total: Long, unread: Long)

object Count {

implicit val countFormat: OFormat[Count] = Json.format[Count]
}
2 changes: 2 additions & 0 deletions conf/app.routes
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

GET /:clientService/messages controllers.ConversationInboxController.display(clientService: String, enrolmentKey: Option[List[String]], enrolment: Option[List[CustomerEnrolment]], tag: Option[List[Tag]])

GET /messages/count controllers.ApiController.count(enrolmentKey: Option[List[String]], enrolment: Option[List[CustomerEnrolment]], tag: Option[List[Tag]])

GET /:clientService/conversation/:client/:conversationId controllers.ConversationController.display(clientService, client, conversationId, showReplyForm: Boolean ?= false)

GET /:clientService/messages/:id controllers.ConversationController.displayMessage(clientService, id)
Expand Down
150 changes: 150 additions & 0 deletions it/ApiEndpointsISpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
* Copyright 2021 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import com.google.inject.AbstractModule
import connectors.SecureMessageConnector
import controllers.generic.models.{ CustomerEnrolment, Tag }
import models.Count
import net.codingwell.scalaguice.ScalaModule
import org.mockito.ArgumentMatchers
import org.mockito.ArgumentMatchers.any
import org.mockito.Mockito.when
import org.scalatest.BeforeAndAfterEach
import org.scalatestplus.mockito.MockitoSugar
import org.scalatestplus.play.PlaySpec
import play.api.http.Status.{ BAD_REQUEST, OK }
import play.api.inject.guice.GuiceableModule
import play.api.libs.json.{ Json, Reads }
import play.api.libs.ws.WSClient
import uk.gov.hmrc.http.HeaderCarrier
import uk.gov.hmrc.integration.ServiceSpec

import scala.concurrent.{ ExecutionContext, Future }

@SuppressWarnings(Array("org.wartremover.warts.NonUnitStatements"))
class ApiEndpointsISpec extends PlaySpec with ServiceSpec with MockitoSugar with BeforeAndAfterEach {

override def externalServices: Seq[String] = Seq.empty

private val mockSecureMessageConnector = mock[SecureMessageConnector]

private val wsClient = app.injector.instanceOf[WSClient]

override def additionalOverrides: Seq[GuiceableModule] =
Seq(new AbstractModule with ScalaModule {
override def configure(): Unit =
bind[SecureMessageConnector].toInstance(mockSecureMessageConnector)
})

"Getting the message count result for total and unread messages" should {

"return status code OK 200" in {
val totalMessagesCount: Long = 5
val unreadMessagesCount: Long = 2
when(
mockSecureMessageConnector.getCount(
ArgumentMatchers.eq(Some(List("HMRC-CUS-ORG"))),
ArgumentMatchers.eq(Some(List(CustomerEnrolment("HMRC-CUS-ORG", "EORIName", "GB7777777777")))),
ArgumentMatchers.eq(Some(List(Tag("notificationType", "CDS Exports"))))
)(any[ExecutionContext], any[HeaderCarrier])).thenReturn(
Future.successful(Count(total = totalMessagesCount, unread = unreadMessagesCount))
)
val response = wsClient
.url(resource("/secure-message-frontend/messages/count?" +
"enrolmentKey=HMRC-CUS-ORG&enrolment=HMRC-CUS-ORG~EORIName~GB7777777777&tag=notificationType~CDS%20Exports"))
.withHttpHeaders(AuthUtil.buildEoriToken)
.get()
.futureValue
response.status mustBe OK
response.body mustBe """{"total":5,"unread":2}"""
}

"return status code BAD REQUEST 400 when provided with filter parameters that are invalid (not allowed)" in {
val totalMessagesCount: Long = 0
val unreadMessagesCount: Long = 0
when(
mockSecureMessageConnector.getCount(
ArgumentMatchers.eq(None),
ArgumentMatchers.eq(None),
ArgumentMatchers.eq(None)
)(any[ExecutionContext], any[HeaderCarrier]))
.thenReturn(Future.successful(Count(total = totalMessagesCount, unread = unreadMessagesCount)))
val response = wsClient
.url(resource("/secure-message-frontend/messages/count?" +
"enrolment_key=HMRC-CUS-ORG&enrolement=HMRC-CUS-ORG~EORIName~GB7777777777&tags=notificationType~CDS%20Exports"))
.withHttpHeaders(AuthUtil.buildEoriToken)
.get()
.futureValue
response.status mustBe BAD_REQUEST
response.body mustBe "Invalid query parameter(s) found: [enrolement, enrolment_key, tags]"
}
}

object AuthUtil {

lazy val ggAuthPort: Int = 8585

implicit val deserialiser: Reads[GatewayToken] = Json.reads[GatewayToken]

case class GatewayToken(gatewayToken: String)

private val NO_EORI_USER_PAYLOAD =
"""
| {
| "credId": "1235",
| "affinityGroup": "Organisation",
| "confidenceLevel": 100,
| "credentialStrength": "none",
| "enrolments": []
| }
""".stripMargin

private val EORI_USER_PAYLOAD =
"""
| {
| "credId": "1235",
| "affinityGroup": "Organisation",
| "confidenceLevel": 200,
| "credentialStrength": "none",
| "enrolments": [
| {
| "key": "HMRC-CUS-ORG",
| "identifiers": [
| {
| "key": "EORINumber",
| "value": "GB1234567890"
| }
| ],
| "state": "Activated"
| }
| ]
| }
""".stripMargin

private def buildUserToken(payload: String): (String, String) = {
val response = wsClient
.url(s"http://localhost:$ggAuthPort/government-gateway/session/login")
.withHttpHeaders(("Content-Type", "application/json"))
.post(payload)
.futureValue

("Authorization", response.header("Authorization").getOrElse(""))
}

def buildEoriToken: (String, String) = buildUserToken(EORI_USER_PAYLOAD)
def buildNonEoriToken: (String, String) = buildUserToken(NO_EORI_USER_PAYLOAD)
}
}
2 changes: 1 addition & 1 deletion project/AppDependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ object AppDependencies {
"uk.gov.hmrc" %% "service-integration-test" % "0.13.0-play-27" % "test, it",
"org.scalatestplus.play" %% "scalatestplus-play" % "4.0.3" % "test, it",
"org.jsoup" % "jsoup" % "1.13.1" % "test, it",
"org.mockito" % "mockito-core" % "3.9.0" % "test, it",
"org.mockito" % "mockito-core" % "3.10.0" % "test, it",
"com.vladsch.flexmark" % "flexmark-all" % "0.36.8" % "test, it",
"org.pegdown" % "pegdown" % "1.6.0" % "test, it",
"net.codingwell" %% "scala-guice" % "5.0.0" % "test, it"
Expand Down
26 changes: 25 additions & 1 deletion test/connectors/SecureMessageConnectorSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ import scala.concurrent.{ ExecutionContext, Future }
@SuppressWarnings(Array("org.wartremover.warts.NonUnitStatements"))
class SecureMessageConnectorSpec extends PlaySpec with MockitoSugar {

"SecureMessgaeConnector.getConversationList" must {
"SecureMessageConnector.getConversationList" must {

"return a list with one conversation" in new TestCase {
val expectedQueryParams = Seq(
("enrolmentKey", "HMRC-CUS-ORG"),
Expand All @@ -60,6 +61,29 @@ class SecureMessageConnectorSpec extends PlaySpec with MockitoSugar {
Some(List(Tag("notificationType", "CDS Exports")))))
result.size mustBe 1
}

"return a count of messages" in new TestCase {
val totalMessagesCount: Long = 5
val unreadMessagesCount: Long = 2
val expectedQueryParams = Seq(
("enrolmentKey", "HMRC-CUS-ORG"),
("enrolment", "HMRC-CUS-ORG~EORIName~GB7777777777"),
("tag", "notificationType~CDS Exports")
)
when(
mockHttpClient
.GET[Count](any[String], ArgumentMatchers.eq(expectedQueryParams), any[Seq[(String, String)]])(
any[HttpReads[Count]],
any[HeaderCarrier],
any[ExecutionContext]))
.thenReturn(Future(Count(total = totalMessagesCount, unread = unreadMessagesCount)))
private val result = await(
connector.getCount(
Some(List("HMRC-CUS-ORG")),
Some(List(CustomerEnrolment("HMRC-CUS-ORG", "EORIName", "GB7777777777"))),
Some(List(Tag("notificationType", "CDS Exports")))))
result mustBe Count(total = totalMessagesCount, unread = unreadMessagesCount)
}
}

"SecureMessgaeConnector.getConversation" must {
Expand Down
Loading

0 comments on commit cf6f4a3

Please sign in to comment.