From 34be7170fdb52f6b8bdc959da1e2a78c9cc9bff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 6 Nov 2024 14:26:31 +0100 Subject: [PATCH] refactor: Remove `KnoraApiCreateValueModel` and move code to service which is directly assembling the `CreateValueV2` (DEV-4305) (#3413) --- .../org/knora/webapi/core/LayersTest.scala | 3 + .../org/knora/webapi/core/LayersLive.scala | 3 + .../valuemessages/ValueMessagesV2.scala | 36 - .../org/knora/webapi/routing/ApiRoutes.scala | 7 +- .../webapi/routing/v2/ValuesRouteV2.scala | 8 +- .../ApiComplexV2JsonLdRequestParser.scala | 179 +++++ .../store/iiif/impl/SipiServiceMock.scala | 73 +- .../valuemessages/CreateValueV2Spec.scala | 75 -- .../ApiComplexV2JsonLdRequestParserSpec.scala | 754 ++++++++++++++++++ .../slice/common/KnoraApiValueModelSpec.scala | 630 --------------- 10 files changed, 1002 insertions(+), 766 deletions(-) create mode 100644 webapi/src/main/scala/org/knora/webapi/slice/common/ApiComplexV2JsonLdRequestParser.scala delete mode 100644 webapi/src/test/scala/org/knora/webapi/messages/v2/responder/valuemessages/CreateValueV2Spec.scala create mode 100644 webapi/src/test/scala/org/knora/webapi/slice/common/ApiComplexV2JsonLdRequestParserSpec.scala delete mode 100644 webapi/src/test/scala/org/knora/webapi/slice/common/KnoraApiValueModelSpec.scala diff --git a/integration/src/test/scala/org/knora/webapi/core/LayersTest.scala b/integration/src/test/scala/org/knora/webapi/core/LayersTest.scala index 9167c13a61..34f6758b3e 100644 --- a/integration/src/test/scala/org/knora/webapi/core/LayersTest.scala +++ b/integration/src/test/scala/org/knora/webapi/core/LayersTest.scala @@ -41,6 +41,7 @@ import org.knora.webapi.slice.admin.api.service.ProjectRestService import org.knora.webapi.slice.admin.api.service.UserRestService import org.knora.webapi.slice.admin.domain.service.* import org.knora.webapi.slice.admin.domain.service.ProjectExportStorageService +import org.knora.webapi.slice.common.ApiComplexV2JsonLdRequestParser import org.knora.webapi.slice.common.api.* import org.knora.webapi.slice.common.repo.service.PredicateObjectMapper import org.knora.webapi.slice.infrastructure.CacheManager @@ -102,6 +103,7 @@ object LayersTest { // format: off AdminApiEndpoints & AdminModule.Provided & + ApiComplexV2JsonLdRequestParser & ApiRoutes & AppRouter & AuthenticationApiModule.Provided & @@ -167,6 +169,7 @@ object LayersTest { ZLayer.makeSome[CommonR0, CommonR]( AdminApiModule.layer, AdminModule.layer, + ApiComplexV2JsonLdRequestParser.layer, ApiRoutes.layer, AppRouter.layer, AssetPermissionsResponder.layer, diff --git a/webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala b/webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala index 99a1a87585..f9abce6010 100644 --- a/webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala +++ b/webapi/src/main/scala/org/knora/webapi/core/LayersLive.scala @@ -38,6 +38,7 @@ import org.knora.webapi.slice.admin.api.service.PermissionRestService import org.knora.webapi.slice.admin.api.service.ProjectRestService import org.knora.webapi.slice.admin.api.service.UserRestService import org.knora.webapi.slice.admin.domain.service.* +import org.knora.webapi.slice.common.ApiComplexV2JsonLdRequestParser import org.knora.webapi.slice.common.api.* import org.knora.webapi.slice.common.repo.service.PredicateObjectMapper import org.knora.webapi.slice.infrastructure.InfrastructureModule @@ -82,6 +83,7 @@ object LayersLive { ActorSystem & AdminApiEndpoints & AdminModule.Provided & + ApiComplexV2JsonLdRequestParser & ApiRoutes & ApiV2Endpoints & AppConfigurations & @@ -139,6 +141,7 @@ object LayersLive { ZLayer.make[DspEnvironmentLive]( AdminApiModule.layer, AdminModule.layer, + ApiComplexV2JsonLdRequestParser.layer, ApiRoutes.layer, ApiV2Endpoints.layer, AppConfig.layer, diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2.scala index 772fa99061..23aee9e900 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2.scala @@ -50,7 +50,6 @@ import org.knora.webapi.slice.admin.api.model.Project import org.knora.webapi.slice.admin.domain.model.KnoraProject.Shortcode import org.knora.webapi.slice.admin.domain.model.Permission import org.knora.webapi.slice.admin.domain.model.User -import org.knora.webapi.slice.common.KnoraApiCreateValueModel import org.knora.webapi.slice.common.jena.JenaConversions.given import org.knora.webapi.slice.common.jena.ResourceOps.* import org.knora.webapi.slice.resourceinfo.domain.InternalIri @@ -566,41 +565,6 @@ case class CreateValueV2( ingestState: AssetIngestState = AssetInTemp, ) -/** - * Constructs [[CreateValueV2]] instances based on JSON-LD input. - */ -object CreateValueV2 { - - /** - * Converts JSON-LD input to a [[CreateValueV2]]. - * - * @param ingestState indicates the state of the file, either ingested or in temp folder - * @param jsonLdString JSON-LD input as String. - * @return a case class instance representing the input. - */ - def fromJsonLd( - ingestState: AssetIngestState, - jsonLdString: String, - ): ZIO[SipiService & IriConverter & MessageRelay, Throwable, CreateValueV2] = ZIO.scoped { - for { - converter <- ZIO.service[IriConverter] - model <- KnoraApiCreateValueModel.fromJsonLd(jsonLdString, converter).mapError(BadRequestException(_)) - fileInfo <- ValueContentV2.fileInfoFromExternal(model.valueFileValueFilename, ingestState, model.shortcode) - valueContent <- model.getValueContent(fileInfo).mapError(BadRequestException(_)) - } yield CreateValueV2( - resourceIri = model.resourceIri.toString, - resourceClassIri = model.resourceClassIri.smartIri, - propertyIri = model.valuePropertyIri.smartIri, - valueContent = valueContent, - valueIri = model.valueIri.map(_.smartIri), - valueUUID = model.valueUuid, - valueCreationDate = model.valueCreationDate, - permissions = model.valuePermissions, - ingestState = ingestState, - ) - } -} - /** A trait for classes representing information to be updated in a value. */ sealed trait UpdateValueV2 { diff --git a/webapi/src/main/scala/org/knora/webapi/routing/ApiRoutes.scala b/webapi/src/main/scala/org/knora/webapi/routing/ApiRoutes.scala index 0998a87784..5502c6cabc 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/ApiRoutes.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/ApiRoutes.scala @@ -28,6 +28,7 @@ import org.knora.webapi.routing.v2.* import org.knora.webapi.slice.admin.api.AdminApiRoutes import org.knora.webapi.slice.admin.domain.service.ProjectService import org.knora.webapi.slice.admin.domain.service.UserService +import org.knora.webapi.slice.common.ApiComplexV2JsonLdRequestParser import org.knora.webapi.slice.common.api.AuthorizationRestService import org.knora.webapi.slice.infrastructure.api.ManagementRoutes import org.knora.webapi.slice.lists.api.ListsApiV2Routes @@ -98,9 +99,9 @@ final case class ApiRoutes( object ApiRoutes { private type ApiRoutesRuntime = - AppConfig & AuthenticationApiRoutes & AuthorizationRestService & core.State & IriConverter & MessageRelay & - ProjectService & RestCardinalityService & WebApiAuthenticator & SearchApiRoutes & SearchResponderV2 & - SipiService & StringFormatter & UserService & ValuesResponderV2 & ListsApiV2Routes + ApiComplexV2JsonLdRequestParser & AppConfig & AuthenticationApiRoutes & AuthorizationRestService & core.State & + IriConverter & MessageRelay & ProjectService & RestCardinalityService & WebApiAuthenticator & SearchApiRoutes & + SearchResponderV2 & SipiService & StringFormatter & UserService & ValuesResponderV2 & ListsApiV2Routes /** * All routes composed together. diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v2/ValuesRouteV2.scala b/webapi/src/main/scala/org/knora/webapi/routing/v2/ValuesRouteV2.scala index 46ddd2e8c7..dee6c6f8af 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v2/ValuesRouteV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v2/ValuesRouteV2.scala @@ -21,6 +21,7 @@ import org.knora.webapi.messages.v2.responder.valuemessages.* import org.knora.webapi.responders.v2.ValuesResponderV2 import org.knora.webapi.routing.RouteUtilV2 import org.knora.webapi.routing.RouteUtilZ +import org.knora.webapi.slice.common.ApiComplexV2JsonLdRequestParser import org.knora.webapi.slice.resourceinfo.domain.IriConverter import org.knora.webapi.slice.security.Authenticator import org.knora.webapi.store.iiif.api.SipiService @@ -30,7 +31,8 @@ import org.knora.webapi.store.iiif.api.SipiService */ final case class ValuesRouteV2()( private implicit val runtime: Runtime[ - AppConfig & Authenticator & IriConverter & SipiService & StringFormatter & MessageRelay & ValuesResponderV2, + ApiComplexV2JsonLdRequestParser & AppConfig & Authenticator & IriConverter & SipiService & StringFormatter & + MessageRelay & ValuesResponderV2, ], ) { @@ -84,7 +86,9 @@ final case class ValuesRouteV2()( requestingUser <- ZIO.serviceWithZIO[Authenticator](_.getUserADM(ctx)) apiRequestId <- Random.nextUUID ingestState = AssetIngestState.headerAssetIngestState(ctx.request.headers) - valueToCreate <- CreateValueV2.fromJsonLd(ingestState, jsonLdString) + valueToCreate <- ZIO.serviceWithZIO[ApiComplexV2JsonLdRequestParser]( + _.createValueV2FromJsonLd(jsonLdString, ingestState).mapError(BadRequestException(_)), + ) response <- ZIO.serviceWithZIO[ValuesResponderV2](_.createValueV2(valueToCreate, requestingUser, apiRequestId)) } yield response, diff --git a/webapi/src/main/scala/org/knora/webapi/slice/common/ApiComplexV2JsonLdRequestParser.scala b/webapi/src/main/scala/org/knora/webapi/slice/common/ApiComplexV2JsonLdRequestParser.scala new file mode 100644 index 0000000000..cec10fe54c --- /dev/null +++ b/webapi/src/main/scala/org/knora/webapi/slice/common/ApiComplexV2JsonLdRequestParser.scala @@ -0,0 +1,179 @@ +/* + * Copyright © 2021 - 2024 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.slice.common +import org.apache.jena.rdf.model.* +import org.apache.jena.vocabulary.RDF +import zio.* +import zio.ZIO +import zio.ZLayer + +import java.time.Instant +import java.util.UUID +import scala.jdk.CollectionConverters.* +import scala.language.implicitConversions + +import dsp.valueobjects.UuidUtil +import org.knora.webapi.core.MessageRelay +import org.knora.webapi.messages.OntologyConstants +import org.knora.webapi.messages.OntologyConstants.KnoraApiV2Complex.* +import org.knora.webapi.messages.OntologyConstants.Xsd +import org.knora.webapi.messages.ValuesValidator +import org.knora.webapi.messages.v2.responder.valuemessages.* +import org.knora.webapi.messages.v2.responder.valuemessages.ValueContentV2.FileInfo +import org.knora.webapi.routing.v2.AssetIngestState +import org.knora.webapi.slice.admin.domain.model.KnoraProject.Shortcode +import org.knora.webapi.slice.common.KnoraIris.* +import org.knora.webapi.slice.common.KnoraIris.ResourceClassIri as KResourceClassIri +import org.knora.webapi.slice.common.KnoraIris.ResourceIri +import org.knora.webapi.slice.common.KnoraIris.ResourceIri as KResourceIri +import org.knora.webapi.slice.common.jena.JenaConversions.given +import org.knora.webapi.slice.common.jena.ModelOps +import org.knora.webapi.slice.common.jena.ModelOps.* +import org.knora.webapi.slice.common.jena.ResourceOps.* +import org.knora.webapi.slice.common.jena.StatementOps.* +import org.knora.webapi.slice.resourceinfo.domain.IriConverter +import org.knora.webapi.store.iiif.api.SipiService + +final case class ApiComplexV2JsonLdRequestParser( + converter: IriConverter, + messageRelay: MessageRelay, + sipiService: SipiService, +) { + + def createValueV2FromJsonLd(str: String, ingestState: AssetIngestState): IO[String, CreateValueV2] = + ZIO.scoped { + for { + model <- ModelOps.fromJsonLd(str) + resourceAndIri <- resourceAndIri(model) + (resource, resourceIri) = resourceAndIri + resourceClassIri <- resourceClassIri(resource) + valueStatement <- valueStatement(resource) + valuePropertyIri <- valuePropertyIri(valueStatement) + valueType <- valueType(valueStatement) + valueResource = valueStatement.getObject.asResource() + valueIri <- valueIri(valueResource) + valueUuid <- ZIO.fromEither(valueHasUuid(valueResource)) + valueCreationDate <- ZIO.fromEither(valueCreationDate(valueResource)) + valuePermissions <- ZIO.fromEither(valuePermissions(valueResource)) + valueFileValueFilename <- ZIO.fromEither(valueFileValueFilename(valueResource)) + valueContent <- + getValueContent(valueType.toString, valueResource, valueFileValueFilename, resourceIri.shortcode, ingestState) + } yield CreateValueV2( + resourceIri.toString, + resourceClassIri.smartIri, + valuePropertyIri.smartIri, + valueContent, + valueIri.map(_.smartIri), + valueUuid, + valueCreationDate, + valuePermissions, + ingestState, + ) + } + + private def resourceAndIri(model: Model): IO[String, (Resource, ResourceIri)] = + ZIO.fromEither(model.singleRootResource).flatMap { (r: Resource) => + converter + .asSmartIri(r.uri.getOrElse("")) + .mapError(_.getMessage) + .flatMap(iri => ZIO.fromEither(KResourceIri.from(iri))) + .map((r, _)) + } + + private def valueStatement(rootResource: Resource): IO[String, Statement] = ZIO + .succeed(rootResource.listProperties().asScala.filter(_.getPredicate != RDF.`type`).toList) + .filterOrFail(_.nonEmpty)("No value property found in root resource") + .filterOrFail(_.size == 1)("Multiple value properties found in root resource") + .map(_.head) + + private def valuePropertyIri(valueStatement: Statement) = + converter + .asSmartIri(valueStatement.predicateUri) + .mapError(_.getMessage) + .flatMap(iri => ZIO.fromEither(PropertyIri.from(iri))) + + private def valueType(stmt: Statement) = ZIO + .fromEither(stmt.objectAsResource().flatMap(_.rdfsType.toRight("No rdf:type found for value."))) + .orElseFail(s"No value type found for value.") + .flatMap(converter.asSmartIri(_).mapError(_.getMessage)) + + private def valueIri(valueResource: Resource): IO[String, Option[ValueIri]] = ZIO + .fromOption(valueResource.uri) + .flatMap(converter.asSmartIri(_).mapError(_.getMessage).asSomeError) + .flatMap(iri => ZIO.fromEither(ValueIri.from(iri)).asSomeError) + .unsome + + private def valueHasUuid(valueResource: Resource): Either[String, Option[UUID]] = + valueResource.objectStringOption(ValueHasUUID).flatMap { + case Some(str) => + UuidUtil.base64Decode(str).map(Some(_)).toEither.left.map(e => s"Invalid UUID '$str': ${e.getMessage}") + case None => Right(None) + } + + private def valueCreationDate(valueResource: Resource): Either[String, Option[Instant]] = + valueResource.objectDataTypeOption(ValueCreationDate, Xsd.DateTimeStamp).flatMap { + case Some(str) => ValuesValidator.parseXsdDateTimeStamp(str).map(Some(_)) + case None => Right(None) + } + + private def valuePermissions(valueResource: Resource): Either[String, Option[String]] = + valueResource.objectStringOption(HasPermissions) + + private def valueFileValueFilename(valueResource: Resource): Either[String, Option[String]] = + valueResource.objectStringOption(FileValueHasFilename) + + private def resourceClassIri(rootResource: Resource): IO[String, KResourceClassIri] = ZIO + .fromOption(rootResource.rdfsType) + .orElseFail("No root resource class IRI found") + .flatMap(converter.asSmartIri(_).mapError(_.getMessage)) + .flatMap(iri => ZIO.fromEither(KResourceClassIri.from(iri))) + + private def getValueContent( + valueType: String, + valueResource: Resource, + maybeFileName: Option[String], + shortcode: Shortcode, + ingestState: AssetIngestState, + ): IO[String, ValueContentV2] = + def withFileInfo[T](fileInfo: Option[FileInfo], f: FileInfo => Either[String, T]): IO[String, T] = + fileInfo match + case None => ZIO.fail("FileInfo is missing") + case Some(info) => ZIO.fromEither(f(info)) + for { + i <- + ValueContentV2 + .fileInfoFromExternal(maybeFileName, ingestState, shortcode) + .provide(ZLayer.succeed(sipiService)) + .mapError(_.getMessage) + content <- + valueType match + case AudioFileValue => withFileInfo(i, AudioFileValueContentV2.from(valueResource, _)) + case ArchiveFileValue => withFileInfo(i, ArchiveFileValueContentV2.from(valueResource, _)) + case BooleanValue => ZIO.fromEither(BooleanValueContentV2.from(valueResource)) + case ColorValue => ZIO.fromEither(ColorValueContentV2.from(valueResource)) + case DateValue => ZIO.fromEither(DateValueContentV2.from(valueResource)) + case DecimalValue => ZIO.fromEither(DecimalValueContentV2.from(valueResource)) + case DocumentFileValue => withFileInfo(i, DocumentFileValueContentV2.from(valueResource, _)) + case GeomValue => ZIO.fromEither(GeomValueContentV2.from(valueResource)) + case GeonameValue => ZIO.fromEither(GeonameValueContentV2.from(valueResource)) + case IntValue => ZIO.fromEither(IntegerValueContentV2.from(valueResource)) + case IntervalValue => ZIO.fromEither(IntervalValueContentV2.from(valueResource)) + case ListValue => HierarchicalListValueContentV2.from(valueResource, converter) + case LinkValue => LinkValueContentV2.from(valueResource, converter) + case MovingImageFileValue => withFileInfo(i, MovingImageFileValueContentV2.from(valueResource, _)) + case StillImageExternalFileValue => ZIO.fromEither(StillImageExternalFileValueContentV2.from(valueResource)) + case StillImageFileValue => withFileInfo(i, StillImageFileValueContentV2.from(valueResource, _)) + case TextValue => TextValueContentV2.from(valueResource).provide(ZLayer.succeed(messageRelay)) + case TextFileValue => withFileInfo(i, TextFileValueContentV2.from(valueResource, _)) + case TimeValue => ZIO.fromEither(TimeValueContentV2.from(valueResource)) + case UriValue => ZIO.fromEither(UriValueContentV2.from(valueResource)) + case _ => ZIO.fail(s"Unsupported value type: $valueType") + } yield content +} + +object ApiComplexV2JsonLdRequestParser { + val layer = ZLayer.derive[ApiComplexV2JsonLdRequestParser] +} diff --git a/webapi/src/main/scala/org/knora/webapi/store/iiif/impl/SipiServiceMock.scala b/webapi/src/main/scala/org/knora/webapi/store/iiif/impl/SipiServiceMock.scala index c545681902..d08916813a 100644 --- a/webapi/src/main/scala/org/knora/webapi/store/iiif/impl/SipiServiceMock.scala +++ b/webapi/src/main/scala/org/knora/webapi/store/iiif/impl/SipiServiceMock.scala @@ -12,37 +12,45 @@ import org.knora.webapi.messages.store.sipimessages.* import org.knora.webapi.messages.v2.responder.SuccessResponseV2 import org.knora.webapi.slice.admin.api.model.MaintenanceRequests.AssetId import org.knora.webapi.slice.admin.domain.model.KnoraProject +import org.knora.webapi.slice.admin.domain.model.KnoraProject.Shortcode import org.knora.webapi.slice.admin.domain.model.User import org.knora.webapi.slice.admin.domain.service.Asset import org.knora.webapi.store.iiif.api.FileMetadataSipiResponse import org.knora.webapi.store.iiif.api.SipiService import org.knora.webapi.store.iiif.errors.SipiException +import org.knora.webapi.store.iiif.impl.SipiServiceMock.SipiMockMethodName +import org.knora.webapi.store.iiif.impl.SipiServiceMock.SipiMockMethodName.* /** * Can be used in place of [[SipiServiceLive]] for tests without an actual Sipi server, by returning hard-coded * responses simulating responses from Sipi. */ -case class SipiServiceMock() extends SipiService { +case class SipiServiceMock(ref: Ref[Map[SipiMockMethodName, Task[Object]]]) extends SipiService { /** * A request with this filename will always cause a Sipi error. */ private val FAILURE_FILENAME: String = "failure.jp2" - override def getFileMetadataFromSipiTemp(filename: String): Task[FileMetadataSipiResponse] = - ZIO.succeed( - FileMetadataSipiResponse( - originalFilename = Some("test2.tiff"), - originalMimeType = Some("image/tiff"), - internalMimeType = "image/jp2", - width = Some(512), - height = Some(256), - numpages = None, - duration = None, - fps = None, - ), + private def getReturnValue[T](method: SipiMockMethodName): Task[T] = + ref.get.flatMap( + _.getOrElse( + method, + throw SipiException(s"No response configured for $method"), + ).map(_.asInstanceOf[T]), ) + def setReturnValue[T](method: SipiMockMethodName, returnValue: Task[Object]): Task[Unit] = + ref.getAndUpdate(_ + (method -> returnValue)).unit + + def assertNoInteraction: UIO[Unit] = { + val fail = ZIO.fail(SipiException("No interaction expected")) + ref.set(SipiMockMethodName.values.map(_ -> fail).toMap) + } + + override def getFileMetadataFromSipiTemp(filename: String): Task[FileMetadataSipiResponse] = + getReturnValue(GetFileMetadataFromSipiTemp) + def moveTemporaryFileToPermanentStorage( moveTemporaryFileToPermanentStorageRequestV2: MoveTemporaryFileToPermanentStorageRequest, ): Task[SuccessResponseV2] = @@ -59,16 +67,41 @@ case class SipiServiceMock() extends SipiService { ZIO.succeed(SuccessResponseV2("Deleted temporary file")) } - override def getTextFileRequest(textFileRequest: SipiGetTextFileRequest): Task[SipiGetTextFileResponse] = ??? + override def getTextFileRequest(textFileRequest: SipiGetTextFileRequest): Task[SipiGetTextFileResponse] = + getReturnValue(GetTextFileRequest) - override def downloadAsset(asset: Asset, targetDir: Path, user: User): Task[Option[Path]] = ??? + override def downloadAsset(asset: Asset, targetDir: Path, user: User): Task[Option[Path]] = + getReturnValue(DownloadAsset) - override def getFileMetadataFromDspIngest( - shortcode: KnoraProject.Shortcode, - assetId: AssetId, - ): Task[FileMetadataSipiResponse] = ??? + override def getFileMetadataFromDspIngest(shortcode: Shortcode, assetId: AssetId): Task[FileMetadataSipiResponse] = + getReturnValue(GetFileMetadataFromDspIngest) } object SipiServiceMock { - val layer: ULayer[SipiServiceMock] = ZLayer.succeed(SipiServiceMock()) + enum SipiMockMethodName: + case GetFileMetadataFromSipiTemp + case GetFileMetadataFromDspIngest + case GetTextFileRequest + case DownloadAsset + + private val defaultGetFileMetadataFromSipiTempResponse = FileMetadataSipiResponse( + originalFilename = Some("test2.tiff"), + originalMimeType = Some("image/tiff"), + internalMimeType = "image/jp2", + width = Some(512), + height = Some(256), + numpages = None, + duration = None, + fps = None, + ) + + val layer: ULayer[SipiServiceMock] = ZLayer.fromZIO( + Ref + .make[Map[SipiMockMethodName, Task[Object]]]( + Map( + GetFileMetadataFromSipiTemp -> ZIO.succeed(defaultGetFileMetadataFromSipiTempResponse), + ), + ) + .map(ref => SipiServiceMock(ref)), + ) } diff --git a/webapi/src/test/scala/org/knora/webapi/messages/v2/responder/valuemessages/CreateValueV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/messages/v2/responder/valuemessages/CreateValueV2Spec.scala deleted file mode 100644 index de81aabbcd..0000000000 --- a/webapi/src/test/scala/org/knora/webapi/messages/v2/responder/valuemessages/CreateValueV2Spec.scala +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright © 2021 - 2024 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.messages.v2.responder.valuemessages - -import zio.ZIO -import zio.test.Gen -import zio.test.Spec -import zio.test.TestResult -import zio.test.ZIOSpecDefault -import zio.test.assertTrue -import zio.test.check - -import org.knora.webapi.ApiV2Complex -import org.knora.webapi.core.MessageRelay -import org.knora.webapi.core.MessageRelayLive -import org.knora.webapi.messages.StringFormatter -import org.knora.webapi.messages.v2.responder.valuemessages.TextValueType.UnformattedText -import org.knora.webapi.routing.v2.AssetIngestState.AssetIngested -import org.knora.webapi.slice.common.JsonLdTestUtil -import org.knora.webapi.slice.common.JsonLdTestUtil.JsonLdTransformations -import org.knora.webapi.slice.resourceinfo.domain.IriConverter -import org.knora.webapi.store.iiif.api.SipiService -import org.knora.webapi.store.iiif.impl.SipiServiceMock - -object CreateValueV2Spec extends ZIOSpecDefault { - - private val unformattedTextValueWithLanguage = - """ -{ - "@id": "http://rdfh.ch/0001/a-thing", - "@type": "http://0.0.0.0:3333/ontology/0001/anything/v2#Thing", - "http://0.0.0.0:3333/ontology/0001/anything/v2#hasText":{ - "@type":"http://api.knora.org/ontology/knora-api/v2#TextValue", - "http://api.knora.org/ontology/knora-api/v2#valueAsString":"This is English", - "http://api.knora.org/ontology/knora-api/v2#textValueHasLanguage":"en" - } -}""".stripMargin - - override def spec: Spec[Any, Throwable] = - suite("CreateValueV2Spec")(test("UnformattedText TextValue fromJsonLd should contain the language") { - val transformations = JsonLdTransformations.all - check(Gen.fromIterable(transformations)) { f => - for { - sf <- ZIO.service[StringFormatter] - value <- CreateValueV2.fromJsonLd(AssetIngested, f(unformattedTextValueWithLanguage)) - } yield assertTrue( - value == CreateValueV2( - resourceIri = "http://rdfh.ch/0001/a-thing", - resourceClassIri = sf.toSmartIri("http://0.0.0.0:3333/ontology/0001/anything/v2#Thing"), - propertyIri = sf.toSmartIri("http://0.0.0.0:3333/ontology/0001/anything/v2#hasText"), - valueContent = TextValueContentV2( - ontologySchema = ApiV2Complex, - maybeValueHasString = Some("This is English"), - textValueType = UnformattedText, - valueHasLanguage = Some("en"), - standoff = Nil, - mappingIri = None, - mapping = None, - xslt = None, - comment = None, - ), - valueIri = None, - valueUUID = None, - valueCreationDate = None, - permissions = None, - ingestState = AssetIngested, - ), - ) - } - }).provide(StringFormatter.test, MessageRelayLive.layer, IriConverter.layer, SipiServiceMock.layer) - -} diff --git a/webapi/src/test/scala/org/knora/webapi/slice/common/ApiComplexV2JsonLdRequestParserSpec.scala b/webapi/src/test/scala/org/knora/webapi/slice/common/ApiComplexV2JsonLdRequestParserSpec.scala new file mode 100644 index 0000000000..c9df575aea --- /dev/null +++ b/webapi/src/test/scala/org/knora/webapi/slice/common/ApiComplexV2JsonLdRequestParserSpec.scala @@ -0,0 +1,754 @@ +/* + * Copyright © 2021 - 2024 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.slice.common + +import zio.* +import zio.json.DecoderOps +import zio.json.EncoderOps +import zio.json.ast.Json +import zio.test.* + +import java.time.Instant + +import org.knora.webapi.ApiV2Complex +import org.knora.webapi.core.MessageRelayLive +import org.knora.webapi.messages.StringFormatter +import org.knora.webapi.messages.util.CalendarNameGregorian +import org.knora.webapi.messages.util.DatePrecisionDay +import org.knora.webapi.messages.v2.responder.valuemessages.ArchiveFileValueContentV2 +import org.knora.webapi.messages.v2.responder.valuemessages.AudioFileValueContentV2 +import org.knora.webapi.messages.v2.responder.valuemessages.BooleanValueContentV2 +import org.knora.webapi.messages.v2.responder.valuemessages.ColorValueContentV2 +import org.knora.webapi.messages.v2.responder.valuemessages.CreateValueV2 +import org.knora.webapi.messages.v2.responder.valuemessages.DateValueContentV2 +import org.knora.webapi.messages.v2.responder.valuemessages.DecimalValueContentV2 +import org.knora.webapi.messages.v2.responder.valuemessages.DocumentFileValueContentV2 +import org.knora.webapi.messages.v2.responder.valuemessages.FileValueV2 +import org.knora.webapi.messages.v2.responder.valuemessages.GeomValueContentV2 +import org.knora.webapi.messages.v2.responder.valuemessages.GeonameValueContentV2 +import org.knora.webapi.messages.v2.responder.valuemessages.HierarchicalListValueContentV2 +import org.knora.webapi.messages.v2.responder.valuemessages.IntegerValueContentV2 +import org.knora.webapi.messages.v2.responder.valuemessages.IntervalValueContentV2 +import org.knora.webapi.messages.v2.responder.valuemessages.LinkValueContentV2 +import org.knora.webapi.messages.v2.responder.valuemessages.MovingImageFileValueContentV2 +import org.knora.webapi.messages.v2.responder.valuemessages.StillImageExternalFileValueContentV2 +import org.knora.webapi.messages.v2.responder.valuemessages.StillImageFileValueContentV2 +import org.knora.webapi.messages.v2.responder.valuemessages.TextFileValueContentV2 +import org.knora.webapi.messages.v2.responder.valuemessages.TextValueContentV2 +import org.knora.webapi.messages.v2.responder.valuemessages.TextValueType.UnformattedText +import org.knora.webapi.messages.v2.responder.valuemessages.TimeValueContentV2 +import org.knora.webapi.messages.v2.responder.valuemessages.UriValueContentV2 +import org.knora.webapi.routing.v2.AssetIngestState.AssetIngested +import org.knora.webapi.slice.common.JsonLdTestUtil.JsonLdTransformations +import org.knora.webapi.slice.common.KnoraIris.* +import org.knora.webapi.slice.resourceinfo.domain.IriConverter +import org.knora.webapi.slice.resources.IiifImageRequestUrl +import org.knora.webapi.store.iiif.api.FileMetadataSipiResponse +import org.knora.webapi.store.iiif.api.SipiService +import org.knora.webapi.store.iiif.impl.SipiServiceMock +import org.knora.webapi.store.iiif.impl.SipiServiceMock.SipiMockMethodName.GetFileMetadataFromDspIngest +import org.knora.webapi.store.iiif.impl.SipiServiceMock.SipiMockMethodName.GetFileMetadataFromSipiTemp + +object ApiComplexV2JsonLdRequestParserSpec extends ZIOSpecDefault { + private val sf = StringFormatter.getInitializedTestInstance + + private val service = ZIO.serviceWithZIO[ApiComplexV2JsonLdRequestParser] + + private val givenFileInfo = + FileMetadataSipiResponse( + Some("originalFilename.orig"), + Some("originalMimeType"), + "internalMimeType", + Some(640), + Some(480), + Some(666), + None, + None, + ) + + private val expectedFileValue = FileValueV2( + "internalFilename.ext", + "internalMimeType", + Some("originalFilename.orig"), + Some("originalMimeType"), + ) + + private val configureSipiServiceMock = for { + sipiMock <- ZIO.service[SipiServiceMock] + _ <- sipiMock.setReturnValue(GetFileMetadataFromSipiTemp, ZIO.fail(Exception("No interaction with sipi expected"))) + _ <- sipiMock.setReturnValue(GetFileMetadataFromDspIngest, ZIO.succeed(givenFileInfo)) + } yield () + + private val createIntegerValue = """ + { + "@id": "http://rdfh.ch/0001/a-thing", + "@type": "anything:Thing", + "anything:hasInteger": { + "@type": "knora-api:IntValue", + "knora-api:intValueAsInt": 4 + }, + "@context": { + "knora-api": "http://api.knora.org/ontology/knora-api/v2#", + "anything": "http://0.0.0.0:3333/ontology/0001/anything/v2#" + } + } + """.fromJson[Json].getOrElse(throw new Exception("Invalid JSON")) + + private val createLinkValue = + s"""{ + "@id" : "http://rdfh.ch/0001/a-thing", + "@type" : "anything:Thing", + "anything:hasOtherThingValue" : { + "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", + "@type" : "knora-api:LinkValue", + "knora-api:valueHasUUID": "mr9i2aUUJolv64V_9hYdTw", + "knora-api:linkValueHasTargetIri" : { + "@id" : "http://rdfh.ch/0001/CNhWoNGGT7iWOrIwxsEqvA" + }, + "knora-api:valueCreationDate" : { + "@type" : "xsd:dateTimeStamp", + "@value" : "2020-06-04T11:36:54.502951Z" + } + }, + "@context" : { + "xsd" : "http://www.w3.org/2001/XMLSchema#", + "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" + } + }""".fromJson[Json].getOrElse(throw new Exception("Invalid JSON")) + + val spec = suite("KnoraApiValueModel")( + test("getResourceIri should get the id") { + check(Gen.fromIterable(Seq(createIntegerValue, createLinkValue).map(_.toJsonPretty))) { json => + for { + actual <- service(_.createValueV2FromJsonLd(json, AssetIngested)) + } yield assertTrue(actual.resourceIri == "http://rdfh.ch/0001/a-thing") + } + }, + test("rootResourceClassIri should get the rdfs:type") { + check(Gen.fromIterable(Seq(createIntegerValue, createLinkValue).map(_.toJsonPretty))) { json => + for { + actual <- service(_.createValueV2FromJsonLd(json, AssetIngested)) + } yield assertTrue(actual.resourceClassIri.toString == "http://0.0.0.0:3333/ontology/0001/anything/v2#Thing") + } + }, + test("value property should be present") { + for { + actual <- service(_.createValueV2FromJsonLd(createIntegerValue.toJsonPretty, AssetIngested)) + } yield assertTrue( + actual.propertyIri == sf.toSmartIri("http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger"), + ) + }, + test("should parse integer value") { + for { + actual <- service( + _.createValueV2FromJsonLd( + """{ + | "@id": "http://rdfh.ch/0001/a-thing", + | "@type": "ex:Thing", + | "ex:someInt": { + | "@type": "ka:IntValue", + | "ka:intValueAsInt": { + | "@type": "xsd:integer", + | "@value": 4 + | } + | }, + | "@context": { + | "ka": "http://api.knora.org/ontology/knora-api/v2#", + | "ex": "https://example.com/test#", + | "xsd": "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin, + AssetIngested, + ), + ) + } yield assertTrue(actual.valueContent == IntegerValueContentV2(ApiV2Complex, 4, None)) + }, + test("should parse DecimalValueContentV2") { + for { + actual <- service( + _.createValueV2FromJsonLd( + """{ + | "@id": "http://rdfh.ch/0001/a-thing", + | "@type": "ex:Thing", + | "ex:someDec": { + | "@type": "ka:DecimalValue", + | "ka:decimalValueAsDecimal": { + | "@type": "xsd:decimal", + | "@value": "4" + | } + | }, + | "@context": { + | "ka": "http://api.knora.org/ontology/knora-api/v2#", + | "ex": "https://example.com/test#", + | "xsd": "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin, + AssetIngested, + ), + ) + } yield assertTrue(actual.valueContent == DecimalValueContentV2(ApiV2Complex, BigDecimal(4), None)) + }, + test("should parse BooleanValueContentV2") { + for { + actual <- service( + _.createValueV2FromJsonLd( + """{ + | "@id": "http://rdfh.ch/0001/a-thing", + | "@type": "ex:Thing", + | "ex:someBool": { + | "@type": "ka:BooleanValue", + | "ka:booleanValueAsBoolean": { + | "@type": "xsd:boolean", + | "@value": "true" + | } + | }, + | "@context": { + | "ka": "http://api.knora.org/ontology/knora-api/v2#", + | "ex": "https://example.com/test#", + | "xsd": "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin, + AssetIngested, + ), + ) + } yield assertTrue(actual.valueContent == BooleanValueContentV2(ApiV2Complex, true, None)) + }, + test("should parse GeomValueContentV2") { + for { + actual <- service( + _.createValueV2FromJsonLd( + """{ + | "@id": "http://rdfh.ch/0001/a-thing", + | "@type": "ex:Thing", + | "ex:someGeom": { + | "@type": "ka:GeomValue", + | "ka:geometryValueAsGeometry": "{}" + | }, + | "@context": { + | "ka": "http://api.knora.org/ontology/knora-api/v2#", + | "ex": "https://example.com/test#", + | "xsd": "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin, + AssetIngested, + ), + ) + } yield assertTrue(actual.valueContent == GeomValueContentV2(ApiV2Complex, "{}", None)) + }, + test("should parse IntervalValueContentV2") { + for { + actual <- service( + _.createValueV2FromJsonLd( + """{ + | "@id": "http://rdfh.ch/0001/a-thing", + | "@type": "ex:Thing", + | "ex:someInterval": { + | "@type": "ka:IntervalValue", + | "ka:intervalValueHasStart": { + | "@type": "xsd:decimal", + | "@value": 4 + | }, + | "ka:intervalValueHasEnd": { + | "@type": "xsd:decimal", + | "@value": 2 + | } + | }, + | "@context": { + | "ka": "http://api.knora.org/ontology/knora-api/v2#", + | "ex": "https://example.com/test#", + | "xsd": "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin, + AssetIngested, + ), + ) + } yield assertTrue( + actual.valueContent == IntervalValueContentV2(ApiV2Complex, BigDecimal(4), BigDecimal(2), None), + ) + }, + test("should parse TimeValueContentV2") { + for { + actual <- service( + _.createValueV2FromJsonLd( + """{ + | "@id": "http://rdfh.ch/0001/a-thing", + | "@type": "ex:Thing", + | "ex:someTimeValue": { + | "@type": "ka:TimeValue", + | "ka:timeValueAsTimeStamp": { + | "@type": "xsd:dateTimeStamp", + | "@value": "2020-06-04T11:36:54.502951Z" + | } + | }, + | "@context": { + | "ka": "http://api.knora.org/ontology/knora-api/v2#", + | "ex": "https://example.com/test#", + | "xsd": "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin, + AssetIngested, + ), + ) + } yield assertTrue( + actual.valueContent == TimeValueContentV2(ApiV2Complex, Instant.parse("2020-06-04T11:36:54.502951Z"), None), + ) + }, + test("should parse LinkValueContentV2") { + for { + actual <- + service( + _.createValueV2FromJsonLd( + s""" + |{ + | "@id" : "http://rdfh.ch/0001/a-thing", + | "@type" : "ex:Thing", + | "ex:hasOtherThingValue" : { + | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", + | "@type" : "ka:LinkValue", + | "ka:linkValueHasTargetIri" : { + | "@id" : "http://rdfh.ch/0001/CNhWoNGGT7iWOrIwxsEqvA" + | } + | }, + | "@context": { + | "ka": "http://api.knora.org/ontology/knora-api/v2#", + | "ex": "https://example.com/test#", + | "xsd": "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin, + AssetIngested, + ), + ) + } yield assertTrue( + actual.valueContent == LinkValueContentV2( + ApiV2Complex, + referredResourceIri = "http://rdfh.ch/0001/CNhWoNGGT7iWOrIwxsEqvA", + comment = None, + ), + ) + }, + test("should parse UriValueContentV2") { + for { + actual <- + service( + _.createValueV2FromJsonLd( + s""" + |{ + | "@id" : "http://rdfh.ch/0001/a-thing", + | "@type" : "ex:Thing", + | "ex:hasOtherThingValue" : { + | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", + | "@type" : "ka:UriValue", + | "ka:uriValueAsUri" : { + | "@type" : "xsd:anyURI", + | "@value" : "http://rdfh.ch/0001/CNhWoNGGT7iWOrIwxsEqvA" + | } + | }, + | "@context": { + | "ka": "http://api.knora.org/ontology/knora-api/v2#", + | "ex": "https://example.com/test#", + | "xsd": "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin, + AssetIngested, + ), + ) + } yield assertTrue( + actual.valueContent == UriValueContentV2(ApiV2Complex, "http://rdfh.ch/0001/CNhWoNGGT7iWOrIwxsEqvA", None), + ) + }, + test("should parse GeonameValueContentV2") { + for { + actual <- + service( + _.createValueV2FromJsonLd( + s""" + |{ + | "@id" : "http://rdfh.ch/0001/a-thing", + | "@type" : "ex:Thing", + | "ex:hasOtherThingValue" : { + | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", + | "@type" : "ka:GeonameValue", + | "ka:geonameValueAsGeonameCode" : "foo" + | }, + | "@context": { + | "ka": "http://api.knora.org/ontology/knora-api/v2#", + | "ex": "https://example.com/test#", + | "xsd": "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin, + AssetIngested, + ), + ) + } yield assertTrue(actual.valueContent == GeonameValueContentV2(ApiV2Complex, "foo", None)) + }, + test("should parse ColorValue") { + for { + actual <- + service( + _.createValueV2FromJsonLd( + s""" + |{ + | "@id" : "http://rdfh.ch/0001/a-thing", + | "@type" : "ex:Thing", + | "ex:hasOtherThingValue" : { + | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", + | "@type" : "ka:ColorValue", + | "ka:colorValueAsColor" : "red" + | }, + | "@context": { + | "ka": "http://api.knora.org/ontology/knora-api/v2#", + | "ex": "https://example.com/test#", + | "xsd": "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin, + AssetIngested, + ), + ) + } yield assertTrue(actual.valueContent == ColorValueContentV2(ApiV2Complex, "red", None)) + }, + test("should parse StillImageFileValue") { + for { + _ <- configureSipiServiceMock + actual <- + service( + _.createValueV2FromJsonLd( + s""" + |{ + | "@id" : "http://rdfh.ch/0001/a-thing", + | "@type" : "ex:Thing", + | "ex:hasOtherThingValue" : { + | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", + | "@type" : "ka:StillImageFileValue", + | "ka:fileValueHasFilename": "internalFilename.ext" + | }, + | "@context": { + | "ka": "http://api.knora.org/ontology/knora-api/v2#", + | "ex": "https://example.com/test#", + | "xsd": "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin, + AssetIngested, + ), + ) + } yield assertTrue( + actual.valueContent == StillImageFileValueContentV2( + ApiV2Complex, + expectedFileValue, + givenFileInfo.width.getOrElse(throw new Exception("width is missing")), + givenFileInfo.height.getOrElse(throw new Exception("height is missing")), + None, + ), + ) + }, + test("should parse StillImageExternalFileValue") { + for { + _ <- ZIO.serviceWithZIO[SipiServiceMock](_.assertNoInteraction) + actual <- + service( + _.createValueV2FromJsonLd( + s""" + |{ + | "@id" : "http://rdfh.ch/0001/a-thing", + | "@type" : "ex:Thing", + | "ex:hasOtherThingValue" : { + | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", + | "@type" : "ka:StillImageExternalFileValue", + | "ka:stillImageFileValueHasExternalUrl" : "http://www.example.org/prefix1/abcd1234/full/0/native.jpg" + | }, + | "@context": { + | "ka": "http://api.knora.org/ontology/knora-api/v2#", + | "ex": "https://example.com/test#", + | "xsd": "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin, + AssetIngested, + ), + ) + } yield assertTrue( + actual.valueContent == StillImageExternalFileValueContentV2( + ApiV2Complex, + FileValueV2( + "internalFilename", + "internalMimeType", + Some("originalFilename"), + Some("originalMimeType"), + ), + IiifImageRequestUrl.unsafeFrom("http://www.example.org/prefix1/abcd1234/full/0/native.jpg"), + None, + ), + ) + }, + test("should parse DocumentFileValue") { + for { + _ <- configureSipiServiceMock + actual <- service( + _.createValueV2FromJsonLd( + s""" + |{ + | "@id" : "http://rdfh.ch/0001/a-thing", + | "@type" : "ex:Thing", + | "ex:hasOtherThingValue" : { + | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", + | "@type" : "ka:DocumentFileValue", + | "ka:fileValueHasFilename": "internalFilename.ext" + | }, + | "@context": { + | "ka": "http://api.knora.org/ontology/knora-api/v2#", + | "ex": "https://example.com/test#", + | "xsd": "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin, + AssetIngested, + ), + ) + } yield assertTrue( + actual.valueContent == DocumentFileValueContentV2( + ApiV2Complex, + expectedFileValue, + givenFileInfo.numpages, + givenFileInfo.width, + givenFileInfo.height, + None, + ), + ) + }, + test("should parse TextFileValueContentV2") { + for { + _ <- configureSipiServiceMock + actual <- service( + _.createValueV2FromJsonLd( + s""" + |{ + | "@id" : "http://rdfh.ch/0001/a-thing", + | "@type" : "ex:Thing", + | "ex:hasOtherThingValue" : { + | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", + | "@type" : "ka:TextFileValue", + | "ka:fileValueHasFilename": "internalFilename.ext" + | }, + | "@context": { + | "ka": "http://api.knora.org/ontology/knora-api/v2#", + | "ex": "https://example.com/test#", + | "xsd": "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin, + AssetIngested, + ), + ) + } yield assertTrue( + actual.valueContent == TextFileValueContentV2(ApiV2Complex, expectedFileValue, None), + ) + }, + test("should parse AudioFileValueContentV2") { + for { + _ <- configureSipiServiceMock + actual <- service( + _.createValueV2FromJsonLd( + s""" + |{ + | "@id" : "http://rdfh.ch/0001/a-thing", + | "@type" : "ex:Thing", + | "ex:hasOtherThingValue" : { + | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", + | "@type" : "ka:AudioFileValue", + | "ka:fileValueHasFilename": "internalFilename.ext" + | }, + | "@context": { + | "ka": "http://api.knora.org/ontology/knora-api/v2#", + | "ex": "https://example.com/test#", + | "xsd": "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin, + AssetIngested, + ), + ) + } yield assertTrue( + actual.valueContent == AudioFileValueContentV2(ApiV2Complex, expectedFileValue, None), + ) + }, + test("should parse MovingImageFileValueContentV2") { + for { + _ <- configureSipiServiceMock + actual <- service( + _.createValueV2FromJsonLd( + s""" + |{ + | "@id" : "http://rdfh.ch/0001/a-thing", + | "@type" : "ex:Thing", + | "ex:hasOtherThingValue" : { + | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", + | "@type" : "ka:MovingImageFileValue", + | "ka:fileValueHasFilename": "internalFilename.ext" + | }, + | "@context": { + | "ka": "http://api.knora.org/ontology/knora-api/v2#", + | "ex": "https://example.com/test#", + | "xsd": "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin, + AssetIngested, + ), + ) + } yield assertTrue( + actual.valueContent == MovingImageFileValueContentV2(ApiV2Complex, expectedFileValue, None), + ) + }, + test("should parse ArchiveFileValueContentV2") { + for { + _ <- configureSipiServiceMock + actual <- service( + _.createValueV2FromJsonLd( + s""" + |{ + | "@id" : "http://rdfh.ch/0001/a-thing", + | "@type" : "ex:Thing", + | "ex:hasOtherThingValue" : { + | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", + | "@type" : "ka:ArchiveFileValue", + | "ka:fileValueHasFilename": "internalFilename.ext" + | }, + | "@context": { + | "ka": "http://api.knora.org/ontology/knora-api/v2#", + | "ex": "https://example.com/test#", + | "xsd": "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin, + AssetIngested, + ), + ) + } yield assertTrue( + actual.valueContent == ArchiveFileValueContentV2(ApiV2Complex, expectedFileValue, None), + ) + }, + test("should parse HierarchicalListValueContentV2") { + for { + actual <- service( + _.createValueV2FromJsonLd( + s""" + |{ + | "@id" : "http://rdfh.ch/0001/a-thing", + | "@type" : "ex:Thing", + | "ex:hasOtherThingValue" : { + | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", + | "@type" : "ka:ListValue", + | "ka:listValueAsListNode": { + | "@id" : "http://rdfh.ch/0001/CNhWoNGGT7iWOrIwxsEqvA" + | } + | }, + | "@context": { + | "ka": "http://api.knora.org/ontology/knora-api/v2#", + | "ex": "https://example.com/test#", + | "xsd": "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin, + AssetIngested, + ), + ) + } yield assertTrue( + actual.valueContent == HierarchicalListValueContentV2( + ApiV2Complex, + "http://rdfh.ch/0001/CNhWoNGGT7iWOrIwxsEqvA", + None, + None, + ), + ) + }, + test("should parse DateValueContentV2") { + for { + actual <- service( + _.createValueV2FromJsonLd( + s""" + |{ + | "@id" : "http://rdfh.ch/0001/a-thing", + | "@type" : "ex:Thing", + | "ex:hasOtherThingValue" : { + | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", + | "@type" : "ka:DateValue", + | "ka:dateValueHasCalendar" : "GREGORIAN", + | "ka:dateValueHasEndEra" : "CE", + | "ka:dateValueHasEndYear" : 1489, + | "ka:dateValueHasEndMonth" : 12, + | "ka:dateValueHasEndDay" : 24, + | "ka:dateValueHasStartEra" : "CE", + | "ka:dateValueHasStartMonth" : 1, + | "ka:dateValueHasStartDay" : 28, + | "ka:dateValueHasStartYear" : 1488 + | }, + | "@context": { + | "ka": "http://api.knora.org/ontology/knora-api/v2#", + | "ex": "https://example.com/test#", + | "xsd": "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin, + AssetIngested, + ), + ) + } yield assertTrue( + actual.valueContent == DateValueContentV2( + ApiV2Complex, + 2264568, + 2265264, + DatePrecisionDay, + DatePrecisionDay, + CalendarNameGregorian, + None, + ), + ) + }, + test("should handle transformed json-ld") { + val transformations = JsonLdTransformations.all + check(Gen.fromIterable(transformations)) { jsonLdTransform => + for { + sf <- ZIO.service[StringFormatter] + value <- service( + _.createValueV2FromJsonLd( + jsonLdTransform(""" + { + "@id": "http://rdfh.ch/0001/a-thing", + "@type": "http://0.0.0.0:3333/ontology/0001/anything/v2#Thing", + "http://0.0.0.0:3333/ontology/0001/anything/v2#hasText":{ + "@type":"http://api.knora.org/ontology/knora-api/v2#TextValue", + "http://api.knora.org/ontology/knora-api/v2#valueAsString":"This is English", + "http://api.knora.org/ontology/knora-api/v2#textValueHasLanguage":"en" + } + }""".stripMargin), + AssetIngested, + ), + ) + } yield assertTrue( + value == CreateValueV2( + resourceIri = "http://rdfh.ch/0001/a-thing", + resourceClassIri = sf.toSmartIri("http://0.0.0.0:3333/ontology/0001/anything/v2#Thing"), + propertyIri = sf.toSmartIri("http://0.0.0.0:3333/ontology/0001/anything/v2#hasText"), + valueContent = TextValueContentV2( + ontologySchema = ApiV2Complex, + maybeValueHasString = Some("This is English"), + textValueType = UnformattedText, + valueHasLanguage = Some("en"), + standoff = Nil, + mappingIri = None, + mapping = None, + xslt = None, + comment = None, + ), + valueIri = None, + valueUUID = None, + valueCreationDate = None, + permissions = None, + ingestState = AssetIngested, + ), + ) + } + }, + ).provideSome[Scope]( + IriConverter.layer, + MessageRelayLive.layer, + StringFormatter.test, + ApiComplexV2JsonLdRequestParser.layer, + SipiServiceMock.layer, + ) +} diff --git a/webapi/src/test/scala/org/knora/webapi/slice/common/KnoraApiValueModelSpec.scala b/webapi/src/test/scala/org/knora/webapi/slice/common/KnoraApiValueModelSpec.scala deleted file mode 100644 index 0a3813fc1d..0000000000 --- a/webapi/src/test/scala/org/knora/webapi/slice/common/KnoraApiValueModelSpec.scala +++ /dev/null @@ -1,630 +0,0 @@ -/* - * Copyright © 2021 - 2024 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.slice.common - -import zio.* -import zio.json.DecoderOps -import zio.json.EncoderOps -import zio.json.ast.Json -import zio.test.* - -import java.time.Instant - -import org.knora.webapi.ApiV2Complex -import org.knora.webapi.core.MessageRelayLive -import org.knora.webapi.messages.StringFormatter -import org.knora.webapi.messages.util.CalendarNameGregorian -import org.knora.webapi.messages.util.DatePrecisionDay -import org.knora.webapi.messages.v2.responder.valuemessages.ArchiveFileValueContentV2 -import org.knora.webapi.messages.v2.responder.valuemessages.AudioFileValueContentV2 -import org.knora.webapi.messages.v2.responder.valuemessages.BooleanValueContentV2 -import org.knora.webapi.messages.v2.responder.valuemessages.ColorValueContentV2 -import org.knora.webapi.messages.v2.responder.valuemessages.DateValueContentV2 -import org.knora.webapi.messages.v2.responder.valuemessages.DecimalValueContentV2 -import org.knora.webapi.messages.v2.responder.valuemessages.DocumentFileValueContentV2 -import org.knora.webapi.messages.v2.responder.valuemessages.FileValueV2 -import org.knora.webapi.messages.v2.responder.valuemessages.GeomValueContentV2 -import org.knora.webapi.messages.v2.responder.valuemessages.GeonameValueContentV2 -import org.knora.webapi.messages.v2.responder.valuemessages.HierarchicalListValueContentV2 -import org.knora.webapi.messages.v2.responder.valuemessages.IntegerValueContentV2 -import org.knora.webapi.messages.v2.responder.valuemessages.IntervalValueContentV2 -import org.knora.webapi.messages.v2.responder.valuemessages.LinkValueContentV2 -import org.knora.webapi.messages.v2.responder.valuemessages.MovingImageFileValueContentV2 -import org.knora.webapi.messages.v2.responder.valuemessages.StillImageExternalFileValueContentV2 -import org.knora.webapi.messages.v2.responder.valuemessages.StillImageFileValueContentV2 -import org.knora.webapi.messages.v2.responder.valuemessages.TextFileValueContentV2 -import org.knora.webapi.messages.v2.responder.valuemessages.TimeValueContentV2 -import org.knora.webapi.messages.v2.responder.valuemessages.UriValueContentV2 -import org.knora.webapi.messages.v2.responder.valuemessages.ValueContentV2.FileInfo -import org.knora.webapi.slice.admin.domain.model.KnoraProject.Shortcode -import org.knora.webapi.slice.common.KnoraIris.* -import org.knora.webapi.slice.resourceinfo.domain.IriConverter -import org.knora.webapi.slice.resources.IiifImageRequestUrl -import org.knora.webapi.store.iiif.api.FileMetadataSipiResponse - -object KnoraApiValueModelSpec extends ZIOSpecDefault { - private val sf = StringFormatter.getInitializedTestInstance - - private val givenFileInfo = FileInfo( - "internalFilename", - FileMetadataSipiResponse( - Some("originalFilename"), - Some("originalMimeType"), - "internalMimeType", - Some(640), - Some(480), - Some(666), - None, - None, - ), - ) - - private val expectedFileValue = FileValueV2( - "internalFilename", - "internalMimeType", - Some("originalFilename"), - Some("originalMimeType"), - ) - - private val createIntegerValue = """ - { - "@id": "http://rdfh.ch/0001/a-thing", - "@type": "anything:Thing", - "anything:hasInteger": { - "@type": "knora-api:IntValue", - "knora-api:intValueAsInt": 4 - }, - "@context": { - "knora-api": "http://api.knora.org/ontology/knora-api/v2#", - "anything": "http://0.0.0.0:3333/ontology/0001/anything/v2#" - } - } - """.fromJson[Json].getOrElse(throw new Exception("Invalid JSON")) - - private val createLinkValue = - s"""{ - "@id" : "http://rdfh.ch/0001/a-thing", - "@type" : "anything:Thing", - "anything:hasOtherThingValue" : { - "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", - "@type" : "knora-api:LinkValue", - "knora-api:valueHasUUID": "mr9i2aUUJolv64V_9hYdTw", - "knora-api:linkValueHasTargetIri" : { - "@id" : "http://rdfh.ch/0001/CNhWoNGGT7iWOrIwxsEqvA" - }, - "knora-api:valueCreationDate" : { - "@type" : "xsd:dateTimeStamp", - "@value" : "2020-06-04T11:36:54.502951Z" - } - }, - "@context" : { - "xsd" : "http://www.w3.org/2001/XMLSchema#", - "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", - "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#" - } - }""".fromJson[Json].getOrElse(throw new Exception("Invalid JSON")) - - val spec = suite("KnoraApiValueModel")( - test("getResourceIri should get the id") { - check(Gen.fromIterable(Seq(createIntegerValue, createLinkValue).map(_.toJsonPretty))) { json => - for { - model <- KnoraApiCreateValueModel.fromJsonLd(json) - } yield assertTrue(model.resourceIri.toString == "http://rdfh.ch/0001/a-thing") - } - }, - test("rootResourceClassIri should get the rdfs:type") { - check(Gen.fromIterable(Seq(createIntegerValue, createLinkValue).map(_.toJsonPretty))) { json => - for { - model <- KnoraApiCreateValueModel.fromJsonLd(json) - } yield assertTrue(model.resourceClassIri.toString == "http://0.0.0.0:3333/ontology/0001/anything/v2#Thing") - } - }, - test("valueNode properties should be present") { - for { - model <- KnoraApiCreateValueModel.fromJsonLd(createIntegerValue.toJsonPretty) - propertyIri = model.valuePropertyIri - valueType = model.valueType - } yield assertTrue( - propertyIri == PropertyIri.unsafeFrom( - sf.toSmartIri("http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger"), - ), - valueType == sf.toSmartIri("http://api.knora.org/ontology/knora-api/v2#IntValue"), - model.shortcode == Shortcode.unsafeFrom("0001"), - ) - }, - test("should parse integer value") { - for { - model <- KnoraApiCreateValueModel.fromJsonLd("""{ - | "@id": "http://rdfh.ch/0001/a-thing", - | "@type": "ex:Thing", - | "ex:someInt": { - | "@type": "ka:IntValue", - | "ka:intValueAsInt": { - | "@type": "xsd:integer", - | "@value": 4 - | } - | }, - | "@context": { - | "ka": "http://api.knora.org/ontology/knora-api/v2#", - | "ex": "https://example.com/test#", - | "xsd": "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin) - content <- model.getValueContent() - } yield assertTrue(content == IntegerValueContentV2(ApiV2Complex, 4, None)) - }, - test("should parse DecimalValueContentV2") { - for { - model <- KnoraApiCreateValueModel.fromJsonLd("""{ - | "@id": "http://rdfh.ch/0001/a-thing", - | "@type": "ex:Thing", - | "ex:someDec": { - | "@type": "ka:DecimalValue", - | "ka:decimalValueAsDecimal": { - | "@type": "xsd:decimal", - | "@value": "4" - | } - | }, - | "@context": { - | "ka": "http://api.knora.org/ontology/knora-api/v2#", - | "ex": "https://example.com/test#", - | "xsd": "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin) - content <- model.getValueContent() - } yield assertTrue(content == DecimalValueContentV2(ApiV2Complex, BigDecimal(4), None)) - }, - test("should parse BooleanValueContentV2") { - for { - model <- KnoraApiCreateValueModel.fromJsonLd("""{ - | "@id": "http://rdfh.ch/0001/a-thing", - | "@type": "ex:Thing", - | "ex:someBool": { - | "@type": "ka:BooleanValue", - | "ka:booleanValueAsBoolean": { - | "@type": "xsd:boolean", - | "@value": "true" - | } - | }, - | "@context": { - | "ka": "http://api.knora.org/ontology/knora-api/v2#", - | "ex": "https://example.com/test#", - | "xsd": "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin) - content <- model.getValueContent() - } yield assertTrue(content == BooleanValueContentV2(ApiV2Complex, true, None)) - }, - test("should parse GeomValueContentV2") { - for { - model <- KnoraApiCreateValueModel.fromJsonLd("""{ - | "@id": "http://rdfh.ch/0001/a-thing", - | "@type": "ex:Thing", - | "ex:someGeom": { - | "@type": "ka:GeomValue", - | "ka:geometryValueAsGeometry": "{}" - | }, - | "@context": { - | "ka": "http://api.knora.org/ontology/knora-api/v2#", - | "ex": "https://example.com/test#", - | "xsd": "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin) - content <- model.getValueContent() - } yield assertTrue(content == GeomValueContentV2(ApiV2Complex, "{}", None)) - }, - test("should parse IntervalValueContentV2") { - for { - model <- KnoraApiCreateValueModel.fromJsonLd("""{ - | "@id": "http://rdfh.ch/0001/a-thing", - | "@type": "ex:Thing", - | "ex:someInterval": { - | "@type": "ka:IntervalValue", - | "ka:intervalValueHasStart": { - | "@type": "xsd:decimal", - | "@value": 4 - | }, - | "ka:intervalValueHasEnd": { - | "@type": "xsd:decimal", - | "@value": 2 - | } - | }, - | "@context": { - | "ka": "http://api.knora.org/ontology/knora-api/v2#", - | "ex": "https://example.com/test#", - | "xsd": "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin) - content <- model.getValueContent() - } yield assertTrue(content == IntervalValueContentV2(ApiV2Complex, BigDecimal(4), BigDecimal(2), None)) - }, - test("should parse TimeValueContentV2") { - for { - model <- KnoraApiCreateValueModel.fromJsonLd("""{ - | "@id": "http://rdfh.ch/0001/a-thing", - | "@type": "ex:Thing", - | "ex:someTimeValue": { - | "@type": "ka:TimeValue", - | "ka:timeValueAsTimeStamp": { - | "@type": "xsd:dateTimeStamp", - | "@value": "2020-06-04T11:36:54.502951Z" - | } - | }, - | "@context": { - | "ka": "http://api.knora.org/ontology/knora-api/v2#", - | "ex": "https://example.com/test#", - | "xsd": "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin) - content <- model.getValueContent() - } yield assertTrue( - content == TimeValueContentV2(ApiV2Complex, Instant.parse("2020-06-04T11:36:54.502951Z"), None), - ) - }, - test("should parse LinkValueContentV2") { - for { - model <- - KnoraApiCreateValueModel.fromJsonLd( - s""" - |{ - | "@id" : "http://rdfh.ch/0001/a-thing", - | "@type" : "ex:Thing", - | "ex:hasOtherThingValue" : { - | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", - | "@type" : "ka:LinkValue", - | "ka:linkValueHasTargetIri" : { - | "@id" : "http://rdfh.ch/0001/CNhWoNGGT7iWOrIwxsEqvA" - | } - | }, - | "@context": { - | "ka": "http://api.knora.org/ontology/knora-api/v2#", - | "ex": "https://example.com/test#", - | "xsd": "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin, - ) - content <- model.getValueContent() - } yield assertTrue( - content == LinkValueContentV2( - ApiV2Complex, - referredResourceIri = "http://rdfh.ch/0001/CNhWoNGGT7iWOrIwxsEqvA", - comment = None, - ), - ) - }, - test("should parse UriValueContentV2") { - for { - model <- - KnoraApiCreateValueModel.fromJsonLd( - s""" - |{ - | "@id" : "http://rdfh.ch/0001/a-thing", - | "@type" : "ex:Thing", - | "ex:hasOtherThingValue" : { - | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", - | "@type" : "ka:UriValue", - | "ka:uriValueAsUri" : { - | "@type" : "xsd:anyURI", - | "@value" : "http://rdfh.ch/0001/CNhWoNGGT7iWOrIwxsEqvA" - | } - | }, - | "@context": { - | "ka": "http://api.knora.org/ontology/knora-api/v2#", - | "ex": "https://example.com/test#", - | "xsd": "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin, - ) - content <- model.getValueContent() - } yield assertTrue(content == UriValueContentV2(ApiV2Complex, "http://rdfh.ch/0001/CNhWoNGGT7iWOrIwxsEqvA", None)) - }, - test("should parse GeonameValueContentV2") { - for { - model <- - KnoraApiCreateValueModel.fromJsonLd( - s""" - |{ - | "@id" : "http://rdfh.ch/0001/a-thing", - | "@type" : "ex:Thing", - | "ex:hasOtherThingValue" : { - | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", - | "@type" : "ka:GeonameValue", - | "ka:geonameValueAsGeonameCode" : "foo" - | }, - | "@context": { - | "ka": "http://api.knora.org/ontology/knora-api/v2#", - | "ex": "https://example.com/test#", - | "xsd": "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin, - ) - content <- model.getValueContent() - } yield assertTrue(content == GeonameValueContentV2(ApiV2Complex, "foo", None)) - }, - test("should parse ColorValue") { - for { - model <- - KnoraApiCreateValueModel.fromJsonLd( - s""" - |{ - | "@id" : "http://rdfh.ch/0001/a-thing", - | "@type" : "ex:Thing", - | "ex:hasOtherThingValue" : { - | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", - | "@type" : "ka:ColorValue", - | "ka:colorValueAsColor" : "red" - | }, - | "@context": { - | "ka": "http://api.knora.org/ontology/knora-api/v2#", - | "ex": "https://example.com/test#", - | "xsd": "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin, - ) - content <- model.getValueContent() - } yield assertTrue(content == ColorValueContentV2(ApiV2Complex, "red", None)) - }, - test("should parse StillImageFileValue") { - for { - model <- - KnoraApiCreateValueModel.fromJsonLd( - s""" - |{ - | "@id" : "http://rdfh.ch/0001/a-thing", - | "@type" : "ex:Thing", - | "ex:hasOtherThingValue" : { - | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", - | "@type" : "ka:StillImageFileValue" - | }, - | "@context": { - | "ka": "http://api.knora.org/ontology/knora-api/v2#", - | "ex": "https://example.com/test#", - | "xsd": "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin, - ) - content <- model.getValueContent(Some(givenFileInfo)) - } yield assertTrue( - content == StillImageFileValueContentV2( - ApiV2Complex, - expectedFileValue, - givenFileInfo.metadata.width.getOrElse(throw new Exception("width is missing")), - givenFileInfo.metadata.height.getOrElse(throw new Exception("height is missing")), - None, - ), - ) - }, - test("should parse StillImageExternalFileValue") { - for { - model <- - KnoraApiCreateValueModel.fromJsonLd( - s""" - |{ - | "@id" : "http://rdfh.ch/0001/a-thing", - | "@type" : "ex:Thing", - | "ex:hasOtherThingValue" : { - | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", - | "@type" : "ka:StillImageExternalFileValue", - | "ka:stillImageFileValueHasExternalUrl" : "http://www.example.org/prefix1/abcd1234/full/0/native.jpg" - | }, - | "@context": { - | "ka": "http://api.knora.org/ontology/knora-api/v2#", - | "ex": "https://example.com/test#", - | "xsd": "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin, - ) - content <- model.getValueContent() - } yield assertTrue( - content == StillImageExternalFileValueContentV2( - ApiV2Complex, - FileValueV2( - "internalFilename", - "internalMimeType", - Some("originalFilename"), - Some("originalMimeType"), - ), - IiifImageRequestUrl.unsafeFrom("http://www.example.org/prefix1/abcd1234/full/0/native.jpg"), - None, - ), - ) - }, - test("should parse DocumentFileValue") { - for { - model <- KnoraApiCreateValueModel.fromJsonLd( - s""" - |{ - | "@id" : "http://rdfh.ch/0001/a-thing", - | "@type" : "ex:Thing", - | "ex:hasOtherThingValue" : { - | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", - | "@type" : "ka:DocumentFileValue" - | }, - | "@context": { - | "ka": "http://api.knora.org/ontology/knora-api/v2#", - | "ex": "https://example.com/test#", - | "xsd": "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin, - ) - content <- model.getValueContent(Some(givenFileInfo)) - } yield assertTrue( - content == DocumentFileValueContentV2( - ApiV2Complex, - expectedFileValue, - givenFileInfo.metadata.numpages, - givenFileInfo.metadata.width, - givenFileInfo.metadata.height, - None, - ), - ) - }, - test("should parse TextFileValueContentV2") { - for { - model <- KnoraApiCreateValueModel.fromJsonLd( - s""" - |{ - | "@id" : "http://rdfh.ch/0001/a-thing", - | "@type" : "ex:Thing", - | "ex:hasOtherThingValue" : { - | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", - | "@type" : "ka:TextFileValue" - | }, - | "@context": { - | "ka": "http://api.knora.org/ontology/knora-api/v2#", - | "ex": "https://example.com/test#", - | "xsd": "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin, - ) - content <- model.getValueContent(Some(givenFileInfo)) - } yield assertTrue( - content == TextFileValueContentV2(ApiV2Complex, expectedFileValue, None), - ) - }, - test("should parse AudioFileValueContentV2") { - for { - model <- KnoraApiCreateValueModel.fromJsonLd( - s""" - |{ - | "@id" : "http://rdfh.ch/0001/a-thing", - | "@type" : "ex:Thing", - | "ex:hasOtherThingValue" : { - | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", - | "@type" : "ka:AudioFileValue" - | }, - | "@context": { - | "ka": "http://api.knora.org/ontology/knora-api/v2#", - | "ex": "https://example.com/test#", - | "xsd": "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin, - ) - content <- model.getValueContent(Some(givenFileInfo)) - } yield assertTrue( - content == AudioFileValueContentV2(ApiV2Complex, expectedFileValue, None), - ) - }, - test("should parse MovingImageFileValueContentV2") { - for { - model <- KnoraApiCreateValueModel.fromJsonLd( - s""" - |{ - | "@id" : "http://rdfh.ch/0001/a-thing", - | "@type" : "ex:Thing", - | "ex:hasOtherThingValue" : { - | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", - | "@type" : "ka:MovingImageFileValue" - | }, - | "@context": { - | "ka": "http://api.knora.org/ontology/knora-api/v2#", - | "ex": "https://example.com/test#", - | "xsd": "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin, - ) - content <- model.getValueContent(Some(givenFileInfo)) - } yield assertTrue( - content == MovingImageFileValueContentV2(ApiV2Complex, expectedFileValue, None), - ) - }, - test("should parse ArchiveFileValueContentV2") { - for { - model <- KnoraApiCreateValueModel.fromJsonLd( - s""" - |{ - | "@id" : "http://rdfh.ch/0001/a-thing", - | "@type" : "ex:Thing", - | "ex:hasOtherThingValue" : { - | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", - | "@type" : "ka:ArchiveFileValue" - | }, - | "@context": { - | "ka": "http://api.knora.org/ontology/knora-api/v2#", - | "ex": "https://example.com/test#", - | "xsd": "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin, - ) - content <- model.getValueContent(Some(givenFileInfo)) - } yield assertTrue( - content == ArchiveFileValueContentV2(ApiV2Complex, expectedFileValue, None), - ) - }, - test("should parse HierarchicalListValueContentV2") { - for { - model <- KnoraApiCreateValueModel.fromJsonLd( - s""" - |{ - | "@id" : "http://rdfh.ch/0001/a-thing", - | "@type" : "ex:Thing", - | "ex:hasOtherThingValue" : { - | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", - | "@type" : "ka:ListValue", - | "ka:listValueAsListNode": { - | "@id" : "http://rdfh.ch/0001/CNhWoNGGT7iWOrIwxsEqvA" - | } - | }, - | "@context": { - | "ka": "http://api.knora.org/ontology/knora-api/v2#", - | "ex": "https://example.com/test#", - | "xsd": "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin, - ) - content <- model.getValueContent(Some(givenFileInfo)) - } yield assertTrue( - content == HierarchicalListValueContentV2( - ApiV2Complex, - "http://rdfh.ch/0001/CNhWoNGGT7iWOrIwxsEqvA", - None, - None, - ), - ) - }, - test("should parse DateValueContentV2") { - for { - model <- KnoraApiCreateValueModel.fromJsonLd( - s""" - |{ - | "@id" : "http://rdfh.ch/0001/a-thing", - | "@type" : "ex:Thing", - | "ex:hasOtherThingValue" : { - | "@id" : "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw", - | "@type" : "ka:DateValue", - | "ka:dateValueHasCalendar" : "GREGORIAN", - | "ka:dateValueHasEndEra" : "CE", - | "ka:dateValueHasEndYear" : 1489, - | "ka:dateValueHasEndMonth" : 12, - | "ka:dateValueHasEndDay" : 24, - | "ka:dateValueHasStartEra" : "CE", - | "ka:dateValueHasStartMonth" : 1, - | "ka:dateValueHasStartDay" : 28, - | "ka:dateValueHasStartYear" : 1488 - | }, - | "@context": { - | "ka": "http://api.knora.org/ontology/knora-api/v2#", - | "ex": "https://example.com/test#", - | "xsd": "http://www.w3.org/2001/XMLSchema#" - | } - |}""".stripMargin, - ) - content <- model.getValueContent() - } yield assertTrue( - content == DateValueContentV2( - ApiV2Complex, - 2264568, - 2265264, - DatePrecisionDay, - DatePrecisionDay, - CalendarNameGregorian, - None, - ), - ) - }, - ).provideSome[Scope](IriConverter.layer, MessageRelayLive.layer, StringFormatter.test) - -}