From b19f81c2ac896b26f18e53ec236881877ba721ea Mon Sep 17 00:00:00 2001 From: irinaschubert Date: Thu, 5 Jan 2023 13:41:06 +0100 Subject: [PATCH] feat: expose GET /admin/projects as ZIO HTTP route (#2366) --- .../admin/ProjectsResponderADMSpec.scala | 8 +- .../ProjectsMessagesADM.scala | 9 +- .../admin/ProjectsResponderADM.scala | 39 +-- .../admin/RestProjectsService.scala | 13 + .../responders/v1/OntologyResponderV1.scala | 4 +- .../routing/admin/ProjectsRouteADM.scala | 22 +- .../webapi/routing/admin/ProjectsRouteZ.scala | 11 + .../routing/admin/ProjectsRouteZSpec.scala | 309 +++++++++++++----- 8 files changed, 263 insertions(+), 152 deletions(-) diff --git a/webapi/src/it/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala b/webapi/src/it/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala index c73468f51b..f62b3cef26 100644 --- a/webapi/src/it/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala +++ b/webapi/src/it/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala @@ -46,9 +46,7 @@ class ProjectsResponderADMSpec extends CoreSpec with ImplicitSender { "The ProjectsResponderADM" when { "used to query for project information" should { "return information for every project" in { - appActor ! ProjectsGetRequestADM( - requestingUser = rootUser - ) + appActor ! ProjectsGetRequestADM() val received = expectMsgType[ProjectsGetResponseADM](timeout) assert(received.projects.contains(SharedTestDataADM.imagesProject)) @@ -648,9 +646,7 @@ class ProjectsResponderADMSpec extends CoreSpec with ImplicitSender { "used to query keywords" should { "return all unique keywords for all projects" in { - appActor ! ProjectsKeywordsGetRequestADM( - SharedTestDataADM.rootUser - ) + appActor ! ProjectsKeywordsGetRequestADM() val received: ProjectsKeywordsGetResponseADM = expectMsgType[ProjectsKeywordsGetResponseADM](timeout) received.keywords.size should be(21) } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsMessagesADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsMessagesADM.scala index c134505085..0c0c47288d 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsMessagesADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsMessagesADM.scala @@ -166,17 +166,14 @@ sealed trait ProjectsResponderRequestADM extends KnoraRequestADM /** * Get all information about all projects in form of [[ProjectsGetResponseADM]]. The ProjectsGetRequestV1 returns either * something or a NotFound exception if there are no projects found. Administration permission checking is performed. - * - * @param requestingUser the user making the request. */ -case class ProjectsGetRequestADM(requestingUser: UserADM) extends ProjectsResponderRequestADM +case class ProjectsGetRequestADM() extends ProjectsResponderRequestADM /** * Get info about a single project identified either through its IRI, shortname or shortcode. The response is in form * of [[ProjectGetResponseADM]]. External use. * * @param identifier the IRI, email, or username of the project. - * @param requestingUser the user making the request. */ case class ProjectGetRequestADM(identifier: ProjectIdentifierADM) extends ProjectsResponderRequestADM @@ -212,10 +209,8 @@ case class ProjectAdminMembersGetRequestADM( /** * Returns all unique keywords for all projects. - * - * @param requestingUser the user making the request. */ -case class ProjectsKeywordsGetRequestADM(requestingUser: UserADM) extends ProjectsResponderRequestADM +case class ProjectsKeywordsGetRequestADM() extends ProjectsResponderRequestADM /** * Returns all keywords for a project identified through IRI. diff --git a/webapi/src/main/scala/org/knora/webapi/responders/admin/ProjectsResponderADM.scala b/webapi/src/main/scala/org/knora/webapi/responders/admin/ProjectsResponderADM.scala index c0fa6e490d..37769ecc92 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/admin/ProjectsResponderADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/admin/ProjectsResponderADM.scala @@ -63,15 +63,14 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings * Receives a message extending [[ProjectsResponderRequestADM]], and returns an appropriate response message. */ def receive(msg: ProjectsResponderRequestADM) = msg match { - case ProjectsGetRequestADM(requestingUser) => projectsGetRequestADM(requestingUser) - case ProjectGetADM(identifier) => getSingleProjectADM(identifier) - case ProjectGetRequestADM(identifier) => getSingleProjectADMRequest(identifier) + case ProjectsGetRequestADM() => projectsGetRequestADM() + case ProjectGetADM(identifier) => getSingleProjectADM(identifier) + case ProjectGetRequestADM(identifier) => getSingleProjectADMRequest(identifier) case ProjectMembersGetRequestADM(identifier, requestingUser) => projectMembersGetRequestADM(identifier, requestingUser) case ProjectAdminMembersGetRequestADM(identifier, requestingUser) => projectAdminMembersGetRequestADM(identifier, requestingUser) - case ProjectsKeywordsGetRequestADM(requestingUser) => - projectsKeywordsGetRequestADM(requestingUser) + case ProjectsKeywordsGetRequestADM() => projectsKeywordsGetRequestADM() case ProjectKeywordsGetRequestADM(projectIri, requestingUser) => projectKeywordsGetRequestADM(projectIri, requestingUser) case ProjectRestrictedViewSettingsGetADM(identifier, requestingUser) => @@ -100,12 +99,9 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings /** * Gets all the projects and returns them as a sequence containing [[ProjectADM]]. * - * @param requestingUser the user making the request. * @return all the projects as a sequence containing [[ProjectADM]]. */ - private def projectsGetADM( - requestingUser: UserADM - ): Future[Seq[ProjectADM]] = + private def projectsGetADM(): Future[Seq[ProjectADM]] = for { sparqlQueryString <- Future( @@ -121,7 +117,7 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings projectsResponse <- appActor.ask(request).mapTo[SparqlExtendedConstructResponse] projectIris = projectsResponse.statements.keySet.map(_.toString) - ontologiesForProjects: Map[IRI, Seq[IRI]] <- getOntologiesForProjects(projectIris, requestingUser) + ontologiesForProjects: Map[IRI, Seq[IRI]] <- getOntologiesForProjects(projectIris) projects = projectsResponse.statements.toList.map { case (projectIriSubject: SubjectV2, propsMap: Map[SmartIri, Seq[LiteralV2]]) => @@ -139,10 +135,9 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings * Given a set of project IRIs, gets the ontologies that belong to each project. * * @param projectIris a set of project IRIs. If empty, returns the ontologies for all projects. - * @param requestingUser the requesting user. * @return a map of project IRIs to sequences of ontology IRIs. */ - private def getOntologiesForProjects(projectIris: Set[IRI], requestingUser: UserADM): Future[Map[IRI, Seq[IRI]]] = { + private def getOntologiesForProjects(projectIris: Set[IRI]): Future[Map[IRI, Seq[IRI]]] = { def getIriPair(ontology: OntologyMetadataV2) = ontology.projectIri.fold( throw InconsistentRepositoryDataException(s"Ontology ${ontology.ontologyIri} has no project") @@ -150,7 +145,7 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings val request = OntologyMetadataGetByProjectRequestV2( projectIris = projectIris.map(_.toSmartIri), - requestingUser = requestingUser + requestingUser = KnoraSystemInstances.Users.SystemUser ) for { @@ -164,15 +159,12 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings /** * Gets all the projects and returns them as a [[ProjectADM]]. * - * @param requestingUser the user that is making the request. * @return all the projects as a [[ProjectADM]]. * @throws NotFoundException if no projects are found. */ - private def projectsGetRequestADM( - requestingUser: UserADM - ): Future[ProjectsGetResponseADM] = + private def projectsGetRequestADM(): Future[ProjectsGetResponseADM] = for { - projects <- projectsGetADM(requestingUser = requestingUser) + projects <- projectsGetADM() result = if (projects.nonEmpty) { ProjectsGetResponseADM(projects = projects) } else { throw NotFoundException(s"No projects found") } } yield result @@ -381,16 +373,11 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings /** * Gets all unique keywords for all projects and returns them. Returns an empty list if none are found. * - * @param requestingUser the user making the request. * @return all keywords for all projects as [[ProjectsKeywordsGetResponseADM]] */ - private def projectsKeywordsGetRequestADM( - requestingUser: UserADM - ): Future[ProjectsKeywordsGetResponseADM] = + private def projectsKeywordsGetRequestADM(): Future[ProjectsKeywordsGetResponseADM] = for { - projects <- projectsGetADM( - requestingUser = KnoraSystemInstances.Users.SystemUser - ) + projects <- projectsGetADM() keywords: Seq[String] = projects.flatMap(_.keywords).distinct.sorted @@ -1175,7 +1162,7 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings ontologies <- if (projectResponse.statements.nonEmpty) { - getOntologiesForProjects(projectIris, KnoraSystemInstances.Users.SystemUser) + getOntologiesForProjects(projectIris) } else { FastFuture.successful(Map.empty[IRI, Seq[IRI]]) } diff --git a/webapi/src/main/scala/org/knora/webapi/responders/admin/RestProjectsService.scala b/webapi/src/main/scala/org/knora/webapi/responders/admin/RestProjectsService.scala index 9d06918125..6bdb89ef4a 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/admin/RestProjectsService.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/admin/RestProjectsService.scala @@ -6,10 +6,23 @@ import zio.ZLayer import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectGetRequestADM import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectGetResponseADM import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectIdentifierADM +import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectsGetRequestADM +import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectsGetResponseADM import org.knora.webapi.responders.ActorToZioBridge final case class RestProjectsService(bridge: ActorToZioBridge) { + /** + * Returns all projects as a [[ProjectsGetResponseADM]]. + * + * @return + * '''success''': information about the projects as a [[ProjectsGetResponseADM]] + * + * '''failure''': [[dsp.errors.NotFoundException]] when no project was found + */ + def getProjectsADMRequest(): Task[ProjectsGetResponseADM] = + bridge.askAppActor(ProjectsGetRequestADM()) + /** * Finds the project by its [[ProjectIdentifierADM]] and returns the information as a [[ProjectGetResponseADM]]. * diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v1/OntologyResponderV1.scala b/webapi/src/main/scala/org/knora/webapi/responders/v1/OntologyResponderV1.scala index b3efa7b2c0..737b1cb5ab 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v1/OntologyResponderV1.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v1/OntologyResponderV1.scala @@ -414,9 +414,7 @@ class OntologyResponderV1(responderData: ResponderData) extends Responder(respon for { projectsResponse <- appActor .ask( - ProjectsGetRequestADM( - requestingUser = userProfile - ) + ProjectsGetRequestADM() ) .mapTo[ProjectsGetResponseADM] diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/ProjectsRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/ProjectsRouteADM.scala index aa63ac4725..082d1dff78 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/ProjectsRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/ProjectsRouteADM.scala @@ -74,17 +74,8 @@ class ProjectsRouteADM(routeData: KnoraRouteData) private def getProjects(): Route = path(projectsBasePath) { get { requestContext => log.info("All projects requested.") - val requestMessage: Future[ProjectsGetRequestADM] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - routeData.appConfig - ) - } yield ProjectsGetRequestADM( - requestingUser = requestingUser - ) - RouteUtilADM.runJsonRoute( - requestMessageF = requestMessage, + requestMessageF = FastFuture.successful(ProjectsGetRequestADM()), requestContext = requestContext, appActor = appActor, log = log @@ -137,17 +128,8 @@ class ProjectsRouteADM(routeData: KnoraRouteData) */ private def getKeywords(): Route = path(projectsBasePath / "Keywords") { get { requestContext => - val requestMessage: Future[ProjectsKeywordsGetRequestADM] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - routeData.appConfig - ) - } yield ProjectsKeywordsGetRequestADM( - requestingUser = requestingUser - ) - RouteUtilADM.runJsonRoute( - requestMessageF = requestMessage, + requestMessageF = FastFuture.successful(ProjectsKeywordsGetRequestADM()), requestContext = requestContext, appActor = appActor, log = log diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/ProjectsRouteZ.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/ProjectsRouteZ.scala index 00b6cb77aa..727e3709ce 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/ProjectsRouteZ.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/ProjectsRouteZ.scala @@ -1,3 +1,8 @@ +/* + * Copyright © 2021 - 2022 Swiss National Data and Service Center for the Humanities and/or DaSCH Service Platform contributors. + * SPDX-License-Identifier: Apache-2.0 + */ + package org.knora.webapi.routing.admin import zhttp.http._ @@ -22,6 +27,7 @@ final case class ProjectsRouteZ( val route: HttpApp[Any, Nothing] = Http .collectZIO[Request] { + case Method.GET -> !! / "admin" / "projects" => getProjects() case Method.GET -> !! / "admin" / "projects" / "iri" / iriUrlEncoded => getProjectByIriEncoded(iriUrlEncoded) case Method.GET -> !! / "admin" / "projects" / "shortname" / shortname => getProjectByShortname(shortname) case Method.GET -> !! / "admin" / "projects" / "shortcode" / shortcode => getProjectByShortcode(shortcode) @@ -31,6 +37,11 @@ final case class ProjectsRouteZ( case InternalServerException(e) => ExceptionHandlerZ.exceptionToJsonHttpResponseZ(e, appConfig) } + private def getProjects(): Task[Response] = + for { + projectGetResponse <- projectsService.getProjectsADMRequest() + } yield Response.json(projectGetResponse.toJsValue.toString()) + private def getProjectByIriEncoded(iriUrlEncoded: String): Task[Response] = for { iriDecoded <- RouteUtilZ.urlDecode(iriUrlEncoded, s"Failed to URL decode IRI parameter $iriUrlEncoded.") diff --git a/webapi/src/test/scala/org/knora/webapi/routing/admin/ProjectsRouteZSpec.scala b/webapi/src/test/scala/org/knora/webapi/routing/admin/ProjectsRouteZSpec.scala index 3eff481517..2fbbe8a262 100644 --- a/webapi/src/test/scala/org/knora/webapi/routing/admin/ProjectsRouteZSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/routing/admin/ProjectsRouteZSpec.scala @@ -8,11 +8,14 @@ import zio.test._ import java.net.URLEncoder +import dsp.errors.NotFoundException import org.knora.webapi.config.AppConfig import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectADM import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectGetRequestADM import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectGetResponseADM import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectIdentifierADM +import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectsGetRequestADM +import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectsGetResponseADM import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2 import org.knora.webapi.responders.ActorToZioBridge import org.knora.webapi.responders.ActorToZioBridgeMock @@ -22,120 +25,246 @@ object ProjectRouteZSpec extends ZIOSpecDefault { private val systemUnderTest: URIO[ProjectsRouteZ, HttpApp[Any, Nothing]] = ZIO.service[ProjectsRouteZ].map(_.route) - // Test data + /** + * Paths + */ + private val basePathProjects: Path = !! / "admin" / "projects" + private val basePathProjectsIri: Path = !! / "admin" / "projects" / "iri" + private val basePathProjectsShortname: Path = !! / "admin" / "projects" / "shortname" + private val basePathProjectsShortcode: Path = !! / "admin" / "projects" / "shortcode" + + /** + * Test data and values used by multiple tests + */ + private val validProject: ProjectADM = ProjectADM( + id = "id", + shortname = "shortname", + shortcode = "AB12", + longname = None, + description = List(StringLiteralV2("description")), + keywords = List.empty, + logo = None, + ontologies = List.empty, + status = false, + selfjoin = false + ) + + private val validProject2: ProjectADM = ProjectADM( + id = "id2", + shortname = "shortname2", + shortcode = "AB13", + longname = None, + description = List(StringLiteralV2("description")), + keywords = List.empty, + logo = None, + ontologies = List.empty, + status = false, + selfjoin = false + ) + + private val expectedResponseNotFoundException: NotFoundException = NotFoundException("xxxx") + + /** + * Common layers and expectations for ActorToZioBridge mock + */ + private val commonLayers: URLayer[ActorToZioBridge, ProjectsRouteZ] = + ZLayer.makeSome[ActorToZioBridge, ProjectsRouteZ](AppConfig.test, ProjectsRouteZ.layer, RestProjectsService.layer) + + def spec = (getAllProjectsSpec + getProjectByIriSpec + getProjectByShortnameSpec + getProjectByShortcodeSpec) + + /** + * tests for /admin/projects + */ + private val expectedProjectsGetRequestADMSuccess: ProjectsGetRequestADM = ProjectsGetRequestADM() + private val expectedProjectsGetRequestADMFailure: ProjectsGetRequestADM = ProjectsGetRequestADM() + + private val expectedResponseMultipleProjectsSuccess: ProjectsGetResponseADM = + ProjectsGetResponseADM(Seq(validProject, validProject2)) + + private val expectMessageToProjectResponderMultipleProjects: ULayer[ActorToZioBridge] = + ActorToZioBridgeMock.AskAppActor + .of[ProjectsGetResponseADM] + .apply( + Assertion.equalTo(expectedProjectsGetRequestADMSuccess), + Expectation.value(expectedResponseMultipleProjectsSuccess) + ) + .toLayer + + private val expectMessageToProjectResponderMultipleProjectsFailure: ULayer[ActorToZioBridge] = + ActorToZioBridgeMock.AskAppActor + .of[NotFoundException] + .apply( + Assertion.equalTo(expectedProjectsGetRequestADMFailure), + Expectation.failure(expectedResponseNotFoundException) + ) + .toLayer + + private val getAllProjectsSpec: Spec[Any, Serializable] = + suite("get all projects")( + test("given projects exist, return all projects") { + val urlGetAllProjects = URL.empty.setPath(basePathProjects) + for { + route <- systemUnderTest + actual <- route.apply(Request(url = urlGetAllProjects)).flatMap(_.body.asString) + } yield assertTrue(actual == expectedResponseMultipleProjectsSuccess.toJsValue.toString()) + }.provide(commonLayers, expectMessageToProjectResponderMultipleProjects), + test("given no projects exist, respond with not found") { + val urlGetAllProjects = URL.empty.setPath(basePathProjects) + for { + route <- systemUnderTest + actual <- route.apply(Request(url = urlGetAllProjects)).map(_.status) + } yield assertTrue(actual == Status.NotFound) + }.provide(commonLayers, expectMessageToProjectResponderMultipleProjectsFailure) + ) + + /** + * common test data for /admin/projects/[iri | shortname | shortcode] + */ + private val expectNoInteractionWithProjectsResponderADM = ActorToZioBridgeMock.empty + private val expectedResponseOneProjectSuccess: ProjectGetResponseADM = ProjectGetResponseADM(validProject) + + /** + * tests for /admin/projects/iri + */ private val projectIri: ProjectIdentifierADM.IriIdentifier = ProjectIdentifierADM.IriIdentifier .fromString("http://rdfh.ch/projects/0001") .getOrElse(throw new IllegalArgumentException()) - private val projectShortname: ProjectIdentifierADM.ShortnameIdentifier = - ProjectIdentifierADM.ShortnameIdentifier - .fromString("shortname") + private val inexistentProjectIri: ProjectIdentifierADM.IriIdentifier = + ProjectIdentifierADM.IriIdentifier + .fromString("http://rdfh.ch/projects/0002") .getOrElse(throw new IllegalArgumentException()) - private val projectShortcode: ProjectIdentifierADM.ShortcodeIdentifier = - ProjectIdentifierADM.ShortcodeIdentifier - .fromString("AB12") - .getOrElse(throw new IllegalArgumentException()) + private val validIriUrlEncoded: String = URLEncoder.encode(projectIri.value.value, "utf-8") - private val basePathIri: Path = !! / "admin" / "projects" / "iri" - private val basePathShortname: Path = !! / "admin" / "projects" / "shortname" - private val basePathShortcode: Path = !! / "admin" / "projects" / "shortcode" - private val validIriUrlEncoded: String = URLEncoder.encode(projectIri.value.value, "utf-8") - private val validShortname: String = "shortname" - private val validShortcode: String = "AB12" - private val expectedRequestSuccessIri: ProjectGetRequestADM = ProjectGetRequestADM(projectIri) - private val expectedRequestSuccessShortname: ProjectGetRequestADM = ProjectGetRequestADM(projectShortname) - private val expectedRequestSuccessShortcode: ProjectGetRequestADM = ProjectGetRequestADM(projectShortcode) - private val expectedResponseSuccess: ProjectGetResponseADM = - ProjectGetResponseADM( - ProjectADM( - id = "id", - shortname = "shortname", - shortcode = "AB12", - longname = None, - description = List(StringLiteralV2("description")), - keywords = List.empty, - logo = None, - ontologies = List.empty, - status = false, - selfjoin = false - ) - ) + private val inexistentIriUrlEncoded: String = URLEncoder.encode(inexistentProjectIri.value.value, "utf-8") - // Expectations and layers for ActorToZioBridge mock - private val commonLayers: URLayer[ActorToZioBridge, ProjectsRouteZ] = - ZLayer.makeSome[ActorToZioBridge, ProjectsRouteZ](AppConfig.test, ProjectsRouteZ.layer, RestProjectsService.layer) + private val expectedProjectGetRequestADMSuccessIri: ProjectGetRequestADM = ProjectGetRequestADM(projectIri) + + private val expectedProjectGetRequestADMFailureIri: ProjectGetRequestADM = ProjectGetRequestADM(inexistentProjectIri) private val expectMessageToProjectResponderIri: ULayer[ActorToZioBridge] = ActorToZioBridgeMock.AskAppActor .of[ProjectGetResponseADM] - .apply(Assertion.equalTo(expectedRequestSuccessIri), Expectation.value(expectedResponseSuccess)) + .apply( + Assertion.equalTo(expectedProjectGetRequestADMSuccessIri), + Expectation.value(expectedResponseOneProjectSuccess) + ) + .toLayer + + private val expectMessageToProjectResponderFailure: ULayer[ActorToZioBridge] = + ActorToZioBridgeMock.AskAppActor + .of[NotFoundException] + .apply( + Assertion.equalTo(expectedProjectGetRequestADMFailureIri), + Expectation.failure(expectedResponseNotFoundException) + ) .toLayer + private val getProjectByIriSpec: Spec[Any, Serializable] = + suite("get project by IRI")( + test("given valid project iri, should respond with success") { + val urlWithValidIri = URL.empty.setPath(basePathProjectsIri / validIriUrlEncoded) + for { + route <- systemUnderTest + actual <- route.apply(Request(url = urlWithValidIri)).flatMap(_.body.asString) + } yield assertTrue(actual == expectedResponseOneProjectSuccess.toJsValue.toString()) + }.provide(commonLayers, expectMessageToProjectResponderIri), + test("given valid project iri that doesn't exist, should respond with not found") { + val urlWithInexistentIri = URL.empty.setPath(basePathProjectsIri / inexistentIriUrlEncoded) + for { + route <- systemUnderTest + actual <- route.apply(Request(url = urlWithInexistentIri)).map(_.status) + } yield assertTrue(actual == Status.NotFound) + }.provide(commonLayers, expectMessageToProjectResponderFailure), + test("given invalid project iri, should respond with bad request") { + val urlWithInvalidIri = URL.empty.setPath(basePathProjectsIri / "invalid") + for { + route <- systemUnderTest + actual <- route.apply(Request(url = urlWithInvalidIri)).map(_.status) + } yield assertTrue(actual == Status.BadRequest) + }.provide(commonLayers, expectNoInteractionWithProjectsResponderADM) + ) + + /** + * tests for /admin/projects/shortname + */ + private val projectShortname: ProjectIdentifierADM.ShortnameIdentifier = + ProjectIdentifierADM.ShortnameIdentifier + .fromString("shortname") + .getOrElse(throw new IllegalArgumentException()) + + private val validShortname: String = "shortname" + + private val expectedProjectGetRequestADMSuccessShortname: ProjectGetRequestADM = + ProjectGetRequestADM(projectShortname) + private val expectMessageToProjectResponderShortname: ULayer[ActorToZioBridge] = ActorToZioBridgeMock.AskAppActor .of[ProjectGetResponseADM] - .apply(Assertion.equalTo(expectedRequestSuccessShortname), Expectation.value(expectedResponseSuccess)) + .apply( + Assertion.equalTo(expectedProjectGetRequestADMSuccessShortname), + Expectation.value(expectedResponseOneProjectSuccess) + ) .toLayer + private val getProjectByShortnameSpec: Spec[Any, Serializable] = + suite("get project by shortname")( + test("given valid project shortname, should respond with success") { + val urlWithValidShortname = URL.empty.setPath(basePathProjectsShortname / validShortname) + for { + route <- systemUnderTest + actual <- route.apply(Request(url = urlWithValidShortname)).flatMap(_.body.asString) + } yield assertTrue(actual == expectedResponseOneProjectSuccess.toJsValue.toString()) + }.provide(commonLayers, expectMessageToProjectResponderShortname), + test("given invalid project shortname, should respond with bad request") { + val urlWithInvalidShortname = URL.empty.setPath(basePathProjectsShortname / "123") + for { + route <- systemUnderTest + actual <- route.apply(Request(url = urlWithInvalidShortname)).map(_.status) + } yield assertTrue(actual == Status.BadRequest) + }.provide(commonLayers, expectNoInteractionWithProjectsResponderADM) + ) + + /** + * tests for /admin/projects/shortcode + */ + private val projectShortcode: ProjectIdentifierADM.ShortcodeIdentifier = + ProjectIdentifierADM.ShortcodeIdentifier + .fromString("AB12") + .getOrElse(throw new IllegalArgumentException()) + + private val validShortcode: String = "AB12" + + private val expectedProjectGetRequestADMSuccessShortcode: ProjectGetRequestADM = + ProjectGetRequestADM(projectShortcode) + private val expectMessageToProjectResponderShortcode: ULayer[ActorToZioBridge] = ActorToZioBridgeMock.AskAppActor .of[ProjectGetResponseADM] - .apply(Assertion.equalTo(expectedRequestSuccessShortcode), Expectation.value(expectedResponseSuccess)) + .apply( + Assertion.equalTo(expectedProjectGetRequestADMSuccessShortcode), + Expectation.value(expectedResponseOneProjectSuccess) + ) .toLayer - private val expectNoInteractionWithProjectsResponderADM = ActorToZioBridgeMock.empty - - val spec: Spec[Any, Serializable] = - suite("ProjectsRouteZSpec")( - suite("get project by IRI")( - test("given valid project iri should respond with success") { - val urlWithValidIri = URL.empty.setPath(basePathIri / validIriUrlEncoded) - for { - route <- systemUnderTest - actual <- route.apply(Request(url = urlWithValidIri)).flatMap(_.body.asString) - } yield assertTrue(actual == expectedResponseSuccess.toJsValue.toString()) - }.provide(commonLayers, expectMessageToProjectResponderIri), - test("given invalid project iri should respond with bad request") { - val urlWithInvalidIri = URL.empty.setPath(basePathIri / "invalid") - for { - route <- systemUnderTest - actual <- route.apply(Request(url = urlWithInvalidIri)).map(_.status) - } yield assertTrue(actual == Status.BadRequest) - }.provide(commonLayers, expectNoInteractionWithProjectsResponderADM) - ), - suite("get project by shortname")( - test("given valid project shortname should respond with success") { - val urlWithValidShortname = URL.empty.setPath(basePathShortname / validShortname) - for { - route <- systemUnderTest - actual <- route.apply(Request(url = urlWithValidShortname)).flatMap(_.body.asString) - } yield assertTrue(actual == expectedResponseSuccess.toJsValue.toString()) - }.provide(commonLayers, expectMessageToProjectResponderShortname), - test("given invalid project shortname should respond with bad request") { - val urlWithInvalidShortname = URL.empty.setPath(basePathShortname / "123") - for { - route <- systemUnderTest - actual <- route.apply(Request(url = urlWithInvalidShortname)).map(_.status) - } yield assertTrue(actual == Status.BadRequest) - }.provide(commonLayers, expectNoInteractionWithProjectsResponderADM) - ), - suite("get project by shortcode")( - test("given valid project shortcode should respond with success") { - val urlWithValidShortcode = URL.empty.setPath(basePathShortcode / validShortcode) - for { - route <- systemUnderTest - actual <- route.apply(Request(url = urlWithValidShortcode)).flatMap(_.body.asString) - } yield assertTrue(actual == expectedResponseSuccess.toJsValue.toString()) - }.provide(commonLayers, expectMessageToProjectResponderShortcode), - test("given invalid project shortcode should respond with bad request") { - val urlWithInvalidShortcode = URL.empty.setPath(basePathShortcode / "invalid") - for { - route <- systemUnderTest - actual <- route.apply(Request(url = urlWithInvalidShortcode)).map(_.status) - } yield assertTrue(actual == Status.BadRequest) - }.provide(commonLayers, expectNoInteractionWithProjectsResponderADM) - ) + private val getProjectByShortcodeSpec: Spec[Any, Serializable] = + suite("get project by shortcode")( + test("given valid project shortcode, should respond with success") { + val urlWithValidShortcode = URL.empty.setPath(basePathProjectsShortcode / validShortcode) + for { + route <- systemUnderTest + actual <- route.apply(Request(url = urlWithValidShortcode)).flatMap(_.body.asString) + } yield assertTrue(actual == expectedResponseOneProjectSuccess.toJsValue.toString()) + }.provide(commonLayers, expectMessageToProjectResponderShortcode), + test("given invalid project shortcode, should respond with bad request") { + val urlWithInvalidShortcode = URL.empty.setPath(basePathProjectsShortcode / "invalid") + for { + route <- systemUnderTest + actual <- route.apply(Request(url = urlWithInvalidShortcode)).map(_.status) + } yield assertTrue(actual == Status.BadRequest) + }.provide(commonLayers, expectNoInteractionWithProjectsResponderADM) ) + }