From d4e531d65b576e476bf1432496151331984e7f2d Mon Sep 17 00:00:00 2001 From: dantb Date: Fri, 6 Oct 2023 11:39:34 +0200 Subject: [PATCH] Add unit tests for tagged creation --- .../nexus/delta/routes/ResourcesRoutes.scala | 49 ++++++++----------- .../delta/routes/ResourcesRoutesSpec.scala | 36 +++++++++++--- .../delta/sdk/directives/UriDirectives.scala | 5 ++ 3 files changed, 54 insertions(+), 36 deletions(-) diff --git a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/ResourcesRoutes.scala b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/ResourcesRoutes.scala index 0a635344b9..6a2d7d5002 100644 --- a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/ResourcesRoutes.scala +++ b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/ResourcesRoutes.scala @@ -25,7 +25,6 @@ import ch.epfl.bluebrain.nexus.delta.sdk.resources.NexusSource.DecodingOption import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.ResourceRejection.{InvalidJsonLdFormat, InvalidSchemaRejection, ResourceNotFound} import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.{Resource, ResourceRejection} import ch.epfl.bluebrain.nexus.delta.sdk.resources.{NexusSource, Resources} -import ch.epfl.bluebrain.nexus.delta.sourcing.model.Tag.UserTag import io.circe.{Json, Printer} import monix.bio.IO import monix.execution.Scheduler @@ -75,21 +74,20 @@ final class ResourcesRoutes( resolveProjectRef.apply { ref => concat( // Create a resource without schema nor id segment - (post & pathEndOrSingleSlash & noParameter("rev") & entity(as[NexusSource]) & indexingMode & parameter( - "tag".as[UserTag].? - )) { (source, mode, tag) => - authorizeFor(ref, Write).apply { - emit( - Created, - resources.create(ref, resourceSchema, source.value, tag).tapEval(index(ref, _, mode)).map(_.void) - ) - } + (post & pathEndOrSingleSlash & noParameter("rev") & entity(as[NexusSource]) & indexingMode & tagParam) { + (source, mode, tag) => + authorizeFor(ref, Write).apply { + emit( + Created, + resources.create(ref, resourceSchema, source.value, tag).tapEval(index(ref, _, mode)).map(_.void) + ) + } }, (idSegment & indexingMode) { (schema, mode) => val schemaOpt = underscoreToOption(schema) concat( // Create a resource with schema but without id segment - (post & pathEndOrSingleSlash & noParameter("rev") & parameter("tag".as[UserTag].?)) { tag => + (post & pathEndOrSingleSlash & noParameter("rev") & tagParam) { tag => authorizeFor(ref, Write).apply { entity(as[NexusSource]) { source => emit( @@ -110,10 +108,8 @@ final class ResourcesRoutes( // Create or update a resource put { authorizeFor(ref, Write).apply { - concat( - (noParameter("rev") & pathEndOrSingleSlash & parameter("tag".as[UserTag].?) & entity( - as[NexusSource] - )) { (tag, source) => + (parameter("rev".as[Int].?) & pathEndOrSingleSlash & entity(as[NexusSource]) & tagParam) { + case (None, source, tag) => // Create a resource with schema and id segments emit( Created, @@ -123,19 +119,16 @@ final class ResourcesRoutes( .map(_.void) .rejectWhen(wrongJsonOrNotFound) ) - }, - (pathEndOrSingleSlash & parameter("rev".as[Int]) & entity(as[NexusSource])) { - (rev, source) => - // Update a resource - emit( - resources - .update(id, ref, schemaOpt, rev, source.value) - .tapEval(index(ref, _, mode)) - .map(_.void) - .rejectWhen(wrongJsonOrNotFound) - ) - } - ) + case (Some(rev), source, _) => + // Update a resource + emit( + resources + .update(id, ref, schemaOpt, rev, source.value) + .tapEval(index(ref, _, mode)) + .map(_.void) + .rejectWhen(wrongJsonOrNotFound) + ) + } } }, // Deprecate a resource diff --git a/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/ResourcesRoutesSpec.scala b/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/ResourcesRoutesSpec.scala index add223b637..2efa498d2c 100644 --- a/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/ResourcesRoutesSpec.scala +++ b/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/ResourcesRoutesSpec.scala @@ -63,13 +63,17 @@ class ResourcesRoutesSpec extends BaseRouteSpec with IOFromMap { private val schema1 = SchemaGen.schema(nxv + "myschema", project.value.ref, schemaSource.removeKeys(keywords.id)) private val schema2 = SchemaGen.schema(schema.Person, project.value.ref, schemaSource.removeKeys(keywords.id)) - private val myId = nxv + "myid" // Resource created against no schema with id present on the payload - private val myId2 = nxv + "myid2" // Resource created against schema1 with id present on the payload - private val myId3 = nxv + "myid3" // Resource created against no schema with id passed and present on the payload - private val myId4 = nxv + "myid4" // Resource created against schema1 with id passed and present on the payload - private val myId5 = nxv + "myid5" // Resource created against schema1 with id passed and present on the payload - private val myId6 = nxv + "myid6" // Resource created and tagged, against no schema with id present on the payload - private val myId7 = nxv + "myid7" // Resource created and tagged, against schema1 with id present on the payload + private val myId = nxv + "myid" // Resource created against no schema with id present on the payload + private val myId2 = nxv + "myid2" // Resource created against schema1 with id present on the payload + private val myId3 = nxv + "myid3" // Resource created against no schema with id passed and present on the payload + private val myId4 = nxv + "myid4" // Resource created against schema1 with id passed and present on the payload + private val myId5 = nxv + "myid5" // Resource created against schema1 with id passed and present on the payload + private val myId6 = nxv + "myid6" // Resource created and tagged against no schema with id present on the payload + private val myId7 = nxv + "myid7" // Resource created and tagged against no schema with id present on the payload + private val myId8 = + nxv + "myid8" // Resource created and tagged against no schema with id passed and present on the payload + private val myId9 = + nxv + "myid9" // Resource created and tagged against schema1 with id passed and present on the payload private val myIdEncoded = UrlUtils.encode(myId.toString) private val myId2Encoded = UrlUtils.encode(myId2.toString) private val payload = jsonContentOf("resources/resource.json", "id" -> myId) @@ -153,7 +157,7 @@ class ResourcesRoutesSpec extends BaseRouteSpec with IOFromMap { } } - "create a resource with a tag" in { + "create a tagged resource" in { val endpoints = List( ("/v1/resources/myorg/myproject?tag=mytag", myId6, schemas.resources), ("/v1/resources/myorg/myproject/myschema?tag=mytag", myId7, schema1.id) @@ -183,6 +187,22 @@ class ResourcesRoutesSpec extends BaseRouteSpec with IOFromMap { } } + "create a tagged resource with an authenticated user and provided id" in { + val endpoints = List( + ("/v1/resources/myorg/myproject/_/myid8?tag=mytag", myId8, schemas.resources), + ("/v1/resources/myorg/myproject/myschema/myid9?tag=mytag", myId9, schema1.id) + ) + forAll(endpoints) { case (endpoint, id, schema) => + val payload = jsonContentOf("resources/resource.json", "id" -> id) + Put(endpoint, payload.toEntity) ~> asAlice ~> routes ~> check { + status shouldEqual StatusCodes.Created + response.asJson shouldEqual + resourceMetadata(projectRef, id, schema, (nxv + "Custom").toString, createdBy = alice, updatedBy = alice) + } + + } + } + "reject the creation of a resource which already exists" in { Put("/v1/resources/myorg/myproject/_/myid", payload.toEntity) ~> routes ~> check { status shouldEqual StatusCodes.Conflict diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/directives/UriDirectives.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/directives/UriDirectives.scala index c8706922fa..6b2b400062 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/directives/UriDirectives.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/directives/UriDirectives.scala @@ -170,6 +170,11 @@ trait UriDirectives extends QueryParamsUnmarshalling { ) } + /** + * Creates optional [[UserTag]] from `tag` query param. + */ + val tagParam: Directive1[Option[UserTag]] = parameter("tag".as[UserTag].?) + def timeRange(paramName: String): Directive1[TimeRange] = parameter(paramName.as[String].?).flatMap { case None => provide(TimeRange.default) case Some(value) =>