diff --git a/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/RunShip.scala b/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/RunShip.scala index 4f46b29a96..70482403cb 100644 --- a/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/RunShip.scala +++ b/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/RunShip.scala @@ -18,7 +18,7 @@ import ch.epfl.bluebrain.nexus.ship.files.FileProcessor import ch.epfl.bluebrain.nexus.ship.organizations.OrganizationProvider import ch.epfl.bluebrain.nexus.ship.projects.{OriginalProjectContext, ProjectProcessor} import ch.epfl.bluebrain.nexus.ship.resolvers.ResolverProcessor -import ch.epfl.bluebrain.nexus.ship.resources.{DistributionPatcher, ResourceProcessor, ResourceWiring} +import ch.epfl.bluebrain.nexus.ship.resources.{ResourceProcessor, ResourceWiring, SourcePatcher} import ch.epfl.bluebrain.nexus.ship.schemas.{SchemaProcessor, SchemaWiring} import ch.epfl.bluebrain.nexus.ship.views.{BlazegraphViewProcessor, CompositeViewProcessor, ElasticSearchViewProcessor, ViewPatcher} import fs2.Stream @@ -63,8 +63,8 @@ object RunShip { resolverProcessor = ResolverProcessor(fetchContext, projectMapper, eventLogConfig, eventClock, xas) schemaProcessor = SchemaProcessor(schemaLog, fetchContext, schemaImports, rcr, projectMapper, eventClock) fileSelf = FileSelf(originalProjectContext)(originalBaseUri) - distributionPatcher = new DistributionPatcher(fileSelf, projectMapper, targetBaseUri) - resourceProcessor = ResourceProcessor(resourceLog, rcr, projectMapper, fetchContext, distributionPatcher, eventClock) + sourcePatcher = SourcePatcher(fileSelf, projectMapper, targetBaseUri) + resourceProcessor = ResourceProcessor(resourceLog, rcr, projectMapper, fetchContext, sourcePatcher, eventClock) viewPatcher = new ViewPatcher(config.projectMapping) esViewsProcessor = ElasticSearchViewProcessor(fetchContext, rcr, projectMapper, viewPatcher, eventLogConfig, eventClock, xas) bgViewsProcessor = BlazegraphViewProcessor(fetchContext, rcr, projectMapper, viewPatcher, eventLogConfig, eventClock, xas) diff --git a/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/resources/ResourceProcessor.scala b/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/resources/ResourceProcessor.scala index 4d2c271eeb..137a3a1716 100644 --- a/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/resources/ResourceProcessor.scala +++ b/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/resources/ResourceProcessor.scala @@ -22,7 +22,7 @@ import io.circe.Decoder class ResourceProcessor private ( resources: Resources, projectMapper: ProjectMapper, - distributionPatcher: DistributionPatcher, + sourcePatcher: SourcePatcher, clock: EventClock ) extends EventProcessor[ResourceEvent] { @@ -48,11 +48,11 @@ class ResourceProcessor private ( event match { case e: ResourceCreated => - distributionPatcher.singleOrArray(e.source).flatMap { patched => + sourcePatcher(e.source).flatMap { patched => resources.create(e.id, project, e.schema.toIdSegment, patched, e.tag) } case e: ResourceUpdated => - distributionPatcher.singleOrArray(e.source).flatMap { patched => + sourcePatcher(e.source).flatMap { patched => resources.update(e.id, project, e.schema.toIdSegment.some, cRev, patched, e.tag) } case e: ResourceSchemaUpdated => @@ -88,11 +88,11 @@ object ResourceProcessor { rcr: ResolverContextResolution, projectMapper: ProjectMapper, fetchContext: FetchContext, - distributionPatcher: DistributionPatcher, + sourcePatcher: SourcePatcher, clock: EventClock )(implicit jsonLdApi: JsonLdApi): ResourceProcessor = { val resources = ResourcesImpl(log, fetchContext, rcr) - new ResourceProcessor(resources, projectMapper, distributionPatcher, clock) + new ResourceProcessor(resources, projectMapper, sourcePatcher, clock) } } diff --git a/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/resources/SourcePatcher.scala b/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/resources/SourcePatcher.scala new file mode 100644 index 0000000000..4a334b215d --- /dev/null +++ b/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/resources/SourcePatcher.scala @@ -0,0 +1,30 @@ +package ch.epfl.bluebrain.nexus.ship.resources + +import cats.effect.IO +import ch.epfl.bluebrain.nexus.delta.plugins.storage.FileSelf +import ch.epfl.bluebrain.nexus.delta.sdk.model.BaseUri +import ch.epfl.bluebrain.nexus.ship.ProjectMapper +import ch.epfl.bluebrain.nexus.ship.resources.SourcePatcher.removeEmptyIds +import io.circe.Json +import io.circe.optics.JsonPath.root + +final class SourcePatcher(distributionPatcher: DistributionPatcher) { + + def apply(json: Json): IO[Json] = distributionPatcher.singleOrArray(json).map(removeEmptyIds) + +} + +object SourcePatcher { + + private val emptyString = Json.fromString("") + + // Remove empty ids from sources as they are not accepted anymore in payloads + // Resources will be keep their ids as we explicitly pass them along the source payload + val removeEmptyIds: Json => Json = root.obj.modify { + case obj if obj("@id").contains(emptyString) => obj.remove("@id") + case obj => obj + } + + def apply(fileSelfParser: FileSelf, projectMapper: ProjectMapper, targetBase: BaseUri): SourcePatcher = + new SourcePatcher(new DistributionPatcher(fileSelfParser, projectMapper, targetBase)) +} diff --git a/ship/src/test/scala/ch/epfl/bluebrain/nexus/ship/resources/SourcePatcherSuite.scala b/ship/src/test/scala/ch/epfl/bluebrain/nexus/ship/resources/SourcePatcherSuite.scala new file mode 100644 index 0000000000..0004d1b001 --- /dev/null +++ b/ship/src/test/scala/ch/epfl/bluebrain/nexus/ship/resources/SourcePatcherSuite.scala @@ -0,0 +1,23 @@ +package ch.epfl.bluebrain.nexus.ship.resources + +import ch.epfl.bluebrain.nexus.testkit.mu.NexusSuite + +class SourcePatcherSuite extends NexusSuite { + + test("Removing empty ids does not modify the source when the @id is non empty") { + val source = json"""{ "@id": "bob-id", "name": "Bob" }""" + assertEquals(SourcePatcher.removeEmptyIds(source), source) + } + + test("Removing empty ids does not modify the source when there is no @id") { + val source = json"""{ "name": "Bob" }""" + assertEquals(SourcePatcher.removeEmptyIds(source), source) + } + + test("Removing empty ids removes the @id field when it is empty") { + val source = json"""{ "@id": "", "name": "Bob" }""" + val expected = json"""{ "name": "Bob" }""" + assertEquals(SourcePatcher.removeEmptyIds(source), expected) + } + +}