Skip to content

Commit

Permalink
Add 'start load' consignment api call
Browse files Browse the repository at this point in the history
  • Loading branch information
TomJKing committed Jul 8, 2024
1 parent 6d2dba9 commit 52d1848
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
package uk.gov.nationalarchives.tdr.transfer.service.api

import cats.data.{Kleisli, OptionT}
import cats.data.Kleisli
import cats.effect.{ExitCode, IO, IOApp}
import cats.implicits.toSemigroupKOps
import com.comcast.ip4s.{IpLiteralSyntax, Port}
import org.http4s.dsl.io._
import org.http4s.ember.server.EmberServerBuilder
import org.http4s.server.Router
import org.http4s.server.middleware.Logger
import org.http4s.{HttpRoutes, Request, Response}
import pureconfig.ConfigSource
import pureconfig.generic.auto._
import sttp.apispec.openapi.Info
import sttp.client3.quick.backend
import sttp.client3.{HttpURLConnectionBackend, Identity, SttpBackend}
import sttp.tapir.server.http4s.Http4sServerInterpreter
import sttp.tapir.swagger.bundle.SwaggerInterpreter
Expand Down Expand Up @@ -53,7 +51,7 @@ object TransferServiceServer extends IOApp {

private val app: Kleisli[IO, Request[IO], Response[IO]] = allRoutes.orNotFound

private val finalApp = Logger.httpApp(logHeaders = true, logBody = true)(app)
private val finalApp = Logger.httpApp(logHeaders = true, logBody = false)(app)

private val transferServiceServer = EmberServerBuilder
.default[IO]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package uk.gov.nationalarchives.tdr.transfer.service.api.controllers

import cats.effect.IO
import graphql.codegen.AddConsignment.{addConsignment => ac}
import graphql.codegen.StartUpload.{startUpload => su}
import org.http4s.HttpRoutes
import sttp.client3.{Identity, SttpBackend}
import sttp.tapir._
Expand Down Expand Up @@ -38,15 +39,24 @@ class LoadController(graphqlApiService: GraphQlApiService) extends BaseControlle

val initiateLoadRoute: HttpRoutes[IO] =
Http4sServerInterpreter[IO]().toRoutes(
initiateLoadEndpoint.serverLogicSuccess(ac => _ => graphqlApiService.addConsignment(ac.token).flatMap(c => loadDetails(c.consignmentid.get, ac.token.userId)))
initiateLoadEndpoint.serverLogicSuccess(ac =>
_ =>
for {
addConsignmentResult <- graphqlApiService.addConsignment(ac.token)
consignmentId = addConsignmentResult.consignmentid.get
_ <- graphqlApiService.startUpload(ac.token, consignmentId)
result <- loadDetails(consignmentId, ac.token.userId)
} yield result
)
)
}

object LoadController {

def apply()(implicit backend: SttpBackend[Identity, Any], appConfig: Configuration) = new LoadController(
GraphQlApiService.apply(
new GraphQLClient[ac.Data, ac.Variables](appConfig.consignmentApi.url)
new GraphQLClient[ac.Data, ac.Variables](appConfig.consignmentApi.url),
new GraphQLClient[su.Data, su.Variables](appConfig.consignmentApi.url)
)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,20 @@ package uk.gov.nationalarchives.tdr.transfer.service.services
import cats.effect.IO
import cats.implicits.catsSyntaxOptionId
import graphql.codegen.AddConsignment
import graphql.codegen.StartUpload
import graphql.codegen.AddConsignment.{addConsignment => ac}
import graphql.codegen.types.AddConsignmentInput
import graphql.codegen.StartUpload.{startUpload => su}
import graphql.codegen.types.{AddConsignmentInput, StartUploadInput}
import sttp.client3.{Identity, SttpBackend}
import uk.gov.nationalarchives.tdr.GraphQLClient
import uk.gov.nationalarchives.tdr.keycloak.Token

import java.util.UUID
import scala.concurrent.Future

class GraphQlApiService(addConsignmentClient: GraphQLClient[ac.Data, ac.Variables])(implicit backend: SttpBackend[Identity, Any]) {
class GraphQlApiService(addConsignmentClient: GraphQLClient[ac.Data, ac.Variables], startUploadClient: GraphQLClient[su.Data, su.Variables])(implicit
backend: SttpBackend[Identity, Any]
) {

implicit class FutureUtils[T](f: Future[T]) {
def toIO: IO[T] = IO.fromFuture(IO(f))
Expand All @@ -23,8 +28,18 @@ class GraphQlApiService(addConsignmentClient: GraphQLClient[ac.Data, ac.Variable
data <- IO.fromOption(result.data)(new RuntimeException("Consignment not added"))
} yield data.addConsignment
}

def startUpload(token: Token, consignmentId: UUID, parentFolder: Option[String] = None): IO[String] = {
for {
result <- startUploadClient.getResult(token.bearerAccessToken, su.document, su.Variables(StartUploadInput(consignmentId, parentFolder.getOrElse(""), None)).some).toIO
data <- IO.fromOption(result.data)(new RuntimeException(s"Load not started for consignment: $consignmentId"))
} yield data.startUpload
}
}

object GraphQlApiService {
def apply(addConsignmentClient: GraphQLClient[ac.Data, ac.Variables])(implicit backend: SttpBackend[Identity, Any]) = new GraphQlApiService(addConsignmentClient)
def apply(
addConsignmentClient: GraphQLClient[ac.Data, ac.Variables],
startUploadClient: GraphQLClient[su.Data, su.Variables]
)(implicit backend: SttpBackend[Identity, Any]) = new GraphQlApiService(addConsignmentClient, startUploadClient)
}
5 changes: 5 additions & 0 deletions src/test/resources/json/start_upload_response.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"data": {
"startUpload": "parentFolder"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,11 @@ class ExternalServicesSpec extends BaseSpec with BeforeAndAfterEach with BeforeA
.withRequestBody(containing("addConsignment"))
.willReturn(okJson(fromResource(s"json/add_consignment_response.json").mkString))
)

wiremockGraphqlServer.stubFor(
post(urlEqualTo(graphQlPath))
.withRequestBody(containing("startUpload"))
.willReturn(okJson(fromResource(s"json/start_upload_response.json").mkString))
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import cats.effect.unsafe.implicits.global
import com.nimbusds.oauth2.sdk.token.BearerAccessToken
import graphql.codegen.AddConsignment.addConsignment.AddConsignment
import graphql.codegen.AddConsignment.{addConsignment => ac}
import graphql.codegen.StartUpload.{startUpload => su}
import org.mockito.ArgumentMatchers.any
import sangria.ast.Document
import sttp.client3.{Identity, SttpBackend}
Expand All @@ -20,6 +21,7 @@ class GraphQlApiServiceSpec extends BaseSpec {
val mockKeycloakToken: Token = mock[Token]
val keycloak: KeycloakUtils = mock[KeycloakUtils]
val addConsignmentClient: GraphQLClient[ac.Data, ac.Variables] = mock[GraphQLClient[ac.Data, ac.Variables]]
val startUploadClient: GraphQLClient[su.Data, su.Variables] = mock[GraphQLClient[su.Data, su.Variables]]
val consignmentId = "6e3b76c4-1745-4467-8ac5-b4dd736e1b3e"

"'addConsignment'" should "return the consignment id of the created consignment" in {
Expand All @@ -37,13 +39,13 @@ class GraphQlApiServiceSpec extends BaseSpec {
.when(addConsignmentClient)
.getResult[Identity](any[BearerAccessToken], any[Document], any[Option[ac.Variables]])(any[SttpBackend[Identity, Any]], any[ClassTag[Identity[_]]])

val response = GraphQlApiService.apply(addConsignmentClient).addConsignment(mockKeycloakToken).unsafeRunSync()
val response = GraphQlApiService.apply(addConsignmentClient, startUploadClient).addConsignment(mockKeycloakToken).unsafeRunSync()

response.consignmentid.get shouldBe UUID.fromString(consignmentId)
response.seriesid shouldBe None
}

"addConsignment" should "throw an exception when no consignment added" in {
"'addConsignment'" should "throw an exception when no consignment added" in {
doAnswer(() => Future(new BearerAccessToken("token")))
.when(keycloak)
.serviceAccountToken[Identity](any[String], any[String])(any[SttpBackend[Identity, Any]], any[ClassTag[Identity[_]]], any[TdrKeycloakDeployment])
Expand All @@ -53,8 +55,62 @@ class GraphQlApiServiceSpec extends BaseSpec {
.getResult[Identity](any[BearerAccessToken], any[Document], any[Option[ac.Variables]])(any[SttpBackend[Identity, Any]], any[ClassTag[Identity[_]]])

val exception = intercept[RuntimeException] {
GraphQlApiService.apply(addConsignmentClient).addConsignment(mockKeycloakToken).unsafeRunSync()
GraphQlApiService.apply(addConsignmentClient, startUploadClient).addConsignment(mockKeycloakToken).unsafeRunSync()
}
exception.getMessage should equal(s"Consignment not added")
}

"'startUpload'" should "return parent folder name if provided" in {
val startUploadData = "parentFolder"

doAnswer(() => new BearerAccessToken("token"))
.when(mockKeycloakToken)
.bearerAccessToken

doAnswer(() => Future(new BearerAccessToken("token")))
.when(keycloak)
.serviceAccountToken[Identity](any[String], any[String])(any[SttpBackend[Identity, Any]], any[ClassTag[Identity[_]]], any[TdrKeycloakDeployment])

doAnswer(() => Future(GraphQlResponse[su.Data](Option(su.Data(startUploadData)), Nil)))
.when(startUploadClient)
.getResult[Identity](any[BearerAccessToken], any[Document], any[Option[su.Variables]])(any[SttpBackend[Identity, Any]], any[ClassTag[Identity[_]]])

val response =
GraphQlApiService.apply(addConsignmentClient, startUploadClient).startUpload(mockKeycloakToken, UUID.fromString(consignmentId), Some("parentFolder")).unsafeRunSync()

response shouldBe "parentFolder"
}

"'startUpload'" should "return empty string if no parent folder provided" in {
doAnswer(() => new BearerAccessToken("token"))
.when(mockKeycloakToken)
.bearerAccessToken

doAnswer(() => Future(new BearerAccessToken("token")))
.when(keycloak)
.serviceAccountToken[Identity](any[String], any[String])(any[SttpBackend[Identity, Any]], any[ClassTag[Identity[_]]], any[TdrKeycloakDeployment])

doAnswer(() => Future(GraphQlResponse[su.Data](Option(su.Data("")), Nil)))
.when(startUploadClient)
.getResult[Identity](any[BearerAccessToken], any[Document], any[Option[su.Variables]])(any[SttpBackend[Identity, Any]], any[ClassTag[Identity[_]]])

val response = GraphQlApiService.apply(addConsignmentClient, startUploadClient).startUpload(mockKeycloakToken, UUID.fromString(consignmentId)).unsafeRunSync()

response shouldBe ""
}

"'startUpload'" should "throw an exception when no consignment added" in {
doAnswer(() => Future(new BearerAccessToken("token")))
.when(keycloak)
.serviceAccountToken[Identity](any[String], any[String])(any[SttpBackend[Identity, Any]], any[ClassTag[Identity[_]]], any[TdrKeycloakDeployment])

doAnswer(() => Future(GraphQlResponse[su.Data](None, Nil)))
.when(startUploadClient)
.getResult[Identity](any[BearerAccessToken], any[Document], any[Option[su.Variables]])(any[SttpBackend[Identity, Any]], any[ClassTag[Identity[_]]])

val exception = intercept[RuntimeException] {
GraphQlApiService.apply(addConsignmentClient, startUploadClient).startUpload(mockKeycloakToken, UUID.fromString(consignmentId)).unsafeRunSync()
}
exception.getMessage should equal(s"Load not started for consignment: 6e3b76c4-1745-4467-8ac5-b4dd736e1b3e")
}
}

0 comments on commit 52d1848

Please sign in to comment.