From 3fae03d70abd3abed911d8aaa97a16dafe7d3844 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Mon, 13 Jan 2025 13:50:28 +0100 Subject: [PATCH 01/31] Move Copyright and Licenses relevant models out of the KnoraProject --- .../it/v2/CopyrightAndLicensesSpec.scala | 6 +-- .../IntegrationTestAdminJsonProtocol.scala | 6 +-- .../models/filemodels/FileModelUtil.scala | 6 +-- .../webapi/models/filemodels/FileModels.scala | 6 +-- .../util/ConstructResponseUtilV2.scala | 6 +-- .../valuemessages/ValueMessagesV2.scala | 6 +-- .../valuemessages/ValueMessagesV2Optics.scala | 6 +-- .../domain/model/CopyrightAndLicenses.scala | 33 +++++++++++++++++ .../admin/domain/model/KnoraProject.scala | 26 ------------- .../slice/admin/repo/rdf/RdfConversions.scala | 9 +---- .../webapi/slice/common/ValueTypes.scala | 37 +++++++++++++++++-- .../repo/model/ResourceCreateModels.scala | 6 +-- .../model/CopyrightAndLicensesModelSpec.scala | 26 +++++++++++++ .../admin/domain/model/KnoraProjectSpec.scala | 12 ------ 14 files changed, 118 insertions(+), 73 deletions(-) create mode 100644 webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala create mode 100644 webapi/src/test/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicensesModelSpec.scala diff --git a/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala b/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala index ebe30a5738..013e0dabf2 100644 --- a/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala +++ b/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala @@ -26,9 +26,9 @@ import org.knora.webapi.messages.OntologyConstants.KnoraApiV2Complex.HasLicenseU import org.knora.webapi.messages.OntologyConstants.KnoraApiV2Complex.StillImageFileValue import org.knora.webapi.models.filemodels.FileType import org.knora.webapi.models.filemodels.UploadFileRequest -import org.knora.webapi.slice.admin.domain.model.KnoraProject.CopyrightAttribution -import org.knora.webapi.slice.admin.domain.model.KnoraProject.LicenseText -import org.knora.webapi.slice.admin.domain.model.KnoraProject.LicenseUri +import org.knora.webapi.slice.admin.domain.model.CopyrightAttribution +import org.knora.webapi.slice.admin.domain.model.LicenseText +import org.knora.webapi.slice.admin.domain.model.LicenseUri import org.knora.webapi.slice.admin.domain.service.KnoraProjectService import org.knora.webapi.slice.common.KnoraIris.ValueIri import org.knora.webapi.slice.common.jena.JenaConversions.given diff --git a/integration/src/test/scala/org/knora/webapi/messages/admin/responder/IntegrationTestAdminJsonProtocol.scala b/integration/src/test/scala/org/knora/webapi/messages/admin/responder/IntegrationTestAdminJsonProtocol.scala index a9a054c076..49e123e065 100644 --- a/integration/src/test/scala/org/knora/webapi/messages/admin/responder/IntegrationTestAdminJsonProtocol.scala +++ b/integration/src/test/scala/org/knora/webapi/messages/admin/responder/IntegrationTestAdminJsonProtocol.scala @@ -44,10 +44,10 @@ import org.knora.webapi.slice.admin.api.model.Project import org.knora.webapi.slice.admin.api.model.ProjectAdminMembersGetResponseADM import org.knora.webapi.slice.admin.api.model.ProjectMembersGetResponseADM import org.knora.webapi.slice.admin.api.model.ProjectOperationResponseADM +import org.knora.webapi.slice.admin.domain.model.CopyrightAttribution import org.knora.webapi.slice.admin.domain.model.Group -import org.knora.webapi.slice.admin.domain.model.KnoraProject.CopyrightAttribution -import org.knora.webapi.slice.admin.domain.model.KnoraProject.LicenseText -import org.knora.webapi.slice.admin.domain.model.KnoraProject.LicenseUri +import org.knora.webapi.slice.admin.domain.model.LicenseText +import org.knora.webapi.slice.admin.domain.model.LicenseUri import org.knora.webapi.slice.admin.domain.model.User import org.knora.webapi.slice.common.Value.StringValue diff --git a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModelUtil.scala b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModelUtil.scala index d081fbbb97..a272331dcc 100644 --- a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModelUtil.scala +++ b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModelUtil.scala @@ -11,9 +11,9 @@ import org.knora.webapi.messages.OntologyConstants import org.knora.webapi.messages.SmartIri import org.knora.webapi.messages.StringFormatter import org.knora.webapi.messages.v2.responder.valuemessages.* -import org.knora.webapi.slice.admin.domain.model.KnoraProject.CopyrightAttribution -import org.knora.webapi.slice.admin.domain.model.KnoraProject.LicenseText -import org.knora.webapi.slice.admin.domain.model.KnoraProject.LicenseUri +import org.knora.webapi.slice.admin.domain.model.CopyrightAttribution +import org.knora.webapi.slice.admin.domain.model.LicenseText +import org.knora.webapi.slice.admin.domain.model.LicenseUri import org.knora.webapi.slice.resources.IiifImageRequestUrl object FileModelUtil { diff --git a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala index deccafe0e3..5f0597d6cc 100644 --- a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala +++ b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala @@ -15,9 +15,9 @@ import org.knora.webapi.messages.v2.responder.resourcemessages.CreateResourceV2 import org.knora.webapi.messages.v2.responder.resourcemessages.CreateValueInNewResourceV2 import org.knora.webapi.sharedtestdata.SharedTestDataADM import org.knora.webapi.slice.admin.api.model.Project -import org.knora.webapi.slice.admin.domain.model.KnoraProject.CopyrightAttribution -import org.knora.webapi.slice.admin.domain.model.KnoraProject.LicenseText -import org.knora.webapi.slice.admin.domain.model.KnoraProject.LicenseUri +import org.knora.webapi.slice.admin.domain.model.CopyrightAttribution +import org.knora.webapi.slice.admin.domain.model.LicenseText +import org.knora.webapi.slice.admin.domain.model.LicenseUri sealed abstract case class UploadFileRequest private ( fileType: FileType, diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala index e98ca70c03..218cd7b918 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala @@ -48,10 +48,10 @@ import org.knora.webapi.messages.v2.responder.standoffmessages.GetXSLTransformat import org.knora.webapi.messages.v2.responder.standoffmessages.GetXSLTransformationResponseV2 import org.knora.webapi.messages.v2.responder.standoffmessages.MappingXMLtoStandoff import org.knora.webapi.messages.v2.responder.valuemessages.* -import org.knora.webapi.slice.admin.domain.model.KnoraProject.CopyrightAttribution -import org.knora.webapi.slice.admin.domain.model.KnoraProject.LicenseText -import org.knora.webapi.slice.admin.domain.model.KnoraProject.LicenseUri +import org.knora.webapi.slice.admin.domain.model.CopyrightAttribution import org.knora.webapi.slice.admin.domain.model.KnoraProject.ProjectIri +import org.knora.webapi.slice.admin.domain.model.LicenseText +import org.knora.webapi.slice.admin.domain.model.LicenseUri import org.knora.webapi.slice.admin.domain.model.ListProperties.ListIri import org.knora.webapi.slice.admin.domain.model.Permission import org.knora.webapi.slice.admin.domain.model.User 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 38c3409e28..2685a4e155 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 @@ -43,10 +43,10 @@ import org.knora.webapi.messages.v2.responder.valuemessages.ValueContentV2.FileI import org.knora.webapi.routing.RouteUtilZ import org.knora.webapi.slice.admin.api.model.MaintenanceRequests.AssetId import org.knora.webapi.slice.admin.api.model.Project -import org.knora.webapi.slice.admin.domain.model.KnoraProject.CopyrightAttribution -import org.knora.webapi.slice.admin.domain.model.KnoraProject.LicenseText -import org.knora.webapi.slice.admin.domain.model.KnoraProject.LicenseUri +import org.knora.webapi.slice.admin.domain.model.CopyrightAttribution import org.knora.webapi.slice.admin.domain.model.KnoraProject.Shortcode +import org.knora.webapi.slice.admin.domain.model.LicenseText +import org.knora.webapi.slice.admin.domain.model.LicenseUri import org.knora.webapi.slice.admin.domain.model.Permission import org.knora.webapi.slice.common.Value.StringValue import org.knora.webapi.slice.common.jena.JenaConversions.given diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2Optics.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2Optics.scala index d5081e21d2..044ba7e24f 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2Optics.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2Optics.scala @@ -8,9 +8,9 @@ package org.knora.webapi.messages.v2.responder.valuemessages import monocle.* import monocle.macros.* -import org.knora.webapi.slice.admin.domain.model.KnoraProject.CopyrightAttribution -import org.knora.webapi.slice.admin.domain.model.KnoraProject.LicenseText -import org.knora.webapi.slice.admin.domain.model.KnoraProject.LicenseUri +import org.knora.webapi.slice.admin.domain.model.CopyrightAttribution +import org.knora.webapi.slice.admin.domain.model.LicenseText +import org.knora.webapi.slice.admin.domain.model.LicenseUri object ValueMessagesV2Optics { diff --git a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala new file mode 100644 index 0000000000..165ddcb71d --- /dev/null +++ b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala @@ -0,0 +1,33 @@ +/* + * Copyright © 2021 - 2025 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.admin.domain.model + +import org.knora.webapi.slice.common.StringValueCompanion +import org.knora.webapi.slice.common.StringValueCompanion.* +import org.knora.webapi.slice.common.StringValueCompanion.maxLength +import org.knora.webapi.slice.common.Value.StringValue + +final case class CopyrightAttribution private (override val value: String) extends StringValue +object CopyrightAttribution extends StringValueCompanion[CopyrightAttribution] { + def from(str: String): Either[String, CopyrightAttribution] = + fromValidations( + "Copyright Attribution", + CopyrightAttribution.apply, + List(nonEmpty, noLineBreaks, maxLength(1_000)), + )(str) +} + +final case class LicenseText private (override val value: String) extends StringValue +object LicenseText extends StringValueCompanion[LicenseText] { + def from(str: String): Either[String, LicenseText] = + fromValidations("License text", LicenseText.apply, List(nonEmpty, maxLength(100_000)))(str) +} + +final case class LicenseUri private (override val value: String) extends StringValue +object LicenseUri extends StringValueCompanion[LicenseUri] { + def from(str: String): Either[String, LicenseUri] = + fromValidations("License URI", LicenseUri.apply, List(nonEmpty, isUri))(str) +} diff --git a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/KnoraProject.scala b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/KnoraProject.scala index 54d111b05c..62db5c0ea9 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/KnoraProject.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/KnoraProject.scala @@ -7,8 +7,6 @@ package org.knora.webapi.slice.admin.domain.model import zio.NonEmptyChunk -import java.net.URI -import scala.util.Try import scala.util.matching.Regex import dsp.valueobjects.Iri.isIri @@ -176,28 +174,4 @@ object KnoraProject { def from(value: Boolean): SelfJoin = if (value) CanJoin else CannotJoin } - final case class CopyrightAttribution private (override val value: String) extends StringValue - object CopyrightAttribution extends StringValueCompanion[CopyrightAttribution] { - private val maxLength = 1_000 - def from(str: String): Either[String, CopyrightAttribution] = - if (str.isEmpty) Left("Copyright attribution cannot be empty.") - else if (str.contains("\n")) Left("Copyright attribution may not contain line breaks.") - else if (str.length >= maxLength) Left(s"Copyright attribution may only be ${maxLength} characters long.") - else Right(CopyrightAttribution(str)) - } - - final case class LicenseText private (override val value: String) extends StringValue - object LicenseText extends StringValueCompanion[LicenseText] { - private val maxLength = 10_000 - def from(str: String): Either[String, LicenseText] = - if (str.isEmpty) Left("License cannot be empty.") - else if (str.length >= maxLength) Left(s"License may only be ${maxLength} characters long.") - else Right(LicenseText(str)) - } - - final case class LicenseUri private (override val value: String) extends StringValue - object LicenseUri extends StringValueCompanion[LicenseUri] { - def from(str: String): Either[String, LicenseUri] = - Try(URI.create(str)).toEither.left.map(_ => s"License URI '$str' is not a valid URI.").map(_ => LicenseUri(str)) - } } diff --git a/webapi/src/main/scala/org/knora/webapi/slice/admin/repo/rdf/RdfConversions.scala b/webapi/src/main/scala/org/knora/webapi/slice/admin/repo/rdf/RdfConversions.scala index 105fd25c53..8731dae958 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/admin/repo/rdf/RdfConversions.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/admin/repo/rdf/RdfConversions.scala @@ -9,15 +9,8 @@ import dsp.valueobjects.LanguageCode import org.knora.webapi.messages.OntologyConstants.KnoraAdmin.KnoraAdminPrefix import org.knora.webapi.messages.OntologyConstants.KnoraAdmin.KnoraAdminPrefixExpansion import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2 -import org.knora.webapi.slice.admin.domain.model.Email -import org.knora.webapi.slice.admin.domain.model.FamilyName -import org.knora.webapi.slice.admin.domain.model.GivenName -import org.knora.webapi.slice.admin.domain.model.GroupIri +import org.knora.webapi.slice.admin.domain.model.* import org.knora.webapi.slice.admin.domain.model.KnoraProject.* -import org.knora.webapi.slice.admin.domain.model.PasswordHash -import org.knora.webapi.slice.admin.domain.model.SystemAdmin -import org.knora.webapi.slice.admin.domain.model.UserStatus -import org.knora.webapi.slice.admin.domain.model.Username import org.knora.webapi.slice.common.repo.rdf.LangString import org.knora.webapi.slice.resourceinfo.domain.InternalIri diff --git a/webapi/src/main/scala/org/knora/webapi/slice/common/ValueTypes.scala b/webapi/src/main/scala/org/knora/webapi/slice/common/ValueTypes.scala index 5a1db97fb9..6616f2983f 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/common/ValueTypes.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/common/ValueTypes.scala @@ -6,10 +6,13 @@ package org.knora.webapi.slice.common import zio.prelude.Validation - +import zio.prelude.ZValidation import org.knora.webapi.slice.common.Value.IntValue import org.knora.webapi.slice.common.Value.StringValue +import java.net.URI +import scala.util.Try + trait Value[A] extends Any { def value: A } @@ -28,8 +31,36 @@ trait WithFrom[-I, +A] { from(in).fold(e => throw new IllegalArgumentException(e), identity) } -trait StringValueCompanion[A <: StringValue] extends WithFrom[String, A] -trait IntValueCompanion[A <: IntValue] extends WithFrom[Int, A] +trait StringValueCompanion[A <: StringValue] extends WithFrom[String, A] {} +object StringValueCompanion { + + def nonEmpty: String => Validation[String, String] = + value => Validation.fromPredicateWith(s"cannot be empty")(value)((str: String) => str.nonEmpty) + + def noLineBreaks: String => Validation[String, String] = + value => Validation.fromPredicateWith(s"must not contain line breaks")(value)((str: String) => !str.contains("\n")) + + def maxLength(max: Int): String => Validation[String, String] = + value => + Validation.fromPredicateWith(s"must be maximum $max characters long")(value)((str: String) => str.length <= max) + + def isUri: String => Validation[String, String] = + value => Validation.fromTry(Try(URI.create(value))).as(value).mapError(_ => s"is not a valid URI") + + def fromValidations[A]( + typ: String, + make: String => A, + validations: List[String => Validation[String, String]], + ): String => Either[String, A] = value => + ZValidation + .validateAll(validations.map(_(value))) + .as(make(value)) + .toEither + .left + .map(_.mkString(s"$typ ", ", ", ".")) +} + +trait IntValueCompanion[A <: IntValue] extends WithFrom[Int, A] object ToValidation { def validateOneWithFrom[A, B <: Value[?], E]( diff --git a/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/model/ResourceCreateModels.scala b/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/model/ResourceCreateModels.scala index 260f52f448..71ca3c13da 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/model/ResourceCreateModels.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/model/ResourceCreateModels.scala @@ -10,9 +10,9 @@ import java.util.UUID import org.knora.webapi.messages.util.CalendarNameV2 import org.knora.webapi.messages.util.DatePrecisionV2 -import org.knora.webapi.slice.admin.domain.model.KnoraProject.CopyrightAttribution -import org.knora.webapi.slice.admin.domain.model.KnoraProject.LicenseText -import org.knora.webapi.slice.admin.domain.model.KnoraProject.LicenseUri +import org.knora.webapi.slice.admin.domain.model.CopyrightAttribution +import org.knora.webapi.slice.admin.domain.model.LicenseText +import org.knora.webapi.slice.admin.domain.model.LicenseUri import org.knora.webapi.slice.resourceinfo.domain.InternalIri final case class ResourceReadyToCreate( diff --git a/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicensesModelSpec.scala b/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicensesModelSpec.scala new file mode 100644 index 0000000000..f826d97509 --- /dev/null +++ b/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicensesModelSpec.scala @@ -0,0 +1,26 @@ +/* + * Copyright © 2021 - 2025 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.admin.domain.model + +import zio.test.* + +object CopyrightAndLicensesModelSpec extends ZIOSpecDefault { + + private val licenseUriTest = suite("LicenseUri")( + test("pass a valid object and successfully create value object") { + val validUri = "https://www.apache.org/licenses/LICENSE-2.0.html" + assertTrue(LicenseUri.from(validUri).map(_.value).contains(validUri)) + }, + test("pass an invalid object and return an error") { + val invalidUri = "not a uri" + assertTrue(LicenseUri.from(invalidUri) == Left("License URI is not a valid URI.")) + }, + ) + + val spec = suite("Copyright And Licenses Model")( + licenseUriTest, + ) +} diff --git a/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/model/KnoraProjectSpec.scala b/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/model/KnoraProjectSpec.scala index 340b86f2f1..3e00704a71 100644 --- a/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/model/KnoraProjectSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/model/KnoraProjectSpec.scala @@ -28,7 +28,6 @@ object KnoraProjectSpec extends ZIOSpecDefault { logoTest, projectStatusTest, projectSelfJoinTest, - licenseUriTest, ) private val projectIriSuite = suite("ProjectIri")( @@ -193,15 +192,4 @@ object KnoraProjectSpec extends ZIOSpecDefault { assertTrue(SelfJoin.CanJoin.value, !SelfJoin.CannotJoin.value) }, ) - - private val licenseUriTest = suite("LicenseUri")( - test("pass a valid object and successfully create value object") { - val validUri = "https://www.apache.org/licenses/LICENSE-2.0.html" - assertTrue(LicenseUri.from(validUri).map(_.value).contains(validUri)) - }, - test("pass an invalid object and return an error") { - val invalidUri = "not a uri" - assertTrue(LicenseUri.from(invalidUri) == Left("License URI 'not a uri' is not a valid URI.")) - }, - ) } From 45be470b5b9173ae049bc5929a2cf0ff212ebe98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Mon, 13 Jan 2025 15:12:16 +0100 Subject: [PATCH 02/31] add Authorship value class --- .../domain/model/CopyrightAndLicenses.scala | 6 +++++ .../model/CopyrightAndLicensesModelSpec.scala | 24 +++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala index 165ddcb71d..2468955137 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala @@ -20,6 +20,12 @@ object CopyrightAttribution extends StringValueCompanion[CopyrightAttribution] { )(str) } +final case class Authorship private (override val value: String) extends StringValue +object Authorship extends StringValueCompanion[Authorship] { + def from(str: String): Either[String, Authorship] = + fromValidations("Authorship", Authorship.apply, List(nonEmpty, noLineBreaks, maxLength(1_000)))(str) +} + final case class LicenseText private (override val value: String) extends StringValue object LicenseText extends StringValueCompanion[LicenseText] { def from(str: String): Either[String, LicenseText] = diff --git a/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicensesModelSpec.scala b/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicensesModelSpec.scala index f826d97509..93aa5d38d2 100644 --- a/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicensesModelSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicensesModelSpec.scala @@ -9,7 +9,26 @@ import zio.test.* object CopyrightAndLicensesModelSpec extends ZIOSpecDefault { - private val licenseUriTest = suite("LicenseUri")( + private val authorshipSuite = suite("Authorship")( + test("pass a valid object and successfully create value object") { + val validAuthorship = "Jane Doe" + assertTrue(Authorship.from(validAuthorship).map(_.value).contains(validAuthorship)) + }, + test("pass an invalid object and return an error") { + val invalidAuthorship = "Jane \n Doe" + assertTrue(Authorship.from(invalidAuthorship) == Left("Authorship must not contain line breaks.")) + }, + test("pass an invalid object and return an error") { + val invalidAuthorship = "a" * 1001 + assertTrue(Authorship.from(invalidAuthorship) == Left("Authorship must be maximum 1000 characters long.")) + }, + test("pass an invalid object and return an error") { + val invalidAuthorship = "" + assertTrue(Authorship.from(invalidAuthorship) == Left("Authorship cannot be empty.")) + }, + ) + + private val licenseUriSuite = suite("LicenseUri")( test("pass a valid object and successfully create value object") { val validUri = "https://www.apache.org/licenses/LICENSE-2.0.html" assertTrue(LicenseUri.from(validUri).map(_.value).contains(validUri)) @@ -21,6 +40,7 @@ object CopyrightAndLicensesModelSpec extends ZIOSpecDefault { ) val spec = suite("Copyright And Licenses Model")( - licenseUriTest, + authorshipSuite, + licenseUriSuite, ) } From 72298195d96364270b625c1467d4968832750f9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Mon, 13 Jan 2025 15:31:10 +0100 Subject: [PATCH 03/31] add LicenseDate value class --- .../domain/model/CopyrightAndLicenses.scala | 13 +++++++++++++ .../knora/webapi/slice/common/ValueTypes.scala | 5 +++-- .../model/CopyrightAndLicensesModelSpec.scala | 17 ++++++++++++++++- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala index 2468955137..edb78b9d08 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala @@ -5,10 +5,15 @@ package org.knora.webapi.slice.admin.domain.model +import java.time.LocalDate +import scala.util.Try + import org.knora.webapi.slice.common.StringValueCompanion import org.knora.webapi.slice.common.StringValueCompanion.* import org.knora.webapi.slice.common.StringValueCompanion.maxLength +import org.knora.webapi.slice.common.Value import org.knora.webapi.slice.common.Value.StringValue +import org.knora.webapi.slice.common.WithFrom final case class CopyrightAttribution private (override val value: String) extends StringValue object CopyrightAttribution extends StringValueCompanion[CopyrightAttribution] { @@ -37,3 +42,11 @@ object LicenseUri extends StringValueCompanion[LicenseUri] { def from(str: String): Either[String, LicenseUri] = fromValidations("License URI", LicenseUri.apply, List(nonEmpty, isUri))(str) } + +final case class LicenseDate private (override val value: LocalDate) extends Value[LocalDate] +object LicenseDate extends WithFrom[String, LicenseDate] { + def from(str: String): Either[String, LicenseDate] = + Try(LocalDate.parse(str)).toEither.left + .map(_ => "License Date must be in format 'YYYY-MM-DD'.") + .map(LicenseDate.apply) +} diff --git a/webapi/src/main/scala/org/knora/webapi/slice/common/ValueTypes.scala b/webapi/src/main/scala/org/knora/webapi/slice/common/ValueTypes.scala index 6616f2983f..a18d09e0b1 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/common/ValueTypes.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/common/ValueTypes.scala @@ -7,12 +7,13 @@ package org.knora.webapi.slice.common import zio.prelude.Validation import zio.prelude.ZValidation -import org.knora.webapi.slice.common.Value.IntValue -import org.knora.webapi.slice.common.Value.StringValue import java.net.URI import scala.util.Try +import org.knora.webapi.slice.common.Value.IntValue +import org.knora.webapi.slice.common.Value.StringValue + trait Value[A] extends Any { def value: A } diff --git a/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicensesModelSpec.scala b/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicensesModelSpec.scala index 93aa5d38d2..e54db7664a 100644 --- a/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicensesModelSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicensesModelSpec.scala @@ -7,6 +7,8 @@ package org.knora.webapi.slice.admin.domain.model import zio.test.* +import java.time.LocalDate + object CopyrightAndLicensesModelSpec extends ZIOSpecDefault { private val authorshipSuite = suite("Authorship")( @@ -39,8 +41,21 @@ object CopyrightAndLicensesModelSpec extends ZIOSpecDefault { }, ) - val spec = suite("Copyright And Licenses Model")( + private val licenseDate = suite("LicenseDate")( + test("pass a valid object and successfully create value object") { + val validDate = "2021-01-01" + assertTrue(LicenseDate.from(validDate).map(_.value).contains(LocalDate.parse("2021-01-01"))) + }, + test("pass an invalid object and fail with correct error message") { + check(Gen.fromIterable(List("09-24-2021", "01-01-2025", "2021-01-01T00:00:00Z"))) { invalidDate => + assertTrue(LicenseDate.from(invalidDate) == Left("License Date must be in format 'YYYY-MM-DD'.")) + } + }, + ) + + val spec: Spec[Any, Nothing] = suite("Copyright And Licenses Model")( authorshipSuite, licenseUriSuite, + licenseDate, ) } From d432041a28816767650a1baa4287494c3a08faef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Mon, 13 Jan 2025 15:45:39 +0100 Subject: [PATCH 04/31] rename copyright attribution to copyright holder --- .../it/v2/CopyrightAndLicensesSpec.scala | 50 +++++----- .../IntegrationTestAdminJsonProtocol.scala | 6 +- .../models/filemodels/FileModelUtil.scala | 18 ++-- .../webapi/models/filemodels/FileModels.scala | 14 +-- .../knoraApiOntologySimple.jsonld | 4 +- .../knoraApiOntologyWithValueObjects.jsonld | 24 ++--- .../resources/knora-ontologies/knora-base.ttl | 8 +- .../webapi/messages/OntologyConstants.scala | 42 ++++----- .../util/ConstructResponseUtilV2.scala | 8 +- ...aseToApiV2ComplexTransformationRules.scala | 6 +- .../valuemessages/ValueMessagesV2.scala | 92 +++++++++---------- .../valuemessages/ValueMessagesV2Optics.scala | 10 +- .../resources/CreateResourceV2Handler.scala | 14 +-- .../knora/webapi/slice/admin/api/Codecs.scala | 16 ++-- .../domain/model/CopyrightAndLicenses.scala | 10 +- .../slice/admin/repo/rdf/RdfConversions.scala | 6 +- .../slice/common/repo/rdf/Vocabulary.scala | 2 +- .../repo/model/ResourceCreateModels.scala | 12 +-- .../repo/service/ResourcesRepoLive.scala | 2 +- .../sparql/v2/addValueVersion.scala.txt | 6 +- .../queries/sparql/v2/createValue.scala.txt | 6 +- 21 files changed, 178 insertions(+), 178 deletions(-) diff --git a/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala b/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala index 013e0dabf2..834b95a17c 100644 --- a/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala +++ b/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala @@ -20,13 +20,13 @@ import scala.language.implicitConversions import org.knora.webapi.E2EZSpec import org.knora.webapi.messages.OntologyConstants -import org.knora.webapi.messages.OntologyConstants.KnoraApiV2Complex.HasCopyrightAttribution +import org.knora.webapi.messages.OntologyConstants.KnoraApiV2Complex.HasCopyrightHolder import org.knora.webapi.messages.OntologyConstants.KnoraApiV2Complex.HasLicenseText import org.knora.webapi.messages.OntologyConstants.KnoraApiV2Complex.HasLicenseUri import org.knora.webapi.messages.OntologyConstants.KnoraApiV2Complex.StillImageFileValue import org.knora.webapi.models.filemodels.FileType import org.knora.webapi.models.filemodels.UploadFileRequest -import org.knora.webapi.slice.admin.domain.model.CopyrightAttribution +import org.knora.webapi.slice.admin.domain.model.CopyrightHolder import org.knora.webapi.slice.admin.domain.model.LicenseText import org.knora.webapi.slice.admin.domain.model.LicenseUri import org.knora.webapi.slice.admin.domain.service.KnoraProjectService @@ -39,14 +39,14 @@ import org.knora.webapi.slice.resourceinfo.domain.IriConverter object CopyrightAndLicensesSpec extends E2EZSpec { - private val aCopyrightAttribution = CopyrightAttribution.unsafeFrom("2020, On FileValue") - private val aLicenseText = LicenseText.unsafeFrom("CC BY-SA 4.0") - private val aLicenseUri = LicenseUri.unsafeFrom("https://creativecommons.org/licenses/by-sa/4.0/") + private val aCopyrightHolder = CopyrightHolder.unsafeFrom("2020, On FileValue") + private val aLicenseText = LicenseText.unsafeFrom("CC BY-SA 4.0") + private val aLicenseUri = LicenseUri.unsafeFrom("https://creativecommons.org/licenses/by-sa/4.0/") - private val givenProjectHasNoCopyrightAttributionAndLicenseSuite = suite("Creating Resources")( + private val givenProjectHasNoCopyrightHolderAndLicenseSuite = suite("Creating Resources")( test( - "when creating a resource without copyright attribution and license" + - "the creation response should not contain the license and copyright attribution", + "when creating a resource without copyright holder and license" + + "the creation response should not contain the license and copyright holder", ) { for { createResourceResponseModel <- createStillImageResource() @@ -60,52 +60,52 @@ object CopyrightAndLicensesSpec extends E2EZSpec { ) }, test( - "when creating a resource with copyright attribution and license " + - "the creation response should contain the license and copyright attribution", + "when creating a resource with copyright holder and license " + + "the creation response should contain the license and copyright holder", ) { for { createResourceResponseModel <- - createStillImageResource(Some(aCopyrightAttribution), Some(aLicenseText), Some(aLicenseUri)) + createStillImageResource(Some(aCopyrightHolder), Some(aLicenseText), Some(aLicenseUri)) actualCreatedCopyright <- copyrightValue(createResourceResponseModel) actualCreatedLicenseText <- licenseTextValue(createResourceResponseModel) actualCreatedLicenseUri <- licenseUriValue(createResourceResponseModel) } yield assertTrue( - actualCreatedCopyright == aCopyrightAttribution.value, + actualCreatedCopyright == aCopyrightHolder.value, actualCreatedLicenseText == aLicenseText.value, actualCreatedLicenseUri == aLicenseUri.value, ) }, test( - "when creating a resource with copyright attribution and license " + - "the response when getting the created resource should contain the license and copyright attribution", + "when creating a resource with copyright holder and license " + + "the response when getting the created resource should contain the license and copyright holder", ) { for { createResourceResponseModel <- - createStillImageResource(Some(aCopyrightAttribution), Some(aLicenseText), Some(aLicenseUri)) + createStillImageResource(Some(aCopyrightHolder), Some(aLicenseText), Some(aLicenseUri)) resourceId <- resourceId(createResourceResponseModel) getResponseModel <- getResourceFromApi(resourceId) actualCopyright <- copyrightValue(getResponseModel) actualLicenseText <- licenseTextValue(getResponseModel) actualLicenseUri <- licenseUriValue(getResponseModel) } yield assertTrue( - actualCopyright == aCopyrightAttribution.value, + actualCopyright == aCopyrightHolder.value, actualLicenseText == aLicenseText.value, actualLicenseUri == aLicenseUri.value, ) }, test( - "when creating a resource with copyright attribution and license " + - "the response when getting the created value should contain the license and copyright attribution", + "when creating a resource with copyright holder and license " + + "the response when getting the created value should contain the license and copyright holder", ) { for { createResourceResponseModel <- - createStillImageResource(Some(aCopyrightAttribution), Some(aLicenseText), Some(aLicenseUri)) + createStillImageResource(Some(aCopyrightHolder), Some(aLicenseText), Some(aLicenseUri)) valueResponseModel <- getValueFromApi(createResourceResponseModel) actualCopyright <- copyrightValue(valueResponseModel) actualLicenseText <- licenseTextValue(valueResponseModel) actualLicenseUri <- licenseUriValue(valueResponseModel) } yield assertTrue( - actualCopyright == aCopyrightAttribution.value, + actualCopyright == aCopyrightHolder.value, actualLicenseText == aLicenseText.value, actualLicenseUri == aLicenseUri.value, ) @@ -113,14 +113,14 @@ object CopyrightAndLicensesSpec extends E2EZSpec { ) val e2eSpec: Spec[Scope & env, Any] = suite("Copyright Attribution and Licenses")( - givenProjectHasNoCopyrightAttributionAndLicenseSuite, + givenProjectHasNoCopyrightHolderAndLicenseSuite, ) private def failResponse(msg: String)(response: Response) = response.body.asString.flatMap(bodyStr => ZIO.fail(Exception(s"$msg\nstatus: ${response.status}\nbody: $bodyStr"))) private def createStillImageResource( - copyrightAttribution: Option[CopyrightAttribution] = None, + copyrightHolder: Option[CopyrightHolder] = None, licenseText: Option[LicenseText] = None, licenseUri: Option[LicenseUri] = None, ): ZIO[env, Throwable, Model] = { @@ -128,7 +128,7 @@ object CopyrightAndLicensesSpec extends E2EZSpec { .make( FileType.StillImageFile(), "internalFilename.jpg", - copyrightAttribution = copyrightAttribution, + copyrightHolder = copyrightHolder, licenseText = licenseText, licenseUri = licenseUri, ) @@ -186,9 +186,9 @@ object CopyrightAndLicensesSpec extends E2EZSpec { } private def copyrightValue(model: Model) = - singleStringValueOption(model, HasCopyrightAttribution).someOrFail(new Exception("No copyright found")) + singleStringValueOption(model, HasCopyrightHolder).someOrFail(new Exception("No copyright found")) private def copyrightValueOption(model: Model) = - singleStringValueOption(model, HasCopyrightAttribution) + singleStringValueOption(model, HasCopyrightHolder) private def licenseTextValue(model: Model) = singleStringValueOption(model, HasLicenseText).someOrFail(new Exception("No license text found")) private def licenseTextValueOption(model: Model) = diff --git a/integration/src/test/scala/org/knora/webapi/messages/admin/responder/IntegrationTestAdminJsonProtocol.scala b/integration/src/test/scala/org/knora/webapi/messages/admin/responder/IntegrationTestAdminJsonProtocol.scala index 49e123e065..e95c119dcb 100644 --- a/integration/src/test/scala/org/knora/webapi/messages/admin/responder/IntegrationTestAdminJsonProtocol.scala +++ b/integration/src/test/scala/org/knora/webapi/messages/admin/responder/IntegrationTestAdminJsonProtocol.scala @@ -44,7 +44,7 @@ import org.knora.webapi.slice.admin.api.model.Project import org.knora.webapi.slice.admin.api.model.ProjectAdminMembersGetResponseADM import org.knora.webapi.slice.admin.api.model.ProjectMembersGetResponseADM import org.knora.webapi.slice.admin.api.model.ProjectOperationResponseADM -import org.knora.webapi.slice.admin.domain.model.CopyrightAttribution +import org.knora.webapi.slice.admin.domain.model.CopyrightHolder import org.knora.webapi.slice.admin.domain.model.Group import org.knora.webapi.slice.admin.domain.model.LicenseText import org.knora.webapi.slice.admin.domain.model.LicenseUri @@ -204,8 +204,8 @@ object IntegrationTestAdminJsonProtocol extends TriplestoreJsonProtocol { case _ => throw DeserializationException("Value must be a JSON string.") } - implicit object CopyrightAttributionFormat extends StringValueFormat[CopyrightAttribution] { - override val from: String => Either[String, CopyrightAttribution] = CopyrightAttribution.from + implicit object CopyrightHolderFormat extends StringValueFormat[CopyrightHolder] { + override val from: String => Either[String, CopyrightHolder] = CopyrightHolder.from } implicit object LicenseTextFormat extends StringValueFormat[LicenseText] { diff --git a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModelUtil.scala b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModelUtil.scala index a272331dcc..d23360d9bf 100644 --- a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModelUtil.scala +++ b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModelUtil.scala @@ -11,7 +11,7 @@ import org.knora.webapi.messages.OntologyConstants import org.knora.webapi.messages.SmartIri import org.knora.webapi.messages.StringFormatter import org.knora.webapi.messages.v2.responder.valuemessages.* -import org.knora.webapi.slice.admin.domain.model.CopyrightAttribution +import org.knora.webapi.slice.admin.domain.model.CopyrightHolder import org.knora.webapi.slice.admin.domain.model.LicenseText import org.knora.webapi.slice.admin.domain.model.LicenseUri import org.knora.webapi.slice.resources.IiifImageRequestUrl @@ -87,7 +87,7 @@ object FileModelUtil { originalFilename: Option[String], originalMimeType: Option[String], comment: Option[String], - copyrightAttribution: Option[CopyrightAttribution], + copyrightHolder: Option[CopyrightHolder], licenseText: Option[LicenseText], licenseUri: Option[LicenseUri], ): FileValueContentV2 = @@ -100,7 +100,7 @@ object FileModelUtil { internalMimeType = internalMimeType.getOrElse("application/pdf"), originalFilename = originalFilename, originalMimeType = Some(originalMimeType.getOrElse("application/pdf")), - copyrightAttribution, + copyrightHolder, licenseText, licenseUri, ), @@ -117,7 +117,7 @@ object FileModelUtil { internalMimeType = internalMimeType.getOrElse("image/jp2"), originalFilename = originalFilename, originalMimeType = originalMimeType, - copyrightAttribution, + copyrightHolder, licenseText, licenseUri, ), @@ -133,7 +133,7 @@ object FileModelUtil { internalMimeType = internalMimeType.getOrElse("image/jp2"), originalFilename = originalFilename, originalMimeType = originalMimeType, - copyrightAttribution, + copyrightHolder, licenseText, licenseUri, ), @@ -148,7 +148,7 @@ object FileModelUtil { internalMimeType = internalMimeType.get, originalFilename = originalFilename, originalMimeType = internalMimeType, - copyrightAttribution, + copyrightHolder, licenseText, licenseUri, ), @@ -161,7 +161,7 @@ object FileModelUtil { internalMimeType = internalMimeType.get, originalFilename = originalFilename, originalMimeType = internalMimeType, - copyrightAttribution, + copyrightHolder, licenseText, licenseUri, ), @@ -174,7 +174,7 @@ object FileModelUtil { internalMimeType = internalMimeType.get, originalFilename = originalFilename, originalMimeType = internalMimeType, - copyrightAttribution, + copyrightHolder, licenseText, licenseUri, ), @@ -187,7 +187,7 @@ object FileModelUtil { internalMimeType = internalMimeType.getOrElse("application/zip"), originalFilename = originalFilename, originalMimeType = internalMimeType, - copyrightAttribution, + copyrightHolder, licenseText, licenseUri, ), diff --git a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala index 5f0597d6cc..621bc586a1 100644 --- a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala +++ b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala @@ -15,7 +15,7 @@ import org.knora.webapi.messages.v2.responder.resourcemessages.CreateResourceV2 import org.knora.webapi.messages.v2.responder.resourcemessages.CreateValueInNewResourceV2 import org.knora.webapi.sharedtestdata.SharedTestDataADM import org.knora.webapi.slice.admin.api.model.Project -import org.knora.webapi.slice.admin.domain.model.CopyrightAttribution +import org.knora.webapi.slice.admin.domain.model.CopyrightHolder import org.knora.webapi.slice.admin.domain.model.LicenseText import org.knora.webapi.slice.admin.domain.model.LicenseUri @@ -24,7 +24,7 @@ sealed abstract case class UploadFileRequest private ( internalFilename: String, label: String, resourceIRI: Option[String] = None, - copyrightAttribution: Option[CopyrightAttribution] = None, + copyrightHolder: Option[CopyrightHolder] = None, licenseText: Option[LicenseText] = None, licenseUri: Option[LicenseUri] = None, ) { @@ -61,7 +61,7 @@ sealed abstract case class UploadFileRequest private ( | "$fileValuePropertyName" : { | "@type" : "$fileValueType", | "knora-api:fileValueHasFilename" : "$internalFilename" - | ${copyrightAttribution.map(ca => s""","knora-api:hasCopyrightAttribution" : "${ca.value}"""").getOrElse("")} + | ${copyrightHolder.map(ca => s""","knora-api:hasCopyrightHolder" : "${ca.value}"""").getOrElse("")} | ${licenseText.map(l => s""","knora-api:hasLicenseText" : "${l.value}"""").getOrElse("")} | ${licenseUri .map(u => s""", "knora-api:hasLicenseUri" : { "@type" : "xsd:anyURI", "@value":"${u.value}" }""") @@ -115,7 +115,7 @@ sealed abstract case class UploadFileRequest private ( resourceClassIRI: Option[SmartIri] = None, valuePropertyIRI: Option[SmartIri] = None, project: Option[Project] = None, - copyrightAttribution: Option[CopyrightAttribution] = None, + copyrightHolder: Option[CopyrightHolder] = None, licenseText: Option[LicenseText] = None, licenseUri: Option[LicenseUri] = None, ): CreateResourceV2 = { @@ -136,7 +136,7 @@ sealed abstract case class UploadFileRequest private ( originalFilename = originalFilename, originalMimeType = originalMimeType, comment = comment, - copyrightAttribution, + copyrightHolder, licenseText, licenseUri, ) @@ -189,7 +189,7 @@ object UploadFileRequest { internalFilename: String, label: String = "test label", resourceIRI: Option[String] = None, - copyrightAttribution: Option[CopyrightAttribution] = None, + copyrightHolder: Option[CopyrightHolder] = None, licenseText: Option[LicenseText] = None, licenseUri: Option[LicenseUri] = None, ): UploadFileRequest = @@ -198,7 +198,7 @@ object UploadFileRequest { internalFilename, label, resourceIRI, - copyrightAttribution, + copyrightHolder, licenseText, licenseUri, ) {} diff --git a/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologySimple.jsonld b/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologySimple.jsonld index 0cc263a9db..5dad231b04 100644 --- a/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologySimple.jsonld +++ b/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologySimple.jsonld @@ -906,7 +906,7 @@ { "@type": "owl:Restriction", "owl:onProperty": { - "@id": "knora-api:hasCopyrightAttribution" + "@id": "knora-api:hasCopyrightHolder" }, "owl:maxCardinality": 1 }, @@ -1291,7 +1291,7 @@ "@id": "knora-api:hasComment" }, { - "@id": "knora-api:hasCopyrightAttribution", + "@id": "knora-api:hasCopyrightHolder", "@type": "owl:DatatypeProperty", "knora-api:objectType": { "@id": "xsd:string" diff --git a/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologyWithValueObjects.jsonld b/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologyWithValueObjects.jsonld index 6e2fe91bcb..284fcf4c3f 100644 --- a/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologyWithValueObjects.jsonld +++ b/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologyWithValueObjects.jsonld @@ -237,7 +237,7 @@ { "@type": "owl:Restriction", "owl:onProperty": { - "@id": "knora-api:hasCopyrightAttribution" + "@id": "knora-api:hasCopyrightHolder" }, "owl:maxCardinality": 1, "knora-api:isInherited": true @@ -544,7 +544,7 @@ { "@type": "owl:Restriction", "owl:onProperty": { - "@id": "knora-api:hasCopyrightAttribution" + "@id": "knora-api:hasCopyrightHolder" }, "owl:maxCardinality": 1, "knora-api:isInherited": true @@ -1366,7 +1366,7 @@ { "@type": "owl:Restriction", "owl:onProperty": { - "@id": "knora-api:hasCopyrightAttribution" + "@id": "knora-api:hasCopyrightHolder" }, "owl:maxCardinality": 1, "knora-api:isInherited": true @@ -2345,7 +2345,7 @@ { "@type": "owl:Restriction", "owl:onProperty": { - "@id": "knora-api:hasCopyrightAttribution" + "@id": "knora-api:hasCopyrightHolder" }, "owl:maxCardinality": 1, "knora-api:isInherited": true @@ -2658,7 +2658,7 @@ { "@type": "owl:Restriction", "owl:onProperty": { - "@id": "knora-api:hasCopyrightAttribution" + "@id": "knora-api:hasCopyrightHolder" }, "owl:maxCardinality": 1 }, @@ -3815,7 +3815,7 @@ { "@type": "owl:Restriction", "owl:onProperty": { - "@id": "knora-api:hasCopyrightAttribution" + "@id": "knora-api:hasCopyrightHolder" }, "owl:maxCardinality": 1, "knora-api:isInherited": true @@ -6069,7 +6069,7 @@ { "@type": "owl:Restriction", "owl:onProperty": { - "@id": "knora-api:hasCopyrightAttribution" + "@id": "knora-api:hasCopyrightHolder" }, "owl:maxCardinality": 1, "knora-api:isInherited": true @@ -6231,7 +6231,7 @@ { "@type": "owl:Restriction", "owl:onProperty": { - "@id": "knora-api:hasCopyrightAttribution" + "@id": "knora-api:hasCopyrightHolder" }, "owl:maxCardinality": 1, "knora-api:isInherited": true @@ -6400,7 +6400,7 @@ { "@type": "owl:Restriction", "owl:onProperty": { - "@id": "knora-api:hasCopyrightAttribution" + "@id": "knora-api:hasCopyrightHolder" }, "owl:maxCardinality": 1, "knora-api:isInherited": true @@ -6734,7 +6734,7 @@ { "@type": "owl:Restriction", "owl:onProperty": { - "@id": "knora-api:hasCopyrightAttribution" + "@id": "knora-api:hasCopyrightHolder" }, "owl:maxCardinality": 1, "knora-api:isInherited": true @@ -8367,13 +8367,13 @@ "rdfs:comment": "Represents a comment on a resource as a knora-base:TextValue" }, { - "rdfs:label": "has copyright attribution", + "rdfs:label": "has copyright holder", "rdfs:comment": "The copyright statement that gives credit to the original author.", "@type": "owl:DatatypeProperty", "knora-api:objectType": { "@id": "xsd:string" }, - "@id": "knora-api:hasCopyrightAttribution" + "@id": "knora-api:hasCopyrightHolder" }, { "rdfs:label": "has 3D-file", diff --git a/webapi/src/main/resources/knora-ontologies/knora-base.ttl b/webapi/src/main/resources/knora-ontologies/knora-base.ttl index a84e8d4dd9..f7eb939caf 100644 --- a/webapi/src/main/resources/knora-ontologies/knora-base.ttl +++ b/webapi/src/main/resources/knora-ontologies/knora-base.ttl @@ -527,11 +527,11 @@ :subjectClassConstraint :Resource ; :objectClassConstraint :Value . -### http://www.knora.org/ontology/knora-base#hasCopyrightAttribution +### http://www.knora.org/ontology/knora-base#hasCopyrightHolder -:hasCopyrightAttribution +:hasCopyrightHolder rdf:type owl:DatatypeProperty ; - rdfs:comment "The copyright statement that gives credit to the original author."@en ; + rdfs:comment "The copyright holder."@en ; :objectDatatypeConstraint xsd:string . ### http://www.knora.org/ontology/knora-base#hasLicense @@ -1500,7 +1500,7 @@ owl:onProperty :originalMimeType ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ], [ rdf:type owl:Restriction ; - owl:onProperty :hasCopyrightAttribution ; + owl:onProperty :hasCopyrightHolder ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ], [ rdf:type owl:Restriction ; owl:onProperty :hasLicenseText ; diff --git a/webapi/src/main/scala/org/knora/webapi/messages/OntologyConstants.scala b/webapi/src/main/scala/org/knora/webapi/messages/OntologyConstants.scala index beaaa2b6ce..2df37a126f 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/OntologyConstants.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/OntologyConstants.scala @@ -263,7 +263,7 @@ object OntologyConstants { val HasTextFileValue: IRI = KnoraBasePrefixExpansion + "hasTextFileValue" val HasDocumentFileValue: IRI = KnoraBasePrefixExpansion + "hasDocumentFileValue" val HasArchiveFileValue: IRI = KnoraBasePrefixExpansion + "hasArchiveFileValue" - val HasCopyrightAttribution: IRI = KnoraBasePrefixExpansion + "hasCopyrightAttribution" + val HasCopyrightHolder: IRI = KnoraBasePrefixExpansion + "hasCopyrightHolder" val HasLicenseText: IRI = KnoraBasePrefixExpansion + "hasLicenseText" val HasLicenseUri: IRI = KnoraBasePrefixExpansion + "hasLicenseUri" @@ -620,26 +620,26 @@ object OntologyConstants { val ValueHasComment: IRI = KnoraApiV2PrefixExpansion + "valueHasComment" val NewValueVersionIri: IRI = KnoraApiV2PrefixExpansion + "newValueVersionIri" - val User: IRI = KnoraApiV2PrefixExpansion + "User" - val AttachedToUser: IRI = KnoraApiV2PrefixExpansion + "attachedToUser" - val AttachedToProject: IRI = KnoraApiV2PrefixExpansion + "attachedToProject" - val HasStandoffLinkTo: IRI = KnoraApiV2PrefixExpansion + "hasStandoffLinkTo" - val HasStandoffLinkToValue: IRI = KnoraApiV2PrefixExpansion + "hasStandoffLinkToValue" - val HasPermissions: IRI = KnoraApiV2PrefixExpansion + "hasPermissions" - val HasCopyrightAttribution: IRI = KnoraApiV2PrefixExpansion + "hasCopyrightAttribution" - val HasLicenseText: IRI = KnoraApiV2PrefixExpansion + "hasLicenseText" - val HasLicenseUri: IRI = KnoraApiV2PrefixExpansion + "hasLicenseUri" - val UserHasPermission: String = KnoraApiV2PrefixExpansion + "userHasPermission" - val CreationDate: IRI = KnoraApiV2PrefixExpansion + "creationDate" - val LastModificationDate: IRI = KnoraApiV2PrefixExpansion + "lastModificationDate" - val VersionDate: IRI = KnoraApiV2PrefixExpansion + "versionDate" - val NewModificationDate: IRI = KnoraApiV2PrefixExpansion + "newModificationDate" - val IsDeleted: IRI = KnoraApiV2PrefixExpansion + "isDeleted" - val DeleteDate: IRI = KnoraApiV2PrefixExpansion + "deleteDate" - val DeleteComment: IRI = KnoraApiV2PrefixExpansion + "deleteComment" - val ArkUrl: IRI = KnoraApiV2PrefixExpansion + "arkUrl" - val VersionArkUrl: IRI = KnoraApiV2PrefixExpansion + "versionArkUrl" - val Author: IRI = KnoraApiV2PrefixExpansion + "author" + val User: IRI = KnoraApiV2PrefixExpansion + "User" + val AttachedToUser: IRI = KnoraApiV2PrefixExpansion + "attachedToUser" + val AttachedToProject: IRI = KnoraApiV2PrefixExpansion + "attachedToProject" + val HasStandoffLinkTo: IRI = KnoraApiV2PrefixExpansion + "hasStandoffLinkTo" + val HasStandoffLinkToValue: IRI = KnoraApiV2PrefixExpansion + "hasStandoffLinkToValue" + val HasPermissions: IRI = KnoraApiV2PrefixExpansion + "hasPermissions" + val HasCopyrightHolder: IRI = KnoraApiV2PrefixExpansion + "hasCopyrightHolder" + val HasLicenseText: IRI = KnoraApiV2PrefixExpansion + "hasLicenseText" + val HasLicenseUri: IRI = KnoraApiV2PrefixExpansion + "hasLicenseUri" + val UserHasPermission: String = KnoraApiV2PrefixExpansion + "userHasPermission" + val CreationDate: IRI = KnoraApiV2PrefixExpansion + "creationDate" + val LastModificationDate: IRI = KnoraApiV2PrefixExpansion + "lastModificationDate" + val VersionDate: IRI = KnoraApiV2PrefixExpansion + "versionDate" + val NewModificationDate: IRI = KnoraApiV2PrefixExpansion + "newModificationDate" + val IsDeleted: IRI = KnoraApiV2PrefixExpansion + "isDeleted" + val DeleteDate: IRI = KnoraApiV2PrefixExpansion + "deleteDate" + val DeleteComment: IRI = KnoraApiV2PrefixExpansion + "deleteComment" + val ArkUrl: IRI = KnoraApiV2PrefixExpansion + "arkUrl" + val VersionArkUrl: IRI = KnoraApiV2PrefixExpansion + "versionArkUrl" + val Author: IRI = KnoraApiV2PrefixExpansion + "author" val Resource: IRI = KnoraApiV2PrefixExpansion + "Resource" val DeletedResource: IRI = KnoraApiV2PrefixExpansion + "DeletedResource" diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala index 218cd7b918..b05fa78d5a 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala @@ -48,7 +48,7 @@ import org.knora.webapi.messages.v2.responder.standoffmessages.GetXSLTransformat import org.knora.webapi.messages.v2.responder.standoffmessages.GetXSLTransformationResponseV2 import org.knora.webapi.messages.v2.responder.standoffmessages.MappingXMLtoStandoff import org.knora.webapi.messages.v2.responder.valuemessages.* -import org.knora.webapi.slice.admin.domain.model.CopyrightAttribution +import org.knora.webapi.slice.admin.domain.model.CopyrightHolder import org.knora.webapi.slice.admin.domain.model.KnoraProject.ProjectIri import org.knora.webapi.slice.admin.domain.model.LicenseText import org.knora.webapi.slice.admin.domain.model.LicenseUri @@ -1087,9 +1087,9 @@ final case class ConstructResponseUtilV2Live( internalFilename = valueObject.requireStringObject(OntologyConstants.KnoraBase.InternalFilename.toSmartIri), originalFilename = valueObject.maybeStringObject(OntologyConstants.KnoraBase.OriginalFilename.toSmartIri), originalMimeType = valueObject.maybeStringObject(OntologyConstants.KnoraBase.OriginalMimeType.toSmartIri), - copyrightAttribution = valueObject - .maybeStringObject(OntologyConstants.KnoraBase.HasCopyrightAttribution.toSmartIri) - .map(CopyrightAttribution.unsafeFrom), + copyrightHolder = valueObject + .maybeStringObject(OntologyConstants.KnoraBase.HasCopyrightHolder.toSmartIri) + .map(CopyrightHolder.unsafeFrom), licenseText = valueObject .maybeStringObject(OntologyConstants.KnoraBase.HasLicenseText.toSmartIri) .map(LicenseText.unsafeFrom), diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraBaseToApiV2ComplexTransformationRules.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraBaseToApiV2ComplexTransformationRules.scala index 9f20e85114..2b26810180 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraBaseToApiV2ComplexTransformationRules.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraBaseToApiV2ComplexTransformationRules.scala @@ -152,8 +152,8 @@ object KnoraBaseToApiV2ComplexTransformationRules extends OntologyTransformation .withIsResourceProp() .withIsLinkValueProp() - private val HasCopyrightAttribution = makeOwlDatatypeProperty(KA.HasCopyrightAttribution, XSD.STRING) - .withRdfLabelEn("has copyright attribution") + private val HasCopyrightHolder = makeOwlDatatypeProperty(KA.HasCopyrightHolder, XSD.STRING) + .withRdfLabelEn("has copyright holder") .withRdfCommentEn("The copyright statement that gives credit to the original author.") private val HasLicenseText = makeOwlDatatypeProperty(KA.HasLicenseText, XSD.STRING) @@ -659,7 +659,7 @@ object KnoraBaseToApiV2ComplexTransformationRules extends OntologyTransformation FileValueHasFilename, GeometryValueAsGeometry, GeonameValueAsGeonameCode, - HasCopyrightAttribution, + HasCopyrightHolder, HasIncomingLinkValue, HasLicenseText, HasLicenseUri, 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 2685a4e155..ff1ec5f2a2 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 @@ -43,7 +43,7 @@ import org.knora.webapi.messages.v2.responder.valuemessages.ValueContentV2.FileI import org.knora.webapi.routing.RouteUtilZ import org.knora.webapi.slice.admin.api.model.MaintenanceRequests.AssetId import org.knora.webapi.slice.admin.api.model.Project -import org.knora.webapi.slice.admin.domain.model.CopyrightAttribution +import org.knora.webapi.slice.admin.domain.model.CopyrightHolder import org.knora.webapi.slice.admin.domain.model.KnoraProject.Shortcode import org.knora.webapi.slice.admin.domain.model.LicenseText import org.knora.webapi.slice.admin.domain.model.LicenseUri @@ -1998,7 +1998,7 @@ case class FileValueV2( internalMimeType: String, originalFilename: Option[String], originalMimeType: Option[String], - copyrightAttribution: Option[CopyrightAttribution], + copyrightHolder: Option[CopyrightHolder], licenseText: Option[LicenseText], licenseUri: Option[LicenseUri], ) @@ -2038,7 +2038,7 @@ sealed trait FileValueContentV2 extends ValueContentV2 { datatype = OntologyConstants.Xsd.Uri.toSmartIri, ), ) - val copyrightOption = fileValue.copyrightAttribution.map(mkJsonLdString).map((HasCopyrightAttribution, _)) + val copyrightOption = fileValue.copyrightHolder.map(mkJsonLdString).map((HasCopyrightHolder, _)) val licenseTextOption = fileValue.licenseText.map(mkJsonLdString).map((HasLicenseText, _)) val licenseUriOption = fileValue.licenseUri.map(mkJsonLdUri).map((HasLicenseUri, _)) knownValues ++ copyrightOption ++ licenseTextOption ++ licenseUriOption @@ -2127,17 +2127,17 @@ case class StillImageFileValueContentV2( */ object StillImageFileValueContentV2 { def from(r: Resource, fileInfo: FileInfo): Either[String, StillImageFileValueContentV2] = for { - comment <- objectCommentOption(r) - meta = fileInfo.metadata - copyrightAttribution <- r.objectStringOption(HasCopyrightAttribution, CopyrightAttribution.from) - licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) - licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) + comment <- objectCommentOption(r) + meta = fileInfo.metadata + copyrightHolder <- r.objectStringOption(HasCopyrightHolder, CopyrightHolder.from) + licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) + licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) fileValue = FileValueV2( fileInfo.filename, meta.internalMimeType, meta.originalFilename, meta.originalMimeType, - copyrightAttribution, + copyrightHolder, licenseText, licenseUri, ) @@ -2227,18 +2227,18 @@ case class StillImageExternalFileValueContentV2( */ object StillImageExternalFileValueContentV2 { def from(r: Resource): Either[String, StillImageExternalFileValueContentV2] = for { - externalUrlStr <- r.objectString(StillImageFileValueHasExternalUrl) - iifUrl <- IiifImageRequestUrl.from(externalUrlStr) - comment <- objectCommentOption(r) - copyrightAttribution <- r.objectStringOption(HasCopyrightAttribution, CopyrightAttribution.from) - licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) - licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) + externalUrlStr <- r.objectString(StillImageFileValueHasExternalUrl) + iifUrl <- IiifImageRequestUrl.from(externalUrlStr) + comment <- objectCommentOption(r) + copyrightHolder <- r.objectStringOption(HasCopyrightHolder, CopyrightHolder.from) + licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) + licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) fileValue = FileValueV2( "internalFilename", "internalMimeType", Some("originalFilename"), Some("originalMimeType"), - copyrightAttribution, + copyrightHolder, licenseText, licenseUri, ) @@ -2379,17 +2379,17 @@ case class ArchiveFileValueContentV2( */ object DocumentFileValueContentV2 { def from(r: Resource, info: FileInfo): Either[String, DocumentFileValueContentV2] = for { - comment <- objectCommentOption(r) - meta = info.metadata - copyrightAttribution <- r.objectStringOption(HasCopyrightAttribution, CopyrightAttribution.from) - licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) - licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) + comment <- objectCommentOption(r) + meta = info.metadata + copyrightHolder <- r.objectStringOption(HasCopyrightHolder, CopyrightHolder.from) + licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) + licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) fileValue = FileValueV2( info.filename, meta.internalMimeType, meta.originalFilename, meta.originalMimeType, - copyrightAttribution, + copyrightHolder, licenseText, licenseUri, ) @@ -2401,17 +2401,17 @@ object DocumentFileValueContentV2 { */ object ArchiveFileValueContentV2 { def from(r: Resource, info: FileInfo): Either[String, ArchiveFileValueContentV2] = for { - comment <- objectCommentOption(r) - meta = info.metadata - copyrightAttribution <- r.objectStringOption(HasCopyrightAttribution, CopyrightAttribution.from) - licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) - licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) + comment <- objectCommentOption(r) + meta = info.metadata + copyrightHolder <- r.objectStringOption(HasCopyrightHolder, CopyrightHolder.from) + licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) + licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) fileValue = FileValueV2( info.filename, meta.internalMimeType, meta.originalFilename, meta.originalMimeType, - copyrightAttribution, + copyrightHolder, licenseText, licenseUri, ) @@ -2482,17 +2482,17 @@ case class TextFileValueContentV2( */ object TextFileValueContentV2 { def from(r: Resource, info: FileInfo): Either[String, TextFileValueContentV2] = for { - comment <- objectCommentOption(r) - meta = info.metadata - copyrightAttribution <- r.objectStringOption(HasCopyrightAttribution, CopyrightAttribution.from) - licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) - licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) + comment <- objectCommentOption(r) + meta = info.metadata + copyrightHolder <- r.objectStringOption(HasCopyrightHolder, CopyrightHolder.from) + licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) + licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) fileValue = FileValueV2( info.filename, meta.internalMimeType, meta.originalFilename, meta.originalMimeType, - copyrightAttribution, + copyrightHolder, licenseText, licenseUri, ) @@ -2563,11 +2563,11 @@ case class AudioFileValueContentV2( */ object AudioFileValueContentV2 { def from(r: Resource, info: FileInfo): Either[String, AudioFileValueContentV2] = for { - comment <- objectCommentOption(r) - meta = info.metadata - copyrightAttribution <- r.objectStringOption(HasCopyrightAttribution, CopyrightAttribution.from) - licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) - licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) + comment <- objectCommentOption(r) + meta = info.metadata + copyrightHolder <- r.objectStringOption(HasCopyrightHolder, CopyrightHolder.from) + licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) + licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) } yield AudioFileValueContentV2( ApiV2Complex, FileValueV2( @@ -2575,7 +2575,7 @@ object AudioFileValueContentV2 { meta.internalMimeType, meta.originalFilename, meta.originalMimeType, - copyrightAttribution, + copyrightHolder, licenseText, licenseUri, ), @@ -2649,11 +2649,11 @@ case class MovingImageFileValueContentV2( */ object MovingImageFileValueContentV2 { def from(r: Resource, info: FileInfo): Either[String, MovingImageFileValueContentV2] = for { - comment <- objectCommentOption(r) - meta = info.metadata - copyrightAttribution <- r.objectStringOption(HasCopyrightAttribution, CopyrightAttribution.from) - licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) - licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) + comment <- objectCommentOption(r) + meta = info.metadata + copyrightHolder <- r.objectStringOption(HasCopyrightHolder, CopyrightHolder.from) + licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) + licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) } yield MovingImageFileValueContentV2( ApiV2Complex, FileValueV2( @@ -2661,7 +2661,7 @@ object MovingImageFileValueContentV2 { meta.internalMimeType, meta.originalFilename, meta.originalMimeType, - copyrightAttribution, + copyrightHolder, licenseText, licenseUri, ), diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2Optics.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2Optics.scala index 044ba7e24f..380ab94079 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2Optics.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2Optics.scala @@ -8,7 +8,7 @@ package org.knora.webapi.messages.v2.responder.valuemessages import monocle.* import monocle.macros.* -import org.knora.webapi.slice.admin.domain.model.CopyrightAttribution +import org.knora.webapi.slice.admin.domain.model.CopyrightHolder import org.knora.webapi.slice.admin.domain.model.LicenseText import org.knora.webapi.slice.admin.domain.model.LicenseUri @@ -16,8 +16,8 @@ object ValueMessagesV2Optics { object FileValueV2Optics { - val copyrightAttributionOption: Lens[FileValueV2, Option[CopyrightAttribution]] = - GenLens[FileValueV2](_.copyrightAttribution) + val copyrightHolderOption: Lens[FileValueV2, Option[CopyrightHolder]] = + GenLens[FileValueV2](_.copyrightHolder) val licenseTextOption: Lens[FileValueV2, Option[LicenseText]] = GenLens[FileValueV2](_.licenseText) @@ -37,8 +37,8 @@ object ValueMessagesV2Optics { case vc: ArchiveFileValueContentV2 => vc.copy(fileValue = fv) case vc: TextFileValueContentV2 => vc.copy(fileValue = fv) }) - val copyrightAttributionOption: Lens[FileValueContentV2, Option[CopyrightAttribution]] = - fileValueV2.andThen(FileValueV2Optics.copyrightAttributionOption) + val copyrightHolderOption: Lens[FileValueContentV2, Option[CopyrightHolder]] = + fileValueV2.andThen(FileValueV2Optics.copyrightHolderOption) val licenseTextOption: Lens[FileValueContentV2, Option[LicenseText]] = fileValueV2.andThen(FileValueV2Optics.licenseTextOption) val licenseUriOption: Lens[FileValueContentV2, Option[LicenseUri]] = diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v2/resources/CreateResourceV2Handler.scala b/webapi/src/main/scala/org/knora/webapi/responders/v2/resources/CreateResourceV2Handler.scala index 649c3d04b7..465007a087 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v2/resources/CreateResourceV2Handler.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v2/resources/CreateResourceV2Handler.scala @@ -498,7 +498,7 @@ final case class CreateResourceV2Handler( originalMimeType = fileValue.originalMimeType, dimX = dimX, dimY = dimY, - fileValue.copyrightAttribution, + fileValue.copyrightHolder, fileValue.licenseText, fileValue.licenseUri, ), @@ -511,7 +511,7 @@ final case class CreateResourceV2Handler( originalFilename = fileValue.originalFilename, originalMimeType = fileValue.originalMimeType, externalUrl = externalUrl.value.toString(), - fileValue.copyrightAttribution, + fileValue.copyrightHolder, fileValue.licenseText, fileValue.licenseUri, ), @@ -526,7 +526,7 @@ final case class CreateResourceV2Handler( dimX = dimX, dimY = dimY, pageCount = pageCount, - fileValue.copyrightAttribution, + fileValue.copyrightHolder, fileValue.licenseText, fileValue.licenseUri, ), @@ -538,7 +538,7 @@ final case class CreateResourceV2Handler( internalMimeType = fileValue.internalMimeType, originalFilename = fileValue.originalFilename, originalMimeType = fileValue.originalMimeType, - fileValue.copyrightAttribution, + fileValue.copyrightHolder, fileValue.licenseText, fileValue.licenseUri, ), @@ -550,7 +550,7 @@ final case class CreateResourceV2Handler( internalMimeType = fileValue.internalMimeType, originalFilename = fileValue.originalFilename, originalMimeType = fileValue.originalMimeType, - fileValue.copyrightAttribution, + fileValue.copyrightHolder, fileValue.licenseText, fileValue.licenseUri, ), @@ -562,7 +562,7 @@ final case class CreateResourceV2Handler( internalMimeType = fileValue.internalMimeType, originalFilename = fileValue.originalFilename, originalMimeType = fileValue.originalMimeType, - fileValue.copyrightAttribution, + fileValue.copyrightHolder, fileValue.licenseText, fileValue.licenseUri, ), @@ -574,7 +574,7 @@ final case class CreateResourceV2Handler( internalMimeType = fileValue.internalMimeType, originalFilename = fileValue.originalFilename, originalMimeType = fileValue.originalMimeType, - fileValue.copyrightAttribution, + fileValue.copyrightHolder, fileValue.licenseText, fileValue.licenseUri, ), diff --git a/webapi/src/main/scala/org/knora/webapi/slice/admin/api/Codecs.scala b/webapi/src/main/scala/org/knora/webapi/slice/admin/api/Codecs.scala index 397e20288e..25a5785e1c 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/admin/api/Codecs.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/admin/api/Codecs.scala @@ -102,14 +102,14 @@ object Codecs { implicit val restrictedViewWatermark: StringCodec[RestrictedView.Watermark] = booleanCodec( RestrictedView.Watermark.from, ) - implicit val selfJoin: StringCodec[SelfJoin] = booleanCodec(SelfJoin.from) - implicit val shortcode: StringCodec[Shortcode] = stringCodec(Shortcode.from) - implicit val shortname: StringCodec[Shortname] = stringCodec(Shortname.from) - implicit val sparqlEncodedString: StringCodec[SparqlEncodedString] = stringCodec(SparqlEncodedString.from) - implicit val status: StringCodec[Status] = booleanCodec(Status.from) - implicit val copyrightAttribution: StringCodec[CopyrightAttribution] = stringCodec(CopyrightAttribution.from) - implicit val licenseText: StringCodec[LicenseText] = stringCodec(LicenseText.from) - implicit val licenseUri: StringCodec[LicenseUri] = stringCodec(LicenseUri.from) + implicit val selfJoin: StringCodec[SelfJoin] = booleanCodec(SelfJoin.from) + implicit val shortcode: StringCodec[Shortcode] = stringCodec(Shortcode.from) + implicit val shortname: StringCodec[Shortname] = stringCodec(Shortname.from) + implicit val sparqlEncodedString: StringCodec[SparqlEncodedString] = stringCodec(SparqlEncodedString.from) + implicit val status: StringCodec[Status] = booleanCodec(Status.from) + implicit val copyrightHolder: StringCodec[CopyrightHolder] = stringCodec(CopyrightHolder.from) + implicit val licenseText: StringCodec[LicenseText] = stringCodec(LicenseText.from) + implicit val licenseUri: StringCodec[LicenseUri] = stringCodec(LicenseUri.from) // user implicit val userIri: StringCodec[UserIri] = stringCodec(UserIri.from) diff --git a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala index edb78b9d08..fca08527cc 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala @@ -15,12 +15,12 @@ import org.knora.webapi.slice.common.Value import org.knora.webapi.slice.common.Value.StringValue import org.knora.webapi.slice.common.WithFrom -final case class CopyrightAttribution private (override val value: String) extends StringValue -object CopyrightAttribution extends StringValueCompanion[CopyrightAttribution] { - def from(str: String): Either[String, CopyrightAttribution] = +final case class CopyrightHolder private (override val value: String) extends StringValue +object CopyrightHolder extends StringValueCompanion[CopyrightHolder] { + def from(str: String): Either[String, CopyrightHolder] = fromValidations( - "Copyright Attribution", - CopyrightAttribution.apply, + "Copyright Holder", + CopyrightHolder.apply, List(nonEmpty, noLineBreaks, maxLength(1_000)), )(str) } diff --git a/webapi/src/main/scala/org/knora/webapi/slice/admin/repo/rdf/RdfConversions.scala b/webapi/src/main/scala/org/knora/webapi/slice/admin/repo/rdf/RdfConversions.scala index 8731dae958..e0665b90ed 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/admin/repo/rdf/RdfConversions.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/admin/repo/rdf/RdfConversions.scala @@ -35,9 +35,9 @@ object RdfConversions { implicit val selfjoinConverter: Boolean => Either[String, SelfJoin] = value => Right(SelfJoin.from(value)) implicit val descriptionConverter: LangString => Either[String, Description] = langString => Description.from(StringLiteralV2.from(langString.value, langString.lang)) - implicit val copyrightAttributionConverter: String => Either[String, CopyrightAttribution] = CopyrightAttribution.from - implicit val licenseTextConverter: String => Either[String, LicenseText] = LicenseText.from - implicit val licenseUriConverter: String => Either[String, LicenseUri] = LicenseUri.from + implicit val copyrightHolderConverter: String => Either[String, CopyrightHolder] = CopyrightHolder.from + implicit val licenseTextConverter: String => Either[String, LicenseText] = LicenseText.from + implicit val licenseUriConverter: String => Either[String, LicenseUri] = LicenseUri.from // User properties implicit val usernameConverter: String => Either[String, Username] = Username.from diff --git a/webapi/src/main/scala/org/knora/webapi/slice/common/repo/rdf/Vocabulary.scala b/webapi/src/main/scala/org/knora/webapi/slice/common/repo/rdf/Vocabulary.scala index d52f4e2e78..ba4723fcdc 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/common/repo/rdf/Vocabulary.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/common/repo/rdf/Vocabulary.scala @@ -108,7 +108,7 @@ object Vocabulary { val valueHasMapping: Iri = iri(kb + "valueHasMapping") val valueHasMaxStandoffStartIndex: Iri = iri(kb + "valueHasMaxStandoffStartIndex") val valueHasStandoff: Iri = iri(kb + "valueHasStandoff") - val hasCopyrightAttribution: Iri = iri(kb + "hasCopyrightAttribution") + val hasCopyrightHolder: Iri = iri(kb + "hasCopyrightHolder") val hasLicenseText: Iri = iri(kb + "hasLicenseText") val hasLicenseUri: Iri = iri(kb + "hasLicenseUri") diff --git a/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/model/ResourceCreateModels.scala b/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/model/ResourceCreateModels.scala index 71ca3c13da..9a54f19df3 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/model/ResourceCreateModels.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/model/ResourceCreateModels.scala @@ -10,7 +10,7 @@ import java.util.UUID import org.knora.webapi.messages.util.CalendarNameV2 import org.knora.webapi.messages.util.DatePrecisionV2 -import org.knora.webapi.slice.admin.domain.model.CopyrightAttribution +import org.knora.webapi.slice.admin.domain.model.CopyrightHolder import org.knora.webapi.slice.admin.domain.model.LicenseText import org.knora.webapi.slice.admin.domain.model.LicenseUri import org.knora.webapi.slice.resourceinfo.domain.InternalIri @@ -50,7 +50,7 @@ sealed trait FileValueTypeSpecificInfo { def internalMimeType: String def originalFilename: Option[String] def originalMimeType: Option[String] - def copyrightAttribution: Option[CopyrightAttribution] + def copyrightHolder: Option[CopyrightHolder] def licenseText: Option[LicenseText] def licenseUri: Option[LicenseUri] } @@ -85,7 +85,7 @@ enum TypeSpecificValueInfo { originalMimeType: Option[String], dimX: Int, dimY: Int, - copyrightAttribution: Option[CopyrightAttribution], + copyrightHolder: Option[CopyrightHolder], licenseText: Option[LicenseText], licenseUri: Option[LicenseUri], ) extends TypeSpecificValueInfo with FileValueTypeSpecificInfo @@ -95,7 +95,7 @@ enum TypeSpecificValueInfo { originalFilename: Option[String], originalMimeType: Option[String], externalUrl: String, - copyrightAttribution: Option[CopyrightAttribution], + copyrightHolder: Option[CopyrightHolder], licenseText: Option[LicenseText], licenseUri: Option[LicenseUri], ) extends TypeSpecificValueInfo with FileValueTypeSpecificInfo @@ -107,7 +107,7 @@ enum TypeSpecificValueInfo { dimX: Option[Int], dimY: Option[Int], pageCount: Option[Int], - copyrightAttribution: Option[CopyrightAttribution], + copyrightHolder: Option[CopyrightHolder], licenseText: Option[LicenseText], licenseUri: Option[LicenseUri], ) extends TypeSpecificValueInfo with FileValueTypeSpecificInfo @@ -116,7 +116,7 @@ enum TypeSpecificValueInfo { internalMimeType: String, originalFilename: Option[String], originalMimeType: Option[String], - copyrightAttribution: Option[CopyrightAttribution], + copyrightHolder: Option[CopyrightHolder], licenseText: Option[LicenseText], licenseUri: Option[LicenseUri], ) extends TypeSpecificValueInfo with FileValueTypeSpecificInfo diff --git a/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLive.scala b/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLive.scala index 7e3b9841a6..a51304c56f 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLive.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLive.scala @@ -281,7 +281,7 @@ object ResourcesRepoLive { .andHas(KB.internalMimeType, literalOf(v.internalMimeType)) .andHasOptional(KB.originalFilename, v.originalFilename.map(literalOf)) .andHasOptional(KB.originalMimeType, v.originalMimeType.map(literalOf)) - .andHasOptional(KB.hasCopyrightAttribution, v.copyrightAttribution.map(_.value).map(literalOf)) + .andHasOptional(KB.hasCopyrightHolder, v.copyrightHolder.map(_.value).map(literalOf)) .andHasOptional(KB.hasLicenseText, v.licenseText.map(_.value).map(literalOf)) .andHasOptional(KB.hasLicenseUri, v.licenseUri.map(_.value).map(literalOfType(_, XSD.ANYURI))) diff --git a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/addValueVersion.scala.txt b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/addValueVersion.scala.txt index a06c833012..aff96ad287 100644 --- a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/addValueVersion.scala.txt +++ b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/addValueVersion.scala.txt @@ -234,9 +234,9 @@ DELETE { case None => {} } - @fileValueContentV2.fileValue.copyrightAttribution match { - case Some(copyrightAttribution) => { - <@newValueIri> knora-base:hasCopyrightAttribution """@copyrightAttribution.value""" . + @fileValueContentV2.fileValue.copyrightHolder match { + case Some(copyrightHolder) => { + <@newValueIri> knora-base:hasCopyrightHolder """@copyrightHolder.value""" . } case None => {} } diff --git a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/createValue.scala.txt b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/createValue.scala.txt index 0bb3c327c4..9948c7799e 100644 --- a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/createValue.scala.txt +++ b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/createValue.scala.txt @@ -228,9 +228,9 @@ DELETE { case None => {} } - @fileValueContentV2.fileValue.copyrightAttribution match { - case Some(copyrightAttribution) => { - <@newValueIri> knora-base:hasCopyrightAttribution """@copyrightAttribution.value""" . + @fileValueContentV2.fileValue.copyrightHolder match { + case Some(copyrightHolder) => { + <@newValueIri> knora-base:hasCopyrightHolder """@copyrightHolder.value""" . } case None => {} } From 20a92b162368c64487914a4bc48f8e2816711e91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Tue, 14 Jan 2025 15:18:21 +0100 Subject: [PATCH 05/31] add hasAuthorship and hasLicenseDate --- .../models/filemodels/FileModelUtil.scala | 68 +++--- .../webapi/models/filemodels/FileModels.scala | 12 +- .../responders/v2/ValuesResponderV2Spec.scala | 8 - .../knoraApiOntologySimple.jsonld | 32 ++- .../knoraApiOntologyWithValueObjects.jsonld | 178 ++++++++++++++- .../resources/knora-ontologies/knora-base.ttl | 30 ++- .../webapi/messages/OntologyConstants.scala | 4 + ...aseToApiV2ComplexTransformationRules.scala | 12 +- .../valuemessages/ValueMessagesV2.scala | 208 ++++++++---------- .../domain/model/CopyrightAndLicenses.scala | 2 + .../ApiComplexV2JsonLdRequestParser.scala | 5 + .../slice/common/jena/ResourceOps.scala | 30 +++ .../sparql/v2/addValueVersion.scala.txt | 14 ++ .../queries/sparql/v2/createValue.scala.txt | 14 ++ .../ApiComplexV2JsonLdRequestParserSpec.scala | 57 ++++- .../slice/common/jena/ModelOpsSpec.scala | 2 +- .../slice/common/jena/ResourceOpsSpec.scala | 113 +++++++++- 17 files changed, 623 insertions(+), 166 deletions(-) diff --git a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModelUtil.scala b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModelUtil.scala index d23360d9bf..45fd13ee81 100644 --- a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModelUtil.scala +++ b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModelUtil.scala @@ -11,7 +11,9 @@ import org.knora.webapi.messages.OntologyConstants import org.knora.webapi.messages.SmartIri import org.knora.webapi.messages.StringFormatter import org.knora.webapi.messages.v2.responder.valuemessages.* +import org.knora.webapi.slice.admin.domain.model.Authorship import org.knora.webapi.slice.admin.domain.model.CopyrightHolder +import org.knora.webapi.slice.admin.domain.model.LicenseDate import org.knora.webapi.slice.admin.domain.model.LicenseText import org.knora.webapi.slice.admin.domain.model.LicenseUri import org.knora.webapi.slice.resources.IiifImageRequestUrl @@ -86,10 +88,12 @@ object FileModelUtil { internalMimeType: Option[String], originalFilename: Option[String], originalMimeType: Option[String], - comment: Option[String], - copyrightHolder: Option[CopyrightHolder], - licenseText: Option[LicenseText], - licenseUri: Option[LicenseUri], + copyrightHolder: Option[CopyrightHolder] = None, + authorship: Option[List[Authorship]] = None, + licenseText: Option[LicenseText] = None, + licenseUri: Option[LicenseUri] = None, + licenseDate: Option[LicenseDate] = None, + comment: Option[String] = None, ): FileValueContentV2 = fileType match { case FileType.DocumentFile(pageCount, dimX, dimY) => @@ -100,9 +104,11 @@ object FileModelUtil { internalMimeType = internalMimeType.getOrElse("application/pdf"), originalFilename = originalFilename, originalMimeType = Some(originalMimeType.getOrElse("application/pdf")), - copyrightHolder, - licenseText, - licenseUri, + copyrightHolder = copyrightHolder, + authorship = authorship, + licenseText = licenseText, + licenseUri = licenseUri, + licenseDate = licenseDate, ), pageCount = pageCount, dimX = dimX, @@ -117,9 +123,11 @@ object FileModelUtil { internalMimeType = internalMimeType.getOrElse("image/jp2"), originalFilename = originalFilename, originalMimeType = originalMimeType, - copyrightHolder, - licenseText, - licenseUri, + copyrightHolder = copyrightHolder, + authorship = authorship, + licenseText = licenseText, + licenseUri = licenseUri, + licenseDate = licenseDate, ), dimX = dimX, dimY = dimY, @@ -133,9 +141,11 @@ object FileModelUtil { internalMimeType = internalMimeType.getOrElse("image/jp2"), originalFilename = originalFilename, originalMimeType = originalMimeType, - copyrightHolder, - licenseText, - licenseUri, + copyrightHolder = copyrightHolder, + authorship = authorship, + licenseText = licenseText, + licenseUri = licenseUri, + licenseDate = licenseDate, ), externalUrl = externalUrl, comment = comment, @@ -148,9 +158,11 @@ object FileModelUtil { internalMimeType = internalMimeType.get, originalFilename = originalFilename, originalMimeType = internalMimeType, - copyrightHolder, - licenseText, - licenseUri, + copyrightHolder = copyrightHolder, + authorship = authorship, + licenseText = licenseText, + licenseUri = licenseUri, + licenseDate = licenseDate, ), ) case FileType.TextFile => @@ -161,9 +173,11 @@ object FileModelUtil { internalMimeType = internalMimeType.get, originalFilename = originalFilename, originalMimeType = internalMimeType, - copyrightHolder, - licenseText, - licenseUri, + copyrightHolder = copyrightHolder, + authorship = authorship, + licenseText = licenseText, + licenseUri = licenseUri, + licenseDate = licenseDate, ), ) case FileType.AudioFile => @@ -174,9 +188,11 @@ object FileModelUtil { internalMimeType = internalMimeType.get, originalFilename = originalFilename, originalMimeType = internalMimeType, - copyrightHolder, - licenseText, - licenseUri, + copyrightHolder = copyrightHolder, + authorship = authorship, + licenseText = licenseText, + licenseUri = licenseUri, + licenseDate = licenseDate, ), ) case FileType.ArchiveFile => @@ -187,9 +203,11 @@ object FileModelUtil { internalMimeType = internalMimeType.getOrElse("application/zip"), originalFilename = originalFilename, originalMimeType = internalMimeType, - copyrightHolder, - licenseText, - licenseUri, + copyrightHolder = copyrightHolder, + authorship = authorship, + licenseText = licenseText, + licenseUri = licenseUri, + licenseDate = licenseDate, ), comment = comment, ) diff --git a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala index 621bc586a1..b894e4a49d 100644 --- a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala +++ b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala @@ -15,7 +15,9 @@ import org.knora.webapi.messages.v2.responder.resourcemessages.CreateResourceV2 import org.knora.webapi.messages.v2.responder.resourcemessages.CreateValueInNewResourceV2 import org.knora.webapi.sharedtestdata.SharedTestDataADM import org.knora.webapi.slice.admin.api.model.Project +import org.knora.webapi.slice.admin.domain.model.Authorship import org.knora.webapi.slice.admin.domain.model.CopyrightHolder +import org.knora.webapi.slice.admin.domain.model.LicenseDate import org.knora.webapi.slice.admin.domain.model.LicenseText import org.knora.webapi.slice.admin.domain.model.LicenseUri @@ -116,8 +118,10 @@ sealed abstract case class UploadFileRequest private ( valuePropertyIRI: Option[SmartIri] = None, project: Option[Project] = None, copyrightHolder: Option[CopyrightHolder] = None, + authorship: Option[List[Authorship]] = None, licenseText: Option[LicenseText] = None, licenseUri: Option[LicenseUri] = None, + licenseDate: Option[LicenseDate] = None, ): CreateResourceV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -136,9 +140,11 @@ sealed abstract case class UploadFileRequest private ( originalFilename = originalFilename, originalMimeType = originalMimeType, comment = comment, - copyrightHolder, - licenseText, - licenseUri, + copyrightHolder = copyrightHolder, + authorship = authorship, + licenseText = licenseText, + licenseUri = licenseUri, + licenseDate = licenseDate, ) val values = List( diff --git a/integration/src/test/scala/org/knora/webapi/responders/v2/ValuesResponderV2Spec.scala b/integration/src/test/scala/org/knora/webapi/responders/v2/ValuesResponderV2Spec.scala index 9f4717d4ca..0dfecfc19e 100644 --- a/integration/src/test/scala/org/knora/webapi/responders/v2/ValuesResponderV2Spec.scala +++ b/integration/src/test/scala/org/knora/webapi/responders/v2/ValuesResponderV2Spec.scala @@ -4318,10 +4318,6 @@ class ValuesResponderV2Spec extends CoreSpec with ImplicitSender { Some(mimeTypeJP2), Some("test.tiff"), Some(mimeTypeTIFF), - None, - None, - None, - None, ), ), anythingUser1, @@ -4370,10 +4366,6 @@ class ValuesResponderV2Spec extends CoreSpec with ImplicitSender { Some(internalMimeType), originalFilename, originalMimeType, - None, - None, - None, - None, ), ), anythingUser1, diff --git a/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologySimple.jsonld b/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologySimple.jsonld index 5dad231b04..d375d8225e 100644 --- a/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologySimple.jsonld +++ b/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologySimple.jsonld @@ -903,6 +903,13 @@ "@id": "knora-api:StillImageAbstractFileValue", "@type": "owl:Class", "rdfs:subClassOf": [ + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasAuthorship" + }, + "owl:minCardinality": 0 + }, { "@type": "owl:Restriction", "owl:onProperty": { @@ -910,6 +917,13 @@ }, "owl:maxCardinality": 1 }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasLicenseDate" + }, + "owl:maxCardinality": 1 + }, { "@type": "owl:Restriction", "owl:onProperty": { @@ -1260,6 +1274,14 @@ }, "@id": "knora-api:hasAudioFile" }, + { + "@id": "knora-api:hasAuthorship", + "@type": "owl:DatatypeProperty", + "knora-api:objectType": { + "@id": "xsd:string" + }, + "rdfs:comment": "Credit, Moral Rights, Author(s)" + }, { "rdfs:label": "Color", "rdfs:subPropertyOf": { @@ -1296,7 +1318,7 @@ "knora-api:objectType": { "@id": "xsd:string" }, - "rdfs:comment": "The copyright statement that gives credit to the original author." + "rdfs:comment": "The copyright holder." }, { "rdfs:label": "has 3D-file", @@ -1425,6 +1447,14 @@ }, "@id": "knora-api:hasKeyword" }, + { + "@id": "knora-api:hasLicenseDate", + "@type": "owl:DatatypeProperty", + "knora-api:objectType": { + "@id": "xsd:date" + }, + "rdfs:comment": "Date of creation on platform." + }, { "@id": "knora-api:hasLicenseText", "@type": "owl:DatatypeProperty", diff --git a/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologyWithValueObjects.jsonld b/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologyWithValueObjects.jsonld index 284fcf4c3f..b143605c58 100644 --- a/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologyWithValueObjects.jsonld +++ b/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologyWithValueObjects.jsonld @@ -234,6 +234,14 @@ "owl:cardinality": 1, "knora-api:isInherited": true }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasAuthorship" + }, + "owl:minCardinality": 0, + "knora-api:isInherited": true + }, { "@type": "owl:Restriction", "owl:onProperty": { @@ -242,6 +250,14 @@ "owl:maxCardinality": 1, "knora-api:isInherited": true }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasLicenseDate" + }, + "owl:maxCardinality": 1, + "knora-api:isInherited": true + }, { "@type": "owl:Restriction", "owl:onProperty": { @@ -541,6 +557,14 @@ "owl:cardinality": 1, "knora-api:isInherited": true }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasAuthorship" + }, + "owl:minCardinality": 0, + "knora-api:isInherited": true + }, { "@type": "owl:Restriction", "owl:onProperty": { @@ -549,6 +573,14 @@ "owl:maxCardinality": 1, "knora-api:isInherited": true }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasLicenseDate" + }, + "owl:maxCardinality": 1, + "knora-api:isInherited": true + }, { "@type": "owl:Restriction", "owl:onProperty": { @@ -1363,6 +1395,14 @@ "owl:cardinality": 1, "knora-api:isInherited": true }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasAuthorship" + }, + "owl:minCardinality": 0, + "knora-api:isInherited": true + }, { "@type": "owl:Restriction", "owl:onProperty": { @@ -1371,6 +1411,14 @@ "owl:maxCardinality": 1, "knora-api:isInherited": true }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasLicenseDate" + }, + "owl:maxCardinality": 1, + "knora-api:isInherited": true + }, { "@type": "owl:Restriction", "owl:onProperty": { @@ -2342,6 +2390,14 @@ "owl:cardinality": 1, "knora-api:isInherited": true }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasAuthorship" + }, + "owl:minCardinality": 0, + "knora-api:isInherited": true + }, { "@type": "owl:Restriction", "owl:onProperty": { @@ -2350,6 +2406,14 @@ "owl:maxCardinality": 1, "knora-api:isInherited": true }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasLicenseDate" + }, + "owl:maxCardinality": 1, + "knora-api:isInherited": true + }, { "@type": "owl:Restriction", "owl:onProperty": { @@ -2655,6 +2719,13 @@ }, "owl:cardinality": 1 }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasAuthorship" + }, + "owl:minCardinality": 0 + }, { "@type": "owl:Restriction", "owl:onProperty": { @@ -2662,6 +2733,13 @@ }, "owl:maxCardinality": 1 }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasLicenseDate" + }, + "owl:maxCardinality": 1 + }, { "@type": "owl:Restriction", "owl:onProperty": { @@ -3812,6 +3890,14 @@ "owl:cardinality": 1, "knora-api:isInherited": true }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasAuthorship" + }, + "owl:minCardinality": 0, + "knora-api:isInherited": true + }, { "@type": "owl:Restriction", "owl:onProperty": { @@ -3820,6 +3906,14 @@ "owl:maxCardinality": 1, "knora-api:isInherited": true }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasLicenseDate" + }, + "owl:maxCardinality": 1, + "knora-api:isInherited": true + }, { "@type": "owl:Restriction", "owl:onProperty": { @@ -6066,6 +6160,14 @@ "owl:cardinality": 1, "knora-api:isInherited": true }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasAuthorship" + }, + "owl:minCardinality": 0, + "knora-api:isInherited": true + }, { "@type": "owl:Restriction", "owl:onProperty": { @@ -6074,6 +6176,14 @@ "owl:maxCardinality": 1, "knora-api:isInherited": true }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasLicenseDate" + }, + "owl:maxCardinality": 1, + "knora-api:isInherited": true + }, { "@type": "owl:Restriction", "owl:onProperty": { @@ -6228,6 +6338,14 @@ "owl:cardinality": 1, "knora-api:isInherited": true }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasAuthorship" + }, + "owl:minCardinality": 0, + "knora-api:isInherited": true + }, { "@type": "owl:Restriction", "owl:onProperty": { @@ -6236,6 +6354,14 @@ "owl:maxCardinality": 1, "knora-api:isInherited": true }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasLicenseDate" + }, + "owl:maxCardinality": 1, + "knora-api:isInherited": true + }, { "@type": "owl:Restriction", "owl:onProperty": { @@ -6397,6 +6523,14 @@ "owl:cardinality": 1, "knora-api:isInherited": true }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasAuthorship" + }, + "owl:minCardinality": 0, + "knora-api:isInherited": true + }, { "@type": "owl:Restriction", "owl:onProperty": { @@ -6405,6 +6539,14 @@ "owl:maxCardinality": 1, "knora-api:isInherited": true }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasLicenseDate" + }, + "owl:maxCardinality": 1, + "knora-api:isInherited": true + }, { "@type": "owl:Restriction", "owl:onProperty": { @@ -6731,6 +6873,14 @@ "owl:cardinality": 1, "knora-api:isInherited": true }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasAuthorship" + }, + "owl:minCardinality": 0, + "knora-api:isInherited": true + }, { "@type": "owl:Restriction", "owl:onProperty": { @@ -6739,6 +6889,14 @@ "owl:maxCardinality": 1, "knora-api:isInherited": true }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasLicenseDate" + }, + "owl:maxCardinality": 1, + "knora-api:isInherited": true + }, { "@type": "owl:Restriction", "owl:onProperty": { @@ -8325,6 +8483,15 @@ }, "rdfs:comment": "Connects a Representation to an audio file" }, + { + "rdfs:label": "has authorship", + "rdfs:comment": "Credit, Moral Rights, Author(s)", + "@type": "owl:DatatypeProperty", + "knora-api:objectType": { + "@id": "xsd:string" + }, + "@id": "knora-api:hasAuthorship" + }, { "rdfs:label": "Color", "rdfs:subPropertyOf": { @@ -8368,7 +8535,7 @@ }, { "rdfs:label": "has copyright holder", - "rdfs:comment": "The copyright statement that gives credit to the original author.", + "rdfs:comment": "The copyright holder.", "@type": "owl:DatatypeProperty", "knora-api:objectType": { "@id": "xsd:string" @@ -8533,6 +8700,15 @@ }, "rdfs:comment": "Indicates a keyword of a resource" }, + { + "rdfs:label": "has license date", + "rdfs:comment": "Date of creation on platform.", + "@type": "owl:DatatypeProperty", + "knora-api:objectType": { + "@id": "xsd:date" + }, + "@id": "knora-api:hasLicenseDate" + }, { "rdfs:label": "has license text", "rdfs:comment": "Specifies the terms under which a work can be used. This statement may be a reference to a well-known license, such as Creative Commons (e.g. CC BY-SA) or a custom license.", diff --git a/webapi/src/main/resources/knora-ontologies/knora-base.ttl b/webapi/src/main/resources/knora-ontologies/knora-base.ttl index f7eb939caf..487bbbfe6c 100644 --- a/webapi/src/main/resources/knora-ontologies/knora-base.ttl +++ b/webapi/src/main/resources/knora-ontologies/knora-base.ttl @@ -527,6 +527,7 @@ :subjectClassConstraint :Resource ; :objectClassConstraint :Value . + ### http://www.knora.org/ontology/knora-base#hasCopyrightHolder :hasCopyrightHolder @@ -534,7 +535,16 @@ rdfs:comment "The copyright holder."@en ; :objectDatatypeConstraint xsd:string . -### http://www.knora.org/ontology/knora-base#hasLicense + +### http://www.knora.org/ontology/knora-base#hasAuthorship + +:hasAuthorship + rdf:type owl:DatatypeProperty ; + rdfs:comment "Credit, Moral Rights, Author(s)"@en ; + :objectDatatypeConstraint xsd:string . + + +### http://www.knora.org/ontology/knora-base#hasLicenseText :hasLicenseText rdf:type owl:DatatypeProperty ; @@ -542,11 +552,22 @@ :objectDatatypeConstraint xsd:string . +### http://www.knora.org/ontology/knora-base#hasLicenseUri + :hasLicenseUri rdf:type owl:DatatypeProperty ; rdfs:comment "Canonical link to license."@en ; :objectDatatypeConstraint xsd:anyUri . + +### http://www.knora.org/ontology/knora-base#hasLicenseDate + +:hasLicenseDate + rdf:type owl:DatatypeProperty ; + rdfs:comment "Date of creation on platform."@en ; + :objectDatatypeConstraint xsd:date. + + ### http://www.knora.org/ontology/knora-base#isAnnotationOf :isAnnotationOf @@ -1502,14 +1523,21 @@ [ rdf:type owl:Restriction ; owl:onProperty :hasCopyrightHolder ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ], + [ rdf:type owl:Restriction ; + owl:onProperty :hasAuthorship ; + owl:minCardinality "0"^^xsd:nonNegativeInteger ], [ rdf:type owl:Restriction ; owl:onProperty :hasLicenseText ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ], + [ rdf:type owl:Restriction ; + owl:onProperty :hasLicenseDate; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ], [ rdf:type owl:Restriction ; owl:onProperty :hasLicenseUri ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ]. + ### http://www.knora.org/ontology/knora-base#DecimalBase :DecimalBase diff --git a/webapi/src/main/scala/org/knora/webapi/messages/OntologyConstants.scala b/webapi/src/main/scala/org/knora/webapi/messages/OntologyConstants.scala index 2df37a126f..ac7426b6ed 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/OntologyConstants.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/OntologyConstants.scala @@ -264,8 +264,10 @@ object OntologyConstants { val HasDocumentFileValue: IRI = KnoraBasePrefixExpansion + "hasDocumentFileValue" val HasArchiveFileValue: IRI = KnoraBasePrefixExpansion + "hasArchiveFileValue" val HasCopyrightHolder: IRI = KnoraBasePrefixExpansion + "hasCopyrightHolder" + val HasAuthorship: IRI = KnoraBasePrefixExpansion + "hasAuthorship" val HasLicenseText: IRI = KnoraBasePrefixExpansion + "hasLicenseText" val HasLicenseUri: IRI = KnoraBasePrefixExpansion + "hasLicenseUri" + val HasLicenseDate: IRI = KnoraBasePrefixExpansion + "hasLicenseDate" val ResourceIcon: IRI = KnoraBasePrefixExpansion + "resourceIcon" @@ -627,8 +629,10 @@ object OntologyConstants { val HasStandoffLinkToValue: IRI = KnoraApiV2PrefixExpansion + "hasStandoffLinkToValue" val HasPermissions: IRI = KnoraApiV2PrefixExpansion + "hasPermissions" val HasCopyrightHolder: IRI = KnoraApiV2PrefixExpansion + "hasCopyrightHolder" + val HasAuthorship: IRI = KnoraApiV2PrefixExpansion + "hasAuthorship" val HasLicenseText: IRI = KnoraApiV2PrefixExpansion + "hasLicenseText" val HasLicenseUri: IRI = KnoraApiV2PrefixExpansion + "hasLicenseUri" + val HasLicenseDate: IRI = KnoraApiV2PrefixExpansion + "hasLicenseDate" val UserHasPermission: String = KnoraApiV2PrefixExpansion + "userHasPermission" val CreationDate: IRI = KnoraApiV2PrefixExpansion + "creationDate" val LastModificationDate: IRI = KnoraApiV2PrefixExpansion + "lastModificationDate" diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraBaseToApiV2ComplexTransformationRules.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraBaseToApiV2ComplexTransformationRules.scala index 2b26810180..897f0c96b6 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraBaseToApiV2ComplexTransformationRules.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraBaseToApiV2ComplexTransformationRules.scala @@ -154,7 +154,11 @@ object KnoraBaseToApiV2ComplexTransformationRules extends OntologyTransformation private val HasCopyrightHolder = makeOwlDatatypeProperty(KA.HasCopyrightHolder, XSD.STRING) .withRdfLabelEn("has copyright holder") - .withRdfCommentEn("The copyright statement that gives credit to the original author.") + .withRdfCommentEn("The copyright holder.") + + private val HasAuthorship = makeOwlDatatypeProperty(KA.HasAuthorship, XSD.STRING) + .withRdfLabelEn("has authorship") + .withRdfCommentEn("Credit, Moral Rights, Author(s)") private val HasLicenseText = makeOwlDatatypeProperty(KA.HasLicenseText, XSD.STRING) .withRdfLabelEn("has license text") @@ -166,6 +170,10 @@ object KnoraBaseToApiV2ComplexTransformationRules extends OntologyTransformation .withRdfLabelEn("has license URI") .withRdfCommentEn("Canonical link to license.") + private val HasLicenseDate = makeOwlDatatypeProperty(KA.HasLicenseDate, XSD.DATE) + .withRdfLabelEn("has license date") + .withRdfCommentEn("Date of creation on platform.") + private val ValueAsString = makeOwlDatatypeProperty(KA.ValueAsString, XSD.STRING) .withSubjectType(KA.Value) .withRdfCommentEn("A plain string representation of a value") @@ -660,9 +668,11 @@ object KnoraBaseToApiV2ComplexTransformationRules extends OntologyTransformation GeometryValueAsGeometry, GeonameValueAsGeonameCode, HasCopyrightHolder, + HasAuthorship, HasIncomingLinkValue, HasLicenseText, HasLicenseUri, + HasLicenseDate, IntValueAsInt, IntervalValueHasEnd, IntervalValueHasStart, 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 ff1ec5f2a2..5219dbfd60 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 @@ -6,12 +6,15 @@ package org.knora.webapi.messages.v2.responder.valuemessages import org.apache.jena.rdf.model.Resource +import org.apache.jena.riot.Lang +import org.apache.jena.riot.RDFDataMgr import org.apache.jena.vocabulary.XSD import zio.IO import zio.ZIO import java.net.URI import java.time.Instant +import java.time.LocalDate import java.util.UUID import scala.language.implicitConversions import scala.util.Try @@ -43,13 +46,17 @@ import org.knora.webapi.messages.v2.responder.valuemessages.ValueContentV2.FileI import org.knora.webapi.routing.RouteUtilZ import org.knora.webapi.slice.admin.api.model.MaintenanceRequests.AssetId import org.knora.webapi.slice.admin.api.model.Project +import org.knora.webapi.slice.admin.domain.model.Authorship import org.knora.webapi.slice.admin.domain.model.CopyrightHolder import org.knora.webapi.slice.admin.domain.model.KnoraProject.Shortcode +import org.knora.webapi.slice.admin.domain.model.LicenseDate import org.knora.webapi.slice.admin.domain.model.LicenseText import org.knora.webapi.slice.admin.domain.model.LicenseUri import org.knora.webapi.slice.admin.domain.model.Permission +import org.knora.webapi.slice.common.Value import org.knora.webapi.slice.common.Value.StringValue import org.knora.webapi.slice.common.jena.JenaConversions.given +import org.knora.webapi.slice.common.jena.ResourceOps import org.knora.webapi.slice.common.jena.ResourceOps.* import org.knora.webapi.slice.resourceinfo.domain.InternalIri import org.knora.webapi.slice.resourceinfo.domain.IriConverter @@ -1996,12 +2003,43 @@ object GeonameValueContentV2 { case class FileValueV2( internalFilename: String, internalMimeType: String, - originalFilename: Option[String], - originalMimeType: Option[String], - copyrightHolder: Option[CopyrightHolder], - licenseText: Option[LicenseText], - licenseUri: Option[LicenseUri], + originalFilename: Option[String] = None, + originalMimeType: Option[String] = None, + copyrightHolder: Option[CopyrightHolder] = None, + authorship: Option[List[Authorship]] = None, + licenseText: Option[LicenseText] = None, + licenseUri: Option[LicenseUri] = None, + licenseDate: Option[LicenseDate] = None, ) +object FileValueV2 { + + def from(r: Resource, info: FileInfo): Either[String, FileValueV2] = { + val meta = info.metadata + for { + copyrightHolder <- r.objectStringOption(HasCopyrightHolder, CopyrightHolder.from) + authorship <- r.objectStringListOption(HasAuthorship, Authorship.from) + _ = { + val out = new java.io.ByteArrayOutputStream() + RDFDataMgr.write(out, r.getModel, Lang.TURTLE) + println("FileValueV2.from: model:") + print(out.toString(java.nio.charset.StandardCharsets.UTF_8)) + } + licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) + licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) + licenseDate <- r.objectLocalDateOption(HasLicenseDate).map(_.map(LicenseDate.from)) + } yield FileValueV2( + info.filename, + meta.internalMimeType, + meta.originalFilename, + meta.originalMimeType, + copyrightHolder, + authorship, + licenseText, + licenseUri, + licenseDate, + ) + } +} /** * A trait for case classes representing different types of file values. @@ -2023,7 +2061,15 @@ sealed trait FileValueContentV2 extends ValueContentV2 { } def toJsonLDObjectMapInComplexSchema(fileUrl: String): Map[IRI, JsonLDValue] = { - def mkJsonLdString: StringValue => JsonLDString = sv => JsonLDString(sv.value) + def mkJsonLdString: StringValue => JsonLDString = sv => JsonLDString(sv.value) + def mkJsonLdStringArray: List[StringValue] => JsonLDArray = values => JsonLDArray(values.map(mkJsonLdString)) + def mkJsonLdDate: Value[LocalDate] => JsonLDObject = ld => + JsonLDObject( + Map( + "@type" -> JsonLDString("http://www.w3.org/2001/XMLSchema#date"), + "@value" -> JsonLDString(ld.value.toString), + ), + ) def mkJsonLdUri: StringValue => JsonLDObject = sv => JsonLDObject( Map( @@ -2038,10 +2084,12 @@ sealed trait FileValueContentV2 extends ValueContentV2 { datatype = OntologyConstants.Xsd.Uri.toSmartIri, ), ) - val copyrightOption = fileValue.copyrightHolder.map(mkJsonLdString).map((HasCopyrightHolder, _)) - val licenseTextOption = fileValue.licenseText.map(mkJsonLdString).map((HasLicenseText, _)) - val licenseUriOption = fileValue.licenseUri.map(mkJsonLdUri).map((HasLicenseUri, _)) - knownValues ++ copyrightOption ++ licenseTextOption ++ licenseUriOption + val copyrightHolder = fileValue.copyrightHolder.map(mkJsonLdString).map((HasCopyrightHolder, _)) + val authorship = fileValue.authorship.map(mkJsonLdStringArray).map((HasAuthorship, _)) + val licenseText = fileValue.licenseText.map(mkJsonLdString).map((HasLicenseText, _)) + val licenseUri = fileValue.licenseUri.map(mkJsonLdUri).map((HasLicenseUri, _)) + val licenseDate = fileValue.licenseDate.map(mkJsonLdDate).map((HasLicenseDate, _)) + knownValues ++ copyrightHolder ++ authorship ++ licenseText ++ licenseUri ++ licenseDate } } @@ -2127,20 +2175,9 @@ case class StillImageFileValueContentV2( */ object StillImageFileValueContentV2 { def from(r: Resource, fileInfo: FileInfo): Either[String, StillImageFileValueContentV2] = for { - comment <- objectCommentOption(r) - meta = fileInfo.metadata - copyrightHolder <- r.objectStringOption(HasCopyrightHolder, CopyrightHolder.from) - licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) - licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) - fileValue = FileValueV2( - fileInfo.filename, - meta.internalMimeType, - meta.originalFilename, - meta.originalMimeType, - copyrightHolder, - licenseText, - licenseUri, - ) + comment <- objectCommentOption(r) + meta = fileInfo.metadata + fileValue <- FileValueV2.from(r, fileInfo) } yield StillImageFileValueContentV2( ApiV2Complex, fileValue, @@ -2226,22 +2263,24 @@ case class StillImageExternalFileValueContentV2( * Constructs [[StillImageFileValueContentV2]] objects based on JSON-LD input. */ object StillImageExternalFileValueContentV2 { + private val fakeInfo = FileInfo( + "internalFilename", + FileMetadataSipiResponse( + Some("originalFilename"), + Some("originalMimeType"), + "internalMimeType", + None, + None, + None, + None, + None, + ), + ) def from(r: Resource): Either[String, StillImageExternalFileValueContentV2] = for { - externalUrlStr <- r.objectString(StillImageFileValueHasExternalUrl) - iifUrl <- IiifImageRequestUrl.from(externalUrlStr) - comment <- objectCommentOption(r) - copyrightHolder <- r.objectStringOption(HasCopyrightHolder, CopyrightHolder.from) - licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) - licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) - fileValue = FileValueV2( - "internalFilename", - "internalMimeType", - Some("originalFilename"), - Some("originalMimeType"), - copyrightHolder, - licenseText, - licenseUri, - ) + externalUrlStr <- r.objectString(StillImageFileValueHasExternalUrl) + iifUrl <- IiifImageRequestUrl.from(externalUrlStr) + comment <- objectCommentOption(r) + fileValue <- FileValueV2.from(r, fakeInfo) } yield StillImageExternalFileValueContentV2(ApiV2Complex, fileValue, iifUrl, comment) } @@ -2379,20 +2418,9 @@ case class ArchiveFileValueContentV2( */ object DocumentFileValueContentV2 { def from(r: Resource, info: FileInfo): Either[String, DocumentFileValueContentV2] = for { - comment <- objectCommentOption(r) - meta = info.metadata - copyrightHolder <- r.objectStringOption(HasCopyrightHolder, CopyrightHolder.from) - licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) - licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) - fileValue = FileValueV2( - info.filename, - meta.internalMimeType, - meta.originalFilename, - meta.originalMimeType, - copyrightHolder, - licenseText, - licenseUri, - ) + comment <- objectCommentOption(r) + fileValue <- FileValueV2.from(r, info) + meta = info.metadata } yield DocumentFileValueContentV2(ApiV2Complex, fileValue, meta.numpages, meta.width, meta.height, comment) } @@ -2401,20 +2429,8 @@ object DocumentFileValueContentV2 { */ object ArchiveFileValueContentV2 { def from(r: Resource, info: FileInfo): Either[String, ArchiveFileValueContentV2] = for { - comment <- objectCommentOption(r) - meta = info.metadata - copyrightHolder <- r.objectStringOption(HasCopyrightHolder, CopyrightHolder.from) - licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) - licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) - fileValue = FileValueV2( - info.filename, - meta.internalMimeType, - meta.originalFilename, - meta.originalMimeType, - copyrightHolder, - licenseText, - licenseUri, - ) + comment <- objectCommentOption(r) + fileValue <- FileValueV2.from(r, info) } yield ArchiveFileValueContentV2(ApiV2Complex, fileValue, comment) } @@ -2482,20 +2498,8 @@ case class TextFileValueContentV2( */ object TextFileValueContentV2 { def from(r: Resource, info: FileInfo): Either[String, TextFileValueContentV2] = for { - comment <- objectCommentOption(r) - meta = info.metadata - copyrightHolder <- r.objectStringOption(HasCopyrightHolder, CopyrightHolder.from) - licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) - licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) - fileValue = FileValueV2( - info.filename, - meta.internalMimeType, - meta.originalFilename, - meta.originalMimeType, - copyrightHolder, - licenseText, - licenseUri, - ) + comment <- objectCommentOption(r) + fileValue <- FileValueV2.from(r, info) } yield TextFileValueContentV2(ApiV2Complex, fileValue, comment) } @@ -2563,24 +2567,9 @@ case class AudioFileValueContentV2( */ object AudioFileValueContentV2 { def from(r: Resource, info: FileInfo): Either[String, AudioFileValueContentV2] = for { - comment <- objectCommentOption(r) - meta = info.metadata - copyrightHolder <- r.objectStringOption(HasCopyrightHolder, CopyrightHolder.from) - licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) - licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) - } yield AudioFileValueContentV2( - ApiV2Complex, - FileValueV2( - info.filename, - meta.internalMimeType, - meta.originalFilename, - meta.originalMimeType, - copyrightHolder, - licenseText, - licenseUri, - ), - comment, - ) + comment <- objectCommentOption(r) + fileValue <- FileValueV2.from(r, info) + } yield AudioFileValueContentV2(ApiV2Complex, fileValue, comment) } /** @@ -2649,24 +2638,9 @@ case class MovingImageFileValueContentV2( */ object MovingImageFileValueContentV2 { def from(r: Resource, info: FileInfo): Either[String, MovingImageFileValueContentV2] = for { - comment <- objectCommentOption(r) - meta = info.metadata - copyrightHolder <- r.objectStringOption(HasCopyrightHolder, CopyrightHolder.from) - licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) - licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) - } yield MovingImageFileValueContentV2( - ApiV2Complex, - FileValueV2( - info.filename, - meta.internalMimeType, - meta.originalFilename, - meta.originalMimeType, - copyrightHolder, - licenseText, - licenseUri, - ), - comment, - ) + comment <- objectCommentOption(r) + fileValue <- FileValueV2.from(r, info) + } yield MovingImageFileValueContentV2(ApiV2Complex, fileValue, comment) } /** diff --git a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala index fca08527cc..58c0ae5516 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala @@ -45,8 +45,10 @@ object LicenseUri extends StringValueCompanion[LicenseUri] { final case class LicenseDate private (override val value: LocalDate) extends Value[LocalDate] object LicenseDate extends WithFrom[String, LicenseDate] { + def makeNew: LicenseDate = LicenseDate(LocalDate.now()) def from(str: String): Either[String, LicenseDate] = Try(LocalDate.parse(str)).toEither.left .map(_ => "License Date must be in format 'YYYY-MM-DD'.") .map(LicenseDate.apply) + def from(date: LocalDate): LicenseDate = LicenseDate(date) } 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 index eb52ad9f6f..1c9f2be8e6 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/common/ApiComplexV2JsonLdRequestParser.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/common/ApiComplexV2JsonLdRequestParser.scala @@ -379,6 +379,11 @@ final case class ApiComplexV2JsonLdRequestParser( ZIO.scoped { for { r <- RootResource.fromJsonLd(str) + _ <- Console.printLine("RootResource: " + r.resource).orDie + _ <- r.resource.getModel.printTurtle + _ <- Console.printLine("Model:\n").orDie + _ <- ModelOps.fromJsonLd(str).flatMap(_.printTurtle) + _ <- Console.printLine("jsonLd:\n" + str).orDie resourceIri <- r.resourceIriOrFail v <- ValueResource.from(r) valueUuid <- v.valueHasUuidOption diff --git a/webapi/src/main/scala/org/knora/webapi/slice/common/jena/ResourceOps.scala b/webapi/src/main/scala/org/knora/webapi/slice/common/jena/ResourceOps.scala index d25d9b9d45..206526c7c2 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/common/jena/ResourceOps.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/common/jena/ResourceOps.scala @@ -11,9 +11,13 @@ import org.apache.jena.rdf.model.Property import org.apache.jena.rdf.model.Resource import org.apache.jena.rdf.model.Statement import org.apache.jena.vocabulary.RDF +import org.apache.jena.vocabulary.XSD import java.time.Instant +import java.time.LocalDate import java.util.UUID +import scala.jdk.CollectionConverters.IteratorHasAsScala +import scala.util.Try import org.knora.webapi.slice.common.jena.StatementOps.* @@ -24,6 +28,8 @@ object ResourceOps { def statement(p: Property): Either[String, Statement] = statementOption(p).toRight(s"Required property not found ${p.getURI}") + def statements(p: Property): List[Statement] = res.listProperties(p).asScala.toList + private inline def fromStatement[A](p: Property, f: Statement => Either[String, A]): Either[String, Option[A]] = statementOption(p) match case Some(stmt) => f.apply(stmt).map(Some(_)) @@ -45,19 +51,43 @@ object ResourceOps { statement(p).flatMap(_.objectAsString) def objectString[A](p: Property, mapper: String => Either[String, A]): Either[String, A] = objectString(p).flatMap(mapper) + def objectStringOption(p: Property): Either[String, Option[String]] = fromStatement(p, _.objectAsString) def objectStringOption[A](p: Property, mapper: String => Either[String, A]): Either[String, Option[A]] = objectStringOption(p).flatMap(_.traverse(mapper)) + def objectStringList(p: Property): Either[String, List[String]] = + statements(p) match + case Nil => Left(s"Required property $p not found") + case stmts => stmts.traverse(_.objectAsString) + def objectStringList[A](p: Property, mapper: String => Either[String, A]): Either[String, List[A]] = + objectStringList(p).flatMap(_.traverse(mapper)) + + def objectStringListOption(p: Property): Either[String, Option[List[String]]] = + statements(p) match + case Nil => Right(None) + case stmts => stmts.traverse(_.objectAsString).map(Some(_)) + def objectStringListOption[A](p: Property, mapper: String => Either[String, A]): Either[String, Option[List[A]]] = + objectStringListOption(p).flatMap(_.traverse(_.traverse(mapper))) + def objectUri(p: Property): Either[String, String] = statement(p).flatMap(stmt => stmt.objectAsUri) def objectUriOption(p: Property): Either[String, Option[String]] = fromStatement(p, _.objectAsUri) def objectUuid(p: Property): Either[String, UUID] = statement(p).flatMap(stmt => stmt.objectAsUuid) def objectUuidOption(p: Property): Either[String, Option[UUID]] = fromStatement(p, _.objectAsUuid) + def objectLocalDate(p: Property): Either[String, LocalDate] = + objectDataType(p, XSD.date.toString, str => Try(LocalDate.parse(str)).toEither.left.map(_.getMessage)) + def objectLocalDateOption(p: Property): Either[String, Option[LocalDate]] = + objectDataTypeOption(p, XSD.date.toString, str => Try(LocalDate.parse(str)).toEither.left.map(_.getMessage)) + def objectDataType(p: Property, dt: String): Either[String, String] = statement(p).flatMap(stmt => stmt.objectAsDataType(dt)) + + def objectDataType[A](p: Property, dt: String, mapper: String => Either[String, A]): Either[String, A] = + statement(p).flatMap(stmt => stmt.objectAsDataType(dt)).flatMap(mapper) + def objectDataTypeOption(p: Property, dt: String): Either[String, Option[String]] = fromStatement(p, _.objectAsDataType(dt)) def objectDataTypeOption[A](p: Property, dt: String, f: String => Either[String, A]): Either[String, Option[A]] = diff --git a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/addValueVersion.scala.txt b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/addValueVersion.scala.txt index aff96ad287..45ecbd3219 100644 --- a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/addValueVersion.scala.txt +++ b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/addValueVersion.scala.txt @@ -241,6 +241,13 @@ DELETE { case None => {} } + @fileValueContentV2.fileValue.authorship match { + case Some(authors) if authors.nonEmpty => { + <@newValueIri> knora-base:hasAuthorship @authors.map(_.value).mkString("\"", ",", "\"") . + } + case None => {} + } + @fileValueContentV2.fileValue.licenseText match { case Some(text) => { <@newValueIri> knora-base:hasLicenseText """@text.value""" . @@ -255,6 +262,13 @@ DELETE { case None => {} } + @fileValueContentV2.fileValue.licenseDate match { + case Some(date) => { + <@newValueIri> knora-base:hasLicenseDate """@date.value.toString"""^^xsd:date . + } + case None => {} + } + @fileValueContentV2 match { case stillImageFileValue: StillImageFileValueContentV2 => { <@newValueIri> knora-base:dimX @stillImageFileValue.dimX ; diff --git a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/createValue.scala.txt b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/createValue.scala.txt index 9948c7799e..635e740805 100644 --- a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/createValue.scala.txt +++ b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/createValue.scala.txt @@ -235,6 +235,13 @@ DELETE { case None => {} } + @fileValueContentV2.fileValue.authorship match { + case Some(authors) if authors.nonEmpty => { + <@newValueIri> knora-base:hasAuthorship @authors.map(_.value).mkString("\"", ",", "\"") . + } + case None => {} + } + @fileValueContentV2.fileValue.licenseText match { case Some(text) => { <@newValueIri> knora-base:hasLicenseText """@text.value""" . @@ -249,6 +256,13 @@ DELETE { case None => {} } + @fileValueContentV2.fileValue.licenseDate match { + case Some(date) => { + <@newValueIri> knora-base:hasLicenseDate """@date.value.toString"""^^xsd:date . + } + case None => {} + } + @fileValueContentV2 match { case stillImageFileValue: StillImageFileValueContentV2 => { <@newValueIri> knora-base:dimX @stillImageFileValue.dimX ; 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 index 90ad1b5f2a..071b7c72cb 100644 --- a/webapi/src/test/scala/org/knora/webapi/slice/common/ApiComplexV2JsonLdRequestParserSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/slice/common/ApiComplexV2JsonLdRequestParserSpec.scala @@ -12,6 +12,7 @@ import zio.json.ast.Json import zio.test.* import java.time.Instant +import java.time.LocalDate import org.knora.webapi.ApiV2Complex import org.knora.webapi.config.AppConfig @@ -81,9 +82,6 @@ object ApiComplexV2JsonLdRequestParserSpec extends ZIOSpecDefault { "internalMimeType", Some("originalFilename.orig"), Some("originalMimeType"), - None, - None, - None, ) private val configureSipiServiceMock = for { @@ -443,6 +441,56 @@ object ApiComplexV2JsonLdRequestParserSpec extends ZIOSpecDefault { ), ) }, + test("should parse StillImageFileValue with copyright and license information") { + 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", + | "ka:hasCopyrightHolder" : "Jane Doe", + | "ka:hasAuthorship" : [ "Mr. Smith", "Author McAuthorface" ], + | "ka:hasLicenseText" : "CC-BY-4.0", + | "ka:hasLicenseDate" : { + | "@type" : "xsd:date", + | "@value" : "1999-12-24" + | }, + | "ka:hasLicenseUri" : { + | "@value" : "http://creativecommons.org/licenses/by/4.0/", + | "@type" : "xsd:anyURI" + | } + | }, + | "@context": { + | "ka": "http://api.knora.org/ontology/knora-api/v2#", + | "ex": "http://0.0.0.0:3333/ontology/0001/anything/v2#", + | "xsd": "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin, + ), + ) + } yield assertTrue( + actual.valueContent == StillImageFileValueContentV2( + ApiV2Complex, + expectedFileValue.copy( + copyrightHolder = Some(CopyrightHolder.unsafeFrom("Jane Doe")), + authorship = Some(List(Authorship.unsafeFrom("Author McAuthorface"), Authorship.unsafeFrom("Mr. Smith"))), + licenseText = Some(LicenseText.unsafeFrom("CC-BY-4.0")), + licenseUri = Some(LicenseUri.unsafeFrom("http://creativecommons.org/licenses/by/4.0/")), + licenseDate = Some(LicenseDate.unsafeFrom("1999-12-24")), + ), + 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) @@ -474,9 +522,6 @@ object ApiComplexV2JsonLdRequestParserSpec extends ZIOSpecDefault { "internalMimeType", Some("originalFilename"), Some("originalMimeType"), - None, - None, - None, ), IiifImageRequestUrl.unsafeFrom("http://www.example.org/prefix1/abcd1234/full/0/native.jpg"), None, diff --git a/webapi/src/test/scala/org/knora/webapi/slice/common/jena/ModelOpsSpec.scala b/webapi/src/test/scala/org/knora/webapi/slice/common/jena/ModelOpsSpec.scala index 01b6ea0918..a7631476a1 100644 --- a/webapi/src/test/scala/org/knora/webapi/slice/common/jena/ModelOpsSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/slice/common/jena/ModelOpsSpec.scala @@ -8,7 +8,7 @@ package org.knora.webapi.slice.common.jena import zio.* import zio.test.* -import org.knora.webapi.slice.common.jena.ModelOps.singleRootResource +import org.knora.webapi.slice.common.jena.ModelOps.* object ModelOpsSpec extends ZIOSpecDefault { diff --git a/webapi/src/test/scala/org/knora/webapi/slice/common/jena/ResourceOpsSpec.scala b/webapi/src/test/scala/org/knora/webapi/slice/common/jena/ResourceOpsSpec.scala index 42b592fd91..246924a5b8 100644 --- a/webapi/src/test/scala/org/knora/webapi/slice/common/jena/ResourceOpsSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/slice/common/jena/ResourceOpsSpec.scala @@ -9,6 +9,7 @@ import zio.* import zio.test.* import java.time.Instant +import java.time.LocalDate import scala.language.implicitConversions import org.knora.webapi.slice.common @@ -30,7 +31,10 @@ object ResourceOpsSpec extends ZIOSpecDefault { | ex:str "Foo" ; | ex:decimal "42.0"^^xsd:decimal ; | ex:bool "true"^^xsd:boolean ; - | ex:dts "1879-03-14T00:00:00Z"^^xsd:dateTimeStamp . + | ex:dts "1879-03-14T00:00:00Z"^^xsd:dateTimeStamp ; + | ex:date "1879-03-14"^^xsd:date ; + | ex:list "Foo" ; + | ex:list "Bar" . |""".stripMargin private def resource() = @@ -271,6 +275,109 @@ object ResourceOpsSpec extends ZIOSpecDefault { ), ) + private val objectStringListSuite = suite("Getting List of String values of Objects")( + suite("objectStringList")( + suite("without mapper")( + test("should succeed if value is present") { + for { + res <- resource() + actual = res.objectStringList("https://example.com/test#list") + } yield assert(actual.getOrElse(List.empty))(Assertion.hasSameElements(List("Foo", "Bar"))) + }, + test("should fail if property is not present ") { + for { + res <- resource() + actual = res.objectStringList("https://example.com/test#notPresent") + } yield assertTrue(actual == Left("Required property https://example.com/test#notPresent not found")) + }, + ), + suite("with mapper")( + test("should succeed if value is present and mapper succeeds") { + for { + res <- resource() + actual = res.objectStringList("https://example.com/test#list", s => Right(s.toUpperCase)) + } yield assert(actual.getOrElse(List.empty))(Assertion.hasSameElements(List("FOO", "BAR"))) + }, + test("should fail if value is present and mapper fails") { + for { + res <- resource() + actual = res.objectStringList("https://example.com/test#list", _ => Left("Error")) + } yield assertTrue(actual == Left("Error")) + }, + ), + ), + suite("objectStringListOption")( + suite("without mapper")( + test("should succeed if value is present") { + for { + res <- resource() + actual = res.objectStringListOption("https://example.com/test#list") + } yield assert(actual.map(_.getOrElse(List.empty)).getOrElse(List.empty))( + Assertion.hasSameElements(List("Foo", "Bar")), + ) + }, + test("should succeed if property is not present ") { + for { + res <- resource() + actual = res.objectStringListOption("https://example.com/test#notPresent") + } yield assertTrue(actual == Right(None)) + }, + ), + suite("with mapper")( + test("should succeed if value is present and mapper succeeds") { + for { + res <- resource() + actual = res.objectStringListOption("https://example.com/test#list", s => Right(s.toUpperCase)) + } yield assert(actual.map(_.getOrElse(List.empty)).getOrElse(List.empty))( + Assertion.hasSameElements(List("FOO", "BAR")), + ) + }, + test("should succeed if value is present and mapper succeeds") { + for { + res <- resource() + actual = res.objectStringListOption("https://example.com/test#list", _ => Left("Error")) + } yield assertTrue(actual == Left("Error")) + }, + ), + ), + ) + private val objectDataTypeOptionSuite = suite("objectDataTypeOption")( + suite("date")( + test("should return a date") { + for { + res <- resource() + actual = res.objectDataTypeOption("https://example.com/test#date", "http://www.w3.org/2001/XMLSchema#date") + } yield assertTrue(actual == Right(Some("1879-03-14"))) + }, + test("should fail if it is not a date") { + for { + res <- resource() + actual = res.objectDataTypeOption("https://example.com/test#str", "http://www.w3.org/2001/XMLSchema#date") + } yield assertTrue( + actual == Left( + "Invalid datatype for property https://example.com/test#str, http://www.w3.org/2001/XMLSchema#date expected", + ), + ) + }, + test("objectLocalDateOption should return a date") { + for { + res <- resource() + actual = res.objectLocalDateOption("https://example.com/test#date") + } yield assertTrue(actual == Right(Some(LocalDate.parse("1879-03-14")))) + }, + test("objectLocalDateOption should fail if it is not a date") { + for { + res <- resource() + actual = res.objectLocalDateOption("https://example.com/test#str") + } yield assertTrue( + actual == Left( + "Invalid datatype for property https://example.com/test#str, http://www.w3.org/2001/XMLSchema#date expected", + ), + ) + }, + ), + ) + private val rdfTypeTest = test("rdfsType should get the type") { for { res <- resource() @@ -278,12 +385,14 @@ object ResourceOpsSpec extends ZIOSpecDefault { } yield assertTrue(actual.contains("https://example.com/test#Thing")) } - val spec = suite("ResourceOps")( + val spec: Spec[Scope, String] = suite("ResourceOps")( objectBigDecimalSuite, objectBooleanSuite, objectIntSuite, objectInstantSuite, objectStringSuite, + objectStringListSuite, + objectDataTypeOptionSuite, rdfTypeTest, ) } From fc95a2ce73896b439813258b6261df08ca8aa1e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Tue, 14 Jan 2025 15:18:55 +0100 Subject: [PATCH 06/31] update knora-base version --- webapi/src/main/resources/knora-ontologies/knora-base.ttl | 2 +- webapi/src/main/scala/org/knora/webapi/package.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/webapi/src/main/resources/knora-ontologies/knora-base.ttl b/webapi/src/main/resources/knora-ontologies/knora-base.ttl index 487bbbfe6c..eb91509952 100644 --- a/webapi/src/main/resources/knora-ontologies/knora-base.ttl +++ b/webapi/src/main/resources/knora-ontologies/knora-base.ttl @@ -20,7 +20,7 @@ rdf:type owl:Ontology ; rdfs:label "The Knora base ontology"@en ; :attachedToProject knora-admin:SystemProject ; - :ontologyVersion "knora-base v45" . + :ontologyVersion "knora-base v46" . ################################################################# diff --git a/webapi/src/main/scala/org/knora/webapi/package.scala b/webapi/src/main/scala/org/knora/webapi/package.scala index c2030592a8..e7697e62ef 100644 --- a/webapi/src/main/scala/org/knora/webapi/package.scala +++ b/webapi/src/main/scala/org/knora/webapi/package.scala @@ -14,7 +14,7 @@ package object webapi { * The version of `knora-base` and of the other built-in ontologies that this version of Knora requires. * Must be the same as the object of `knora-base:ontologyVersion` in the `knora-base` ontology being used. */ - val KnoraBaseVersion: Int = 45 + val KnoraBaseVersion: Int = 46 val KnoraBaseVersionString: String = s"$versionPrefix$KnoraBaseVersion" /** From 378290df6c557e0451664419b35388982b3500c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Tue, 14 Jan 2025 16:11:29 +0100 Subject: [PATCH 07/31] add ResourcesResponderV2 test for copyright and licenses infos --- .../webapi/models/filemodels/FileModels.scala | 30 +++++++----- .../v2/ResourcesResponderV2Spec.scala | 48 ++++++++++++++++++- 2 files changed, 66 insertions(+), 12 deletions(-) diff --git a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala index b894e4a49d..545d4f778a 100644 --- a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala +++ b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala @@ -27,9 +27,11 @@ sealed abstract case class UploadFileRequest private ( label: String, resourceIRI: Option[String] = None, copyrightHolder: Option[CopyrightHolder] = None, + authorship: Option[List[Authorship]] = None, licenseText: Option[LicenseText] = None, licenseUri: Option[LicenseUri] = None, -) { + licenseDate: Option[LicenseDate] = None, +) { self => /** * Create a JSON-LD serialization of the request. This can be used for e2e and integration tests. @@ -64,10 +66,17 @@ sealed abstract case class UploadFileRequest private ( | "@type" : "$fileValueType", | "knora-api:fileValueHasFilename" : "$internalFilename" | ${copyrightHolder.map(ca => s""","knora-api:hasCopyrightHolder" : "${ca.value}"""").getOrElse("")} + | ${authorship + .filter(_.nonEmpty) + .map(a => s""","knora-api:hasAuthorship" : [ ${a.map(_.value).mkString("\"", ",", " \"")} ]""") + .getOrElse("")} | ${licenseText.map(l => s""","knora-api:hasLicenseText" : "${l.value}"""").getOrElse("")} | ${licenseUri .map(u => s""", "knora-api:hasLicenseUri" : { "@type" : "xsd:anyURI", "@value":"${u.value}" }""") .getOrElse("")} + | ${licenseDate + .map(d => s""", "knora-api:hasLicenseDate" : { "@type" : "xsd:date", "@value":"${d.value.toString}" }""") + .getOrElse("")} | }, | "knora-api:attachedToProject" : { | "@id" : "http://rdfh.ch/projects/$shortcode" @@ -117,11 +126,6 @@ sealed abstract case class UploadFileRequest private ( resourceClassIRI: Option[SmartIri] = None, valuePropertyIRI: Option[SmartIri] = None, project: Option[Project] = None, - copyrightHolder: Option[CopyrightHolder] = None, - authorship: Option[List[Authorship]] = None, - licenseText: Option[LicenseText] = None, - licenseUri: Option[LicenseUri] = None, - licenseDate: Option[LicenseDate] = None, ): CreateResourceV2 = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -140,11 +144,11 @@ sealed abstract case class UploadFileRequest private ( originalFilename = originalFilename, originalMimeType = originalMimeType, comment = comment, - copyrightHolder = copyrightHolder, - authorship = authorship, - licenseText = licenseText, - licenseUri = licenseUri, - licenseDate = licenseDate, + copyrightHolder = self.copyrightHolder, + authorship = self.authorship, + licenseText = self.licenseText, + licenseUri = self.licenseUri, + licenseDate = self.licenseDate, ) val values = List( @@ -196,8 +200,10 @@ object UploadFileRequest { label: String = "test label", resourceIRI: Option[String] = None, copyrightHolder: Option[CopyrightHolder] = None, + authorship: Option[List[Authorship]] = None, licenseText: Option[LicenseText] = None, licenseUri: Option[LicenseUri] = None, + licenseDate: Option[LicenseDate] = None, ): UploadFileRequest = new UploadFileRequest( fileType, @@ -205,8 +211,10 @@ object UploadFileRequest { label, resourceIRI, copyrightHolder, + authorship, licenseText, licenseUri, + licenseDate, ) {} } diff --git a/integration/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala b/integration/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala index f7ce9dbeaf..b161e27e12 100644 --- a/integration/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala +++ b/integration/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala @@ -40,7 +40,7 @@ import org.knora.webapi.models.filemodels.* import org.knora.webapi.responders.v2.ResourcesResponseCheckerV2.compareReadResourcesSequenceV2Response import org.knora.webapi.routing.UnsafeZioRun import org.knora.webapi.sharedtestdata.SharedTestDataADM -import org.knora.webapi.slice.admin.domain.model.User +import org.knora.webapi.slice.admin.domain.model.* import org.knora.webapi.slice.resources.IiifImageRequestUrl import org.knora.webapi.store.triplestore.api.TriplestoreService import org.knora.webapi.store.triplestore.api.TriplestoreService.Queries.Ask @@ -1144,6 +1144,52 @@ class ResourcesResponderV2Spec extends CoreSpec with ImplicitSender { ) } + "create a still image file value with copyright and license information" in { + val resourceIri: IRI = stringFormatter.makeRandomResourceIri(SharedTestDataADM.anythingProject.shortcode) + + val copyrightHolder = CopyrightHolder.unsafeFrom("The University of Basel") + val authorship = List("Hans Meier", "Peter Müller").map(Authorship.unsafeFrom) + val licenseText = LicenseText.unsafeFrom("CC BY-SA 4.0") + val licenseUri = LicenseUri.unsafeFrom("https://creativecommons.org/licenses/by-sa/4.0/") + val licenseDate = LicenseDate.unsafeFrom("2020-01-01") + + val inputResource = UploadFileRequest + .make( + fileType = FileType.StillImageFile( + dimX = 512, + dimY = 256, + ), + internalFilename = "bar.jp2", + copyrightHolder = Some(copyrightHolder), + authorship = Some(authorship), + licenseText = Some(licenseText), + licenseUri = Some(licenseUri), + licenseDate = Some(licenseDate), + ) + .toMessage(resourceIri = Some(resourceIri)) + + val _ = UnsafeZioRun.runOrThrow( + resourcesResponderV2( + _.createResource(CreateResourceRequestV2(inputResource, anythingUserProfile, UUID.randomUUID)).logError, + ), + ) + + val actual: ReadResourceV2 = getResource(resourceIri) + val fileValue: FileValueV2 = + actual.values.head._2 + .map(_.valueContent) + .collect { case sifvc: StillImageFileValueContentV2 => sifvc } + .map(_.fileValue) + .head + + assert(fileValue.copyrightHolder.contains(copyrightHolder)) + assert(fileValue.licenseText.contains(licenseText)) + assert(fileValue.licenseUri.contains(licenseUri)) + // not (yet) working: + // assert(fileValue.licenseDate.contains(licenseDate)) + // assert(fileValue.authorship.contains(authorship)) + } + "create a resource with an external still image file value" in { val resourceIri: IRI = stringFormatter.makeRandomResourceIri(SharedTestDataADM.anythingProject.shortcode) From b1ea5cdb02eab3f8dd5e5a679554f8bd4aeec7ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Tue, 14 Jan 2025 17:09:52 +0100 Subject: [PATCH 08/31] add date writing and reading --- .../v2/ResourcesResponderV2Spec.scala | 4 +- .../webapi/messages/OntologyConstants.scala | 1 + .../webapi/messages/ValuesValidator.scala | 4 ++ .../TriplestoreMessages.scala | 58 +++++++++++-------- .../util/ConstructResponseUtilV2.scala | 22 ++++++- .../resources/CreateResourceV2Handler.scala | 14 +++++ .../slice/common/repo/rdf/Vocabulary.scala | 1 + .../repo/model/ResourceCreateModels.scala | 13 ++++- .../repo/service/ResourcesRepoLive.scala | 1 + .../repo/service/ResourcesRepoLiveSpec.scala | 8 +++ 10 files changed, 97 insertions(+), 29 deletions(-) diff --git a/integration/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala b/integration/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala index b161e27e12..a946fb0919 100644 --- a/integration/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala +++ b/integration/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala @@ -1185,8 +1185,8 @@ class ResourcesResponderV2Spec extends CoreSpec with ImplicitSender { assert(fileValue.copyrightHolder.contains(copyrightHolder)) assert(fileValue.licenseText.contains(licenseText)) assert(fileValue.licenseUri.contains(licenseUri)) - // not (yet) working: - // assert(fileValue.licenseDate.contains(licenseDate)) + assert(fileValue.licenseDate.contains(licenseDate)) + // TODO: not (yet) working: // assert(fileValue.authorship.contains(authorship)) } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/OntologyConstants.scala b/webapi/src/main/scala/org/knora/webapi/messages/OntologyConstants.scala index ac7426b6ed..8e87bca259 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/OntologyConstants.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/OntologyConstants.scala @@ -115,6 +115,7 @@ object OntologyConstants { val Uri: IRI = XsdPrefixExpansion + "anyURI" val Pattern: IRI = XsdPrefixExpansion + "pattern" val DateTime: IRI = XsdPrefixExpansion + "dateTime" + val Date: IRI = XsdPrefixExpansion + "date" val DateTimeStamp: IRI = XsdPrefixExpansion + "dateTimeStamp" } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/ValuesValidator.scala b/webapi/src/main/scala/org/knora/webapi/messages/ValuesValidator.scala index 3e16c2c0fd..6ff8211248 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/ValuesValidator.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/ValuesValidator.scala @@ -8,6 +8,7 @@ package org.knora.webapi.messages import spray.json.JsonParser import java.time.Instant +import java.time.LocalDate import java.time.OffsetDateTime import java.time.ZoneOffset import java.time.format.DateTimeFormatter @@ -86,6 +87,9 @@ object ValuesValidator { s"Invalid xsd:dateTimeStamp value '$s': ${e.getMessage}", ) + def xsdDateToLocalDate(s: String): Option[LocalDate] = + Try(LocalDate.parse(s)).toOption + /** * Parses a DSP ARK timestamp. * diff --git a/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala b/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala index 66b378e50d..39a0d79ca8 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala @@ -15,7 +15,6 @@ import zio.json.JsonCodec import java.time.Instant import scala.collection.mutable - import dsp.errors.* import org.knora.webapi.* import org.knora.webapi.messages.* @@ -23,6 +22,9 @@ import org.knora.webapi.messages.IriConversions.* import org.knora.webapi.messages.util.ErrorHandlingMap import org.knora.webapi.messages.util.rdf.* +import java.time.LocalDate +import scala.reflect.ClassTag + /** * A response to a [[org.knora.webapi.store.triplestore.api.TriplestoreService.Queries.Construct]] query. * @@ -101,6 +103,15 @@ object SparqlExtendedConstructResponse { ), ) + case OntologyConstants.Xsd.Date => + DateLiteralV2( + ValuesValidator + .xsdDateToLocalDate(datatypeLiteral.value) + .getOrElse( + throw InconsistentRepositoryDataException(s"Invalid xsd:date: ${datatypeLiteral.value}"), + ), + ) + case OntologyConstants.Xsd.Boolean => BooleanLiteralV2( datatypeLiteral.booleanValue( @@ -139,7 +150,7 @@ object SparqlExtendedConstructResponse { SparqlExtendedConstructResponse(statementMap.toMap) }.foldZIO( - _ => ZIO.fail(DataConversionException("Couldn't parse Turtle document")), + err => ZIO.fail(DataConversionException(s"Couldn't parse Turtle document ${err.getMessage}")), ZIO.succeed(_), ) } @@ -190,7 +201,7 @@ case class BlankNodeSubjectV2(value: String) extends SubjectV2 { * Represents a literal read from the triplestore. There are different subclasses * representing literals with the extended type information stored in the triplestore. */ -sealed trait LiteralV2 { +sealed trait LiteralV2 { self => /** * Returns this [[LiteralV2]] as an [[IriLiteralV2]]. @@ -200,10 +211,7 @@ sealed trait LiteralV2 { * @return an [[IriLiteralV2]]. */ def asIriLiteral(errorFun: => Nothing): IriLiteralV2 = - this match { - case iriLiteral: IriLiteralV2 => iriLiteral - case _ => errorFun - } + as[IriLiteralV2]().getOrElse(errorFun) /** * Returns this [[LiteralV2]] as a [[StringLiteralV2]]. @@ -213,10 +221,7 @@ sealed trait LiteralV2 { * @return a [[StringLiteralV2]]. */ def asStringLiteral(errorFun: => Nothing): StringLiteralV2 = - this match { - case stringLiteral: StringLiteralV2 => stringLiteral - case _ => errorFun - } + as[StringLiteralV2]().getOrElse(errorFun) /** * Returns this [[LiteralV2]] as a [[BooleanLiteralV2]]. @@ -226,10 +231,7 @@ sealed trait LiteralV2 { * @return a [[BooleanLiteralV2]]. */ def asBooleanLiteral(errorFun: => Nothing): BooleanLiteralV2 = - this match { - case booleanLiteral: BooleanLiteralV2 => booleanLiteral - case _ => errorFun - } + as[BooleanLiteralV2]().getOrElse(errorFun) /** * Returns this [[LiteralV2]] as an [[IntLiteralV2]]. @@ -239,10 +241,7 @@ sealed trait LiteralV2 { * @return an [[IntLiteralV2]]. */ def asIntLiteral(errorFun: => Nothing): IntLiteralV2 = - this match { - case intLiteral: IntLiteralV2 => intLiteral - case _ => errorFun - } + as[IntLiteralV2]().getOrElse(errorFun) /** * Returns this [[LiteralV2]] as a [[DecimalLiteralV2]]. @@ -252,10 +251,7 @@ sealed trait LiteralV2 { * @return a [[DecimalLiteralV2]]. */ def asDecimalLiteral(errorFun: => Nothing): DecimalLiteralV2 = - this match { - case decimalLiteral: DecimalLiteralV2 => decimalLiteral - case _ => errorFun - } + as[DecimalLiteralV2]().getOrElse(errorFun) /** * Returns this [[LiteralV2]] as a [[DateTimeLiteralV2]]. @@ -265,9 +261,12 @@ sealed trait LiteralV2 { * @return a [[DateTimeLiteralV2]]. */ def asDateTimeLiteral(errorFun: => Nothing): DateTimeLiteralV2 = + as[DateTimeLiteralV2]().getOrElse(errorFun) + + def as[A <: LiteralV2]()(implicit tag: ClassTag[A]): Option[A] = this match { - case dateTimeLiteral: DateTimeLiteralV2 => dateTimeLiteral - case _ => errorFun + case a: A => Some(a) + case _ => None } } @@ -450,6 +449,15 @@ case class DateTimeLiteralV2(value: Instant) extends LiteralV2 { override def toString: String = value.toString } +/** + * Represents a local date. + * + * @param value the date. + */ +case class DateLiteralV2(value: LocalDate) extends LiteralV2 { + override def toString: String = value.toString +} + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // JSON formatting diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala index b05fa78d5a..06f99288b3 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala @@ -9,7 +9,6 @@ import zio.* import java.time.Instant import java.util.UUID - import dsp.errors.BadRequestException import dsp.errors.InconsistentRepositoryDataException import dsp.errors.NotFoundException @@ -50,6 +49,7 @@ import org.knora.webapi.messages.v2.responder.standoffmessages.MappingXMLtoStand import org.knora.webapi.messages.v2.responder.valuemessages.* import org.knora.webapi.slice.admin.domain.model.CopyrightHolder import org.knora.webapi.slice.admin.domain.model.KnoraProject.ProjectIri +import org.knora.webapi.slice.admin.domain.model.LicenseDate import org.knora.webapi.slice.admin.domain.model.LicenseText import org.knora.webapi.slice.admin.domain.model.LicenseUri import org.knora.webapi.slice.admin.domain.model.ListProperties.ListIri @@ -62,6 +62,8 @@ import org.knora.webapi.slice.resources.IiifImageRequestUrl import org.knora.webapi.store.iiif.errors.SipiException import org.knora.webapi.util.ZioHelper +import java.time.LocalDate + trait ConstructResponseUtilV2 { /** @@ -335,6 +337,22 @@ object ConstructResponseUtilV2 { .value } + /** + * Returns the optional timestamp object of the specified predicate. Throws an exception if the object is not a timestamp. + * + * @param predicateIri the predicate. + * @return the timestamp object of the predicate. + */ + def maybeDateObject(predicateIri: SmartIri): Option[LocalDate] = + assertions.get(predicateIri).map { literal => + literal + .as[DateLiteralV2]() + .map(_.value) + .getOrElse( + throw InconsistentRepositoryDataException(s"Unexpected object of $subjectIri $predicateIri: $literal"), + ) + } + /** * Returns the required timestamp object of the specified predicate. Throws an exception if the object is not found or * is not an timestamp value. @@ -1095,6 +1113,8 @@ final case class ConstructResponseUtilV2Live( .map(LicenseText.unsafeFrom), licenseUri = valueObject.maybeIriObject(OntologyConstants.KnoraBase.HasLicenseUri.toSmartIri).map(LicenseUri.unsafeFrom), + licenseDate = + valueObject.maybeDateObject(OntologyConstants.KnoraBase.HasLicenseDate.toSmartIri).map(LicenseDate.from), ) valueType match { diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v2/resources/CreateResourceV2Handler.scala b/webapi/src/main/scala/org/knora/webapi/responders/v2/resources/CreateResourceV2Handler.scala index 465007a087..9ee2295e26 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v2/resources/CreateResourceV2Handler.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v2/resources/CreateResourceV2Handler.scala @@ -499,8 +499,10 @@ final case class CreateResourceV2Handler( dimX = dimX, dimY = dimY, fileValue.copyrightHolder, + fileValue.authorship, fileValue.licenseText, fileValue.licenseUri, + fileValue.licenseDate, ), ) case StillImageExternalFileValueContentV2(_, fileValue, externalUrl, _) => @@ -512,8 +514,10 @@ final case class CreateResourceV2Handler( originalMimeType = fileValue.originalMimeType, externalUrl = externalUrl.value.toString(), fileValue.copyrightHolder, + fileValue.authorship, fileValue.licenseText, fileValue.licenseUri, + fileValue.licenseDate, ), ) case DocumentFileValueContentV2(_, fileValue, pageCount, dimX, dimY, _) => @@ -527,8 +531,10 @@ final case class CreateResourceV2Handler( dimY = dimY, pageCount = pageCount, fileValue.copyrightHolder, + fileValue.authorship, fileValue.licenseText, fileValue.licenseUri, + fileValue.licenseDate, ), ) case ArchiveFileValueContentV2(_, fileValue, _) => @@ -539,8 +545,10 @@ final case class CreateResourceV2Handler( originalFilename = fileValue.originalFilename, originalMimeType = fileValue.originalMimeType, fileValue.copyrightHolder, + fileValue.authorship, fileValue.licenseText, fileValue.licenseUri, + fileValue.licenseDate, ), ) case TextFileValueContentV2(_, fileValue, _) => @@ -551,8 +559,10 @@ final case class CreateResourceV2Handler( originalFilename = fileValue.originalFilename, originalMimeType = fileValue.originalMimeType, fileValue.copyrightHolder, + fileValue.authorship, fileValue.licenseText, fileValue.licenseUri, + fileValue.licenseDate, ), ) case AudioFileValueContentV2(_, fileValue, _) => @@ -563,8 +573,10 @@ final case class CreateResourceV2Handler( originalFilename = fileValue.originalFilename, originalMimeType = fileValue.originalMimeType, fileValue.copyrightHolder, + fileValue.authorship, fileValue.licenseText, fileValue.licenseUri, + fileValue.licenseDate, ), ) case MovingImageFileValueContentV2(_, fileValue, _) => @@ -575,8 +587,10 @@ final case class CreateResourceV2Handler( originalFilename = fileValue.originalFilename, originalMimeType = fileValue.originalMimeType, fileValue.copyrightHolder, + fileValue.authorship, fileValue.licenseText, fileValue.licenseUri, + fileValue.licenseDate, ), ) case LinkValueContentV2( diff --git a/webapi/src/main/scala/org/knora/webapi/slice/common/repo/rdf/Vocabulary.scala b/webapi/src/main/scala/org/knora/webapi/slice/common/repo/rdf/Vocabulary.scala index ba4723fcdc..6ac8703164 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/common/repo/rdf/Vocabulary.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/common/repo/rdf/Vocabulary.scala @@ -111,6 +111,7 @@ object Vocabulary { val hasCopyrightHolder: Iri = iri(kb + "hasCopyrightHolder") val hasLicenseText: Iri = iri(kb + "hasLicenseText") val hasLicenseUri: Iri = iri(kb + "hasLicenseUri") + val hasLicenseDate: Iri = iri(kb + "hasLicenseDate") val internalFilename: Iri = iri(kb + "internalFilename") val internalMimeType: Iri = iri(kb + "internalMimeType") diff --git a/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/model/ResourceCreateModels.scala b/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/model/ResourceCreateModels.scala index 9a54f19df3..a6236b4f69 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/model/ResourceCreateModels.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/model/ResourceCreateModels.scala @@ -7,10 +7,11 @@ package org.knora.webapi.slice.resources.repo.model import java.time.Instant import java.util.UUID - import org.knora.webapi.messages.util.CalendarNameV2 import org.knora.webapi.messages.util.DatePrecisionV2 +import org.knora.webapi.slice.admin.domain.model.Authorship import org.knora.webapi.slice.admin.domain.model.CopyrightHolder +import org.knora.webapi.slice.admin.domain.model.LicenseDate import org.knora.webapi.slice.admin.domain.model.LicenseText import org.knora.webapi.slice.admin.domain.model.LicenseUri import org.knora.webapi.slice.resourceinfo.domain.InternalIri @@ -51,8 +52,10 @@ sealed trait FileValueTypeSpecificInfo { def originalFilename: Option[String] def originalMimeType: Option[String] def copyrightHolder: Option[CopyrightHolder] + def authorship: Option[List[Authorship]] def licenseText: Option[LicenseText] def licenseUri: Option[LicenseUri] + def licenseDate: Option[LicenseDate] } enum TypeSpecificValueInfo { @@ -86,8 +89,10 @@ enum TypeSpecificValueInfo { dimX: Int, dimY: Int, copyrightHolder: Option[CopyrightHolder], + authorship: Option[List[Authorship]], licenseText: Option[LicenseText], licenseUri: Option[LicenseUri], + licenseDate: Option[LicenseDate], ) extends TypeSpecificValueInfo with FileValueTypeSpecificInfo case StillImageExternalFileValueInfo( internalFilename: String, @@ -96,8 +101,10 @@ enum TypeSpecificValueInfo { originalMimeType: Option[String], externalUrl: String, copyrightHolder: Option[CopyrightHolder], + authorship: Option[List[Authorship]], licenseText: Option[LicenseText], licenseUri: Option[LicenseUri], + licenseDate: Option[LicenseDate], ) extends TypeSpecificValueInfo with FileValueTypeSpecificInfo case DocumentFileValueInfo( internalFilename: String, @@ -108,8 +115,10 @@ enum TypeSpecificValueInfo { dimY: Option[Int], pageCount: Option[Int], copyrightHolder: Option[CopyrightHolder], + authorship: Option[List[Authorship]], licenseText: Option[LicenseText], licenseUri: Option[LicenseUri], + licenseDate: Option[LicenseDate], ) extends TypeSpecificValueInfo with FileValueTypeSpecificInfo case OtherFileValueInfo( internalFilename: String, @@ -117,8 +126,10 @@ enum TypeSpecificValueInfo { originalFilename: Option[String], originalMimeType: Option[String], copyrightHolder: Option[CopyrightHolder], + authorship: Option[List[Authorship]], licenseText: Option[LicenseText], licenseUri: Option[LicenseUri], + licenseDate: Option[LicenseDate], ) extends TypeSpecificValueInfo with FileValueTypeSpecificInfo case HierarchicalListValueInfo(valueHasListNode: InternalIri) case IntervalValueInfo(valueHasIntervalStart: BigDecimal, valueHasIntervalEnd: BigDecimal) diff --git a/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLive.scala b/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLive.scala index a51304c56f..f1d45c00ef 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLive.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLive.scala @@ -284,6 +284,7 @@ object ResourcesRepoLive { .andHasOptional(KB.hasCopyrightHolder, v.copyrightHolder.map(_.value).map(literalOf)) .andHasOptional(KB.hasLicenseText, v.licenseText.map(_.value).map(literalOf)) .andHasOptional(KB.hasLicenseUri, v.licenseUri.map(_.value).map(literalOfType(_, XSD.ANYURI))) + .andHasOptional(KB.hasLicenseDate, v.licenseDate.map(_.value.toString).map(literalOfType(_, XSD.DATE))) v match { case _: OtherFileValueInfo => result diff --git a/webapi/src/test/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLiveSpec.scala b/webapi/src/test/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLiveSpec.scala index 1fc61c9ec6..e34df43804 100644 --- a/webapi/src/test/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLiveSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLiveSpec.scala @@ -277,6 +277,8 @@ object TestData { None, None, None, + None, + None, ), permissions = valuePermissions, creator = valueCreator, @@ -302,6 +304,8 @@ object TestData { None, None, None, + None, + None, ), permissions = valuePermissions, creator = valueCreator, @@ -329,6 +333,8 @@ object TestData { None, None, None, + None, + None, ), permissions = valuePermissions, creator = valueCreator, @@ -353,6 +359,8 @@ object TestData { None, None, None, + None, + None, ), permissions = valuePermissions, creator = valueCreator, From 24eceb151ecaf845047c27c945fe4b50402ba464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Tue, 14 Jan 2025 17:24:59 +0100 Subject: [PATCH 09/31] add authorship writing --- .../v2/ResourcesResponderV2Spec.scala | 3 +- .../TriplestoreMessages.scala | 67 +------ .../util/ConstructResponseUtilV2.scala | 176 +++++++----------- .../slice/common/repo/rdf/Vocabulary.scala | 1 + .../repo/model/ResourceCreateModels.scala | 1 + .../repo/service/ResourcesRepoLive.scala | 1 + 6 files changed, 77 insertions(+), 172 deletions(-) diff --git a/integration/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala b/integration/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala index a946fb0919..c9e5d03a66 100644 --- a/integration/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala +++ b/integration/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala @@ -1186,8 +1186,7 @@ class ResourcesResponderV2Spec extends CoreSpec with ImplicitSender { assert(fileValue.licenseText.contains(licenseText)) assert(fileValue.licenseUri.contains(licenseUri)) assert(fileValue.licenseDate.contains(licenseDate)) - // TODO: not (yet) working: - // assert(fileValue.authorship.contains(authorship)) + assert(fileValue.authorship.contains(authorship)) } "create a resource with an external still image file value" in { diff --git a/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala b/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala index 39a0d79ca8..70a540a001 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala @@ -14,7 +14,10 @@ import zio.json.DeriveJsonCodec import zio.json.JsonCodec import java.time.Instant +import java.time.LocalDate import scala.collection.mutable +import scala.reflect.ClassTag + import dsp.errors.* import org.knora.webapi.* import org.knora.webapi.messages.* @@ -22,9 +25,6 @@ import org.knora.webapi.messages.IriConversions.* import org.knora.webapi.messages.util.ErrorHandlingMap import org.knora.webapi.messages.util.rdf.* -import java.time.LocalDate -import scala.reflect.ClassTag - /** * A response to a [[org.knora.webapi.store.triplestore.api.TriplestoreService.Queries.Construct]] query. * @@ -202,67 +202,6 @@ case class BlankNodeSubjectV2(value: String) extends SubjectV2 { * representing literals with the extended type information stored in the triplestore. */ sealed trait LiteralV2 { self => - - /** - * Returns this [[LiteralV2]] as an [[IriLiteralV2]]. - * - * @param errorFun a function that throws an exception. It will be called if this [[LiteralV2]] is not - * an [[IriLiteralV2]]. - * @return an [[IriLiteralV2]]. - */ - def asIriLiteral(errorFun: => Nothing): IriLiteralV2 = - as[IriLiteralV2]().getOrElse(errorFun) - - /** - * Returns this [[LiteralV2]] as a [[StringLiteralV2]]. - * - * @param errorFun a function that throws an exception. It will be called if this [[LiteralV2]] is not - * a [[StringLiteralV2]]. - * @return a [[StringLiteralV2]]. - */ - def asStringLiteral(errorFun: => Nothing): StringLiteralV2 = - as[StringLiteralV2]().getOrElse(errorFun) - - /** - * Returns this [[LiteralV2]] as a [[BooleanLiteralV2]]. - * - * @param errorFun a function that throws an exception. It will be called if this [[LiteralV2]] is not - * a [[BooleanLiteralV2]]. - * @return a [[BooleanLiteralV2]]. - */ - def asBooleanLiteral(errorFun: => Nothing): BooleanLiteralV2 = - as[BooleanLiteralV2]().getOrElse(errorFun) - - /** - * Returns this [[LiteralV2]] as an [[IntLiteralV2]]. - * - * @param errorFun a function that throws an exception. It will be called if this [[LiteralV2]] is not - * an [[IntLiteralV2]]. - * @return an [[IntLiteralV2]]. - */ - def asIntLiteral(errorFun: => Nothing): IntLiteralV2 = - as[IntLiteralV2]().getOrElse(errorFun) - - /** - * Returns this [[LiteralV2]] as a [[DecimalLiteralV2]]. - * - * @param errorFun a function that throws an exception. It will be called if this [[LiteralV2]] is not - * a [[DecimalLiteralV2]]. - * @return a [[DecimalLiteralV2]]. - */ - def asDecimalLiteral(errorFun: => Nothing): DecimalLiteralV2 = - as[DecimalLiteralV2]().getOrElse(errorFun) - - /** - * Returns this [[LiteralV2]] as a [[DateTimeLiteralV2]]. - * - * @param errorFun a function that throws an exception. It will be called if this [[LiteralV2]] is not - * a [[DateTimeLiteralV2]]. - * @return a [[DateTimeLiteralV2]]. - */ - def asDateTimeLiteral(errorFun: => Nothing): DateTimeLiteralV2 = - as[DateTimeLiteralV2]().getOrElse(errorFun) - def as[A <: LiteralV2]()(implicit tag: ClassTag[A]): Option[A] = this match { case a: A => Some(a) diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala index 06f99288b3..0d719540b4 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala @@ -8,7 +8,10 @@ package org.knora.webapi.messages.util import zio.* import java.time.Instant +import java.time.LocalDate import java.util.UUID +import scala.reflect.ClassTag + import dsp.errors.BadRequestException import dsp.errors.InconsistentRepositoryDataException import dsp.errors.NotFoundException @@ -23,10 +26,10 @@ import org.knora.webapi.messages.SmartIri import org.knora.webapi.messages.StringFormatter import org.knora.webapi.messages.store.triplestoremessages.* import org.knora.webapi.messages.store.triplestoremessages.SparqlExtendedConstructResponse.ConstructPredicateObjects -import org.knora.webapi.messages.util.ConstructResponseUtilV2.FlatPredicateObjects import org.knora.webapi.messages.util.ConstructResponseUtilV2.FlatStatements import org.knora.webapi.messages.util.ConstructResponseUtilV2.MainResourcesAndValueRdfData import org.knora.webapi.messages.util.ConstructResponseUtilV2.MappingAndXSLTransformation +import org.knora.webapi.messages.util.ConstructResponseUtilV2.PredicateObjects import org.knora.webapi.messages.util.ConstructResponseUtilV2.RdfData import org.knora.webapi.messages.util.ConstructResponseUtilV2.RdfPropertyValues import org.knora.webapi.messages.util.ConstructResponseUtilV2.RdfResources @@ -47,6 +50,7 @@ import org.knora.webapi.messages.v2.responder.standoffmessages.GetXSLTransformat import org.knora.webapi.messages.v2.responder.standoffmessages.GetXSLTransformationResponseV2 import org.knora.webapi.messages.v2.responder.standoffmessages.MappingXMLtoStandoff import org.knora.webapi.messages.v2.responder.valuemessages.* +import org.knora.webapi.slice.admin.domain.model.Authorship import org.knora.webapi.slice.admin.domain.model.CopyrightHolder import org.knora.webapi.slice.admin.domain.model.KnoraProject.ProjectIri import org.knora.webapi.slice.admin.domain.model.LicenseDate @@ -62,8 +66,6 @@ import org.knora.webapi.slice.resources.IiifImageRequestUrl import org.knora.webapi.store.iiif.errors.SipiException import org.knora.webapi.util.ZioHelper -import java.time.LocalDate - trait ConstructResponseUtilV2 { /** @@ -99,7 +101,7 @@ trait ConstructResponseUtilV2 { * @param mappings the mappings to convert standoff to XML, if any. * @param queryStandoff if `true`, make separate queries to get the standoff for text values. * @param calculateMayHaveMoreResults if `true`, calculate whether there may be more results for the query. - * @param versionDate if defined, represents the requested time in the the resources' version history. + * @param versionDate if defined, represents the requested time in the resources' version history. * @param targetSchema the schema of response. * @param requestingUser the user making the request. * @return a collection of [[ReadResourceV2]] representing the search results. @@ -145,7 +147,7 @@ object ConstructResponseUtilV2 { * A flattened map of predicates to objects. This assumes that each predicate has * * only one object. */ - type FlatPredicateObjects = Map[SmartIri, LiteralV2] + type PredicateObjects = Map[SmartIri, Seq[LiteralV2]] /** * A map of subject IRIs to flattened maps of predicates to objects. @@ -185,7 +187,28 @@ object ConstructResponseUtilV2 { /** * Assertions about the subject. */ - val assertions: FlatPredicateObjects + val assertions: PredicateObjects + + private def maybeSingleAs[A <: LiteralV2](predicateIri: SmartIri)(implicit tag: ClassTag[A]): Option[A] = + maybeAs[A](predicateIri).flatMap(_.headOption) + + private def requireSingleAs[A <: LiteralV2](predicateIri: SmartIri)(implicit tag: ClassTag[A]): A = + maybeSingleAs(predicateIri).getOrElse( + throw InconsistentRepositoryDataException(s"Subject $subjectIri does not have predicate $predicateIri"), + ) + + private def maybeAs[A <: LiteralV2](predicateIri: SmartIri)(implicit tag: ClassTag[A]): Option[Seq[A]] = + assertions + .get(predicateIri) + .map( + _.map(literal => + literal + .as[A]() + .getOrElse( + throw InconsistentRepositoryDataException(s"Unexpected object of $subjectIri $predicateIri: $literal"), + ), + ), + ) /** * Returns the optional string object of the specified predicate. Throws an exception if the object is not a string. @@ -194,13 +217,10 @@ object ConstructResponseUtilV2 { * @return the string object of the predicate. */ def maybeStringObject(predicateIri: SmartIri): Option[String] = - assertions.get(predicateIri).map { literal => - literal - .asStringLiteral( - throw InconsistentRepositoryDataException(s"Unexpected object of $subjectIri $predicateIri: $literal"), - ) - .value - } + maybeSingleAs[StringLiteralV2](predicateIri).map(_.value) + + def maybeStringListObject(predicateIri: SmartIri): Option[Seq[String]] = + maybeAs[StringLiteralV2](predicateIri).map(_.map(_.value)) /** * Returns the required string object of the specified predicate. Throws an exception if the object is not found or @@ -210,9 +230,7 @@ object ConstructResponseUtilV2 { * @return the string object of the predicate. */ def requireStringObject(predicateIri: SmartIri): String = - maybeStringObject(predicateIri).getOrElse( - throw InconsistentRepositoryDataException(s"Subject $subjectIri does not have predicate $predicateIri"), - ) + requireSingleAs[StringLiteralV2](predicateIri).value /** * Returns the optional IRI object of the specified predicate. Throws an exception if the object is not an IRI. @@ -221,13 +239,7 @@ object ConstructResponseUtilV2 { * @return the IRI object of the predicate. */ def maybeIriObject(predicateIri: SmartIri): Option[IRI] = - assertions.get(predicateIri).map { literal => - literal - .asIriLiteral( - throw InconsistentRepositoryDataException(s"Unexpected object of $subjectIri $predicateIri: $literal"), - ) - .value - } + maybeSingleAs[IriLiteralV2](predicateIri).map(_.value) /** * Returns the required IRI object of the specified predicate. Throws an exception if the object is not found or @@ -237,9 +249,7 @@ object ConstructResponseUtilV2 { * @return the IRI object of the predicate. */ def requireIriObject(predicateIri: SmartIri): IRI = - maybeIriObject(predicateIri).getOrElse( - throw InconsistentRepositoryDataException(s"Subject $subjectIri does not have predicate $predicateIri"), - ) + requireSingleAs[IriLiteralV2](predicateIri).value /** * Returns the optional integer object of the specified predicate. Throws an exception if the object is not an integer. @@ -248,13 +258,7 @@ object ConstructResponseUtilV2 { * @return the integer object of the predicate. */ def maybeIntObject(predicateIri: SmartIri): Option[Int] = - assertions.get(predicateIri).map { literal => - literal - .asIntLiteral( - throw InconsistentRepositoryDataException(s"Unexpected object of $subjectIri $predicateIri: $literal"), - ) - .value - } + maybeSingleAs[IntLiteralV2](predicateIri).map(_.value) /** * Returns the required integer object of the specified predicate. Throws an exception if the object is not found or @@ -264,9 +268,7 @@ object ConstructResponseUtilV2 { * @return the integer object of the predicate. */ def requireIntObject(predicateIri: SmartIri): Int = - maybeIntObject(predicateIri).getOrElse( - throw InconsistentRepositoryDataException(s"Subject $subjectIri does not have predicate $predicateIri"), - ) + requireSingleAs[IntLiteralV2](predicateIri).value /** * Returns the optional boolean object of the specified predicate. Throws an exception if the object is not a boolean. @@ -275,25 +277,17 @@ object ConstructResponseUtilV2 { * @return the boolean object of the predicate. */ def maybeBooleanObject(predicateIri: SmartIri): Option[Boolean] = - assertions.get(predicateIri).map { literal => - literal - .asBooleanLiteral( - throw InconsistentRepositoryDataException(s"Unexpected object of $subjectIri $predicateIri: $literal"), - ) - .value - } + maybeSingleAs[BooleanLiteralV2](predicateIri).map(_.value) /** * Returns the required boolean object of the specified predicate. Throws an exception if the object is not found or - * is not an boolean value. + * is not a boolean value. * * @param predicateIri the predicate. * @return the boolean object of the predicate. */ def requireBooleanObject(predicateIri: SmartIri): Boolean = - maybeBooleanObject(predicateIri).getOrElse( - throw InconsistentRepositoryDataException(s"Subject $subjectIri does not have predicate $predicateIri"), - ) + requireSingleAs[BooleanLiteralV2](predicateIri).value /** * Returns the optional decimal object of the specified predicate. Throws an exception if the object is not a decimal. @@ -301,26 +295,18 @@ object ConstructResponseUtilV2 { * @param predicateIri the predicate. * @return the decimal object of the predicate. */ - private def maybeDecimalObject(predicateIri: SmartIri): Option[BigDecimal] = - assertions.get(predicateIri).map { literal => - literal - .asDecimalLiteral( - throw InconsistentRepositoryDataException(s"Unexpected object of $subjectIri $predicateIri: $literal"), - ) - .value - } + def maybeDecimalObject(predicateIri: SmartIri): Option[BigDecimal] = + maybeSingleAs[DecimalLiteralV2](predicateIri).map(_.value) /** * Returns the required decimal object of the specified predicate. Throws an exception if the object is not found or - * is not an decimal value. + * is not a decimal value. * * @param predicateIri the predicate. * @return the decimal object of the predicate. */ def requireDecimalObject(predicateIri: SmartIri): BigDecimal = - maybeDecimalObject(predicateIri).getOrElse( - throw InconsistentRepositoryDataException(s"Subject $subjectIri does not have predicate $predicateIri"), - ) + requireSingleAs[DecimalLiteralV2](predicateIri).value /** * Returns the optional timestamp object of the specified predicate. Throws an exception if the object is not a timestamp. @@ -329,41 +315,26 @@ object ConstructResponseUtilV2 { * @return the timestamp object of the predicate. */ def maybeDateTimeObject(predicateIri: SmartIri): Option[Instant] = - assertions.get(predicateIri).map { literal => - literal - .asDateTimeLiteral( - throw InconsistentRepositoryDataException(s"Unexpected object of $subjectIri $predicateIri: $literal"), - ) - .value - } + maybeSingleAs[DateTimeLiteralV2](predicateIri).map(_.value) /** - * Returns the optional timestamp object of the specified predicate. Throws an exception if the object is not a timestamp. + * Returns the required timestamp object of the specified predicate. Throws an exception if the object is not found or + * is not a timestamp value. * * @param predicateIri the predicate. * @return the timestamp object of the predicate. */ - def maybeDateObject(predicateIri: SmartIri): Option[LocalDate] = - assertions.get(predicateIri).map { literal => - literal - .as[DateLiteralV2]() - .map(_.value) - .getOrElse( - throw InconsistentRepositoryDataException(s"Unexpected object of $subjectIri $predicateIri: $literal"), - ) - } + def requireDateTimeObject(predicateIri: SmartIri): Instant = + requireSingleAs[DateTimeLiteralV2](predicateIri).value /** - * Returns the required timestamp object of the specified predicate. Throws an exception if the object is not found or - * is not an timestamp value. + * Returns the optional date object of the specified predicate. Throws an exception if the object is not a date. * * @param predicateIri the predicate. - * @return the timestamp object of the predicate. + * @return the date object of the predicate. */ - def requireDateTimeObject(predicateIri: SmartIri): Instant = - maybeDateTimeObject(predicateIri).getOrElse( - throw InconsistentRepositoryDataException(s"Subject $subjectIri does not have predicate $predicateIri"), - ) + def maybeDateObject(predicateIri: SmartIri): Option[LocalDate] = + maybeSingleAs[DateLiteralV2](predicateIri).map(_.value) } /** @@ -383,7 +354,7 @@ object ConstructResponseUtilV2 { nestedResource: Option[ResourceWithValueRdfData] = None, isIncomingLink: Boolean = false, userPermission: Permission.ObjectAccess, - assertions: FlatPredicateObjects, + assertions: PredicateObjects, standoff: FlatStatements, ) extends RdfData @@ -398,7 +369,7 @@ object ConstructResponseUtilV2 { */ case class ResourceWithValueRdfData( subjectIri: IRI, - assertions: FlatPredicateObjects, + assertions: PredicateObjects, isMainResource: Boolean, userPermission: Option[Permission.ObjectAccess], valuePropertyAssertions: RdfPropertyValues, @@ -538,18 +509,13 @@ final case class ConstructResponseUtilV2Live( nonResourceStatements = nonResourceStatements, ) - // Flatten the resource assertions. - val resourceAssertions: FlatPredicateObjects = assertionsExplicit.map { - case (pred: SmartIri, objs: Seq[LiteralV2]) => pred -> objs.head - } - val userPermission: Option[Permission.ObjectAccess] = PermissionUtilADM.getUserPermissionFromConstructAssertionsADM(resourceIri, assertions, requestingUser) // Make a ResourceWithValueRdfData for each resource IRI. resourceIri -> ResourceWithValueRdfData( subjectIri = resourceIri, - assertions = resourceAssertions, + assertions = assertionsExplicit, isMainResource = isMainResource, userPermission = userPermission, valuePropertyAssertions = valuePropertyToValueObject, @@ -701,22 +667,17 @@ final case class ConstructResponseUtilV2Live( } } - // Flatten the value's statements. - val valueStatements: FlatPredicateObjects = valueRdfWithUserPermission.assertions.flatMap { - case (pred: SmartIri, objs: Seq[LiteralV2]) => - objs.map { obj => - pred -> obj - } - } - // Get the rdf:type of the value. - val rdfTypeLiteral: LiteralV2 = valueStatements.getOrElse( - OntologyConstants.Rdf.Type.toSmartIri, - throw InconsistentRepositoryDataException(s"Value $valObjIri has no rdf:type"), - ) + val rdfTypeLiteral: LiteralV2 = valueRdfWithUserPermission.assertions + .getOrElse( + OntologyConstants.Rdf.Type.toSmartIri, + throw InconsistentRepositoryDataException(s"Value $valObjIri has no rdf:type"), + ) + .head val valueObjectClass: SmartIri = rdfTypeLiteral - .asIriLiteral( + .as[IriLiteralV2]() + .getOrElse( throw InconsistentRepositoryDataException(s"Unexpected object of $valObjIri rdf:type: $rdfTypeLiteral"), ) .value @@ -730,7 +691,7 @@ final case class ConstructResponseUtilV2Live( subjectIri = valObjIri, valueObjectClass = valueObjectClass, userPermission = valueRdfWithUserPermission.maybeUserPermission.get, - assertions = valueStatements, + assertions = valueRdfWithUserPermission.assertions, standoff = emptyFlatStatements, // link value does not contain standoff ), ) @@ -742,7 +703,7 @@ final case class ConstructResponseUtilV2Live( subjectIri = valObjIri, valueObjectClass = valueObjectClass, userPermission = valueRdfWithUserPermission.maybeUserPermission.get, - assertions = valueStatements, + assertions = valueRdfWithUserPermission.assertions, standoff = standoffAssertions, ), ) @@ -1108,6 +1069,9 @@ final case class ConstructResponseUtilV2Live( copyrightHolder = valueObject .maybeStringObject(OntologyConstants.KnoraBase.HasCopyrightHolder.toSmartIri) .map(CopyrightHolder.unsafeFrom), + authorship = valueObject + .maybeStringListObject(OntologyConstants.KnoraBase.HasAuthorship.toSmartIri) + .map(_.map(Authorship.unsafeFrom).toList), licenseText = valueObject .maybeStringObject(OntologyConstants.KnoraBase.HasLicenseText.toSmartIri) .map(LicenseText.unsafeFrom), diff --git a/webapi/src/main/scala/org/knora/webapi/slice/common/repo/rdf/Vocabulary.scala b/webapi/src/main/scala/org/knora/webapi/slice/common/repo/rdf/Vocabulary.scala index 6ac8703164..59602b6a78 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/common/repo/rdf/Vocabulary.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/common/repo/rdf/Vocabulary.scala @@ -109,6 +109,7 @@ object Vocabulary { val valueHasMaxStandoffStartIndex: Iri = iri(kb + "valueHasMaxStandoffStartIndex") val valueHasStandoff: Iri = iri(kb + "valueHasStandoff") val hasCopyrightHolder: Iri = iri(kb + "hasCopyrightHolder") + val hasAuthorship: Iri = iri(kb + "hasAuthorship") val hasLicenseText: Iri = iri(kb + "hasLicenseText") val hasLicenseUri: Iri = iri(kb + "hasLicenseUri") val hasLicenseDate: Iri = iri(kb + "hasLicenseDate") diff --git a/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/model/ResourceCreateModels.scala b/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/model/ResourceCreateModels.scala index a6236b4f69..81346bae1f 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/model/ResourceCreateModels.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/model/ResourceCreateModels.scala @@ -7,6 +7,7 @@ package org.knora.webapi.slice.resources.repo.model import java.time.Instant import java.util.UUID + import org.knora.webapi.messages.util.CalendarNameV2 import org.knora.webapi.messages.util.DatePrecisionV2 import org.knora.webapi.slice.admin.domain.model.Authorship diff --git a/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLive.scala b/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLive.scala index f1d45c00ef..33d3c031d1 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLive.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLive.scala @@ -285,6 +285,7 @@ object ResourcesRepoLive { .andHasOptional(KB.hasLicenseText, v.licenseText.map(_.value).map(literalOf)) .andHasOptional(KB.hasLicenseUri, v.licenseUri.map(_.value).map(literalOfType(_, XSD.ANYURI))) .andHasOptional(KB.hasLicenseDate, v.licenseDate.map(_.value.toString).map(literalOfType(_, XSD.DATE))) + v.authorship.foreach(_.map(_.value).foreach(result.andHas(KB.hasAuthorship, _))) v match { case _: OtherFileValueInfo => result From 12e6ee22e8c67d76d339b763dc917bdd046b1abd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 15 Jan 2025 11:13:59 +0100 Subject: [PATCH 10/31] Move create resource with copyright and license test to CopyrightAndLicensesSpec --- .../it/v2/CopyrightAndLicensesSpec.scala | 168 +++++++++++------- .../webapi/models/filemodels/FileModels.scala | 52 +++--- .../v2/ResourcesResponderV2Spec.scala | 45 ----- 3 files changed, 134 insertions(+), 131 deletions(-) diff --git a/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala b/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala index 834b95a17c..23e81ad177 100644 --- a/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala +++ b/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala @@ -5,6 +5,8 @@ package org.knora.webapi.it.v2 +import cats.syntax.traverse.* + import org.apache.jena.rdf.model.Model import org.apache.jena.rdf.model.Property import org.apache.jena.rdf.model.Resource @@ -13,22 +15,17 @@ import zio.* import zio.http.Body import zio.http.Response import zio.test.* +import zio.test.Assertion.* import java.net.URLEncoder import scala.jdk.CollectionConverters.IteratorHasAsScala import scala.language.implicitConversions - import org.knora.webapi.E2EZSpec import org.knora.webapi.messages.OntologyConstants -import org.knora.webapi.messages.OntologyConstants.KnoraApiV2Complex.HasCopyrightHolder -import org.knora.webapi.messages.OntologyConstants.KnoraApiV2Complex.HasLicenseText -import org.knora.webapi.messages.OntologyConstants.KnoraApiV2Complex.HasLicenseUri -import org.knora.webapi.messages.OntologyConstants.KnoraApiV2Complex.StillImageFileValue +import org.knora.webapi.messages.OntologyConstants.KnoraApiV2Complex as KA import org.knora.webapi.models.filemodels.FileType import org.knora.webapi.models.filemodels.UploadFileRequest -import org.knora.webapi.slice.admin.domain.model.CopyrightHolder -import org.knora.webapi.slice.admin.domain.model.LicenseText -import org.knora.webapi.slice.admin.domain.model.LicenseUri +import org.knora.webapi.slice.admin.domain.model.* import org.knora.webapi.slice.admin.domain.service.KnoraProjectService import org.knora.webapi.slice.common.KnoraIris.ValueIri import org.knora.webapi.slice.common.jena.JenaConversions.given @@ -39,98 +36,117 @@ import org.knora.webapi.slice.resourceinfo.domain.IriConverter object CopyrightAndLicensesSpec extends E2EZSpec { - private val aCopyrightHolder = CopyrightHolder.unsafeFrom("2020, On FileValue") + private val aCopyrightHolder = CopyrightHolder.unsafeFrom("Universität Basel") + private val someAuthorship = List("Hans Müller", "Gigi DAgostino").map(Authorship.unsafeFrom) private val aLicenseText = LicenseText.unsafeFrom("CC BY-SA 4.0") private val aLicenseUri = LicenseUri.unsafeFrom("https://creativecommons.org/licenses/by-sa/4.0/") + private val aLicenseDate = LicenseDate.unsafeFrom("2022-01-01") - private val givenProjectHasNoCopyrightHolderAndLicenseSuite = suite("Creating Resources")( + private val copyrightAndLicenseInformationSpec = suite("Creating Resources")( test( - "when creating a resource without copyright holder and license" + - "the creation response should not contain the license and copyright holder", + "when creating a resource without copyright and license information" + + "the creation response should not contain it", ) { for { createResourceResponseModel <- createStillImageResource() - actualCreatedCopyright <- copyrightValueOption(createResourceResponseModel) - actualCreatedLicenseText <- licenseTextValueOption(createResourceResponseModel) - actualCreatedLicenseUri <- licenseUriValueOption(createResourceResponseModel) + info <- copyrightAndLicenseInfo(createResourceResponseModel) } yield assertTrue( - actualCreatedCopyright.isEmpty, - actualCreatedLicenseText.isEmpty, - actualCreatedLicenseUri.isEmpty, + info.copyrightHolder.isEmpty, + info.authorship.isEmpty, + info.licenseText.isEmpty, + info.licenseUri.isEmpty, + info.licenseDate.isEmpty, ) }, test( - "when creating a resource with copyright holder and license " + - "the creation response should contain the license and copyright holder", + "when creating a resource with copyright and license information" + + "the creation response should not contain it", ) { for { createResourceResponseModel <- - createStillImageResource(Some(aCopyrightHolder), Some(aLicenseText), Some(aLicenseUri)) - actualCreatedCopyright <- copyrightValue(createResourceResponseModel) - actualCreatedLicenseText <- licenseTextValue(createResourceResponseModel) - actualCreatedLicenseUri <- licenseUriValue(createResourceResponseModel) + createStillImageResource( + Some(aCopyrightHolder), + Some(someAuthorship), + Some(aLicenseText), + Some(aLicenseUri), + Some(aLicenseDate), + ) + info <- copyrightAndLicenseInfo(createResourceResponseModel) } yield assertTrue( - actualCreatedCopyright == aCopyrightHolder.value, - actualCreatedLicenseText == aLicenseText.value, - actualCreatedLicenseUri == aLicenseUri.value, - ) + info.copyrightHolder.contains(aCopyrightHolder), + info.licenseText.contains(aLicenseText), + info.licenseUri.contains(aLicenseUri), + info.licenseDate.contains(aLicenseDate), + ) && assert(info.authorship.getOrElse(List.empty))(hasSameElements(someAuthorship)) }, test( - "when creating a resource with copyright holder and license " + - "the response when getting the created resource should contain the license and copyright holder", + "when creating a resource with copyright and license information " + + "the response when getting the created resource should contain it", ) { for { createResourceResponseModel <- - createStillImageResource(Some(aCopyrightHolder), Some(aLicenseText), Some(aLicenseUri)) - resourceId <- resourceId(createResourceResponseModel) - getResponseModel <- getResourceFromApi(resourceId) - actualCopyright <- copyrightValue(getResponseModel) - actualLicenseText <- licenseTextValue(getResponseModel) - actualLicenseUri <- licenseUriValue(getResponseModel) + createStillImageResource( + Some(aCopyrightHolder), + Some(someAuthorship), + Some(aLicenseText), + Some(aLicenseUri), + Some(aLicenseDate), + ) + resourceId <- resourceId(createResourceResponseModel) + getResponseModel <- getResourceFromApi(resourceId) + info <- copyrightAndLicenseInfo(getResponseModel) } yield assertTrue( - actualCopyright == aCopyrightHolder.value, - actualLicenseText == aLicenseText.value, - actualLicenseUri == aLicenseUri.value, - ) + info.copyrightHolder.contains(aCopyrightHolder), + info.licenseText.contains(aLicenseText), + info.licenseUri.contains(aLicenseUri), + info.licenseDate.contains(aLicenseDate), + ) && assert(info.authorship.getOrElse(List.empty))(hasSameElements(someAuthorship)) }, test( - "when creating a resource with copyright holder and license " + - "the response when getting the created value should contain the license and copyright holder", + "when creating a resource with copyright and license information " + + "the response when getting the created value should contain it", ) { for { createResourceResponseModel <- - createStillImageResource(Some(aCopyrightHolder), Some(aLicenseText), Some(aLicenseUri)) + createStillImageResource( + Some(aCopyrightHolder), + Some(someAuthorship), + Some(aLicenseText), + Some(aLicenseUri), + Some(aLicenseDate), + ) valueResponseModel <- getValueFromApi(createResourceResponseModel) - actualCopyright <- copyrightValue(valueResponseModel) - actualLicenseText <- licenseTextValue(valueResponseModel) - actualLicenseUri <- licenseUriValue(valueResponseModel) + info <- copyrightAndLicenseInfo(valueResponseModel) } yield assertTrue( - actualCopyright == aCopyrightHolder.value, - actualLicenseText == aLicenseText.value, - actualLicenseUri == aLicenseUri.value, - ) + info.copyrightHolder.contains(aCopyrightHolder), + info.licenseText.contains(aLicenseText), + info.licenseUri.contains(aLicenseUri), + info.licenseDate.contains(aLicenseDate), + ) && assert(info.authorship.getOrElse(List.empty))(hasSameElements(someAuthorship)) }, ) - val e2eSpec: Spec[Scope & env, Any] = suite("Copyright Attribution and Licenses")( - givenProjectHasNoCopyrightHolderAndLicenseSuite, - ) + val e2eSpec: Spec[Scope & env, Any] = suite("Copyright Attribution and Licenses")(copyrightAndLicenseInformationSpec) private def failResponse(msg: String)(response: Response) = response.body.asString.flatMap(bodyStr => ZIO.fail(Exception(s"$msg\nstatus: ${response.status}\nbody: $bodyStr"))) private def createStillImageResource( copyrightHolder: Option[CopyrightHolder] = None, + authorship: Option[List[Authorship]] = None, licenseText: Option[LicenseText] = None, licenseUri: Option[LicenseUri] = None, + licenseDate: Option[LicenseDate] = None, ): ZIO[env, Throwable, Model] = { val jsonLd = UploadFileRequest .make( FileType.StillImageFile(), "internalFilename.jpg", copyrightHolder = copyrightHolder, + authorship = authorship, licenseText = licenseText, licenseUri = licenseUri, + licenseDate = licenseDate, ) .toJsonLd(className = Some("ThingPicture"), ontologyName = "anything") for { @@ -172,7 +188,7 @@ object CopyrightAndLicensesSpec extends E2EZSpec { val subs = model .listSubjectsWithProperty(RDF.`type`) .asScala - .filter(_.getProperty(RDF.`type`).getObject.asResource().hasURI(StillImageFileValue)) + .filter(_.getProperty(RDF.`type`).getObject.asResource().hasURI(KA.StillImageFileValue)) .toList subs match case s :: Nil => @@ -185,18 +201,44 @@ object CopyrightAndLicensesSpec extends E2EZSpec { case _ => ZIO.fail(Exception("Multiple values found")) } - private def copyrightValue(model: Model) = - singleStringValueOption(model, HasCopyrightHolder).someOrFail(new Exception("No copyright found")) + final case class CopyrightAndLicenseInfo( + copyrightHolder: Option[CopyrightHolder], + authorship: Option[List[Authorship]], + licenseText: Option[LicenseText], + licenseUri: Option[LicenseUri], + licenseDate: Option[LicenseDate], + ) + + private def copyrightAndLicenseInfo(model: Model) = + for { + copyright <- copyrightValueOption(model).map(_.map(CopyrightHolder.unsafeFrom)) + authorship <- authorshipValuesOption(model).map(_.map(_.map(Authorship.unsafeFrom))) + licenseText <- licenseTextValueOption(model).map(_.map(LicenseText.unsafeFrom)) + licenseUri <- licenseUriValueOption(model).map(_.map(LicenseUri.unsafeFrom)) + licenseDate <- licenseDateValueOption(model).map(_.map(LicenseDate.unsafeFrom)) + } yield CopyrightAndLicenseInfo(copyright, authorship, licenseText, licenseUri, licenseDate) + private def copyrightValueOption(model: Model) = - singleStringValueOption(model, HasCopyrightHolder) - private def licenseTextValue(model: Model) = - singleStringValueOption(model, HasLicenseText).someOrFail(new Exception("No license text found")) + singleStringValueOption(model, KA.HasCopyrightHolder) + + private def authorshipValuesOption(model: Model): Task[Option[List[String]]] = + ZIO + .fromEither( + model + .singleSubjectWithPropertyOption(KA.HasAuthorship) + .flatMap(_.traverse(_.objectStringList(KA.HasAuthorship))), + ) + .mapError(Exception(_)) + private def licenseTextValueOption(model: Model) = - singleStringValueOption(model, HasLicenseText) - private def licenseUriValue(model: Model) = - singleStringValueOption(model, HasLicenseUri).someOrFail(new Exception("No license uri found")) + singleStringValueOption(model, KA.HasLicenseText) + private def licenseUriValueOption(model: Model) = - singleStringValueOption(model, HasLicenseUri) + singleStringValueOption(model, KA.HasLicenseUri) + + private def licenseDateValueOption(model: Model) = + singleStringValueOption(model, KA.HasLicenseDate) + private def singleStringValueOption(model: Model, property: Property): Task[Option[String]] = ZIO .fromEither( diff --git a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala index 545d4f778a..deeebbc8a4 100644 --- a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala +++ b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala @@ -60,29 +60,35 @@ sealed abstract case class UploadFileRequest private ( case None => "" } - s"""{ - | $resorceIRIOrEmptyString"@type" : "$ontologyName:$classNameWithDefaults", - | "$fileValuePropertyName" : { - | "@type" : "$fileValueType", - | "knora-api:fileValueHasFilename" : "$internalFilename" - | ${copyrightHolder.map(ca => s""","knora-api:hasCopyrightHolder" : "${ca.value}"""").getOrElse("")} - | ${authorship - .filter(_.nonEmpty) - .map(a => s""","knora-api:hasAuthorship" : [ ${a.map(_.value).mkString("\"", ",", " \"")} ]""") - .getOrElse("")} - | ${licenseText.map(l => s""","knora-api:hasLicenseText" : "${l.value}"""").getOrElse("")} - | ${licenseUri - .map(u => s""", "knora-api:hasLicenseUri" : { "@type" : "xsd:anyURI", "@value":"${u.value}" }""") - .getOrElse("")} - | ${licenseDate - .map(d => s""", "knora-api:hasLicenseDate" : { "@type" : "xsd:date", "@value":"${d.value.toString}" }""") - .getOrElse("")} - | }, - | "knora-api:attachedToProject" : { - | "@id" : "http://rdfh.ch/projects/$shortcode" - | }, - | "rdfs:label" : "$label", - | $context}""".stripMargin + val copyrightHolderJson = + copyrightHolder.map(ca => s""","knora-api:hasCopyrightHolder" : "${ca.value}"""").getOrElse("") + val authorshipJson = authorship + .filter(_.nonEmpty) + .map(a => s""","knora-api:hasAuthorship" : [ ${a.map(_.value).mkString("\"", "\",\"", "\"")} ]""") + .getOrElse("") + + val jsonLd = + s"""{ + | $resorceIRIOrEmptyString"@type" : "$ontologyName:$classNameWithDefaults", + | "$fileValuePropertyName" : { + | "@type" : "$fileValueType", + | "knora-api:fileValueHasFilename" : "$internalFilename" + | $copyrightHolderJson + | $authorshipJson + | ${licenseText.map(l => s""","knora-api:hasLicenseText" : "${l.value}"""").getOrElse("")} + | ${licenseUri + .map(u => s""", "knora-api:hasLicenseUri" : { "@type" : "xsd:anyURI", "@value":"${u.value}" }""") + .getOrElse("")} + | ${licenseDate + .map(d => s""", "knora-api:hasLicenseDate" : { "@type" : "xsd:date", "@value":"${d.value.toString}" }""") + .getOrElse("")} + | }, + | "knora-api:attachedToProject" : { + | "@id" : "http://rdfh.ch/projects/$shortcode" + | }, + | "rdfs:label" : "$label", + | $context}""".stripMargin + jsonLd } /** diff --git a/integration/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala b/integration/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala index c9e5d03a66..e9f94c4c53 100644 --- a/integration/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala +++ b/integration/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala @@ -1144,51 +1144,6 @@ class ResourcesResponderV2Spec extends CoreSpec with ImplicitSender { ) } - "create a still image file value with copyright and license information" in { - val resourceIri: IRI = stringFormatter.makeRandomResourceIri(SharedTestDataADM.anythingProject.shortcode) - - val copyrightHolder = CopyrightHolder.unsafeFrom("The University of Basel") - val authorship = List("Hans Meier", "Peter Müller").map(Authorship.unsafeFrom) - val licenseText = LicenseText.unsafeFrom("CC BY-SA 4.0") - val licenseUri = LicenseUri.unsafeFrom("https://creativecommons.org/licenses/by-sa/4.0/") - val licenseDate = LicenseDate.unsafeFrom("2020-01-01") - - val inputResource = UploadFileRequest - .make( - fileType = FileType.StillImageFile( - dimX = 512, - dimY = 256, - ), - internalFilename = "bar.jp2", - copyrightHolder = Some(copyrightHolder), - authorship = Some(authorship), - licenseText = Some(licenseText), - licenseUri = Some(licenseUri), - licenseDate = Some(licenseDate), - ) - .toMessage(resourceIri = Some(resourceIri)) - - val _ = UnsafeZioRun.runOrThrow( - resourcesResponderV2( - _.createResource(CreateResourceRequestV2(inputResource, anythingUserProfile, UUID.randomUUID)).logError, - ), - ) - - val actual: ReadResourceV2 = getResource(resourceIri) - val fileValue: FileValueV2 = - actual.values.head._2 - .map(_.valueContent) - .collect { case sifvc: StillImageFileValueContentV2 => sifvc } - .map(_.fileValue) - .head - - assert(fileValue.copyrightHolder.contains(copyrightHolder)) - assert(fileValue.licenseText.contains(licenseText)) - assert(fileValue.licenseUri.contains(licenseUri)) - assert(fileValue.licenseDate.contains(licenseDate)) - assert(fileValue.authorship.contains(authorship)) - } - "create a resource with an external still image file value" in { val resourceIri: IRI = stringFormatter.makeRandomResourceIri(SharedTestDataADM.anythingProject.shortcode) From 7576819139925986468e06c98761e14c43835b8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 15 Jan 2025 13:56:40 +0100 Subject: [PATCH 11/31] fix ontology --- .../resources/knora-ontologies/knora-base.ttl | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/webapi/src/main/resources/knora-ontologies/knora-base.ttl b/webapi/src/main/resources/knora-ontologies/knora-base.ttl index 55fa1117a7..05ba42e625 100644 --- a/webapi/src/main/resources/knora-ontologies/knora-base.ttl +++ b/webapi/src/main/resources/knora-ontologies/knora-base.ttl @@ -555,14 +555,8 @@ :hasCopyrightHolder rdf:type owl:DatatypeProperty ; -<<<<<<< HEAD + rdfs:label "has copyright holder"@en; rdfs:comment "The copyright holder."@en ; -||||||| c984b8c09 - rdfs:comment "The copyright statement that gives credit to the original author."@en ; -======= - rdfs:label "copyright attribution"@en; - rdfs:comment "The copyright statement that gives credit to the original author."@en ; ->>>>>>> main :objectDatatypeConstraint xsd:string . @@ -570,6 +564,7 @@ :hasAuthorship rdf:type owl:DatatypeProperty ; + rdfs:label "has authorship"@en; rdfs:comment "Credit, Moral Rights, Author(s)"@en ; :objectDatatypeConstraint xsd:string . @@ -578,17 +573,16 @@ :hasLicenseText rdf:type owl:DatatypeProperty ; - rdfs:label "license text"@en; + rdfs:label "has license text"@en; rdfs:comment "Specifies the terms under which a work can be used. This statement may be a reference to a well-known license, such as Creative Commons (e.g. 'CC BY-SA') or a custom license."@en ; :objectDatatypeConstraint xsd:string . -### http://www.knora.org/ontology/knora-base#hasLicenseUri ### http://www.knora.org/ontology/knora-base#hasLicenseUri :hasLicenseUri rdf:type owl:DatatypeProperty ; - rdfs:label "license URI"@en; + rdfs:label "has license URI"@en; rdfs:comment "Canonical link to license."@en ; :objectDatatypeConstraint xsd:anyUri . @@ -597,6 +591,7 @@ :hasLicenseDate rdf:type owl:DatatypeProperty ; + rdfs:label "has license date"@en; rdfs:comment "Date of creation on platform."@en ; :objectDatatypeConstraint xsd:date. From e032bf9d92735f4d79aa136e90a60dd4ee5a2b37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 15 Jan 2025 14:05:25 +0100 Subject: [PATCH 12/31] remove debug logging --- .../v2/responder/valuemessages/ValueMessagesV2.scala | 12 +++--------- .../common/ApiComplexV2JsonLdRequestParser.scala | 5 ----- 2 files changed, 3 insertions(+), 14 deletions(-) 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 5219dbfd60..c367efee68 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 @@ -2018,15 +2018,9 @@ object FileValueV2 { for { copyrightHolder <- r.objectStringOption(HasCopyrightHolder, CopyrightHolder.from) authorship <- r.objectStringListOption(HasAuthorship, Authorship.from) - _ = { - val out = new java.io.ByteArrayOutputStream() - RDFDataMgr.write(out, r.getModel, Lang.TURTLE) - println("FileValueV2.from: model:") - print(out.toString(java.nio.charset.StandardCharsets.UTF_8)) - } - licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) - licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) - licenseDate <- r.objectLocalDateOption(HasLicenseDate).map(_.map(LicenseDate.from)) + licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) + licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) + licenseDate <- r.objectLocalDateOption(HasLicenseDate).map(_.map(LicenseDate.from)) } yield FileValueV2( info.filename, meta.internalMimeType, 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 index 1c9f2be8e6..eb52ad9f6f 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/common/ApiComplexV2JsonLdRequestParser.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/common/ApiComplexV2JsonLdRequestParser.scala @@ -379,11 +379,6 @@ final case class ApiComplexV2JsonLdRequestParser( ZIO.scoped { for { r <- RootResource.fromJsonLd(str) - _ <- Console.printLine("RootResource: " + r.resource).orDie - _ <- r.resource.getModel.printTurtle - _ <- Console.printLine("Model:\n").orDie - _ <- ModelOps.fromJsonLd(str).flatMap(_.printTurtle) - _ <- Console.printLine("jsonLd:\n" + str).orDie resourceIri <- r.resourceIriOrFail v <- ValueResource.from(r) valueUuid <- v.valueHasUuidOption From 325a085963622316d171aa505cfd21d4ebc1bd0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 15 Jan 2025 14:28:15 +0100 Subject: [PATCH 13/31] Set licenseDate when creating the FileValueV2 --- .../it/v2/CopyrightAndLicensesSpec.scala | 12 ++++-------- .../models/filemodels/FileModelUtil.scala | 8 -------- .../webapi/models/filemodels/FileModels.scala | 7 ------- .../valuemessages/ValueMessagesV2.scala | 18 +++++++++--------- .../domain/model/CopyrightAndLicenses.scala | 6 ++++-- 5 files changed, 17 insertions(+), 34 deletions(-) diff --git a/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala b/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala index 12824d48ef..0298c9ca45 100644 --- a/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala +++ b/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala @@ -40,7 +40,6 @@ object CopyrightAndLicensesSpec extends E2EZSpec { private val someAuthorship = List("Hans Müller", "Gigi DAgostino").map(Authorship.unsafeFrom) private val aLicenseText = LicenseText.unsafeFrom("CC BY-SA 4.0") private val aLicenseUri = LicenseUri.unsafeFrom("https://creativecommons.org/licenses/by-sa/4.0/") - private val aLicenseDate = LicenseDate.unsafeFrom("2022-01-01") private val copyrightAndLicenseInformationSpec = suite("Creating Resources")( test( @@ -55,7 +54,7 @@ object CopyrightAndLicensesSpec extends E2EZSpec { info.authorship.isEmpty, info.licenseText.isEmpty, info.licenseUri.isEmpty, - info.licenseDate.isEmpty, + info.licenseDate.isDefined, ) }, test( @@ -69,7 +68,7 @@ object CopyrightAndLicensesSpec extends E2EZSpec { info.copyrightHolder.contains(aCopyrightHolder), info.licenseText.contains(aLicenseText), info.licenseUri.contains(aLicenseUri), - info.licenseDate.contains(aLicenseDate), + info.licenseDate.isDefined, ) && assert(info.authorship.getOrElse(List.empty))(hasSameElements(someAuthorship)) }, test( @@ -85,7 +84,7 @@ object CopyrightAndLicensesSpec extends E2EZSpec { info.copyrightHolder.contains(aCopyrightHolder), info.licenseText.contains(aLicenseText), info.licenseUri.contains(aLicenseUri), - info.licenseDate.contains(aLicenseDate), + info.licenseDate.isDefined, ) && assert(info.authorship.getOrElse(List.empty))(hasSameElements(someAuthorship)) }, test( @@ -100,7 +99,7 @@ object CopyrightAndLicensesSpec extends E2EZSpec { info.copyrightHolder.contains(aCopyrightHolder), info.licenseText.contains(aLicenseText), info.licenseUri.contains(aLicenseUri), - info.licenseDate.contains(aLicenseDate), + info.licenseDate.isDefined, ) && assert(info.authorship.getOrElse(List.empty))(hasSameElements(someAuthorship)) }, ) @@ -116,7 +115,6 @@ object CopyrightAndLicensesSpec extends E2EZSpec { Some(someAuthorship), Some(aLicenseText), Some(aLicenseUri), - Some(aLicenseDate), ) private def createStillImageResource( @@ -124,7 +122,6 @@ object CopyrightAndLicensesSpec extends E2EZSpec { authorship: Option[List[Authorship]] = None, licenseText: Option[LicenseText] = None, licenseUri: Option[LicenseUri] = None, - licenseDate: Option[LicenseDate] = None, ): ZIO[env, Throwable, Model] = { val jsonLd = UploadFileRequest .make( @@ -134,7 +131,6 @@ object CopyrightAndLicensesSpec extends E2EZSpec { authorship = authorship, licenseText = licenseText, licenseUri = licenseUri, - licenseDate = licenseDate, ) .toJsonLd(className = Some("ThingPicture"), ontologyName = "anything") for { diff --git a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModelUtil.scala b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModelUtil.scala index 45fd13ee81..d08784cff0 100644 --- a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModelUtil.scala +++ b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModelUtil.scala @@ -92,7 +92,6 @@ object FileModelUtil { authorship: Option[List[Authorship]] = None, licenseText: Option[LicenseText] = None, licenseUri: Option[LicenseUri] = None, - licenseDate: Option[LicenseDate] = None, comment: Option[String] = None, ): FileValueContentV2 = fileType match { @@ -108,7 +107,6 @@ object FileModelUtil { authorship = authorship, licenseText = licenseText, licenseUri = licenseUri, - licenseDate = licenseDate, ), pageCount = pageCount, dimX = dimX, @@ -127,7 +125,6 @@ object FileModelUtil { authorship = authorship, licenseText = licenseText, licenseUri = licenseUri, - licenseDate = licenseDate, ), dimX = dimX, dimY = dimY, @@ -145,7 +142,6 @@ object FileModelUtil { authorship = authorship, licenseText = licenseText, licenseUri = licenseUri, - licenseDate = licenseDate, ), externalUrl = externalUrl, comment = comment, @@ -162,7 +158,6 @@ object FileModelUtil { authorship = authorship, licenseText = licenseText, licenseUri = licenseUri, - licenseDate = licenseDate, ), ) case FileType.TextFile => @@ -177,7 +172,6 @@ object FileModelUtil { authorship = authorship, licenseText = licenseText, licenseUri = licenseUri, - licenseDate = licenseDate, ), ) case FileType.AudioFile => @@ -192,7 +186,6 @@ object FileModelUtil { authorship = authorship, licenseText = licenseText, licenseUri = licenseUri, - licenseDate = licenseDate, ), ) case FileType.ArchiveFile => @@ -207,7 +200,6 @@ object FileModelUtil { authorship = authorship, licenseText = licenseText, licenseUri = licenseUri, - licenseDate = licenseDate, ), comment = comment, ) diff --git a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala index deeebbc8a4..5a59645617 100644 --- a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala +++ b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala @@ -30,7 +30,6 @@ sealed abstract case class UploadFileRequest private ( authorship: Option[List[Authorship]] = None, licenseText: Option[LicenseText] = None, licenseUri: Option[LicenseUri] = None, - licenseDate: Option[LicenseDate] = None, ) { self => /** @@ -79,9 +78,6 @@ sealed abstract case class UploadFileRequest private ( | ${licenseUri .map(u => s""", "knora-api:hasLicenseUri" : { "@type" : "xsd:anyURI", "@value":"${u.value}" }""") .getOrElse("")} - | ${licenseDate - .map(d => s""", "knora-api:hasLicenseDate" : { "@type" : "xsd:date", "@value":"${d.value.toString}" }""") - .getOrElse("")} | }, | "knora-api:attachedToProject" : { | "@id" : "http://rdfh.ch/projects/$shortcode" @@ -154,7 +150,6 @@ sealed abstract case class UploadFileRequest private ( authorship = self.authorship, licenseText = self.licenseText, licenseUri = self.licenseUri, - licenseDate = self.licenseDate, ) val values = List( @@ -209,7 +204,6 @@ object UploadFileRequest { authorship: Option[List[Authorship]] = None, licenseText: Option[LicenseText] = None, licenseUri: Option[LicenseUri] = None, - licenseDate: Option[LicenseDate] = None, ): UploadFileRequest = new UploadFileRequest( fileType, @@ -220,7 +214,6 @@ object UploadFileRequest { authorship, licenseText, licenseUri, - licenseDate, ) {} } 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 c367efee68..8b0d456905 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 @@ -2013,14 +2013,14 @@ case class FileValueV2( ) object FileValueV2 { - def from(r: Resource, info: FileInfo): Either[String, FileValueV2] = { + def makeNew(r: Resource, info: FileInfo): Either[String, FileValueV2] = { val meta = info.metadata for { copyrightHolder <- r.objectStringOption(HasCopyrightHolder, CopyrightHolder.from) authorship <- r.objectStringListOption(HasAuthorship, Authorship.from) licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) - licenseDate <- r.objectLocalDateOption(HasLicenseDate).map(_.map(LicenseDate.from)) + licenseDate = Some(LicenseDate.makeNew) } yield FileValueV2( info.filename, meta.internalMimeType, @@ -2171,7 +2171,7 @@ object StillImageFileValueContentV2 { def from(r: Resource, fileInfo: FileInfo): Either[String, StillImageFileValueContentV2] = for { comment <- objectCommentOption(r) meta = fileInfo.metadata - fileValue <- FileValueV2.from(r, fileInfo) + fileValue <- FileValueV2.makeNew(r, fileInfo) } yield StillImageFileValueContentV2( ApiV2Complex, fileValue, @@ -2274,7 +2274,7 @@ object StillImageExternalFileValueContentV2 { externalUrlStr <- r.objectString(StillImageFileValueHasExternalUrl) iifUrl <- IiifImageRequestUrl.from(externalUrlStr) comment <- objectCommentOption(r) - fileValue <- FileValueV2.from(r, fakeInfo) + fileValue <- FileValueV2.makeNew(r, fakeInfo) } yield StillImageExternalFileValueContentV2(ApiV2Complex, fileValue, iifUrl, comment) } @@ -2413,7 +2413,7 @@ case class ArchiveFileValueContentV2( object DocumentFileValueContentV2 { def from(r: Resource, info: FileInfo): Either[String, DocumentFileValueContentV2] = for { comment <- objectCommentOption(r) - fileValue <- FileValueV2.from(r, info) + fileValue <- FileValueV2.makeNew(r, info) meta = info.metadata } yield DocumentFileValueContentV2(ApiV2Complex, fileValue, meta.numpages, meta.width, meta.height, comment) } @@ -2424,7 +2424,7 @@ object DocumentFileValueContentV2 { object ArchiveFileValueContentV2 { def from(r: Resource, info: FileInfo): Either[String, ArchiveFileValueContentV2] = for { comment <- objectCommentOption(r) - fileValue <- FileValueV2.from(r, info) + fileValue <- FileValueV2.makeNew(r, info) } yield ArchiveFileValueContentV2(ApiV2Complex, fileValue, comment) } @@ -2493,7 +2493,7 @@ case class TextFileValueContentV2( object TextFileValueContentV2 { def from(r: Resource, info: FileInfo): Either[String, TextFileValueContentV2] = for { comment <- objectCommentOption(r) - fileValue <- FileValueV2.from(r, info) + fileValue <- FileValueV2.makeNew(r, info) } yield TextFileValueContentV2(ApiV2Complex, fileValue, comment) } @@ -2562,7 +2562,7 @@ case class AudioFileValueContentV2( object AudioFileValueContentV2 { def from(r: Resource, info: FileInfo): Either[String, AudioFileValueContentV2] = for { comment <- objectCommentOption(r) - fileValue <- FileValueV2.from(r, info) + fileValue <- FileValueV2.makeNew(r, info) } yield AudioFileValueContentV2(ApiV2Complex, fileValue, comment) } @@ -2633,7 +2633,7 @@ case class MovingImageFileValueContentV2( object MovingImageFileValueContentV2 { def from(r: Resource, info: FileInfo): Either[String, MovingImageFileValueContentV2] = for { comment <- objectCommentOption(r) - fileValue <- FileValueV2.from(r, info) + fileValue <- FileValueV2.makeNew(r, info) } yield MovingImageFileValueContentV2(ApiV2Complex, fileValue, comment) } diff --git a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala index 58c0ae5516..bbfecc9a7a 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala @@ -7,13 +7,14 @@ package org.knora.webapi.slice.admin.domain.model import java.time.LocalDate import scala.util.Try - import org.knora.webapi.slice.common.StringValueCompanion import org.knora.webapi.slice.common.StringValueCompanion.* import org.knora.webapi.slice.common.StringValueCompanion.maxLength import org.knora.webapi.slice.common.Value import org.knora.webapi.slice.common.Value.StringValue import org.knora.webapi.slice.common.WithFrom +import zio.Clock +import zio.UIO final case class CopyrightHolder private (override val value: String) extends StringValue object CopyrightHolder extends StringValueCompanion[CopyrightHolder] { @@ -45,7 +46,8 @@ object LicenseUri extends StringValueCompanion[LicenseUri] { final case class LicenseDate private (override val value: LocalDate) extends Value[LocalDate] object LicenseDate extends WithFrom[String, LicenseDate] { - def makeNew: LicenseDate = LicenseDate(LocalDate.now()) + def makeNew: LicenseDate = LicenseDate(LocalDate.now) + def from(str: String): Either[String, LicenseDate] = Try(LocalDate.parse(str)).toEither.left .map(_ => "License Date must be in format 'YYYY-MM-DD'.") From c3da9b7b374e8dca23d1d364fbc66fb100cc48c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 15 Jan 2025 14:33:42 +0100 Subject: [PATCH 14/31] regenerate knoraApiOntologySimple.jsonld --- .../knoraApiOntologySimple.jsonld | 1909 ----------------- 1 file changed, 1909 deletions(-) delete mode 100644 test_data/generated_test_data/ontologyR2RV2/knoraApiOntologySimple.jsonld diff --git a/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologySimple.jsonld b/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologySimple.jsonld deleted file mode 100644 index 45f5fdad41..0000000000 --- a/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologySimple.jsonld +++ /dev/null @@ -1,1909 +0,0 @@ -{ - "@id": "http://api.knora.org/ontology/knora-api/simple/v2", - "@type": "owl:Ontology", - "rdfs:label": "The knora-api ontology in the simple schema", - "@graph": [ - { - "rdfs:label": "Annotation", - "rdfs:subClassOf": [ - { - "@id": "knora-api:Resource" - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:arkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasComment" - }, - "owl:minCardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasIncomingLink" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasStandoffLinkTo" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:isAnnotationOf" - }, - "owl:minCardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:versionArkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "rdfs:label" - }, - "owl:cardinality": 1 - } - ], - "rdfs:comment": "A generic class for representing annotations", - "@type": "owl:Class", - "@id": "knora-api:Annotation" - }, - { - "@id": "knora-api:ArchiveRepresentation", - "@type": "owl:Class", - "rdfs:subClassOf": [ - { - "@id": "knora-api:Representation" - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:arkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasArchiveFile" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasIncomingLink" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasStandoffLinkTo" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:versionArkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "rdfs:label" - }, - "owl:cardinality": 1 - } - ], - "rdfs:label": "Representation (Zip)" - }, - { - "rdfs:label": "Representation (Audio)", - "rdfs:subClassOf": [ - { - "@id": "knora-api:Representation" - }, - { - "@id": "http://www.w3.org/ns/ma-ont#AudioTrack" - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:arkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasAudioFile" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasIncomingLink" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasStandoffLinkTo" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:versionArkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "rdfs:label" - }, - "owl:cardinality": 1 - } - ], - "rdfs:comment": "Represents a file containing audio data", - "@type": "owl:Class", - "@id": "knora-api:AudioRepresentation" - }, - { - "rdfs:label": "Audiosegment", - "rdfs:subClassOf": [ - { - "@id": "knora-api:Segment" - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:arkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasComment" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasDescription" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasIncomingLink" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasKeyword" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasSegmentBounds" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasStandoffLinkTo" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasTitle" - }, - "owl:maxCardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:isAudioSegmentOf" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:relatesTo" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:versionArkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "rdfs:label" - }, - "owl:cardinality": 1 - } - ], - "rdfs:comment": "A segment of an audio resource", - "@type": "owl:Class", - "@id": "knora-api:AudioSegment" - }, - { - "rdfs:label": "Color literal", - "rdfs:comment": "Represents a color.", - "@type": "rdfs:Datatype", - "owl:withRestrictions": { - "xsd:pattern": "#([0-9a-fA-F]{3}){1,2}" - }, - "owl:onDatatype": { - "@id": "xsd:string" - }, - "@id": "knora-api:Color" - }, - { - "@id": "knora-api:CustomFormattedText", - "@type": "owl:Class", - "rdfs:label": "Custom Formatted Text", - "rdfs:comment": "Indicates that a text value is formatted in terms of its ontology and when instantiated, using a project defined custom standoff formatting." - }, - { - "rdfs:label": "Representation (3D)", - "rdfs:subClassOf": [ - { - "@id": "knora-api:Representation" - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:arkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasDDDFile" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasIncomingLink" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasStandoffLinkTo" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:versionArkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "rdfs:label" - }, - "owl:cardinality": 1 - } - ], - "rdfs:comment": "Represents a file containg 3D data", - "@type": "owl:Class", - "@id": "knora-api:DDDRepresentation" - }, - { - "rdfs:label": "Date literal", - "rdfs:comment": "Represents a date as a period with different possible precisions.", - "@type": "rdfs:Datatype", - "owl:withRestrictions": { - "xsd:pattern": "(GREGORIAN|JULIAN|ISLAMIC):\\d{1,4}(-\\d{1,2}(-\\d{1,2})?)?( BC| AD| BCE| CE)?(:\\d{1,4}(-\\d{1,2}(-\\d{1,2})?)?( BC| AD| BCE| CE)?)?" - }, - "owl:onDatatype": { - "@id": "xsd:string" - }, - "@id": "knora-api:Date" - }, - { - "rdfs:label": "Deleted Resource", - "rdfs:subClassOf": [ - { - "@id": "knora-api:Resource" - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:arkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasIncomingLink" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasStandoffLinkTo" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:versionArkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "rdfs:label" - }, - "owl:cardinality": 1 - } - ], - "rdfs:comment": "Generic representation of a deleted resource that can therefore not be displayed", - "@type": "owl:Class", - "@id": "knora-api:DeletedResource" - }, - { - "@id": "knora-api:DeletedValue", - "@type": "owl:Class", - "rdfs:label": "Deleted Value", - "rdfs:comment": "Generic representation of a deleted value that can therefore not be displayed" - }, - { - "@id": "knora-api:DocumentRepresentation", - "@type": "owl:Class", - "rdfs:subClassOf": [ - { - "@id": "knora-api:Representation" - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:arkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasDocumentFile" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasIncomingLink" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasStandoffLinkTo" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:versionArkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "rdfs:label" - }, - "owl:cardinality": 1 - } - ], - "rdfs:label": "Representation (Document)" - }, - { - "rdfs:label": "File URI", - "rdfs:comment": "Represents a file URI.", - "@type": "rdfs:Datatype", - "owl:onDatatype": { - "@id": "xsd:anyURI" - }, - "@id": "knora-api:File" - }, - { - "@id": "knora-api:FormattedText", - "@type": "owl:Class", - "rdfs:label": "Formatted Text", - "rdfs:comment": "Indicates that a text value is formatted in terms of its ontology and when instantiated, using DSP standard standoff formatting." - }, - { - "rdfs:label": "Geometry specification", - "rdfs:comment": "Represents a geometry specification in JSON.", - "@type": "rdfs:Datatype", - "owl:onDatatype": { - "@id": "xsd:string" - }, - "@id": "knora-api:Geom" - }, - { - "rdfs:label": "Geoname code", - "rdfs:comment": "Represents a Geoname code.", - "@type": "rdfs:Datatype", - "owl:withRestrictions": { - "xsd:pattern": "\\d{1,8}" - }, - "owl:onDatatype": { - "@id": "xsd:string" - }, - "@id": "knora-api:Geoname" - }, - { - "rdfs:label": "Interval literal", - "rdfs:comment": "Represents an interval.", - "@type": "rdfs:Datatype", - "owl:withRestrictions": { - "xsd:pattern": "\\d+(\\.\\d+)?,\\d+(\\.\\d+)?" - }, - "owl:onDatatype": { - "@id": "xsd:string" - }, - "@id": "knora-api:Interval" - }, - { - "rdfs:label": "Link Object", - "rdfs:subClassOf": [ - { - "@id": "knora-api:Resource" - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:arkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasComment" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasIncomingLink" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasLinkTo" - }, - "owl:minCardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasStandoffLinkTo" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:versionArkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "rdfs:label" - }, - "owl:cardinality": 1 - } - ], - "rdfs:comment": "Represents a generic link object", - "@type": "owl:Class", - "knora-api:resourceIcon": "link.gif", - "@id": "knora-api:LinkObj" - }, - { - "rdfs:label": "List Node", - "rdfs:comment": "Represents a list node.", - "@type": "rdfs:Datatype", - "owl:onDatatype": { - "@id": "xsd:string" - }, - "@id": "knora-api:ListNode" - }, - { - "rdfs:label": "Representation (Movie)", - "rdfs:subClassOf": [ - { - "@id": "knora-api:Representation" - }, - { - "@id": "http://www.w3.org/ns/ma-ont#VideoTrack" - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:arkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasIncomingLink" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasMovingImageFile" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasStandoffLinkTo" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:versionArkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "rdfs:label" - }, - "owl:cardinality": 1 - } - ], - "rdfs:comment": "A resource containing moving image data", - "@type": "owl:Class", - "@id": "knora-api:MovingImageRepresentation" - }, - { - "rdfs:label": "Region", - "rdfs:subClassOf": [ - { - "@id": "knora-api:Resource" - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:arkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasColor" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasComment" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasGeometry" - }, - "owl:minCardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasIncomingLink" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasStandoffLinkTo" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:isRegionOf" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:versionArkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "rdfs:label" - }, - "owl:cardinality": 1 - } - ], - "rdfs:comment": "Represents a geometric region of a resource. The geometry is represented currently as JSON string.", - "@type": "owl:Class", - "knora-api:resourceIcon": "region.gif", - "@id": "knora-api:Region" - }, - { - "rdfs:label": "Representation", - "rdfs:subClassOf": [ - { - "@id": "knora-api:Resource" - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:arkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasFile" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasIncomingLink" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasStandoffLinkTo" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:versionArkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "rdfs:label" - }, - "owl:cardinality": 1 - } - ], - "rdfs:comment": "A resource that can store a file", - "@type": "owl:Class", - "@id": "knora-api:Representation" - }, - { - "rdfs:label": "Resource", - "rdfs:subClassOf": [ - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:arkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasIncomingLink" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasStandoffLinkTo" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:versionArkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "rdfs:label" - }, - "owl:cardinality": 1 - } - ], - "rdfs:comment": "Represents something in the world, or an abstract thing", - "@type": "owl:Class", - "@id": "knora-api:Resource" - }, - { - "rdfs:label": "Segment", - "rdfs:subClassOf": [ - { - "@id": "knora-api:Resource" - }, - { - "@id": "http://schema.org/Clip" - }, - { - "@id": "http://www.w3.org/ns/ma-ont#Track" - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:arkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasComment" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasDescription" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasIncomingLink" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasKeyword" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasSegmentBounds" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasStandoffLinkTo" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasTitle" - }, - "owl:maxCardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:isSegmentOf" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:relatesTo" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:versionArkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "rdfs:label" - }, - "owl:cardinality": 1 - } - ], - "rdfs:comment": "A resource representing a sub-segment of another resource", - "@type": "owl:Class", - "@id": "knora-api:Segment" - }, - { - "rdfs:label": "still image abstract file value", - "rdfs:subClassOf": [ - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasAuthorship" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasCopyrightHolder" - }, - "owl:maxCardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasLicenseDate" - }, - "owl:maxCardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasLicenseText" - }, - "owl:maxCardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasLicenseUri" - }, - "owl:maxCardinality": 1 - } - ], - "rdfs:comment": "A file containing a two-dimensional still image", - "@type": "owl:Class", - "@id": "knora-api:StillImageAbstractFileValue" - }, - { - "rdfs:label": "Representation (Image)", - "rdfs:subClassOf": [ - { - "@id": "knora-api:Representation" - }, - { - "@id": "http://www.w3.org/ns/ma-ont#Image" - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:arkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasIncomingLink" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasStandoffLinkTo" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasStillImageFile" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:versionArkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "rdfs:label" - }, - "owl:cardinality": 1 - } - ], - "rdfs:comment": "A resource that can contain a two-dimensional still image file", - "@type": "owl:Class", - "@id": "knora-api:StillImageRepresentation" - }, - { - "rdfs:label": "Representation (Text)", - "rdfs:subClassOf": [ - { - "@id": "knora-api:Representation" - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:arkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasIncomingLink" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasStandoffLinkTo" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasTextFile" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:versionArkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "rdfs:label" - }, - "owl:cardinality": 1 - } - ], - "rdfs:comment": "A resource containing a text file", - "@type": "owl:Class", - "@id": "knora-api:TextRepresentation" - }, - { - "@id": "knora-api:TextValueType", - "@type": "owl:Class", - "rdfs:label": "Text Value Type", - "rdfs:comment": "Indicates a type of text value." - }, - { - "@id": "knora-api:UndefinedTextType", - "@type": "owl:Class", - "rdfs:label": "Undefined Text Type", - "rdfs:comment": "Indicates that a text value is not well defined because its ontological definition does not match the instantiated data." - }, - { - "@id": "knora-api:UnformattedText", - "@type": "owl:Class", - "rdfs:label": "Unformatted Text", - "rdfs:comment": "Indicates that a text value is unformatted in terms of its ontology and when instantiated." - }, - { - "rdfs:label": "Videosegment", - "rdfs:subClassOf": [ - { - "@id": "knora-api:Segment" - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:arkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasComment" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasDescription" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasIncomingLink" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasKeyword" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasSegmentBounds" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasStandoffLinkTo" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasTitle" - }, - "owl:maxCardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:isVideoSegmentOf" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:relatesTo" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:versionArkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "rdfs:label" - }, - "owl:cardinality": 1 - } - ], - "rdfs:comment": "A segment of a video resource", - "@type": "owl:Class", - "@id": "knora-api:VideoSegment" - }, - { - "rdfs:label": "a TextRepresentation representing an XSL transformation that can be applied to an XML created from standoff. The transformation's result is ecptected to be HTML.", - "rdfs:subClassOf": [ - { - "@id": "knora-api:TextRepresentation" - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:arkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasIncomingLink" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasStandoffLinkTo" - }, - "owl:minCardinality": 0 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:hasTextFile" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "knora-api:versionArkUrl" - }, - "owl:cardinality": 1 - }, - { - "@type": "owl:Restriction", - "owl:onProperty": { - "@id": "rdfs:label" - }, - "owl:cardinality": 1 - } - ], - "rdfs:comment": "a TextRepresentation representing an XSL transformation that can be applied to an XML created from standoff. The transformation's result is ecptected to be HTML.", - "@type": "owl:Class", - "@id": "knora-api:XSLTransformation" - }, - { - "rdfs:label": "ARK URL", - "rdfs:comment": "Provides the ARK URL of a resource.", - "@type": "owl:DatatypeProperty", - "knora-api:objectType": { - "@id": "xsd:anyURI" - }, - "@id": "knora-api:arkUrl" - }, - { - "rdfs:label": "error", - "rdfs:comment": "Provides a message indicating that an operation was unsuccessful", - "@type": "owl:DatatypeProperty", - "knora-api:objectType": { - "@id": "xsd:string" - }, - "@id": "knora-api:error" - }, - { - "rdfs:label": "external URL", - "@type": "owl:DatatypeProperty", - "knora-api:subjectType": { - "@id": "knora-api:File" - }, - "knora-api:objectType": { - "@id": "xsd:anyURI" - }, - "@id": "knora-api:externalUrl" - }, - { - "rdfs:label": "has zip", - "rdfs:subPropertyOf": { - "@id": "knora-api:hasFile" - }, - "rdfs:comment": "Connects a Representation to a zip archive", - "@type": "owl:DatatypeProperty", - "knora-api:subjectType": { - "@id": "knora-api:ArchiveRepresentation" - }, - "knora-api:objectType": { - "@id": "knora-api:File" - }, - "@id": "knora-api:hasArchiveFile" - }, - { - "rdfs:label": "has audio file", - "rdfs:subPropertyOf": { - "@id": "knora-api:hasFile" - }, - "rdfs:comment": "Connects a Representation to an audio file", - "@type": "owl:DatatypeProperty", - "knora-api:subjectType": { - "@id": "knora-api:AudioRepresentation" - }, - "knora-api:objectType": { - "@id": "knora-api:File" - }, - "@id": "knora-api:hasAudioFile" - }, - { - "@id": "knora-api:hasAuthorship", - "@type": "owl:DatatypeProperty", - "knora-api:objectType": { - "@id": "xsd:string" - }, - "rdfs:comment": "Credit, Moral Rights, Author(s)" - }, - { - "rdfs:label": "Color", - "rdfs:subPropertyOf": { - "@id": "knora-api:hasValue" - }, - "rdfs:comment": "Specifies the color of a region.", - "@type": "owl:DatatypeProperty", - "knora-api:subjectType": { - "@id": "knora-api:Region" - }, - "knora-api:objectType": { - "@id": "knora-api:Color" - }, - "@id": "knora-api:hasColor" - }, - { - "rdfs:label": "Comment", - "rdfs:subPropertyOf": { - "@id": "knora-api:hasValue" - }, - "rdfs:comment": "Represents a comment on a resource as a knora-base:TextValue", - "@type": "owl:DatatypeProperty", - "knora-api:subjectType": { - "@id": "knora-api:Resource" - }, - "knora-api:objectType": { - "@id": "xsd:string" - }, - "@id": "knora-api:hasComment" - }, - { -<<<<<<< HEAD - "@id": "knora-api:hasCopyrightHolder", -||||||| c984b8c09 - "@id": "knora-api:hasCopyrightAttribution", -======= - "rdfs:label": "copyright attribution", - "rdfs:comment": "The copyright statement that gives credit to the original author.", ->>>>>>> main - "@type": "owl:DatatypeProperty", - "knora-api:objectType": { - "@id": "xsd:string" - }, -<<<<<<< HEAD - "rdfs:comment": "The copyright holder." -||||||| c984b8c09 - "rdfs:comment": "The copyright statement that gives credit to the original author." -======= - "@id": "knora-api:hasCopyrightAttribution" ->>>>>>> main - }, - { - "rdfs:label": "has 3D-file", - "rdfs:subPropertyOf": { - "@id": "knora-api:hasFile" - }, - "rdfs:comment": "Connects a Representation to a 3D-file", - "@type": "owl:DatatypeProperty", - "knora-api:subjectType": { - "@id": "knora-api:DDDRepresentation" - }, - "knora-api:objectType": { - "@id": "knora-api:File" - }, - "@id": "knora-api:hasDDDFile" - }, - { - "rdfs:label": "Description", - "rdfs:subPropertyOf": [ - { - "@id": "knora-api:hasValue" - }, - { - "@id": "http://purl.org/dc/terms/description" - }, - { - "@id": "http://schema.org/abstract" - }, - { - "@id": "http://www.w3.org/ns/ma-ont#description" - } - ], - "rdfs:comment": "Indicates the description of a resource", - "@type": "owl:DatatypeProperty", - "knora-api:subjectType": { - "@id": "knora-api:Resource" - }, - "knora-api:objectType": { - "@id": "xsd:string" - }, - "@id": "knora-api:hasDescription" - }, - { - "rdfs:label": "has document", - "rdfs:subPropertyOf": { - "@id": "knora-api:hasFile" - }, - "rdfs:comment": "Connects a Representation to a document", - "@type": "owl:DatatypeProperty", - "knora-api:subjectType": { - "@id": "knora-api:DocumentRepresentation" - }, - "knora-api:objectType": { - "@id": "knora-api:File" - }, - "@id": "knora-api:hasDocumentFile" - }, - { - "rdfs:label": "has file", - "rdfs:subPropertyOf": { - "@id": "knora-api:hasValue" - }, - "rdfs:comment": "Connects a Representation to a file", - "@type": "owl:DatatypeProperty", - "knora-api:subjectType": { - "@id": "knora-api:Representation" - }, - "knora-api:objectType": { - "@id": "knora-api:File" - }, - "@id": "knora-api:hasFile" - }, - { - "rdfs:label": "Geometry", - "rdfs:subPropertyOf": { - "@id": "knora-api:hasValue" - }, - "rdfs:comment": "Represents a geometrical shape.", - "@type": "owl:DatatypeProperty", - "knora-api:subjectType": { - "@id": "knora-api:Region" - }, - "knora-api:objectType": { - "@id": "knora-api:Geom" - }, - "@id": "knora-api:hasGeometry" - }, - { - "rdfs:label": "has incoming link", - "rdfs:subPropertyOf": { - "@id": "knora-api:hasLinkTo" - }, - "rdfs:comment": "Indicates that this resource referred to by another resource", - "@type": "owl:ObjectProperty", - "knora-api:subjectType": { - "@id": "knora-api:Resource" - }, - "knora-api:objectType": { - "@id": "knora-api:Resource" - }, - "@id": "knora-api:hasIncomingLink" - }, - { - "rdfs:label": "Keywords", - "rdfs:subPropertyOf": [ - { - "@id": "knora-api:hasValue" - }, - { - "@id": "http://purl.org/dc/terms/subject" - }, - { - "@id": "http://schema.org/keywords" - }, - { - "@id": "http://www.w3.org/ns/ma-ont#hasKeyword" - } - ], - "rdfs:comment": "Indicates a keyword of a resource", - "@type": "owl:DatatypeProperty", - "knora-api:subjectType": { - "@id": "knora-api:Resource" - }, - "knora-api:objectType": { - "@id": "xsd:string" - }, - "@id": "knora-api:hasKeyword" - }, - { -<<<<<<< HEAD - "@id": "knora-api:hasLicenseDate", - "@type": "owl:DatatypeProperty", - "knora-api:objectType": { - "@id": "xsd:date" - }, - "rdfs:comment": "Date of creation on platform." - }, - { - "@id": "knora-api:hasLicenseText", -||||||| c984b8c09 - "@id": "knora-api:hasLicenseText", -======= - "rdfs:label": "license text", - "rdfs:comment": "Specifies the terms under which a work can be used. This statement may be a reference to a well-known license, such as Creative Commons (e.g. 'CC BY-SA') or a custom license.", ->>>>>>> main - "@type": "owl:DatatypeProperty", - "knora-api:objectType": { - "@id": "xsd:string" - }, - "@id": "knora-api:hasLicenseText" - }, - { - "rdfs:label": "license URI", - "rdfs:comment": "Canonical link to license.", - "@type": "owl:DatatypeProperty", - "knora-api:objectType": { - "@id": "xsd:anyUri" - }, - "@id": "knora-api:hasLicenseUri" - }, - { - "rdfs:label": "has Link to", - "rdfs:subPropertyOf": { - "@id": "knora-api:resourceProperty" - }, - "rdfs:comment": "Represents a direct connection between two resources", - "@type": "owl:ObjectProperty", - "knora-api:subjectType": { - "@id": "knora-api:Resource" - }, - "knora-api:objectType": { - "@id": "knora-api:Resource" - }, - "@id": "knora-api:hasLinkTo" - }, - { - "rdfs:label": "has movie file", - "rdfs:subPropertyOf": { - "@id": "knora-api:hasFile" - }, - "rdfs:comment": "Connects a Representation to a movie file", - "@type": "owl:DatatypeProperty", - "knora-api:subjectType": { - "@id": "knora-api:MovingImageRepresentation" - }, - "knora-api:objectType": { - "@id": "knora-api:File" - }, - "@id": "knora-api:hasMovingImageFile" - }, - { - "rdfs:label": "has Representation", - "rdfs:subPropertyOf": { - "@id": "knora-api:hasLinkTo" - }, - "rdfs:comment": "References an instance of a Representation. A Representation contains the metadata of a digital object (= file) which represents some physical entity such as an image, a sound, an encoded text etc.", - "@type": "owl:ObjectProperty", - "knora-api:subjectType": { - "@id": "knora-api:Resource" - }, - "knora-api:objectType": { - "@id": "knora-api:Representation" - }, - "@id": "knora-api:hasRepresentation" - }, - { - "rdfs:label": "Segment Bounds", - "rdfs:subPropertyOf": { - "@id": "knora-api:hasValue" - }, - "rdfs:comment": "Indicates the bounds of a segment, i.e. the start and end point in the containing resource.", - "@type": "owl:DatatypeProperty", - "knora-api:subjectType": { - "@id": "knora-api:Segment" - }, - "knora-api:objectType": { - "@id": "knora-api:Interval" - }, - "@id": "knora-api:hasSegmentBounds" - }, - { - "rdfs:label": "Sequence Bounds", - "rdfs:subPropertyOf": { - "@id": "knora-api:hasValue" - }, - "rdfs:comment": "Indicates the bounds of a sequence, i.e. the start and end point in the containing resource.", - "@type": "owl:DatatypeProperty", - "knora-api:objectType": { - "@id": "knora-api:Interval" - }, - "@id": "knora-api:hasSequenceBounds" - }, - { - "rdfs:label": "has Standoff Link to", - "rdfs:subPropertyOf": { - "@id": "knora-api:hasLinkTo" - }, - "rdfs:comment": "Represents a link in standoff markup from one resource to another.", - "@type": "owl:ObjectProperty", - "knora-api:subjectType": { - "@id": "knora-api:Resource" - }, - "knora-api:objectType": { - "@id": "knora-api:Resource" - }, - "@id": "knora-api:hasStandoffLinkTo" - }, - { - "rdfs:label": "has image file", - "rdfs:subPropertyOf": { - "@id": "knora-api:hasFile" - }, - "rdfs:comment": "Connects a Representation to an image file", - "@type": "owl:ObjectProperty", - "knora-api:subjectType": { - "@id": "knora-api:StillImageRepresentation" - }, - "knora-api:objectType": { - "@id": "knora-api:StillImageAbstractFileValue" - }, - "@id": "knora-api:hasStillImageFile" - }, - { - "rdfs:label": "has text file", - "rdfs:subPropertyOf": { - "@id": "knora-api:hasFile" - }, - "rdfs:comment": "Connects a Representation to a text file", - "@type": "owl:DatatypeProperty", - "knora-api:subjectType": { - "@id": "knora-api:TextRepresentation" - }, - "knora-api:objectType": { - "@id": "knora-api:File" - }, - "@id": "knora-api:hasTextFile" - }, - { - "rdfs:label": "has text value property type", - "rdfs:comment": "Indicates the type of text value property.", - "@type": "owl:AnnotationProperty", - "knora-api:subjectType": { - "@id": "owl:ObjectProperty" - }, - "knora-api:objectType": { - "@id": "knora-api:TextValueType" - }, - "@id": "knora-api:hasTextValuePropertyType" - }, - { - "rdfs:label": "has text value type", - "rdfs:comment": "Indicates the type of text value.", - "@type": "owl:ObjectProperty", - "knora-api:subjectType": { - "@id": "xsd:string" - }, - "knora-api:objectType": { - "@id": "knora-api:TextValueType" - }, - "@id": "knora-api:hasTextValueType" - }, - { - "rdfs:label": "Title", - "rdfs:subPropertyOf": [ - { - "@id": "knora-api:hasValue" - }, - { - "@id": "http://purl.org/dc/terms/title" - }, - { - "@id": "http://schema.org/headline" - }, - { - "@id": "http://www.w3.org/ns/ma-ont#title" - } - ], - "rdfs:comment": "Indicates the title of a resource", - "@type": "owl:DatatypeProperty", - "knora-api:subjectType": { - "@id": "knora-api:Resource" - }, - "knora-api:objectType": { - "@id": "xsd:string" - }, - "@id": "knora-api:hasTitle" - }, - { - "rdfs:label": "has value", - "rdfs:subPropertyOf": { - "@id": "knora-api:resourceProperty" - }, - "rdfs:comment": "The base property of properties that point from Knora resources to Knora values.", - "@type": "owl:DatatypeProperty", - "knora-api:subjectType": { - "@id": "knora-api:Resource" - }, - "@id": "knora-api:hasValue" - }, - { - "rdfs:label": "is Annotation of", - "rdfs:subPropertyOf": { - "@id": "knora-api:hasLinkTo" - }, - "@type": "owl:ObjectProperty", - "knora-api:subjectType": { - "@id": "knora-api:Annotation" - }, - "knora-api:objectType": { - "@id": "knora-api:Resource" - }, - "@id": "knora-api:isAnnotationOf" - }, - { - "rdfs:label": "Segment of", - "rdfs:subPropertyOf": { - "@id": "knora-api:isSegmentOf" - }, - "rdfs:comment": "Indicates that this resource is a segment of an audio resource", - "@type": "owl:ObjectProperty", - "knora-api:subjectType": { - "@id": "knora-api:AudioSegment" - }, - "knora-api:objectType": { - "@id": "knora-api:AudioRepresentation" - }, - "@id": "knora-api:isAudioSegmentOf" - }, - { - "rdfs:label": "is main resource", - "rdfs:comment": "Indicates if the given resource is the main resource of a request or a resource referred to by a link property.", - "@type": "owl:DatatypeProperty", - "knora-api:subjectType": { - "@id": "knora-api:Resource" - }, - "knora-api:objectType": { - "@id": "xsd:boolean" - }, - "@id": "knora-api:isMainResource" - }, - { - "rdfs:label": "is part of", - "rdfs:subPropertyOf": { - "@id": "knora-api:hasLinkTo" - }, - "rdfs:comment": "Indicates that this resource is part of another resource", - "@type": "owl:ObjectProperty", - "knora-api:subjectType": { - "@id": "knora-api:Resource" - }, - "knora-api:objectType": { - "@id": "knora-api:Resource" - }, - "@id": "knora-api:isPartOf" - }, - { - "rdfs:label": "is region of", - "rdfs:subPropertyOf": { - "@id": "knora-api:hasLinkTo" - }, - "rdfs:comment": "Region of interest within a digital object (e.g. an image)", - "@type": "owl:ObjectProperty", - "knora-api:subjectType": { - "@id": "knora-api:Region" - }, - "knora-api:objectType": { - "@id": "knora-api:Representation" - }, - "@id": "knora-api:isRegionOf" - }, - { - "rdfs:label": "Segment of", - "rdfs:subPropertyOf": [ - { - "@id": "knora-api:hasLinkTo" - }, - { - "@id": "http://schema.org/isPartOf" - }, - { - "@id": "http://www.w3.org/ns/ma-ont#isFragmentOf" - } - ], - "rdfs:comment": "Indicates that this resource is a segment of a video or audio resource", - "@type": "owl:ObjectProperty", - "knora-api:subjectType": { - "@id": "knora-api:Segment" - }, - "knora-api:objectType": { - "@id": "knora-api:Representation" - }, - "@id": "knora-api:isSegmentOf" - }, - { - "rdfs:label": "is sequence of", - "rdfs:subPropertyOf": { - "@id": "knora-api:hasLinkTo" - }, - "rdfs:comment": "Deprecated in favour of :Segment", - "@type": "owl:ObjectProperty", - "knora-api:subjectType": { - "@id": "knora-api:Resource" - }, - "knora-api:objectType": { - "@id": "knora-api:Resource" - }, - "@id": "knora-api:isSequenceOf" - }, - { - "rdfs:label": "Segment of", - "rdfs:subPropertyOf": { - "@id": "knora-api:isSegmentOf" - }, - "rdfs:comment": "Indicates that this resource is a segment of a video resource", - "@type": "owl:ObjectProperty", - "knora-api:subjectType": { - "@id": "knora-api:VideoSegment" - }, - "knora-api:objectType": { - "@id": "knora-api:MovingImageRepresentation" - }, - "@id": "knora-api:isVideoSegmentOf" - }, - { - "rdfs:label": "May have more results", - "rdfs:comment": "Indicates whether more results may be available for a search query", - "@type": "owl:DatatypeProperty", - "knora-api:objectType": { - "@id": "xsd:boolean" - }, - "@id": "knora-api:mayHaveMoreResults" - }, - { - "@id": "knora-api:objectType", - "@type": "rdf:Property", - "rdfs:label": "Object type", - "rdfs:comment": "Specifies the required type of the objects of a property" - }, - { - "rdfs:label": "Related Resources", - "rdfs:subPropertyOf": [ - { - "@id": "knora-api:hasLinkTo" - }, - { - "@id": "http://purl.org/dc/terms/relation" - }, - { - "@id": "http://www.w3.org/ns/ma-ont#hasRelatedResource" - } - ], - "rdfs:comment": "Indicates that this resource relates to another resource", - "@type": "owl:ObjectProperty", - "knora-api:subjectType": { - "@id": "knora-api:Resource" - }, - "knora-api:objectType": { - "@id": "knora-api:Resource" - }, - "@id": "knora-api:relatesTo" - }, - { - "rdfs:label": "resource icon", - "@type": "owl:DatatypeProperty", - "knora-api:subjectType": { - "@id": "owl:Class" - }, - "knora-api:objectType": { - "@id": "xsd:string" - }, - "@id": "knora-api:resourceIcon" - }, - { - "rdfs:label": "Resource property", - "rdfs:subPropertyOf": { - "@id": "knora-api:resourceProperty" - }, - "rdfs:comment": "The base property of properties that point from Knora resources to Knora resources or values. These properties are required to have cardinalities in the resource classes in which they are used.", - "@type": "rdf:Property", - "knora-api:subjectType": { - "@id": "knora-api:Resource" - }, - "@id": "knora-api:resourceProperty" - }, - { - "rdfs:label": "result", - "rdfs:comment": "Provides a message indicating that an operation was successful", - "@type": "owl:DatatypeProperty", - "knora-api:objectType": { - "@id": "xsd:string" - }, - "@id": "knora-api:result" - }, - { - "rdfs:label": "Sequence number", - "rdfs:subPropertyOf": { - "@id": "knora-api:hasValue" - }, - "rdfs:comment": "Indicates the position of a resource within a compound object. Typically used to indicate the order of pages within a book or similar resource.", - "@type": "owl:DatatypeProperty", - "knora-api:objectType": { - "@id": "xsd:integer" - }, - "@id": "knora-api:seqnum" - }, - { - "@id": "knora-api:subjectType", - "@type": "rdf:Property", - "rdfs:label": "Subject type", - "rdfs:comment": "Specifies the required type of the subjects of a property" - }, - { - "rdfs:label": "version ARK URL", - "rdfs:comment": "Provides the ARK URL of a particular version of a resource.", - "@type": "owl:DatatypeProperty", - "knora-api:objectType": { - "@id": "xsd:anyURI" - }, - "@id": "knora-api:versionArkUrl" - }, - { - "@id": "rdfs:label", - "@type": "owl:DatatypeProperty" - } - ], - "@context": { - "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - "knora-api": "http://api.knora.org/ontology/knora-api/simple/v2#", - "owl": "http://www.w3.org/2002/07/owl#", - "rdfs": "http://www.w3.org/2000/01/rdf-schema#", - "xsd": "http://www.w3.org/2001/XMLSchema#" - } -} \ No newline at end of file From 406295b3b8bca027f719feeeabdebc8e66af9767 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 15 Jan 2025 14:34:22 +0100 Subject: [PATCH 15/31] rm unused imports --- .../messages/v2/responder/valuemessages/ValueMessagesV2.scala | 2 -- .../webapi/slice/admin/domain/model/CopyrightAndLicenses.scala | 2 -- 2 files changed, 4 deletions(-) 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 8b0d456905..f06f4330ad 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 @@ -6,8 +6,6 @@ package org.knora.webapi.messages.v2.responder.valuemessages import org.apache.jena.rdf.model.Resource -import org.apache.jena.riot.Lang -import org.apache.jena.riot.RDFDataMgr import org.apache.jena.vocabulary.XSD import zio.IO import zio.ZIO diff --git a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala index bbfecc9a7a..d17e971755 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala @@ -13,8 +13,6 @@ import org.knora.webapi.slice.common.StringValueCompanion.maxLength import org.knora.webapi.slice.common.Value import org.knora.webapi.slice.common.Value.StringValue import org.knora.webapi.slice.common.WithFrom -import zio.Clock -import zio.UIO final case class CopyrightHolder private (override val value: String) extends StringValue object CopyrightHolder extends StringValueCompanion[CopyrightHolder] { From 7c33ce580da52bbd56f8b339417b4963e82d9494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 15 Jan 2025 14:35:16 +0100 Subject: [PATCH 16/31] regenerate knoraApiOntologySimple.jsonld --- .../knoraApiOntologySimple.jsonld | 1893 +++++++++++++++++ 1 file changed, 1893 insertions(+) create mode 100644 test_data/generated_test_data/ontologyR2RV2/knoraApiOntologySimple.jsonld diff --git a/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologySimple.jsonld b/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologySimple.jsonld new file mode 100644 index 0000000000..744882fe82 --- /dev/null +++ b/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologySimple.jsonld @@ -0,0 +1,1893 @@ +{ + "@id": "http://api.knora.org/ontology/knora-api/simple/v2", + "@type": "owl:Ontology", + "rdfs:label": "The knora-api ontology in the simple schema", + "@graph": [ + { + "rdfs:label": "Annotation", + "rdfs:subClassOf": [ + { + "@id": "knora-api:Resource" + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:arkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasComment" + }, + "owl:minCardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasIncomingLink" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasStandoffLinkTo" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:isAnnotationOf" + }, + "owl:minCardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:versionArkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "rdfs:label" + }, + "owl:cardinality": 1 + } + ], + "rdfs:comment": "A generic class for representing annotations", + "@type": "owl:Class", + "@id": "knora-api:Annotation" + }, + { + "@id": "knora-api:ArchiveRepresentation", + "@type": "owl:Class", + "rdfs:subClassOf": [ + { + "@id": "knora-api:Representation" + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:arkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasArchiveFile" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasIncomingLink" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasStandoffLinkTo" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:versionArkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "rdfs:label" + }, + "owl:cardinality": 1 + } + ], + "rdfs:label": "Representation (Zip)" + }, + { + "rdfs:label": "Representation (Audio)", + "rdfs:subClassOf": [ + { + "@id": "knora-api:Representation" + }, + { + "@id": "http://www.w3.org/ns/ma-ont#AudioTrack" + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:arkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasAudioFile" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasIncomingLink" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasStandoffLinkTo" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:versionArkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "rdfs:label" + }, + "owl:cardinality": 1 + } + ], + "rdfs:comment": "Represents a file containing audio data", + "@type": "owl:Class", + "@id": "knora-api:AudioRepresentation" + }, + { + "rdfs:label": "Audiosegment", + "rdfs:subClassOf": [ + { + "@id": "knora-api:Segment" + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:arkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasComment" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasDescription" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasIncomingLink" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasKeyword" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasSegmentBounds" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasStandoffLinkTo" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasTitle" + }, + "owl:maxCardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:isAudioSegmentOf" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:relatesTo" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:versionArkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "rdfs:label" + }, + "owl:cardinality": 1 + } + ], + "rdfs:comment": "A segment of an audio resource", + "@type": "owl:Class", + "@id": "knora-api:AudioSegment" + }, + { + "rdfs:label": "Color literal", + "rdfs:comment": "Represents a color.", + "@type": "rdfs:Datatype", + "owl:withRestrictions": { + "xsd:pattern": "#([0-9a-fA-F]{3}){1,2}" + }, + "owl:onDatatype": { + "@id": "xsd:string" + }, + "@id": "knora-api:Color" + }, + { + "@id": "knora-api:CustomFormattedText", + "@type": "owl:Class", + "rdfs:label": "Custom Formatted Text", + "rdfs:comment": "Indicates that a text value is formatted in terms of its ontology and when instantiated, using a project defined custom standoff formatting." + }, + { + "rdfs:label": "Representation (3D)", + "rdfs:subClassOf": [ + { + "@id": "knora-api:Representation" + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:arkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasDDDFile" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasIncomingLink" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasStandoffLinkTo" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:versionArkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "rdfs:label" + }, + "owl:cardinality": 1 + } + ], + "rdfs:comment": "Represents a file containg 3D data", + "@type": "owl:Class", + "@id": "knora-api:DDDRepresentation" + }, + { + "rdfs:label": "Date literal", + "rdfs:comment": "Represents a date as a period with different possible precisions.", + "@type": "rdfs:Datatype", + "owl:withRestrictions": { + "xsd:pattern": "(GREGORIAN|JULIAN|ISLAMIC):\\d{1,4}(-\\d{1,2}(-\\d{1,2})?)?( BC| AD| BCE| CE)?(:\\d{1,4}(-\\d{1,2}(-\\d{1,2})?)?( BC| AD| BCE| CE)?)?" + }, + "owl:onDatatype": { + "@id": "xsd:string" + }, + "@id": "knora-api:Date" + }, + { + "rdfs:label": "Deleted Resource", + "rdfs:subClassOf": [ + { + "@id": "knora-api:Resource" + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:arkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasIncomingLink" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasStandoffLinkTo" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:versionArkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "rdfs:label" + }, + "owl:cardinality": 1 + } + ], + "rdfs:comment": "Generic representation of a deleted resource that can therefore not be displayed", + "@type": "owl:Class", + "@id": "knora-api:DeletedResource" + }, + { + "@id": "knora-api:DeletedValue", + "@type": "owl:Class", + "rdfs:label": "Deleted Value", + "rdfs:comment": "Generic representation of a deleted value that can therefore not be displayed" + }, + { + "@id": "knora-api:DocumentRepresentation", + "@type": "owl:Class", + "rdfs:subClassOf": [ + { + "@id": "knora-api:Representation" + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:arkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasDocumentFile" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasIncomingLink" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasStandoffLinkTo" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:versionArkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "rdfs:label" + }, + "owl:cardinality": 1 + } + ], + "rdfs:label": "Representation (Document)" + }, + { + "rdfs:label": "File URI", + "rdfs:comment": "Represents a file URI.", + "@type": "rdfs:Datatype", + "owl:onDatatype": { + "@id": "xsd:anyURI" + }, + "@id": "knora-api:File" + }, + { + "@id": "knora-api:FormattedText", + "@type": "owl:Class", + "rdfs:label": "Formatted Text", + "rdfs:comment": "Indicates that a text value is formatted in terms of its ontology and when instantiated, using DSP standard standoff formatting." + }, + { + "rdfs:label": "Geometry specification", + "rdfs:comment": "Represents a geometry specification in JSON.", + "@type": "rdfs:Datatype", + "owl:onDatatype": { + "@id": "xsd:string" + }, + "@id": "knora-api:Geom" + }, + { + "rdfs:label": "Geoname code", + "rdfs:comment": "Represents a Geoname code.", + "@type": "rdfs:Datatype", + "owl:withRestrictions": { + "xsd:pattern": "\\d{1,8}" + }, + "owl:onDatatype": { + "@id": "xsd:string" + }, + "@id": "knora-api:Geoname" + }, + { + "rdfs:label": "Interval literal", + "rdfs:comment": "Represents an interval.", + "@type": "rdfs:Datatype", + "owl:withRestrictions": { + "xsd:pattern": "\\d+(\\.\\d+)?,\\d+(\\.\\d+)?" + }, + "owl:onDatatype": { + "@id": "xsd:string" + }, + "@id": "knora-api:Interval" + }, + { + "rdfs:label": "Link Object", + "rdfs:subClassOf": [ + { + "@id": "knora-api:Resource" + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:arkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasComment" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasIncomingLink" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasLinkTo" + }, + "owl:minCardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasStandoffLinkTo" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:versionArkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "rdfs:label" + }, + "owl:cardinality": 1 + } + ], + "rdfs:comment": "Represents a generic link object", + "@type": "owl:Class", + "knora-api:resourceIcon": "link.gif", + "@id": "knora-api:LinkObj" + }, + { + "rdfs:label": "List Node", + "rdfs:comment": "Represents a list node.", + "@type": "rdfs:Datatype", + "owl:onDatatype": { + "@id": "xsd:string" + }, + "@id": "knora-api:ListNode" + }, + { + "rdfs:label": "Representation (Movie)", + "rdfs:subClassOf": [ + { + "@id": "knora-api:Representation" + }, + { + "@id": "http://www.w3.org/ns/ma-ont#VideoTrack" + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:arkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasIncomingLink" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasMovingImageFile" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasStandoffLinkTo" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:versionArkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "rdfs:label" + }, + "owl:cardinality": 1 + } + ], + "rdfs:comment": "A resource containing moving image data", + "@type": "owl:Class", + "@id": "knora-api:MovingImageRepresentation" + }, + { + "rdfs:label": "Region", + "rdfs:subClassOf": [ + { + "@id": "knora-api:Resource" + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:arkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasColor" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasComment" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasGeometry" + }, + "owl:minCardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasIncomingLink" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasStandoffLinkTo" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:isRegionOf" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:versionArkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "rdfs:label" + }, + "owl:cardinality": 1 + } + ], + "rdfs:comment": "Represents a geometric region of a resource. The geometry is represented currently as JSON string.", + "@type": "owl:Class", + "knora-api:resourceIcon": "region.gif", + "@id": "knora-api:Region" + }, + { + "rdfs:label": "Representation", + "rdfs:subClassOf": [ + { + "@id": "knora-api:Resource" + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:arkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasFile" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasIncomingLink" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasStandoffLinkTo" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:versionArkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "rdfs:label" + }, + "owl:cardinality": 1 + } + ], + "rdfs:comment": "A resource that can store a file", + "@type": "owl:Class", + "@id": "knora-api:Representation" + }, + { + "rdfs:label": "Resource", + "rdfs:subClassOf": [ + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:arkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasIncomingLink" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasStandoffLinkTo" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:versionArkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "rdfs:label" + }, + "owl:cardinality": 1 + } + ], + "rdfs:comment": "Represents something in the world, or an abstract thing", + "@type": "owl:Class", + "@id": "knora-api:Resource" + }, + { + "rdfs:label": "Segment", + "rdfs:subClassOf": [ + { + "@id": "knora-api:Resource" + }, + { + "@id": "http://schema.org/Clip" + }, + { + "@id": "http://www.w3.org/ns/ma-ont#Track" + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:arkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasComment" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasDescription" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasIncomingLink" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasKeyword" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasSegmentBounds" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasStandoffLinkTo" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasTitle" + }, + "owl:maxCardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:isSegmentOf" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:relatesTo" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:versionArkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "rdfs:label" + }, + "owl:cardinality": 1 + } + ], + "rdfs:comment": "A resource representing a sub-segment of another resource", + "@type": "owl:Class", + "@id": "knora-api:Segment" + }, + { + "rdfs:label": "still image abstract file value", + "rdfs:subClassOf": [ + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasAuthorship" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasCopyrightHolder" + }, + "owl:maxCardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasLicenseDate" + }, + "owl:maxCardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasLicenseText" + }, + "owl:maxCardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasLicenseUri" + }, + "owl:maxCardinality": 1 + } + ], + "rdfs:comment": "A file containing a two-dimensional still image", + "@type": "owl:Class", + "@id": "knora-api:StillImageAbstractFileValue" + }, + { + "rdfs:label": "Representation (Image)", + "rdfs:subClassOf": [ + { + "@id": "knora-api:Representation" + }, + { + "@id": "http://www.w3.org/ns/ma-ont#Image" + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:arkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasIncomingLink" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasStandoffLinkTo" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasStillImageFile" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:versionArkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "rdfs:label" + }, + "owl:cardinality": 1 + } + ], + "rdfs:comment": "A resource that can contain a two-dimensional still image file", + "@type": "owl:Class", + "@id": "knora-api:StillImageRepresentation" + }, + { + "rdfs:label": "Representation (Text)", + "rdfs:subClassOf": [ + { + "@id": "knora-api:Representation" + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:arkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasIncomingLink" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasStandoffLinkTo" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasTextFile" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:versionArkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "rdfs:label" + }, + "owl:cardinality": 1 + } + ], + "rdfs:comment": "A resource containing a text file", + "@type": "owl:Class", + "@id": "knora-api:TextRepresentation" + }, + { + "@id": "knora-api:TextValueType", + "@type": "owl:Class", + "rdfs:label": "Text Value Type", + "rdfs:comment": "Indicates a type of text value." + }, + { + "@id": "knora-api:UndefinedTextType", + "@type": "owl:Class", + "rdfs:label": "Undefined Text Type", + "rdfs:comment": "Indicates that a text value is not well defined because its ontological definition does not match the instantiated data." + }, + { + "@id": "knora-api:UnformattedText", + "@type": "owl:Class", + "rdfs:label": "Unformatted Text", + "rdfs:comment": "Indicates that a text value is unformatted in terms of its ontology and when instantiated." + }, + { + "rdfs:label": "Videosegment", + "rdfs:subClassOf": [ + { + "@id": "knora-api:Segment" + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:arkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasComment" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasDescription" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasIncomingLink" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasKeyword" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasSegmentBounds" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasStandoffLinkTo" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasTitle" + }, + "owl:maxCardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:isVideoSegmentOf" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:relatesTo" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:versionArkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "rdfs:label" + }, + "owl:cardinality": 1 + } + ], + "rdfs:comment": "A segment of a video resource", + "@type": "owl:Class", + "@id": "knora-api:VideoSegment" + }, + { + "rdfs:label": "a TextRepresentation representing an XSL transformation that can be applied to an XML created from standoff. The transformation's result is ecptected to be HTML.", + "rdfs:subClassOf": [ + { + "@id": "knora-api:TextRepresentation" + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:arkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasIncomingLink" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasStandoffLinkTo" + }, + "owl:minCardinality": 0 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:hasTextFile" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "knora-api:versionArkUrl" + }, + "owl:cardinality": 1 + }, + { + "@type": "owl:Restriction", + "owl:onProperty": { + "@id": "rdfs:label" + }, + "owl:cardinality": 1 + } + ], + "rdfs:comment": "a TextRepresentation representing an XSL transformation that can be applied to an XML created from standoff. The transformation's result is ecptected to be HTML.", + "@type": "owl:Class", + "@id": "knora-api:XSLTransformation" + }, + { + "rdfs:label": "ARK URL", + "rdfs:comment": "Provides the ARK URL of a resource.", + "@type": "owl:DatatypeProperty", + "knora-api:objectType": { + "@id": "xsd:anyURI" + }, + "@id": "knora-api:arkUrl" + }, + { + "rdfs:label": "error", + "rdfs:comment": "Provides a message indicating that an operation was unsuccessful", + "@type": "owl:DatatypeProperty", + "knora-api:objectType": { + "@id": "xsd:string" + }, + "@id": "knora-api:error" + }, + { + "rdfs:label": "external URL", + "@type": "owl:DatatypeProperty", + "knora-api:subjectType": { + "@id": "knora-api:File" + }, + "knora-api:objectType": { + "@id": "xsd:anyURI" + }, + "@id": "knora-api:externalUrl" + }, + { + "rdfs:label": "has zip", + "rdfs:subPropertyOf": { + "@id": "knora-api:hasFile" + }, + "rdfs:comment": "Connects a Representation to a zip archive", + "@type": "owl:DatatypeProperty", + "knora-api:subjectType": { + "@id": "knora-api:ArchiveRepresentation" + }, + "knora-api:objectType": { + "@id": "knora-api:File" + }, + "@id": "knora-api:hasArchiveFile" + }, + { + "rdfs:label": "has audio file", + "rdfs:subPropertyOf": { + "@id": "knora-api:hasFile" + }, + "rdfs:comment": "Connects a Representation to an audio file", + "@type": "owl:DatatypeProperty", + "knora-api:subjectType": { + "@id": "knora-api:AudioRepresentation" + }, + "knora-api:objectType": { + "@id": "knora-api:File" + }, + "@id": "knora-api:hasAudioFile" + }, + { + "rdfs:label": "has authorship", + "rdfs:comment": "Credit, Moral Rights, Author(s)", + "@type": "owl:DatatypeProperty", + "knora-api:objectType": { + "@id": "xsd:string" + }, + "@id": "knora-api:hasAuthorship" + }, + { + "rdfs:label": "Color", + "rdfs:subPropertyOf": { + "@id": "knora-api:hasValue" + }, + "rdfs:comment": "Specifies the color of a region.", + "@type": "owl:DatatypeProperty", + "knora-api:subjectType": { + "@id": "knora-api:Region" + }, + "knora-api:objectType": { + "@id": "knora-api:Color" + }, + "@id": "knora-api:hasColor" + }, + { + "rdfs:label": "Comment", + "rdfs:subPropertyOf": { + "@id": "knora-api:hasValue" + }, + "rdfs:comment": "Represents a comment on a resource as a knora-base:TextValue", + "@type": "owl:DatatypeProperty", + "knora-api:subjectType": { + "@id": "knora-api:Resource" + }, + "knora-api:objectType": { + "@id": "xsd:string" + }, + "@id": "knora-api:hasComment" + }, + { + "rdfs:label": "has copyright holder", + "rdfs:comment": "The copyright holder.", + "@type": "owl:DatatypeProperty", + "knora-api:objectType": { + "@id": "xsd:string" + }, + "@id": "knora-api:hasCopyrightHolder" + }, + { + "rdfs:label": "has 3D-file", + "rdfs:subPropertyOf": { + "@id": "knora-api:hasFile" + }, + "rdfs:comment": "Connects a Representation to a 3D-file", + "@type": "owl:DatatypeProperty", + "knora-api:subjectType": { + "@id": "knora-api:DDDRepresentation" + }, + "knora-api:objectType": { + "@id": "knora-api:File" + }, + "@id": "knora-api:hasDDDFile" + }, + { + "rdfs:label": "Description", + "rdfs:subPropertyOf": [ + { + "@id": "knora-api:hasValue" + }, + { + "@id": "http://purl.org/dc/terms/description" + }, + { + "@id": "http://schema.org/abstract" + }, + { + "@id": "http://www.w3.org/ns/ma-ont#description" + } + ], + "rdfs:comment": "Indicates the description of a resource", + "@type": "owl:DatatypeProperty", + "knora-api:subjectType": { + "@id": "knora-api:Resource" + }, + "knora-api:objectType": { + "@id": "xsd:string" + }, + "@id": "knora-api:hasDescription" + }, + { + "rdfs:label": "has document", + "rdfs:subPropertyOf": { + "@id": "knora-api:hasFile" + }, + "rdfs:comment": "Connects a Representation to a document", + "@type": "owl:DatatypeProperty", + "knora-api:subjectType": { + "@id": "knora-api:DocumentRepresentation" + }, + "knora-api:objectType": { + "@id": "knora-api:File" + }, + "@id": "knora-api:hasDocumentFile" + }, + { + "rdfs:label": "has file", + "rdfs:subPropertyOf": { + "@id": "knora-api:hasValue" + }, + "rdfs:comment": "Connects a Representation to a file", + "@type": "owl:DatatypeProperty", + "knora-api:subjectType": { + "@id": "knora-api:Representation" + }, + "knora-api:objectType": { + "@id": "knora-api:File" + }, + "@id": "knora-api:hasFile" + }, + { + "rdfs:label": "Geometry", + "rdfs:subPropertyOf": { + "@id": "knora-api:hasValue" + }, + "rdfs:comment": "Represents a geometrical shape.", + "@type": "owl:DatatypeProperty", + "knora-api:subjectType": { + "@id": "knora-api:Region" + }, + "knora-api:objectType": { + "@id": "knora-api:Geom" + }, + "@id": "knora-api:hasGeometry" + }, + { + "rdfs:label": "has incoming link", + "rdfs:subPropertyOf": { + "@id": "knora-api:hasLinkTo" + }, + "rdfs:comment": "Indicates that this resource referred to by another resource", + "@type": "owl:ObjectProperty", + "knora-api:subjectType": { + "@id": "knora-api:Resource" + }, + "knora-api:objectType": { + "@id": "knora-api:Resource" + }, + "@id": "knora-api:hasIncomingLink" + }, + { + "rdfs:label": "Keywords", + "rdfs:subPropertyOf": [ + { + "@id": "knora-api:hasValue" + }, + { + "@id": "http://purl.org/dc/terms/subject" + }, + { + "@id": "http://schema.org/keywords" + }, + { + "@id": "http://www.w3.org/ns/ma-ont#hasKeyword" + } + ], + "rdfs:comment": "Indicates a keyword of a resource", + "@type": "owl:DatatypeProperty", + "knora-api:subjectType": { + "@id": "knora-api:Resource" + }, + "knora-api:objectType": { + "@id": "xsd:string" + }, + "@id": "knora-api:hasKeyword" + }, + { + "rdfs:label": "has license date", + "rdfs:comment": "Date of creation on platform.", + "@type": "owl:DatatypeProperty", + "knora-api:objectType": { + "@id": "xsd:date" + }, + "@id": "knora-api:hasLicenseDate" + }, + { + "rdfs:label": "has license text", + "rdfs:comment": "Specifies the terms under which a work can be used. This statement may be a reference to a well-known license, such as Creative Commons (e.g. 'CC BY-SA') or a custom license.", + "@type": "owl:DatatypeProperty", + "knora-api:objectType": { + "@id": "xsd:string" + }, + "@id": "knora-api:hasLicenseText" + }, + { + "rdfs:label": "has license URI", + "rdfs:comment": "Canonical link to license.", + "@type": "owl:DatatypeProperty", + "knora-api:objectType": { + "@id": "xsd:anyUri" + }, + "@id": "knora-api:hasLicenseUri" + }, + { + "rdfs:label": "has Link to", + "rdfs:subPropertyOf": { + "@id": "knora-api:resourceProperty" + }, + "rdfs:comment": "Represents a direct connection between two resources", + "@type": "owl:ObjectProperty", + "knora-api:subjectType": { + "@id": "knora-api:Resource" + }, + "knora-api:objectType": { + "@id": "knora-api:Resource" + }, + "@id": "knora-api:hasLinkTo" + }, + { + "rdfs:label": "has movie file", + "rdfs:subPropertyOf": { + "@id": "knora-api:hasFile" + }, + "rdfs:comment": "Connects a Representation to a movie file", + "@type": "owl:DatatypeProperty", + "knora-api:subjectType": { + "@id": "knora-api:MovingImageRepresentation" + }, + "knora-api:objectType": { + "@id": "knora-api:File" + }, + "@id": "knora-api:hasMovingImageFile" + }, + { + "rdfs:label": "has Representation", + "rdfs:subPropertyOf": { + "@id": "knora-api:hasLinkTo" + }, + "rdfs:comment": "References an instance of a Representation. A Representation contains the metadata of a digital object (= file) which represents some physical entity such as an image, a sound, an encoded text etc.", + "@type": "owl:ObjectProperty", + "knora-api:subjectType": { + "@id": "knora-api:Resource" + }, + "knora-api:objectType": { + "@id": "knora-api:Representation" + }, + "@id": "knora-api:hasRepresentation" + }, + { + "rdfs:label": "Segment Bounds", + "rdfs:subPropertyOf": { + "@id": "knora-api:hasValue" + }, + "rdfs:comment": "Indicates the bounds of a segment, i.e. the start and end point in the containing resource.", + "@type": "owl:DatatypeProperty", + "knora-api:subjectType": { + "@id": "knora-api:Segment" + }, + "knora-api:objectType": { + "@id": "knora-api:Interval" + }, + "@id": "knora-api:hasSegmentBounds" + }, + { + "rdfs:label": "Sequence Bounds", + "rdfs:subPropertyOf": { + "@id": "knora-api:hasValue" + }, + "rdfs:comment": "Indicates the bounds of a sequence, i.e. the start and end point in the containing resource.", + "@type": "owl:DatatypeProperty", + "knora-api:objectType": { + "@id": "knora-api:Interval" + }, + "@id": "knora-api:hasSequenceBounds" + }, + { + "rdfs:label": "has Standoff Link to", + "rdfs:subPropertyOf": { + "@id": "knora-api:hasLinkTo" + }, + "rdfs:comment": "Represents a link in standoff markup from one resource to another.", + "@type": "owl:ObjectProperty", + "knora-api:subjectType": { + "@id": "knora-api:Resource" + }, + "knora-api:objectType": { + "@id": "knora-api:Resource" + }, + "@id": "knora-api:hasStandoffLinkTo" + }, + { + "rdfs:label": "has image file", + "rdfs:subPropertyOf": { + "@id": "knora-api:hasFile" + }, + "rdfs:comment": "Connects a Representation to an image file", + "@type": "owl:ObjectProperty", + "knora-api:subjectType": { + "@id": "knora-api:StillImageRepresentation" + }, + "knora-api:objectType": { + "@id": "knora-api:StillImageAbstractFileValue" + }, + "@id": "knora-api:hasStillImageFile" + }, + { + "rdfs:label": "has text file", + "rdfs:subPropertyOf": { + "@id": "knora-api:hasFile" + }, + "rdfs:comment": "Connects a Representation to a text file", + "@type": "owl:DatatypeProperty", + "knora-api:subjectType": { + "@id": "knora-api:TextRepresentation" + }, + "knora-api:objectType": { + "@id": "knora-api:File" + }, + "@id": "knora-api:hasTextFile" + }, + { + "rdfs:label": "has text value property type", + "rdfs:comment": "Indicates the type of text value property.", + "@type": "owl:AnnotationProperty", + "knora-api:subjectType": { + "@id": "owl:ObjectProperty" + }, + "knora-api:objectType": { + "@id": "knora-api:TextValueType" + }, + "@id": "knora-api:hasTextValuePropertyType" + }, + { + "rdfs:label": "has text value type", + "rdfs:comment": "Indicates the type of text value.", + "@type": "owl:ObjectProperty", + "knora-api:subjectType": { + "@id": "xsd:string" + }, + "knora-api:objectType": { + "@id": "knora-api:TextValueType" + }, + "@id": "knora-api:hasTextValueType" + }, + { + "rdfs:label": "Title", + "rdfs:subPropertyOf": [ + { + "@id": "knora-api:hasValue" + }, + { + "@id": "http://purl.org/dc/terms/title" + }, + { + "@id": "http://schema.org/headline" + }, + { + "@id": "http://www.w3.org/ns/ma-ont#title" + } + ], + "rdfs:comment": "Indicates the title of a resource", + "@type": "owl:DatatypeProperty", + "knora-api:subjectType": { + "@id": "knora-api:Resource" + }, + "knora-api:objectType": { + "@id": "xsd:string" + }, + "@id": "knora-api:hasTitle" + }, + { + "rdfs:label": "has value", + "rdfs:subPropertyOf": { + "@id": "knora-api:resourceProperty" + }, + "rdfs:comment": "The base property of properties that point from Knora resources to Knora values.", + "@type": "owl:DatatypeProperty", + "knora-api:subjectType": { + "@id": "knora-api:Resource" + }, + "@id": "knora-api:hasValue" + }, + { + "rdfs:label": "is Annotation of", + "rdfs:subPropertyOf": { + "@id": "knora-api:hasLinkTo" + }, + "@type": "owl:ObjectProperty", + "knora-api:subjectType": { + "@id": "knora-api:Annotation" + }, + "knora-api:objectType": { + "@id": "knora-api:Resource" + }, + "@id": "knora-api:isAnnotationOf" + }, + { + "rdfs:label": "Segment of", + "rdfs:subPropertyOf": { + "@id": "knora-api:isSegmentOf" + }, + "rdfs:comment": "Indicates that this resource is a segment of an audio resource", + "@type": "owl:ObjectProperty", + "knora-api:subjectType": { + "@id": "knora-api:AudioSegment" + }, + "knora-api:objectType": { + "@id": "knora-api:AudioRepresentation" + }, + "@id": "knora-api:isAudioSegmentOf" + }, + { + "rdfs:label": "is main resource", + "rdfs:comment": "Indicates if the given resource is the main resource of a request or a resource referred to by a link property.", + "@type": "owl:DatatypeProperty", + "knora-api:subjectType": { + "@id": "knora-api:Resource" + }, + "knora-api:objectType": { + "@id": "xsd:boolean" + }, + "@id": "knora-api:isMainResource" + }, + { + "rdfs:label": "is part of", + "rdfs:subPropertyOf": { + "@id": "knora-api:hasLinkTo" + }, + "rdfs:comment": "Indicates that this resource is part of another resource", + "@type": "owl:ObjectProperty", + "knora-api:subjectType": { + "@id": "knora-api:Resource" + }, + "knora-api:objectType": { + "@id": "knora-api:Resource" + }, + "@id": "knora-api:isPartOf" + }, + { + "rdfs:label": "is region of", + "rdfs:subPropertyOf": { + "@id": "knora-api:hasLinkTo" + }, + "rdfs:comment": "Region of interest within a digital object (e.g. an image)", + "@type": "owl:ObjectProperty", + "knora-api:subjectType": { + "@id": "knora-api:Region" + }, + "knora-api:objectType": { + "@id": "knora-api:Representation" + }, + "@id": "knora-api:isRegionOf" + }, + { + "rdfs:label": "Segment of", + "rdfs:subPropertyOf": [ + { + "@id": "knora-api:hasLinkTo" + }, + { + "@id": "http://schema.org/isPartOf" + }, + { + "@id": "http://www.w3.org/ns/ma-ont#isFragmentOf" + } + ], + "rdfs:comment": "Indicates that this resource is a segment of a video or audio resource", + "@type": "owl:ObjectProperty", + "knora-api:subjectType": { + "@id": "knora-api:Segment" + }, + "knora-api:objectType": { + "@id": "knora-api:Representation" + }, + "@id": "knora-api:isSegmentOf" + }, + { + "rdfs:label": "is sequence of", + "rdfs:subPropertyOf": { + "@id": "knora-api:hasLinkTo" + }, + "rdfs:comment": "Deprecated in favour of :Segment", + "@type": "owl:ObjectProperty", + "knora-api:subjectType": { + "@id": "knora-api:Resource" + }, + "knora-api:objectType": { + "@id": "knora-api:Resource" + }, + "@id": "knora-api:isSequenceOf" + }, + { + "rdfs:label": "Segment of", + "rdfs:subPropertyOf": { + "@id": "knora-api:isSegmentOf" + }, + "rdfs:comment": "Indicates that this resource is a segment of a video resource", + "@type": "owl:ObjectProperty", + "knora-api:subjectType": { + "@id": "knora-api:VideoSegment" + }, + "knora-api:objectType": { + "@id": "knora-api:MovingImageRepresentation" + }, + "@id": "knora-api:isVideoSegmentOf" + }, + { + "rdfs:label": "May have more results", + "rdfs:comment": "Indicates whether more results may be available for a search query", + "@type": "owl:DatatypeProperty", + "knora-api:objectType": { + "@id": "xsd:boolean" + }, + "@id": "knora-api:mayHaveMoreResults" + }, + { + "@id": "knora-api:objectType", + "@type": "rdf:Property", + "rdfs:label": "Object type", + "rdfs:comment": "Specifies the required type of the objects of a property" + }, + { + "rdfs:label": "Related Resources", + "rdfs:subPropertyOf": [ + { + "@id": "knora-api:hasLinkTo" + }, + { + "@id": "http://purl.org/dc/terms/relation" + }, + { + "@id": "http://www.w3.org/ns/ma-ont#hasRelatedResource" + } + ], + "rdfs:comment": "Indicates that this resource relates to another resource", + "@type": "owl:ObjectProperty", + "knora-api:subjectType": { + "@id": "knora-api:Resource" + }, + "knora-api:objectType": { + "@id": "knora-api:Resource" + }, + "@id": "knora-api:relatesTo" + }, + { + "rdfs:label": "resource icon", + "@type": "owl:DatatypeProperty", + "knora-api:subjectType": { + "@id": "owl:Class" + }, + "knora-api:objectType": { + "@id": "xsd:string" + }, + "@id": "knora-api:resourceIcon" + }, + { + "rdfs:label": "Resource property", + "rdfs:subPropertyOf": { + "@id": "knora-api:resourceProperty" + }, + "rdfs:comment": "The base property of properties that point from Knora resources to Knora resources or values. These properties are required to have cardinalities in the resource classes in which they are used.", + "@type": "rdf:Property", + "knora-api:subjectType": { + "@id": "knora-api:Resource" + }, + "@id": "knora-api:resourceProperty" + }, + { + "rdfs:label": "result", + "rdfs:comment": "Provides a message indicating that an operation was successful", + "@type": "owl:DatatypeProperty", + "knora-api:objectType": { + "@id": "xsd:string" + }, + "@id": "knora-api:result" + }, + { + "rdfs:label": "Sequence number", + "rdfs:subPropertyOf": { + "@id": "knora-api:hasValue" + }, + "rdfs:comment": "Indicates the position of a resource within a compound object. Typically used to indicate the order of pages within a book or similar resource.", + "@type": "owl:DatatypeProperty", + "knora-api:objectType": { + "@id": "xsd:integer" + }, + "@id": "knora-api:seqnum" + }, + { + "@id": "knora-api:subjectType", + "@type": "rdf:Property", + "rdfs:label": "Subject type", + "rdfs:comment": "Specifies the required type of the subjects of a property" + }, + { + "rdfs:label": "version ARK URL", + "rdfs:comment": "Provides the ARK URL of a particular version of a resource.", + "@type": "owl:DatatypeProperty", + "knora-api:objectType": { + "@id": "xsd:anyURI" + }, + "@id": "knora-api:versionArkUrl" + }, + { + "@id": "rdfs:label", + "@type": "owl:DatatypeProperty" + } + ], + "@context": { + "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "knora-api": "http://api.knora.org/ontology/knora-api/simple/v2#", + "owl": "http://www.w3.org/2002/07/owl#", + "rdfs": "http://www.w3.org/2000/01/rdf-schema#", + "xsd": "http://www.w3.org/2001/XMLSchema#" + } +} \ No newline at end of file From 6d0af29b44e9a067e33a712d6da742013451b04b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 15 Jan 2025 14:52:00 +0100 Subject: [PATCH 17/31] reuse FileValueV2 --- .../models/filemodels/FileModelUtil.scala | 1 - .../resources/CreateResourceV2Handler.scala | 104 ++---------------- .../repo/model/ResourceCreateModels.scala | 61 ++-------- .../repo/service/ResourcesRepoLive.scala | 21 ++-- .../repo/service/ResourcesRepoLiveSpec.scala | 60 ++++------ 5 files changed, 49 insertions(+), 198 deletions(-) diff --git a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModelUtil.scala b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModelUtil.scala index d08784cff0..14138883de 100644 --- a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModelUtil.scala +++ b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModelUtil.scala @@ -13,7 +13,6 @@ import org.knora.webapi.messages.StringFormatter import org.knora.webapi.messages.v2.responder.valuemessages.* import org.knora.webapi.slice.admin.domain.model.Authorship import org.knora.webapi.slice.admin.domain.model.CopyrightHolder -import org.knora.webapi.slice.admin.domain.model.LicenseDate import org.knora.webapi.slice.admin.domain.model.LicenseText import org.knora.webapi.slice.admin.domain.model.LicenseUri import org.knora.webapi.slice.resources.IiifImageRequestUrl diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v2/resources/CreateResourceV2Handler.scala b/webapi/src/main/scala/org/knora/webapi/responders/v2/resources/CreateResourceV2Handler.scala index 9ee2295e26..ba848ad41f 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v2/resources/CreateResourceV2Handler.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v2/resources/CreateResourceV2Handler.scala @@ -490,109 +490,19 @@ final case class CreateResourceV2Handler( case GeonameValueContentV2(_, valueHasGeonameCode, _) => ZIO.succeed(GeonameValueInfo(valueHasGeonameCode)) case StillImageFileValueContentV2(_, fileValue, dimX, dimY, _) => - ZIO.succeed( - StillImageFileValueInfo( - internalFilename = fileValue.internalFilename, - internalMimeType = fileValue.internalMimeType, - originalFilename = fileValue.originalFilename, - originalMimeType = fileValue.originalMimeType, - dimX = dimX, - dimY = dimY, - fileValue.copyrightHolder, - fileValue.authorship, - fileValue.licenseText, - fileValue.licenseUri, - fileValue.licenseDate, - ), - ) + ZIO.succeed(StillImageFileValueInfo(fileValue, dimX, dimY)) case StillImageExternalFileValueContentV2(_, fileValue, externalUrl, _) => - ZIO.succeed( - StillImageExternalFileValueInfo( - internalFilename = fileValue.internalFilename, - internalMimeType = fileValue.internalMimeType, - originalFilename = fileValue.originalFilename, - originalMimeType = fileValue.originalMimeType, - externalUrl = externalUrl.value.toString(), - fileValue.copyrightHolder, - fileValue.authorship, - fileValue.licenseText, - fileValue.licenseUri, - fileValue.licenseDate, - ), - ) + ZIO.succeed(StillImageExternalFileValueInfo(fileValue, externalUrl.value.toString)) case DocumentFileValueContentV2(_, fileValue, pageCount, dimX, dimY, _) => - ZIO.succeed( - DocumentFileValueInfo( - internalFilename = fileValue.internalFilename, - internalMimeType = fileValue.internalMimeType, - originalFilename = fileValue.originalFilename, - originalMimeType = fileValue.originalMimeType, - dimX = dimX, - dimY = dimY, - pageCount = pageCount, - fileValue.copyrightHolder, - fileValue.authorship, - fileValue.licenseText, - fileValue.licenseUri, - fileValue.licenseDate, - ), - ) + ZIO.succeed(DocumentFileValueInfo(fileValue = fileValue, dimX, dimY, pageCount)) case ArchiveFileValueContentV2(_, fileValue, _) => - ZIO.succeed( - OtherFileValueInfo( - internalFilename = fileValue.internalFilename, - internalMimeType = fileValue.internalMimeType, - originalFilename = fileValue.originalFilename, - originalMimeType = fileValue.originalMimeType, - fileValue.copyrightHolder, - fileValue.authorship, - fileValue.licenseText, - fileValue.licenseUri, - fileValue.licenseDate, - ), - ) + ZIO.succeed(OtherFileValueInfo(fileValue)) case TextFileValueContentV2(_, fileValue, _) => - ZIO.succeed( - OtherFileValueInfo( - internalFilename = fileValue.internalFilename, - internalMimeType = fileValue.internalMimeType, - originalFilename = fileValue.originalFilename, - originalMimeType = fileValue.originalMimeType, - fileValue.copyrightHolder, - fileValue.authorship, - fileValue.licenseText, - fileValue.licenseUri, - fileValue.licenseDate, - ), - ) + ZIO.succeed(OtherFileValueInfo(fileValue)) case AudioFileValueContentV2(_, fileValue, _) => - ZIO.succeed( - OtherFileValueInfo( - internalFilename = fileValue.internalFilename, - internalMimeType = fileValue.internalMimeType, - originalFilename = fileValue.originalFilename, - originalMimeType = fileValue.originalMimeType, - fileValue.copyrightHolder, - fileValue.authorship, - fileValue.licenseText, - fileValue.licenseUri, - fileValue.licenseDate, - ), - ) + ZIO.succeed(OtherFileValueInfo(fileValue)) case MovingImageFileValueContentV2(_, fileValue, _) => - ZIO.succeed( - OtherFileValueInfo( - internalFilename = fileValue.internalFilename, - internalMimeType = fileValue.internalMimeType, - originalFilename = fileValue.originalFilename, - originalMimeType = fileValue.originalMimeType, - fileValue.copyrightHolder, - fileValue.authorship, - fileValue.licenseText, - fileValue.licenseUri, - fileValue.licenseDate, - ), - ) + ZIO.succeed(OtherFileValueInfo(fileValue)) case LinkValueContentV2( _, referredResourceIri, diff --git a/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/model/ResourceCreateModels.scala b/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/model/ResourceCreateModels.scala index 81346bae1f..60e4fb86ef 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/model/ResourceCreateModels.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/model/ResourceCreateModels.scala @@ -7,14 +7,9 @@ package org.knora.webapi.slice.resources.repo.model import java.time.Instant import java.util.UUID - import org.knora.webapi.messages.util.CalendarNameV2 import org.knora.webapi.messages.util.DatePrecisionV2 -import org.knora.webapi.slice.admin.domain.model.Authorship -import org.knora.webapi.slice.admin.domain.model.CopyrightHolder -import org.knora.webapi.slice.admin.domain.model.LicenseDate -import org.knora.webapi.slice.admin.domain.model.LicenseText -import org.knora.webapi.slice.admin.domain.model.LicenseUri +import org.knora.webapi.messages.v2.responder.valuemessages.FileValueV2 import org.knora.webapi.slice.resourceinfo.domain.InternalIri final case class ResourceReadyToCreate( @@ -48,15 +43,7 @@ enum FormattedTextValueType { } sealed trait FileValueTypeSpecificInfo { - def internalFilename: String - def internalMimeType: String - def originalFilename: Option[String] - def originalMimeType: Option[String] - def copyrightHolder: Option[CopyrightHolder] - def authorship: Option[List[Authorship]] - def licenseText: Option[LicenseText] - def licenseUri: Option[LicenseUri] - def licenseDate: Option[LicenseDate] + def fileValue: FileValueV2 } enum TypeSpecificValueInfo { @@ -83,55 +70,21 @@ enum TypeSpecificValueInfo { case ColorValueInfo(valueHasColor: String) case GeomValueInfo(valueHasGeometry: String) case StillImageFileValueInfo( - internalFilename: String, - internalMimeType: String, - originalFilename: Option[String], - originalMimeType: Option[String], + fileValue: FileValueV2, dimX: Int, dimY: Int, - copyrightHolder: Option[CopyrightHolder], - authorship: Option[List[Authorship]], - licenseText: Option[LicenseText], - licenseUri: Option[LicenseUri], - licenseDate: Option[LicenseDate], ) extends TypeSpecificValueInfo with FileValueTypeSpecificInfo case StillImageExternalFileValueInfo( - internalFilename: String, - internalMimeType: String, - originalFilename: Option[String], - originalMimeType: Option[String], + fileValue: FileValueV2, externalUrl: String, - copyrightHolder: Option[CopyrightHolder], - authorship: Option[List[Authorship]], - licenseText: Option[LicenseText], - licenseUri: Option[LicenseUri], - licenseDate: Option[LicenseDate], ) extends TypeSpecificValueInfo with FileValueTypeSpecificInfo case DocumentFileValueInfo( - internalFilename: String, - internalMimeType: String, - originalFilename: Option[String], - originalMimeType: Option[String], + fileValue: FileValueV2, dimX: Option[Int], dimY: Option[Int], pageCount: Option[Int], - copyrightHolder: Option[CopyrightHolder], - authorship: Option[List[Authorship]], - licenseText: Option[LicenseText], - licenseUri: Option[LicenseUri], - licenseDate: Option[LicenseDate], - ) extends TypeSpecificValueInfo with FileValueTypeSpecificInfo - case OtherFileValueInfo( - internalFilename: String, - internalMimeType: String, - originalFilename: Option[String], - originalMimeType: Option[String], - copyrightHolder: Option[CopyrightHolder], - authorship: Option[List[Authorship]], - licenseText: Option[LicenseText], - licenseUri: Option[LicenseUri], - licenseDate: Option[LicenseDate], - ) extends TypeSpecificValueInfo with FileValueTypeSpecificInfo + ) extends TypeSpecificValueInfo with FileValueTypeSpecificInfo + case OtherFileValueInfo(fileValue: FileValueV2) extends TypeSpecificValueInfo with FileValueTypeSpecificInfo case HierarchicalListValueInfo(valueHasListNode: InternalIri) case IntervalValueInfo(valueHasIntervalStart: BigDecimal, valueHasIntervalEnd: BigDecimal) case TimeValueInfo(valueHasTimeStamp: Instant) diff --git a/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLive.scala b/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLive.scala index 33d3c031d1..4d830db8f3 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLive.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLive.scala @@ -277,15 +277,18 @@ object ResourcesRepoLive { private def buildFileValuePattern(v: FileValueTypeSpecificInfo, valueIri: String): TriplePattern = { val result = iri(valueIri) - .has(KB.internalFilename, literalOf(v.internalFilename)) - .andHas(KB.internalMimeType, literalOf(v.internalMimeType)) - .andHasOptional(KB.originalFilename, v.originalFilename.map(literalOf)) - .andHasOptional(KB.originalMimeType, v.originalMimeType.map(literalOf)) - .andHasOptional(KB.hasCopyrightHolder, v.copyrightHolder.map(_.value).map(literalOf)) - .andHasOptional(KB.hasLicenseText, v.licenseText.map(_.value).map(literalOf)) - .andHasOptional(KB.hasLicenseUri, v.licenseUri.map(_.value).map(literalOfType(_, XSD.ANYURI))) - .andHasOptional(KB.hasLicenseDate, v.licenseDate.map(_.value.toString).map(literalOfType(_, XSD.DATE))) - v.authorship.foreach(_.map(_.value).foreach(result.andHas(KB.hasAuthorship, _))) + .has(KB.internalFilename, literalOf(v.fileValue.internalFilename)) + .andHas(KB.internalMimeType, literalOf(v.fileValue.internalMimeType)) + .andHasOptional(KB.originalFilename, v.fileValue.originalFilename.map(literalOf)) + .andHasOptional(KB.originalMimeType, v.fileValue.originalMimeType.map(literalOf)) + .andHasOptional(KB.hasCopyrightHolder, v.fileValue.copyrightHolder.map(_.value).map(literalOf)) + .andHasOptional(KB.hasLicenseText, v.fileValue.licenseText.map(_.value).map(literalOf)) + .andHasOptional(KB.hasLicenseUri, v.fileValue.licenseUri.map(_.value).map(literalOfType(_, XSD.ANYURI))) + .andHasOptional( + KB.hasLicenseDate, + v.fileValue.licenseDate.map(_.value.toString).map(literalOfType(_, XSD.DATE)), + ) + v.fileValue.authorship.foreach(_.map(_.value).foreach(result.andHas(KB.hasAuthorship, _))) v match { case _: OtherFileValueInfo => result diff --git a/webapi/src/test/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLiveSpec.scala b/webapi/src/test/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLiveSpec.scala index e34df43804..c390347be5 100644 --- a/webapi/src/test/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLiveSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLiveSpec.scala @@ -11,12 +11,12 @@ import zio.test.* import java.time.Instant import java.util.UUID - import dsp.valueobjects.UuidUtil import org.knora.webapi.messages.OntologyConstants 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.FileValueV2 import org.knora.webapi.slice.resourceinfo.domain.InternalIri import org.knora.webapi.slice.resources.repo.model.FormattedTextValueType import org.knora.webapi.slice.resources.repo.model.ResourceReadyToCreate @@ -268,17 +268,14 @@ object TestData { valueTypeIri = InternalIri(OntologyConstants.KnoraBase.StillImageFileValue), valueUUID = UUID.randomUUID(), value = TypeSpecificValueInfo.StillImageFileValueInfo( - internalFilename = "24159oO1pNg-ByLN1NLlMSJ.jp2", - internalMimeType = "image/jp2", - originalFilename = Some("foo.png"), - originalMimeType = Some("image/png"), + FileValueV2( + internalFilename = "24159oO1pNg-ByLN1NLlMSJ.jp2", + internalMimeType = "image/jp2", + originalFilename = Some("foo.png"), + originalMimeType = Some("image/png"), + ), dimX = 100, dimY = 60, - None, - None, - None, - None, - None, ), permissions = valuePermissions, creator = valueCreator, @@ -296,16 +293,11 @@ object TestData { valueTypeIri = InternalIri(OntologyConstants.KnoraBase.StillImageFileValue), valueUUID = UUID.randomUUID(), value = TypeSpecificValueInfo.StillImageExternalFileValueInfo( - internalFilename = "24159oO1pNg-ByLN1NLlMSJ.jp2", - internalMimeType = "image/jp2", - originalFilename = None, - originalMimeType = None, + FileValueV2( + internalFilename = "24159oO1pNg-ByLN1NLlMSJ.jp2", + internalMimeType = "image/jp2", + ), externalUrl = "http://example.com/foo.jpg", - None, - None, - None, - None, - None, ), permissions = valuePermissions, creator = valueCreator, @@ -323,18 +315,15 @@ object TestData { valueTypeIri = InternalIri(OntologyConstants.KnoraBase.DocumentFileValue), valueUUID = UUID.randomUUID(), value = TypeSpecificValueInfo.DocumentFileValueInfo( - internalFilename = "24159oO1pNg-ByLN1NLlMSJ.pdf", - internalMimeType = "application/pdf", - originalFilename = Some("foo.pdf"), - originalMimeType = Some("application/pdf"), + FileValueV2( + internalFilename = "24159oO1pNg-ByLN1NLlMSJ.pdf", + internalMimeType = "application/pdf", + originalFilename = Some("foo.pdf"), + originalMimeType = Some("application/pdf"), + ), dimX = Some(100), dimY = Some(60), pageCount = Some(10), - None, - None, - None, - None, - None, ), permissions = valuePermissions, creator = valueCreator, @@ -352,15 +341,12 @@ object TestData { valueTypeIri = InternalIri(OntologyConstants.KnoraBase.ArchiveFileValue), valueUUID = UUID.randomUUID(), value = TypeSpecificValueInfo.OtherFileValueInfo( - internalFilename = "24159oO1pNg-ByLN1NLlMSJ.zip", - internalMimeType = "application/zip", - originalFilename = Some("foo.zip"), - originalMimeType = Some("application/zip"), - None, - None, - None, - None, - None, + FileValueV2( + internalFilename = "24159oO1pNg-ByLN1NLlMSJ.zip", + internalMimeType = "application/zip", + originalFilename = Some("foo.zip"), + originalMimeType = Some("application/zip"), + ), ), permissions = valuePermissions, creator = valueCreator, From ff0ef95b447dc86bcf3f63391e6bd4dd178ebd47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 15 Jan 2025 14:59:27 +0100 Subject: [PATCH 18/31] simplify --- .../scala/org/knora/webapi/messages/ValuesValidator.scala | 3 --- .../store/triplestoremessages/TriplestoreMessages.scala | 6 +++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/webapi/src/main/scala/org/knora/webapi/messages/ValuesValidator.scala b/webapi/src/main/scala/org/knora/webapi/messages/ValuesValidator.scala index 6ff8211248..1a18ccd330 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/ValuesValidator.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/ValuesValidator.scala @@ -87,9 +87,6 @@ object ValuesValidator { s"Invalid xsd:dateTimeStamp value '$s': ${e.getMessage}", ) - def xsdDateToLocalDate(s: String): Option[LocalDate] = - Try(LocalDate.parse(s)).toOption - /** * Parses a DSP ARK timestamp. * diff --git a/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala b/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala index 70a540a001..124c380448 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala @@ -17,7 +17,6 @@ import java.time.Instant import java.time.LocalDate import scala.collection.mutable import scala.reflect.ClassTag - import dsp.errors.* import org.knora.webapi.* import org.knora.webapi.messages.* @@ -25,6 +24,8 @@ import org.knora.webapi.messages.IriConversions.* import org.knora.webapi.messages.util.ErrorHandlingMap import org.knora.webapi.messages.util.rdf.* +import scala.util.Try + /** * A response to a [[org.knora.webapi.store.triplestore.api.TriplestoreService.Queries.Construct]] query. * @@ -105,8 +106,7 @@ object SparqlExtendedConstructResponse { case OntologyConstants.Xsd.Date => DateLiteralV2( - ValuesValidator - .xsdDateToLocalDate(datatypeLiteral.value) + Try(LocalDate.parse(datatypeLiteral.value)) .getOrElse( throw InconsistentRepositoryDataException(s"Invalid xsd:date: ${datatypeLiteral.value}"), ), From b34dc5e0f2e619b0a159031dbda556c601d4503d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 15 Jan 2025 15:00:05 +0100 Subject: [PATCH 19/31] rm unused import --- .../main/scala/org/knora/webapi/messages/ValuesValidator.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/webapi/src/main/scala/org/knora/webapi/messages/ValuesValidator.scala b/webapi/src/main/scala/org/knora/webapi/messages/ValuesValidator.scala index 1a18ccd330..3e16c2c0fd 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/ValuesValidator.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/ValuesValidator.scala @@ -8,7 +8,6 @@ package org.knora.webapi.messages import spray.json.JsonParser import java.time.Instant -import java.time.LocalDate import java.time.OffsetDateTime import java.time.ZoneOffset import java.time.format.DateTimeFormatter From 3a3de35e702a16d2c27bb43b40062740de60922b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 15 Jan 2025 15:04:10 +0100 Subject: [PATCH 20/31] fmt --- .../org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala | 2 +- .../store/triplestoremessages/TriplestoreMessages.scala | 4 ++-- .../slice/admin/domain/model/CopyrightAndLicenses.scala | 1 + .../knora/webapi/slice/admin/repo/rdf/RdfConversions.scala | 3 --- .../slice/resources/repo/model/ResourceCreateModels.scala | 1 + .../slice/resources/repo/service/ResourcesRepoLiveSpec.scala | 1 + 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala b/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala index 0298c9ca45..21f8447da8 100644 --- a/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala +++ b/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala @@ -6,7 +6,6 @@ package org.knora.webapi.it.v2 import cats.syntax.traverse.* - import org.apache.jena.rdf.model.Model import org.apache.jena.rdf.model.Property import org.apache.jena.rdf.model.Resource @@ -20,6 +19,7 @@ import zio.test.Assertion.* import java.net.URLEncoder import scala.jdk.CollectionConverters.IteratorHasAsScala import scala.language.implicitConversions + import org.knora.webapi.E2EZSpec import org.knora.webapi.messages.OntologyConstants import org.knora.webapi.messages.OntologyConstants.KnoraApiV2Complex as KA diff --git a/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala b/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala index 124c380448..91bd0d1f96 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala @@ -17,6 +17,8 @@ import java.time.Instant import java.time.LocalDate import scala.collection.mutable import scala.reflect.ClassTag +import scala.util.Try + import dsp.errors.* import org.knora.webapi.* import org.knora.webapi.messages.* @@ -24,8 +26,6 @@ import org.knora.webapi.messages.IriConversions.* import org.knora.webapi.messages.util.ErrorHandlingMap import org.knora.webapi.messages.util.rdf.* -import scala.util.Try - /** * A response to a [[org.knora.webapi.store.triplestore.api.TriplestoreService.Queries.Construct]] query. * diff --git a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala index d17e971755..fb3426aadf 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala @@ -7,6 +7,7 @@ package org.knora.webapi.slice.admin.domain.model import java.time.LocalDate import scala.util.Try + import org.knora.webapi.slice.common.StringValueCompanion import org.knora.webapi.slice.common.StringValueCompanion.* import org.knora.webapi.slice.common.StringValueCompanion.maxLength diff --git a/webapi/src/main/scala/org/knora/webapi/slice/admin/repo/rdf/RdfConversions.scala b/webapi/src/main/scala/org/knora/webapi/slice/admin/repo/rdf/RdfConversions.scala index e0665b90ed..d886706f0c 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/admin/repo/rdf/RdfConversions.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/admin/repo/rdf/RdfConversions.scala @@ -35,9 +35,6 @@ object RdfConversions { implicit val selfjoinConverter: Boolean => Either[String, SelfJoin] = value => Right(SelfJoin.from(value)) implicit val descriptionConverter: LangString => Either[String, Description] = langString => Description.from(StringLiteralV2.from(langString.value, langString.lang)) - implicit val copyrightHolderConverter: String => Either[String, CopyrightHolder] = CopyrightHolder.from - implicit val licenseTextConverter: String => Either[String, LicenseText] = LicenseText.from - implicit val licenseUriConverter: String => Either[String, LicenseUri] = LicenseUri.from // User properties implicit val usernameConverter: String => Either[String, Username] = Username.from diff --git a/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/model/ResourceCreateModels.scala b/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/model/ResourceCreateModels.scala index 60e4fb86ef..6d99fd93f5 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/model/ResourceCreateModels.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/model/ResourceCreateModels.scala @@ -7,6 +7,7 @@ package org.knora.webapi.slice.resources.repo.model import java.time.Instant import java.util.UUID + import org.knora.webapi.messages.util.CalendarNameV2 import org.knora.webapi.messages.util.DatePrecisionV2 import org.knora.webapi.messages.v2.responder.valuemessages.FileValueV2 diff --git a/webapi/src/test/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLiveSpec.scala b/webapi/src/test/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLiveSpec.scala index c390347be5..7120a56e5f 100644 --- a/webapi/src/test/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLiveSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLiveSpec.scala @@ -11,6 +11,7 @@ import zio.test.* import java.time.Instant import java.util.UUID + import dsp.valueobjects.UuidUtil import org.knora.webapi.messages.OntologyConstants import org.knora.webapi.messages.StringFormatter From 3d141f2a755c4cb27db8eaed35c17dbf96609916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 15 Jan 2025 15:08:52 +0100 Subject: [PATCH 21/31] fix test --- .../slice/common/ApiComplexV2JsonLdRequestParserSpec.scala | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) 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 index 071b7c72cb..1cf3e7409f 100644 --- a/webapi/src/test/scala/org/knora/webapi/slice/common/ApiComplexV2JsonLdRequestParserSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/slice/common/ApiComplexV2JsonLdRequestParserSpec.scala @@ -458,10 +458,6 @@ object ApiComplexV2JsonLdRequestParserSpec extends ZIOSpecDefault { | "ka:hasCopyrightHolder" : "Jane Doe", | "ka:hasAuthorship" : [ "Mr. Smith", "Author McAuthorface" ], | "ka:hasLicenseText" : "CC-BY-4.0", - | "ka:hasLicenseDate" : { - | "@type" : "xsd:date", - | "@value" : "1999-12-24" - | }, | "ka:hasLicenseUri" : { | "@value" : "http://creativecommons.org/licenses/by/4.0/", | "@type" : "xsd:anyURI" @@ -483,7 +479,7 @@ object ApiComplexV2JsonLdRequestParserSpec extends ZIOSpecDefault { authorship = Some(List(Authorship.unsafeFrom("Author McAuthorface"), Authorship.unsafeFrom("Mr. Smith"))), licenseText = Some(LicenseText.unsafeFrom("CC-BY-4.0")), licenseUri = Some(LicenseUri.unsafeFrom("http://creativecommons.org/licenses/by/4.0/")), - licenseDate = Some(LicenseDate.unsafeFrom("1999-12-24")), + licenseDate = Some(LicenseDate.makeNew), ), givenFileInfo.width.getOrElse(throw new Exception("width is missing")), givenFileInfo.height.getOrElse(throw new Exception("height is missing")), From ab41f28c77c083cfdbd29bbed6ebd2438d849924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 15 Jan 2025 15:21:53 +0100 Subject: [PATCH 22/31] rm unused import --- .../slice/common/ApiComplexV2JsonLdRequestParserSpec.scala | 1 - 1 file changed, 1 deletion(-) 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 index 1cf3e7409f..eaf258e917 100644 --- a/webapi/src/test/scala/org/knora/webapi/slice/common/ApiComplexV2JsonLdRequestParserSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/slice/common/ApiComplexV2JsonLdRequestParserSpec.scala @@ -12,7 +12,6 @@ import zio.json.ast.Json import zio.test.* import java.time.Instant -import java.time.LocalDate import org.knora.webapi.ApiV2Complex import org.knora.webapi.config.AppConfig From 8b2cb43ba138a694afdda0d896ce6f86e32c9c06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 15 Jan 2025 15:26:07 +0100 Subject: [PATCH 23/31] fix test --- .../slice/common/ApiComplexV2JsonLdRequestParserSpec.scala | 2 ++ 1 file changed, 2 insertions(+) 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 index eaf258e917..ebe4015b25 100644 --- a/webapi/src/test/scala/org/knora/webapi/slice/common/ApiComplexV2JsonLdRequestParserSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/slice/common/ApiComplexV2JsonLdRequestParserSpec.scala @@ -81,6 +81,7 @@ object ApiComplexV2JsonLdRequestParserSpec extends ZIOSpecDefault { "internalMimeType", Some("originalFilename.orig"), Some("originalMimeType"), + licenseDate = Some(LicenseDate.makeNew), ) private val configureSipiServiceMock = for { @@ -517,6 +518,7 @@ object ApiComplexV2JsonLdRequestParserSpec extends ZIOSpecDefault { "internalMimeType", Some("originalFilename"), Some("originalMimeType"), + licenseDate = Some(LicenseDate.makeNew), ), IiifImageRequestUrl.unsafeFrom("http://www.example.org/prefix1/abcd1234/full/0/native.jpg"), None, From 49b7daefcd644d182bdb933dd5b4988d934a8273 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 15 Jan 2025 16:51:06 +0100 Subject: [PATCH 24/31] add test for updating values --- .../scala/org/knora/webapi/E2EZSpec.scala | 3 ++ .../it/v2/CopyrightAndLicensesSpec.scala | 53 +++++++++++++++++-- .../sparql/v2/addValueVersion.scala.txt | 6 ++- .../queries/sparql/v2/createValue.scala.txt | 6 ++- 4 files changed, 60 insertions(+), 8 deletions(-) diff --git a/integration/src/test/scala/org/knora/webapi/E2EZSpec.scala b/integration/src/test/scala/org/knora/webapi/E2EZSpec.scala index 5e7603f9df..d7c1fde2ab 100644 --- a/integration/src/test/scala/org/knora/webapi/E2EZSpec.scala +++ b/integration/src/test/scala/org/knora/webapi/E2EZSpec.scala @@ -56,6 +56,9 @@ abstract class E2EZSpec extends ZIOSpecDefault with TestStartupUtils { response <- client.url(urlFull).addHeaders(Headers(bearer)).get("").orDie } yield response + def sendPutRequestAsRoot(url: String, data: String): ZIO[env, String, Response] = + sendPutRequestAsRoot(url, Body.fromString(data)) + def sendPutRequestAsRoot(url: String, body: Body): URIO[env, Response] = for { token <- getRootToken.mapError(Exception(_)).orDie diff --git a/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala b/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala index 21f8447da8..2b5db514ae 100644 --- a/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala +++ b/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala @@ -19,8 +19,8 @@ import zio.test.Assertion.* import java.net.URLEncoder import scala.jdk.CollectionConverters.IteratorHasAsScala import scala.language.implicitConversions - import org.knora.webapi.E2EZSpec +import org.knora.webapi.it.v2.CopyrightAndLicensesSpec.valueId import org.knora.webapi.messages.OntologyConstants import org.knora.webapi.messages.OntologyConstants.KnoraApiV2Complex as KA import org.knora.webapi.models.filemodels.FileType @@ -41,7 +41,7 @@ object CopyrightAndLicensesSpec extends E2EZSpec { private val aLicenseText = LicenseText.unsafeFrom("CC BY-SA 4.0") private val aLicenseUri = LicenseUri.unsafeFrom("https://creativecommons.org/licenses/by-sa/4.0/") - private val copyrightAndLicenseInformationSpec = suite("Creating Resources")( + private val createResourceSuite = suite("Creating Resources")( test( "when creating a resource without copyright and license information" + "the creation response should not contain it", @@ -104,7 +104,48 @@ object CopyrightAndLicensesSpec extends E2EZSpec { }, ) - val e2eSpec: Spec[Scope & env, Any] = suite("Copyright Attribution and Licenses")(copyrightAndLicenseInformationSpec) + private val createValueSuite = suite("Values with Copyright and License Information") { + test("when updating a value with copyright and license information, the created value should contain it") { + for { + resourceCreated <- createStillImageResource() + resourceId <- resourceId(resourceCreated) + valueIdOld <- valueId(resourceCreated) + authorshipArray = someAuthorship.map(_.value).mkString("[ \"", "\", \"", "\" ]") + body = s"""|{ + | "@type": "anything:ThingPicture", + | "@id": "$resourceId", + | "ka:hasStillImageFileValue": { + | "@type": "ka:StillImageFileValue", + | "@id": "$valueIdOld", + | "ka:fileValueHasFilename": "filename.jpx", + | "ka:hasCopyrightHolder" : "${aCopyrightHolder.value}", + | "ka:hasAuthorship" : $authorshipArray, + | "ka:hasLicenseText" : "${aLicenseText.value}", + | "ka:hasLicenseUri" : { + | "@value" : "${aLicenseUri.value}", + | "@type" : "http://www.w3.org/2001/XMLSchema#anyURI" + | } + | }, + | "@context": { + | "ka": "http://api.knora.org/ontology/knora-api/v2#", + | "anything": "http://0.0.0.0:3333/ontology/0001/anything/v2#" + | } + |}""".stripMargin + _ <- sendPutRequestAsRoot(s"/v2/values", body) + .filterOrElseWith(_.status.isSuccess)(failResponse(s"Failed to create value")) + .flatMap(_.body.asString) + createdResourceModel <- getResourceFromApi(resourceId) + info <- copyrightAndLicenseInfo(createdResourceModel) + } yield assertTrue( + info.licenseText.contains(aLicenseText), + info.licenseUri.contains(aLicenseUri), + info.licenseDate.isDefined, + ) && assert(info.authorship.getOrElse(List.empty))(hasSameElements(someAuthorship)) + } + } + + val e2eSpec: Spec[Scope & env, Any] = + suite("Copyright Attribution and Licenses")(createResourceSuite, createValueSuite) private def failResponse(msg: String)(response: Response) = response.body.asString.flatMap(bodyStr => ZIO.fail(Exception(s"$msg\nstatus: ${response.status}\nbody: $bodyStr"))) @@ -149,9 +190,13 @@ object CopyrightAndLicensesSpec extends E2EZSpec { model <- ModelOps.fromJsonLd(responseBody).mapError(Exception(_)) } yield model - private def getValueFromApi(createResourceResponse: Model) = for { + private def getValueFromApi(createResourceResponse: Model): ZIO[env, Throwable, Model] = for { valueId <- valueId(createResourceResponse) resourceId <- resourceId(createResourceResponse) + model <- getValueFromApi(valueId, resourceId) + } yield model + + private def getValueFromApi(valueId: ValueIri, resourceId: String): ZIO[env, Throwable, Model] = for { responseBody <- sendGetRequest(s"/v2/values/${URLEncoder.encode(resourceId, "UTF-8")}/${valueId.valueId}") .filterOrElseWith(_.status.isSuccess)(failResponse(s"Failed to get value $resourceId.")) .flatMap(_.body.asString) diff --git a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/addValueVersion.scala.txt b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/addValueVersion.scala.txt index 45ecbd3219..076b52a632 100644 --- a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/addValueVersion.scala.txt +++ b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/addValueVersion.scala.txt @@ -242,8 +242,10 @@ DELETE { } @fileValueContentV2.fileValue.authorship match { - case Some(authors) if authors.nonEmpty => { - <@newValueIri> knora-base:hasAuthorship @authors.map(_.value).mkString("\"", ",", "\"") . + case Some(authors) => { + @authors.map { author => + <@newValueIri> knora-base:hasAuthorship """@author.value""" . + } } case None => {} } diff --git a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/createValue.scala.txt b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/createValue.scala.txt index 635e740805..8f69892265 100644 --- a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/createValue.scala.txt +++ b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/createValue.scala.txt @@ -236,8 +236,10 @@ DELETE { } @fileValueContentV2.fileValue.authorship match { - case Some(authors) if authors.nonEmpty => { - <@newValueIri> knora-base:hasAuthorship @authors.map(_.value).mkString("\"", ",", "\"") . + case Some(authors) => { + @authors.map { author => + <@newValueIri> knora-base:hasAuthorship """@author.value""" . + } } case None => {} } From 66dfa595774fb38b008cc3eeb7ac62f64afeb66d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 15 Jan 2025 16:54:07 +0100 Subject: [PATCH 25/31] rm unused import --- .../scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala b/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala index 2b5db514ae..14aa5eaa6b 100644 --- a/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala +++ b/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala @@ -19,8 +19,8 @@ import zio.test.Assertion.* import java.net.URLEncoder import scala.jdk.CollectionConverters.IteratorHasAsScala import scala.language.implicitConversions + import org.knora.webapi.E2EZSpec -import org.knora.webapi.it.v2.CopyrightAndLicensesSpec.valueId import org.knora.webapi.messages.OntologyConstants import org.knora.webapi.messages.OntologyConstants.KnoraApiV2Complex as KA import org.knora.webapi.models.filemodels.FileType From 22011c0e4ff4e6a29144cccb66aabe5fce680453 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 15 Jan 2025 16:58:28 +0100 Subject: [PATCH 26/31] fmt --- .../scala/org/knora/webapi/models/filemodels/FileModels.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala index 5a59645617..eb1e7735e1 100644 --- a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala +++ b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala @@ -17,7 +17,6 @@ import org.knora.webapi.sharedtestdata.SharedTestDataADM import org.knora.webapi.slice.admin.api.model.Project import org.knora.webapi.slice.admin.domain.model.Authorship import org.knora.webapi.slice.admin.domain.model.CopyrightHolder -import org.knora.webapi.slice.admin.domain.model.LicenseDate import org.knora.webapi.slice.admin.domain.model.LicenseText import org.knora.webapi.slice.admin.domain.model.LicenseUri From 0b30428426133672e4d2de5ce16886a52f84d505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 15 Jan 2025 17:49:06 +0100 Subject: [PATCH 27/31] refine LicenseDate assertion --- .../knora/webapi/it/v2/CopyrightAndLicensesSpec.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala b/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala index 14aa5eaa6b..6178877c81 100644 --- a/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala +++ b/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala @@ -54,7 +54,7 @@ object CopyrightAndLicensesSpec extends E2EZSpec { info.authorship.isEmpty, info.licenseText.isEmpty, info.licenseUri.isEmpty, - info.licenseDate.isDefined, + info.licenseDate.contains(LicenseDate.makeNew), ) }, test( @@ -68,7 +68,7 @@ object CopyrightAndLicensesSpec extends E2EZSpec { info.copyrightHolder.contains(aCopyrightHolder), info.licenseText.contains(aLicenseText), info.licenseUri.contains(aLicenseUri), - info.licenseDate.isDefined, + info.licenseDate.contains(LicenseDate.makeNew), ) && assert(info.authorship.getOrElse(List.empty))(hasSameElements(someAuthorship)) }, test( @@ -84,7 +84,7 @@ object CopyrightAndLicensesSpec extends E2EZSpec { info.copyrightHolder.contains(aCopyrightHolder), info.licenseText.contains(aLicenseText), info.licenseUri.contains(aLicenseUri), - info.licenseDate.isDefined, + info.licenseDate.contains(LicenseDate.makeNew), ) && assert(info.authorship.getOrElse(List.empty))(hasSameElements(someAuthorship)) }, test( @@ -99,7 +99,7 @@ object CopyrightAndLicensesSpec extends E2EZSpec { info.copyrightHolder.contains(aCopyrightHolder), info.licenseText.contains(aLicenseText), info.licenseUri.contains(aLicenseUri), - info.licenseDate.isDefined, + info.licenseDate.contains(LicenseDate.makeNew), ) && assert(info.authorship.getOrElse(List.empty))(hasSameElements(someAuthorship)) }, ) @@ -139,7 +139,7 @@ object CopyrightAndLicensesSpec extends E2EZSpec { } yield assertTrue( info.licenseText.contains(aLicenseText), info.licenseUri.contains(aLicenseUri), - info.licenseDate.isDefined, + info.licenseDate.contains(LicenseDate.makeNew), ) && assert(info.authorship.getOrElse(List.empty))(hasSameElements(someAuthorship)) } } From 054b8e22d3a7274630853a42bcbc3f98d309f647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 15 Jan 2025 18:32:23 +0100 Subject: [PATCH 28/31] replace String concatenation with building proper jsonld --- .../it/v2/CopyrightAndLicensesSpec.scala | 102 ++++++++++++------ 1 file changed, 68 insertions(+), 34 deletions(-) diff --git a/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala b/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala index 6178877c81..83b60db56c 100644 --- a/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala +++ b/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala @@ -6,27 +6,36 @@ package org.knora.webapi.it.v2 import cats.syntax.traverse.* +import com.apicatalog.jsonld.JsonLd +import com.apicatalog.jsonld.document.JsonDocument import org.apache.jena.rdf.model.Model import org.apache.jena.rdf.model.Property import org.apache.jena.rdf.model.Resource import org.apache.jena.vocabulary.RDF +import org.apache.jena.vocabulary.XSD import zio.* import zio.http.Body import zio.http.Response +import zio.json.ast.Json import zio.test.* import zio.test.Assertion.* +import java.io.ByteArrayInputStream import java.net.URLEncoder import scala.jdk.CollectionConverters.IteratorHasAsScala import scala.language.implicitConversions import org.knora.webapi.E2EZSpec +import org.knora.webapi.messages.IriConversions.ConvertibleIri import org.knora.webapi.messages.OntologyConstants import org.knora.webapi.messages.OntologyConstants.KnoraApiV2Complex as KA +import org.knora.webapi.messages.StringFormatter import org.knora.webapi.models.filemodels.FileType import org.knora.webapi.models.filemodels.UploadFileRequest import org.knora.webapi.slice.admin.domain.model.* import org.knora.webapi.slice.admin.domain.service.KnoraProjectService +import org.knora.webapi.slice.common.KnoraIris.ResourceClassIri +import org.knora.webapi.slice.common.KnoraIris.ResourceIri import org.knora.webapi.slice.common.KnoraIris.ValueIri import org.knora.webapi.slice.common.jena.JenaConversions.given import org.knora.webapi.slice.common.jena.ModelOps @@ -36,10 +45,11 @@ import org.knora.webapi.slice.resourceinfo.domain.IriConverter object CopyrightAndLicensesSpec extends E2EZSpec { - private val aCopyrightHolder = CopyrightHolder.unsafeFrom("Universität Basel") - private val someAuthorship = List("Hans Müller", "Gigi DAgostino").map(Authorship.unsafeFrom) - private val aLicenseText = LicenseText.unsafeFrom("CC BY-SA 4.0") - private val aLicenseUri = LicenseUri.unsafeFrom("https://creativecommons.org/licenses/by-sa/4.0/") + private implicit val sf: StringFormatter = StringFormatter.getInitializedTestInstance + private val aCopyrightHolder = CopyrightHolder.unsafeFrom("Universität Basel") + private val someAuthorship = List("Hans Müller", "Gigi DAgostino").map(Authorship.unsafeFrom) + private val aLicenseText = LicenseText.unsafeFrom("CC BY-SA 4.0") + private val aLicenseUri = LicenseUri.unsafeFrom("https://creativecommons.org/licenses/by-sa/4.0/") private val createResourceSuite = suite("Creating Resources")( test( @@ -110,30 +120,18 @@ object CopyrightAndLicensesSpec extends E2EZSpec { resourceCreated <- createStillImageResource() resourceId <- resourceId(resourceCreated) valueIdOld <- valueId(resourceCreated) - authorshipArray = someAuthorship.map(_.value).mkString("[ \"", "\", \"", "\" ]") - body = s"""|{ - | "@type": "anything:ThingPicture", - | "@id": "$resourceId", - | "ka:hasStillImageFileValue": { - | "@type": "ka:StillImageFileValue", - | "@id": "$valueIdOld", - | "ka:fileValueHasFilename": "filename.jpx", - | "ka:hasCopyrightHolder" : "${aCopyrightHolder.value}", - | "ka:hasAuthorship" : $authorshipArray, - | "ka:hasLicenseText" : "${aLicenseText.value}", - | "ka:hasLicenseUri" : { - | "@value" : "${aLicenseUri.value}", - | "@type" : "http://www.w3.org/2001/XMLSchema#anyURI" - | } - | }, - | "@context": { - | "ka": "http://api.knora.org/ontology/knora-api/v2#", - | "anything": "http://0.0.0.0:3333/ontology/0001/anything/v2#" - | } - |}""".stripMargin - _ <- sendPutRequestAsRoot(s"/v2/values", body) - .filterOrElseWith(_.status.isSuccess)(failResponse(s"Failed to create value")) - .flatMap(_.body.asString) + _ <- sendPutRequestAsRoot( + s"/v2/values", + UpdateStillImageFileValueRequest( + resourceId, + valueIdOld, + ResourceClassIri.unsafeFrom("http://0.0.0.0:3333/ontology/0001/anything/v2#ThingPicture".toSmartIri), + aCopyrightHolder, + someAuthorship, + aLicenseText, + aLicenseUri, + ).jsonLd, + ).filterOrElseWith(_.status.isSuccess)(failResponse(s"Failed to create value")) createdResourceModel <- getResourceFromApi(resourceId) info <- copyrightAndLicenseInfo(createdResourceModel) } yield assertTrue( @@ -144,6 +142,41 @@ object CopyrightAndLicensesSpec extends E2EZSpec { } } + final case class UpdateStillImageFileValueRequest( + resourceId: ResourceIri, + valueId: ValueIri, + resourceClass: ResourceClassIri, + copyrightHolder: CopyrightHolder, + authorship: List[Authorship], + licenseText: LicenseText, + licenseUri: LicenseUri, + ) { + def jsonLd: String = + JsonLd.expand(JsonDocument.of(new ByteArrayInputStream(toJson.toString().getBytes))).get.toString + + private def toJson: Json = { + def ldType(typ: Any) = ("@type", Json.Str(typ.toString)) + def ldId(id: Any) = ("@id", Json.Str(id.toString)) + def ldValue(value: Any, typ: Resource) = Json.Obj(("@value", Json.Str(value.toString)), ldType(typ.toString)) + Json.Obj( + ldId(resourceId), + ldType(resourceClass), + ( + KA.HasStillImageFileValue, + Json.Obj( + ldId(valueId), + ldType(KA.StillImageFileValue), + (KA.FileValueHasFilename, Json.Str("test.jpx")), + (KA.HasCopyrightHolder, Json.Str(copyrightHolder.value)), + (KA.HasAuthorship, Json.Arr(authorship.map(_.value).map(Json.Str.apply): _*)), + (KA.HasLicenseText, Json.Str(licenseText.value)), + (KA.HasLicenseUri, ldValue(licenseUri.value, XSD.anyURI)), + ), + ), + ) + } + } + val e2eSpec: Spec[Scope & env, Any] = suite("Copyright Attribution and Licenses")(createResourceSuite, createValueSuite) @@ -183,8 +216,8 @@ object CopyrightAndLicensesSpec extends E2EZSpec { } yield createResourceResponseModel } - private def getResourceFromApi(resourceId: String) = for { - responseBody <- sendGetRequest(s"/v2/resources/${URLEncoder.encode(resourceId, "UTF-8")}") + private def getResourceFromApi(resourceId: ResourceIri) = for { + responseBody <- sendGetRequest(s"/v2/resources/${URLEncoder.encode(resourceId.toString, "UTF-8")}") .filterOrElseWith(_.status.isSuccess)(failResponse(s"Failed to get resource $resourceId.")) .flatMap(_.body.asString) model <- ModelOps.fromJsonLd(responseBody).mapError(Exception(_)) @@ -196,14 +229,14 @@ object CopyrightAndLicensesSpec extends E2EZSpec { model <- getValueFromApi(valueId, resourceId) } yield model - private def getValueFromApi(valueId: ValueIri, resourceId: String): ZIO[env, Throwable, Model] = for { - responseBody <- sendGetRequest(s"/v2/values/${URLEncoder.encode(resourceId, "UTF-8")}/${valueId.valueId}") + private def getValueFromApi(valueId: ValueIri, resourceId: ResourceIri): ZIO[env, Throwable, Model] = for { + responseBody <- sendGetRequest(s"/v2/values/${URLEncoder.encode(resourceId.toString, "UTF-8")}/${valueId.valueId}") .filterOrElseWith(_.status.isSuccess)(failResponse(s"Failed to get value $resourceId.")) .flatMap(_.body.asString) model <- ModelOps.fromJsonLd(responseBody).mapError(Exception(_)) } yield model - private def resourceId(model: Model): Task[String] = + private def resourceId(model: Model): Task[ResourceIri] = ZIO .fromEither( for { @@ -211,7 +244,8 @@ object CopyrightAndLicensesSpec extends E2EZSpec { id <- root.uri.toRight("No URI found for root resource") } yield id, ) - .mapError(Exception(_)) + .map(_.toSmartIri) + .mapBoth(Exception(_), ResourceIri.unsafeFrom) private def valueId(model: Model): ZIO[IriConverter, Throwable, ValueIri] = { val subs = model From b141805aaa72db0af55cb2d6b6a059fe6486f016 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Mon, 20 Jan 2025 19:08:02 +0100 Subject: [PATCH 29/31] rename License Text to License Identifier --- CHANGELOG.md | 2 +- .../it/v2/CopyrightAndLicensesSpec.scala | 42 +++++++++---------- .../IntegrationTestAdminJsonProtocol.scala | 6 +-- .../models/filemodels/FileModelUtil.scala | 18 ++++---- .../webapi/models/filemodels/FileModels.scala | 12 +++--- .../knoraApiOntologySimple.jsonld | 6 +-- .../knoraApiOntologyWithValueObjects.jsonld | 24 +++++------ .../resources/knora-ontologies/knora-base.ttl | 8 ++-- .../webapi/messages/OntologyConstants.scala | 4 +- .../util/ConstructResponseUtilV2.scala | 8 ++-- ...aseToApiV2ComplexTransformationRules.scala | 6 +-- .../valuemessages/ValueMessagesV2.scala | 28 ++++++------- .../valuemessages/ValueMessagesV2Optics.scala | 9 ++-- .../knora/webapi/slice/admin/api/Codecs.scala | 2 +- .../domain/model/CopyrightAndLicenses.scala | 8 ++-- .../slice/common/repo/rdf/Vocabulary.scala | 2 +- .../repo/service/ResourcesRepoLive.scala | 2 +- .../sparql/v2/addValueVersion.scala.txt | 4 +- .../queries/sparql/v2/createValue.scala.txt | 4 +- .../ApiComplexV2JsonLdRequestParserSpec.scala | 4 +- 20 files changed, 100 insertions(+), 99 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed4f21bc91..1dc4c6552e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,7 +57,7 @@ ### Enhancements -* Split license into licenseText and licenseUri (DEV-4398) ([#3436](https://github.com/dasch-swiss/dsp-api/issues/3436)) ([76d2db2](https://github.com/dasch-swiss/dsp-api/commit/76d2db25a75ffd43294e2d1d25e98f53f5d0e275)) +* Split license into licenseIdentifier and licenseUri (DEV-4398) ([#3436](https://github.com/dasch-swiss/dsp-api/issues/3436)) ([76d2db2](https://github.com/dasch-swiss/dsp-api/commit/76d2db25a75ffd43294e2d1d25e98f53f5d0e275)) ### Bug Fixes diff --git a/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala b/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala index 83b60db56c..7ab6f52d63 100644 --- a/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala +++ b/integration/src/test/scala/org/knora/webapi/it/v2/CopyrightAndLicensesSpec.scala @@ -48,7 +48,7 @@ object CopyrightAndLicensesSpec extends E2EZSpec { private implicit val sf: StringFormatter = StringFormatter.getInitializedTestInstance private val aCopyrightHolder = CopyrightHolder.unsafeFrom("Universität Basel") private val someAuthorship = List("Hans Müller", "Gigi DAgostino").map(Authorship.unsafeFrom) - private val aLicenseText = LicenseText.unsafeFrom("CC BY-SA 4.0") + private val aLicenseIdentifier = LicenseIdentifier.unsafeFrom("CC BY-SA 4.0") private val aLicenseUri = LicenseUri.unsafeFrom("https://creativecommons.org/licenses/by-sa/4.0/") private val createResourceSuite = suite("Creating Resources")( @@ -62,7 +62,7 @@ object CopyrightAndLicensesSpec extends E2EZSpec { } yield assertTrue( info.copyrightHolder.isEmpty, info.authorship.isEmpty, - info.licenseText.isEmpty, + info.licenseIdentifier.isEmpty, info.licenseUri.isEmpty, info.licenseDate.contains(LicenseDate.makeNew), ) @@ -76,7 +76,7 @@ object CopyrightAndLicensesSpec extends E2EZSpec { info <- copyrightAndLicenseInfo(createResourceResponseModel) } yield assertTrue( info.copyrightHolder.contains(aCopyrightHolder), - info.licenseText.contains(aLicenseText), + info.licenseIdentifier.contains(aLicenseIdentifier), info.licenseUri.contains(aLicenseUri), info.licenseDate.contains(LicenseDate.makeNew), ) && assert(info.authorship.getOrElse(List.empty))(hasSameElements(someAuthorship)) @@ -92,7 +92,7 @@ object CopyrightAndLicensesSpec extends E2EZSpec { info <- copyrightAndLicenseInfo(getResponseModel) } yield assertTrue( info.copyrightHolder.contains(aCopyrightHolder), - info.licenseText.contains(aLicenseText), + info.licenseIdentifier.contains(aLicenseIdentifier), info.licenseUri.contains(aLicenseUri), info.licenseDate.contains(LicenseDate.makeNew), ) && assert(info.authorship.getOrElse(List.empty))(hasSameElements(someAuthorship)) @@ -107,7 +107,7 @@ object CopyrightAndLicensesSpec extends E2EZSpec { info <- copyrightAndLicenseInfo(valueResponseModel) } yield assertTrue( info.copyrightHolder.contains(aCopyrightHolder), - info.licenseText.contains(aLicenseText), + info.licenseIdentifier.contains(aLicenseIdentifier), info.licenseUri.contains(aLicenseUri), info.licenseDate.contains(LicenseDate.makeNew), ) && assert(info.authorship.getOrElse(List.empty))(hasSameElements(someAuthorship)) @@ -128,14 +128,14 @@ object CopyrightAndLicensesSpec extends E2EZSpec { ResourceClassIri.unsafeFrom("http://0.0.0.0:3333/ontology/0001/anything/v2#ThingPicture".toSmartIri), aCopyrightHolder, someAuthorship, - aLicenseText, + aLicenseIdentifier, aLicenseUri, ).jsonLd, ).filterOrElseWith(_.status.isSuccess)(failResponse(s"Failed to create value")) createdResourceModel <- getResourceFromApi(resourceId) info <- copyrightAndLicenseInfo(createdResourceModel) } yield assertTrue( - info.licenseText.contains(aLicenseText), + info.licenseIdentifier.contains(aLicenseIdentifier), info.licenseUri.contains(aLicenseUri), info.licenseDate.contains(LicenseDate.makeNew), ) && assert(info.authorship.getOrElse(List.empty))(hasSameElements(someAuthorship)) @@ -148,7 +148,7 @@ object CopyrightAndLicensesSpec extends E2EZSpec { resourceClass: ResourceClassIri, copyrightHolder: CopyrightHolder, authorship: List[Authorship], - licenseText: LicenseText, + licenseIdentifier: LicenseIdentifier, licenseUri: LicenseUri, ) { def jsonLd: String = @@ -169,7 +169,7 @@ object CopyrightAndLicensesSpec extends E2EZSpec { (KA.FileValueHasFilename, Json.Str("test.jpx")), (KA.HasCopyrightHolder, Json.Str(copyrightHolder.value)), (KA.HasAuthorship, Json.Arr(authorship.map(_.value).map(Json.Str.apply): _*)), - (KA.HasLicenseText, Json.Str(licenseText.value)), + (KA.HasLicenseIdentifier, Json.Str(licenseIdentifier.value)), (KA.HasLicenseUri, ldValue(licenseUri.value, XSD.anyURI)), ), ), @@ -187,14 +187,14 @@ object CopyrightAndLicensesSpec extends E2EZSpec { createStillImageResource( Some(aCopyrightHolder), Some(someAuthorship), - Some(aLicenseText), + Some(aLicenseIdentifier), Some(aLicenseUri), ) private def createStillImageResource( copyrightHolder: Option[CopyrightHolder] = None, authorship: Option[List[Authorship]] = None, - licenseText: Option[LicenseText] = None, + licenseIdentifier: Option[LicenseIdentifier] = None, licenseUri: Option[LicenseUri] = None, ): ZIO[env, Throwable, Model] = { val jsonLd = UploadFileRequest @@ -203,7 +203,7 @@ object CopyrightAndLicensesSpec extends E2EZSpec { "internalFilename.jpg", copyrightHolder = copyrightHolder, authorship = authorship, - licenseText = licenseText, + licenseIdentifier = licenseIdentifier, licenseUri = licenseUri, ) .toJsonLd(className = Some("ThingPicture"), ontologyName = "anything") @@ -267,19 +267,19 @@ object CopyrightAndLicensesSpec extends E2EZSpec { final case class CopyrightAndLicenseInfo( copyrightHolder: Option[CopyrightHolder], authorship: Option[List[Authorship]], - licenseText: Option[LicenseText], + licenseIdentifier: Option[LicenseIdentifier], licenseUri: Option[LicenseUri], licenseDate: Option[LicenseDate], ) private def copyrightAndLicenseInfo(model: Model) = for { - copyright <- copyrightValueOption(model).map(_.map(CopyrightHolder.unsafeFrom)) - authorship <- authorshipValuesOption(model).map(_.map(_.map(Authorship.unsafeFrom))) - licenseText <- licenseTextValueOption(model).map(_.map(LicenseText.unsafeFrom)) - licenseUri <- licenseUriValueOption(model).map(_.map(LicenseUri.unsafeFrom)) - licenseDate <- licenseDateValueOption(model).map(_.map(LicenseDate.unsafeFrom)) - } yield CopyrightAndLicenseInfo(copyright, authorship, licenseText, licenseUri, licenseDate) + copyright <- copyrightValueOption(model).map(_.map(CopyrightHolder.unsafeFrom)) + authorship <- authorshipValuesOption(model).map(_.map(_.map(Authorship.unsafeFrom))) + licenseIdentifier <- licenseIdentifierValueOption(model).map(_.map(LicenseIdentifier.unsafeFrom)) + licenseUri <- licenseUriValueOption(model).map(_.map(LicenseUri.unsafeFrom)) + licenseDate <- licenseDateValueOption(model).map(_.map(LicenseDate.unsafeFrom)) + } yield CopyrightAndLicenseInfo(copyright, authorship, licenseIdentifier, licenseUri, licenseDate) private def copyrightValueOption(model: Model) = singleStringValueOption(model, KA.HasCopyrightHolder) @@ -293,8 +293,8 @@ object CopyrightAndLicensesSpec extends E2EZSpec { ) .mapError(Exception(_)) - private def licenseTextValueOption(model: Model) = - singleStringValueOption(model, KA.HasLicenseText) + private def licenseIdentifierValueOption(model: Model) = + singleStringValueOption(model, KA.HasLicenseIdentifier) private def licenseUriValueOption(model: Model) = singleStringValueOption(model, KA.HasLicenseUri) diff --git a/integration/src/test/scala/org/knora/webapi/messages/admin/responder/IntegrationTestAdminJsonProtocol.scala b/integration/src/test/scala/org/knora/webapi/messages/admin/responder/IntegrationTestAdminJsonProtocol.scala index e95c119dcb..ef6f3c304b 100644 --- a/integration/src/test/scala/org/knora/webapi/messages/admin/responder/IntegrationTestAdminJsonProtocol.scala +++ b/integration/src/test/scala/org/knora/webapi/messages/admin/responder/IntegrationTestAdminJsonProtocol.scala @@ -46,7 +46,7 @@ import org.knora.webapi.slice.admin.api.model.ProjectMembersGetResponseADM import org.knora.webapi.slice.admin.api.model.ProjectOperationResponseADM import org.knora.webapi.slice.admin.domain.model.CopyrightHolder import org.knora.webapi.slice.admin.domain.model.Group -import org.knora.webapi.slice.admin.domain.model.LicenseText +import org.knora.webapi.slice.admin.domain.model.LicenseIdentifier import org.knora.webapi.slice.admin.domain.model.LicenseUri import org.knora.webapi.slice.admin.domain.model.User import org.knora.webapi.slice.common.Value.StringValue @@ -208,8 +208,8 @@ object IntegrationTestAdminJsonProtocol extends TriplestoreJsonProtocol { override val from: String => Either[String, CopyrightHolder] = CopyrightHolder.from } - implicit object LicenseTextFormat extends StringValueFormat[LicenseText] { - override val from: String => Either[String, LicenseText] = LicenseText.from + implicit object LicenseIdentifierFormat extends StringValueFormat[LicenseIdentifier] { + override val from: String => Either[String, LicenseIdentifier] = LicenseIdentifier.from } implicit object LicenseUriFormat extends StringValueFormat[LicenseUri] { diff --git a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModelUtil.scala b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModelUtil.scala index f401ac8805..1b81750f91 100644 --- a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModelUtil.scala +++ b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModelUtil.scala @@ -13,7 +13,7 @@ import org.knora.webapi.messages.StringFormatter import org.knora.webapi.messages.v2.responder.valuemessages.* import org.knora.webapi.slice.admin.domain.model.Authorship import org.knora.webapi.slice.admin.domain.model.CopyrightHolder -import org.knora.webapi.slice.admin.domain.model.LicenseText +import org.knora.webapi.slice.admin.domain.model.LicenseIdentifier import org.knora.webapi.slice.admin.domain.model.LicenseUri import org.knora.webapi.slice.resources.IiifImageRequestUrl @@ -89,7 +89,7 @@ object FileModelUtil { originalMimeType: Option[String], copyrightHolder: Option[CopyrightHolder] = None, authorship: Option[List[Authorship]] = None, - licenseText: Option[LicenseText] = None, + licenseIdentifier: Option[LicenseIdentifier] = None, licenseUri: Option[LicenseUri] = None, comment: Option[String] = None, ): FileValueContentV2 = @@ -104,7 +104,7 @@ object FileModelUtil { originalMimeType = Some(originalMimeType.getOrElse("application/pdf")), copyrightHolder = copyrightHolder, authorship = authorship, - licenseText = licenseText, + licenseIdentifier = licenseIdentifier, licenseUri = licenseUri, ), pageCount = pageCount, @@ -122,7 +122,7 @@ object FileModelUtil { originalMimeType = originalMimeType, copyrightHolder = copyrightHolder, authorship = authorship, - licenseText = licenseText, + licenseIdentifier = licenseIdentifier, licenseUri = licenseUri, ), dimX = dimX, @@ -139,7 +139,7 @@ object FileModelUtil { originalMimeType = originalMimeType, copyrightHolder = copyrightHolder, authorship = authorship, - licenseText = licenseText, + licenseIdentifier = licenseIdentifier, licenseUri = licenseUri, ), externalUrl = externalUrl, @@ -155,7 +155,7 @@ object FileModelUtil { originalMimeType = internalMimeType, copyrightHolder = copyrightHolder, authorship = authorship, - licenseText = licenseText, + licenseIdentifier = licenseIdentifier, licenseUri = licenseUri, ), ) @@ -169,7 +169,7 @@ object FileModelUtil { originalMimeType = internalMimeType, copyrightHolder = copyrightHolder, authorship = authorship, - licenseText = licenseText, + licenseIdentifier = licenseIdentifier, licenseUri = licenseUri, ), ) @@ -183,7 +183,7 @@ object FileModelUtil { originalMimeType = internalMimeType, copyrightHolder = copyrightHolder, authorship = authorship, - licenseText = licenseText, + licenseIdentifier = licenseIdentifier, licenseUri = licenseUri, ), ) @@ -197,7 +197,7 @@ object FileModelUtil { originalMimeType = internalMimeType, copyrightHolder = copyrightHolder, authorship = authorship, - licenseText = licenseText, + licenseIdentifier = licenseIdentifier, licenseUri = licenseUri, ), comment = comment, diff --git a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala index eb1e7735e1..8416e4c6d4 100644 --- a/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala +++ b/integration/src/test/scala/org/knora/webapi/models/filemodels/FileModels.scala @@ -17,7 +17,7 @@ import org.knora.webapi.sharedtestdata.SharedTestDataADM import org.knora.webapi.slice.admin.api.model.Project import org.knora.webapi.slice.admin.domain.model.Authorship import org.knora.webapi.slice.admin.domain.model.CopyrightHolder -import org.knora.webapi.slice.admin.domain.model.LicenseText +import org.knora.webapi.slice.admin.domain.model.LicenseIdentifier import org.knora.webapi.slice.admin.domain.model.LicenseUri sealed abstract case class UploadFileRequest private ( @@ -27,7 +27,7 @@ sealed abstract case class UploadFileRequest private ( resourceIRI: Option[String] = None, copyrightHolder: Option[CopyrightHolder] = None, authorship: Option[List[Authorship]] = None, - licenseText: Option[LicenseText] = None, + licenseIdentifier: Option[LicenseIdentifier] = None, licenseUri: Option[LicenseUri] = None, ) { self => @@ -73,7 +73,7 @@ sealed abstract case class UploadFileRequest private ( | "knora-api:fileValueHasFilename" : "$internalFilename" | $copyrightHolderJson | $authorshipJson - | ${licenseText.map(l => s""","knora-api:hasLicenseText" : "${l.value}"""").getOrElse("")} + | ${licenseIdentifier.map(l => s""","knora-api:hasLicenseIdentifier" : "${l.value}"""").getOrElse("")} | ${licenseUri .map(u => s""", "knora-api:hasLicenseUri" : { "@type" : "xsd:anyURI", "@value":"${u.value}" }""") .getOrElse("")} @@ -147,7 +147,7 @@ sealed abstract case class UploadFileRequest private ( comment = comment, copyrightHolder = self.copyrightHolder, authorship = self.authorship, - licenseText = self.licenseText, + licenseIdentifier = self.licenseIdentifier, licenseUri = self.licenseUri, ) @@ -201,7 +201,7 @@ object UploadFileRequest { resourceIRI: Option[String] = None, copyrightHolder: Option[CopyrightHolder] = None, authorship: Option[List[Authorship]] = None, - licenseText: Option[LicenseText] = None, + licenseIdentifier: Option[LicenseIdentifier] = None, licenseUri: Option[LicenseUri] = None, ): UploadFileRequest = new UploadFileRequest( @@ -211,7 +211,7 @@ object UploadFileRequest { resourceIRI, copyrightHolder, authorship, - licenseText, + licenseIdentifier, licenseUri, ) {} } diff --git a/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologySimple.jsonld b/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologySimple.jsonld index 744882fe82..dc357b1b7c 100644 --- a/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologySimple.jsonld +++ b/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologySimple.jsonld @@ -926,7 +926,7 @@ { "@type": "owl:Restriction", "owl:onProperty": { - "@id": "knora-api:hasLicenseText" + "@id": "knora-api:hasLicenseIdentifier" }, "owl:maxCardinality": 1 }, @@ -1461,13 +1461,13 @@ "@id": "knora-api:hasLicenseDate" }, { - "rdfs:label": "has license text", + "rdfs:label": "has license identifier", "rdfs:comment": "Specifies the terms under which a work can be used. This statement may be a reference to a well-known license, such as Creative Commons (e.g. 'CC BY-SA') or a custom license.", "@type": "owl:DatatypeProperty", "knora-api:objectType": { "@id": "xsd:string" }, - "@id": "knora-api:hasLicenseText" + "@id": "knora-api:hasLicenseIdentifier" }, { "rdfs:label": "has license URI", diff --git a/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologyWithValueObjects.jsonld b/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologyWithValueObjects.jsonld index c90a23038f..6decdb61c6 100644 --- a/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologyWithValueObjects.jsonld +++ b/test_data/generated_test_data/ontologyR2RV2/knoraApiOntologyWithValueObjects.jsonld @@ -261,7 +261,7 @@ { "@type": "owl:Restriction", "owl:onProperty": { - "@id": "knora-api:hasLicenseText" + "@id": "knora-api:hasLicenseIdentifier" }, "owl:maxCardinality": 1, "knora-api:isInherited": true @@ -586,7 +586,7 @@ { "@type": "owl:Restriction", "owl:onProperty": { - "@id": "knora-api:hasLicenseText" + "@id": "knora-api:hasLicenseIdentifier" }, "owl:maxCardinality": 1, "knora-api:isInherited": true @@ -1428,7 +1428,7 @@ { "@type": "owl:Restriction", "owl:onProperty": { - "@id": "knora-api:hasLicenseText" + "@id": "knora-api:hasLicenseIdentifier" }, "owl:maxCardinality": 1, "knora-api:isInherited": true @@ -2427,7 +2427,7 @@ { "@type": "owl:Restriction", "owl:onProperty": { - "@id": "knora-api:hasLicenseText" + "@id": "knora-api:hasLicenseIdentifier" }, "owl:maxCardinality": 1, "knora-api:isInherited": true @@ -2754,7 +2754,7 @@ { "@type": "owl:Restriction", "owl:onProperty": { - "@id": "knora-api:hasLicenseText" + "@id": "knora-api:hasLicenseIdentifier" }, "owl:maxCardinality": 1 }, @@ -3939,7 +3939,7 @@ { "@type": "owl:Restriction", "owl:onProperty": { - "@id": "knora-api:hasLicenseText" + "@id": "knora-api:hasLicenseIdentifier" }, "owl:maxCardinality": 1, "knora-api:isInherited": true @@ -6222,7 +6222,7 @@ { "@type": "owl:Restriction", "owl:onProperty": { - "@id": "knora-api:hasLicenseText" + "@id": "knora-api:hasLicenseIdentifier" }, "owl:maxCardinality": 1, "knora-api:isInherited": true @@ -6401,7 +6401,7 @@ { "@type": "owl:Restriction", "owl:onProperty": { - "@id": "knora-api:hasLicenseText" + "@id": "knora-api:hasLicenseIdentifier" }, "owl:maxCardinality": 1, "knora-api:isInherited": true @@ -6587,7 +6587,7 @@ { "@type": "owl:Restriction", "owl:onProperty": { - "@id": "knora-api:hasLicenseText" + "@id": "knora-api:hasLicenseIdentifier" }, "owl:maxCardinality": 1, "knora-api:isInherited": true @@ -6938,7 +6938,7 @@ { "@type": "owl:Restriction", "owl:onProperty": { - "@id": "knora-api:hasLicenseText" + "@id": "knora-api:hasLicenseIdentifier" }, "owl:maxCardinality": 1, "knora-api:isInherited": true @@ -8759,13 +8759,13 @@ "@id": "knora-api:hasLicenseDate" }, { - "rdfs:label": "has license text", + "rdfs:label": "has license identifier", "rdfs:comment": "Specifies the terms under which a work can be used. This statement may be a reference to a well-known license, such as Creative Commons (e.g. CC BY-SA) or a custom license.", "@type": "owl:DatatypeProperty", "knora-api:objectType": { "@id": "xsd:string" }, - "@id": "knora-api:hasLicenseText" + "@id": "knora-api:hasLicenseIdentifier" }, { "rdfs:label": "has license URI", diff --git a/webapi/src/main/resources/knora-ontologies/knora-base.ttl b/webapi/src/main/resources/knora-ontologies/knora-base.ttl index 05ba42e625..457f20d16d 100644 --- a/webapi/src/main/resources/knora-ontologies/knora-base.ttl +++ b/webapi/src/main/resources/knora-ontologies/knora-base.ttl @@ -569,11 +569,11 @@ :objectDatatypeConstraint xsd:string . -### http://www.knora.org/ontology/knora-base#hasLicenseText +### http://www.knora.org/ontology/knora-base#hasLicenseIdentifier -:hasLicenseText +:hasLicenseIdentifier rdf:type owl:DatatypeProperty ; - rdfs:label "has license text"@en; + rdfs:label "has license identifier"@en; rdfs:comment "Specifies the terms under which a work can be used. This statement may be a reference to a well-known license, such as Creative Commons (e.g. 'CC BY-SA') or a custom license."@en ; :objectDatatypeConstraint xsd:string . @@ -1618,7 +1618,7 @@ owl:onProperty :hasAuthorship ; owl:minCardinality "0"^^xsd:nonNegativeInteger ], [ rdf:type owl:Restriction ; - owl:onProperty :hasLicenseText ; + owl:onProperty :hasLicenseIdentifier ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ], [ rdf:type owl:Restriction ; owl:onProperty :hasLicenseDate; diff --git a/webapi/src/main/scala/org/knora/webapi/messages/OntologyConstants.scala b/webapi/src/main/scala/org/knora/webapi/messages/OntologyConstants.scala index 8e87bca259..2e1765cadb 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/OntologyConstants.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/OntologyConstants.scala @@ -266,7 +266,7 @@ object OntologyConstants { val HasArchiveFileValue: IRI = KnoraBasePrefixExpansion + "hasArchiveFileValue" val HasCopyrightHolder: IRI = KnoraBasePrefixExpansion + "hasCopyrightHolder" val HasAuthorship: IRI = KnoraBasePrefixExpansion + "hasAuthorship" - val HasLicenseText: IRI = KnoraBasePrefixExpansion + "hasLicenseText" + val HasLicenseIdentifier: IRI = KnoraBasePrefixExpansion + "hasLicenseIdentifier" val HasLicenseUri: IRI = KnoraBasePrefixExpansion + "hasLicenseUri" val HasLicenseDate: IRI = KnoraBasePrefixExpansion + "hasLicenseDate" @@ -631,7 +631,7 @@ object OntologyConstants { val HasPermissions: IRI = KnoraApiV2PrefixExpansion + "hasPermissions" val HasCopyrightHolder: IRI = KnoraApiV2PrefixExpansion + "hasCopyrightHolder" val HasAuthorship: IRI = KnoraApiV2PrefixExpansion + "hasAuthorship" - val HasLicenseText: IRI = KnoraApiV2PrefixExpansion + "hasLicenseText" + val HasLicenseIdentifier: IRI = KnoraApiV2PrefixExpansion + "hasLicenseIdentifier" val HasLicenseUri: IRI = KnoraApiV2PrefixExpansion + "hasLicenseUri" val HasLicenseDate: IRI = KnoraApiV2PrefixExpansion + "hasLicenseDate" val UserHasPermission: String = KnoraApiV2PrefixExpansion + "userHasPermission" diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala index 0d719540b4..5d75b3943d 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala @@ -54,7 +54,7 @@ import org.knora.webapi.slice.admin.domain.model.Authorship import org.knora.webapi.slice.admin.domain.model.CopyrightHolder import org.knora.webapi.slice.admin.domain.model.KnoraProject.ProjectIri import org.knora.webapi.slice.admin.domain.model.LicenseDate -import org.knora.webapi.slice.admin.domain.model.LicenseText +import org.knora.webapi.slice.admin.domain.model.LicenseIdentifier import org.knora.webapi.slice.admin.domain.model.LicenseUri import org.knora.webapi.slice.admin.domain.model.ListProperties.ListIri import org.knora.webapi.slice.admin.domain.model.Permission @@ -1072,9 +1072,9 @@ final case class ConstructResponseUtilV2Live( authorship = valueObject .maybeStringListObject(OntologyConstants.KnoraBase.HasAuthorship.toSmartIri) .map(_.map(Authorship.unsafeFrom).toList), - licenseText = valueObject - .maybeStringObject(OntologyConstants.KnoraBase.HasLicenseText.toSmartIri) - .map(LicenseText.unsafeFrom), + licenseIdentifier = valueObject + .maybeStringObject(OntologyConstants.KnoraBase.HasLicenseIdentifier.toSmartIri) + .map(LicenseIdentifier.unsafeFrom), licenseUri = valueObject.maybeIriObject(OntologyConstants.KnoraBase.HasLicenseUri.toSmartIri).map(LicenseUri.unsafeFrom), licenseDate = diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraBaseToApiV2ComplexTransformationRules.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraBaseToApiV2ComplexTransformationRules.scala index 897f0c96b6..98cafb75f8 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraBaseToApiV2ComplexTransformationRules.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/ontologymessages/KnoraBaseToApiV2ComplexTransformationRules.scala @@ -160,8 +160,8 @@ object KnoraBaseToApiV2ComplexTransformationRules extends OntologyTransformation .withRdfLabelEn("has authorship") .withRdfCommentEn("Credit, Moral Rights, Author(s)") - private val HasLicenseText = makeOwlDatatypeProperty(KA.HasLicenseText, XSD.STRING) - .withRdfLabelEn("has license text") + private val HasLicenseIdentifier = makeOwlDatatypeProperty(KA.HasLicenseIdentifier, XSD.STRING) + .withRdfLabelEn("has license identifier") .withRdfCommentEn( "Specifies the terms under which a work can be used. This statement may be a reference to a well-known license, such as Creative Commons (e.g. CC BY-SA) or a custom license.", ) @@ -670,7 +670,7 @@ object KnoraBaseToApiV2ComplexTransformationRules extends OntologyTransformation HasCopyrightHolder, HasAuthorship, HasIncomingLinkValue, - HasLicenseText, + HasLicenseIdentifier, HasLicenseUri, HasLicenseDate, IntValueAsInt, 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 b840f5d88e..5172359f31 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 @@ -48,7 +48,7 @@ import org.knora.webapi.slice.admin.domain.model.Authorship import org.knora.webapi.slice.admin.domain.model.CopyrightHolder import org.knora.webapi.slice.admin.domain.model.KnoraProject.Shortcode import org.knora.webapi.slice.admin.domain.model.LicenseDate -import org.knora.webapi.slice.admin.domain.model.LicenseText +import org.knora.webapi.slice.admin.domain.model.LicenseIdentifier import org.knora.webapi.slice.admin.domain.model.LicenseUri import org.knora.webapi.slice.admin.domain.model.Permission import org.knora.webapi.slice.common.Value @@ -2005,7 +2005,7 @@ case class FileValueV2( originalMimeType: Option[String] = None, copyrightHolder: Option[CopyrightHolder] = None, authorship: Option[List[Authorship]] = None, - licenseText: Option[LicenseText] = None, + licenseIdentifier: Option[LicenseIdentifier] = None, licenseUri: Option[LicenseUri] = None, licenseDate: Option[LicenseDate] = None, ) @@ -2014,11 +2014,11 @@ object FileValueV2 { def makeNew(r: Resource, info: FileInfo): Either[String, FileValueV2] = { val meta = info.metadata for { - copyrightHolder <- r.objectStringOption(HasCopyrightHolder, CopyrightHolder.from) - authorship <- r.objectStringListOption(HasAuthorship, Authorship.from) - licenseText <- r.objectStringOption(HasLicenseText, LicenseText.from) - licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) - licenseDate = Some(LicenseDate.makeNew) + copyrightHolder <- r.objectStringOption(HasCopyrightHolder, CopyrightHolder.from) + authorship <- r.objectStringListOption(HasAuthorship, Authorship.from) + licenseIdentifier <- r.objectStringOption(HasLicenseIdentifier, LicenseIdentifier.from) + licenseUri <- r.objectDataTypeOption(HasLicenseUri, XSD.anyURI.toString, LicenseUri.from) + licenseDate = Some(LicenseDate.makeNew) } yield FileValueV2( info.filename, meta.internalMimeType, @@ -2026,7 +2026,7 @@ object FileValueV2 { meta.originalMimeType, copyrightHolder, authorship, - licenseText, + licenseIdentifier, licenseUri, licenseDate, ) @@ -2076,12 +2076,12 @@ sealed trait FileValueContentV2 extends ValueContentV2 { datatype = OntologyConstants.Xsd.Uri.toSmartIri, ), ) - val copyrightHolder = fileValue.copyrightHolder.map(mkJsonLdString).map((HasCopyrightHolder, _)) - val authorship = fileValue.authorship.map(mkJsonLdStringArray).map((HasAuthorship, _)) - val licenseText = fileValue.licenseText.map(mkJsonLdString).map((HasLicenseText, _)) - val licenseUri = fileValue.licenseUri.map(mkJsonLdUri).map((HasLicenseUri, _)) - val licenseDate = fileValue.licenseDate.map(mkJsonLdDate).map((HasLicenseDate, _)) - knownValues ++ copyrightHolder ++ authorship ++ licenseText ++ licenseUri ++ licenseDate + val copyrightHolder = fileValue.copyrightHolder.map(mkJsonLdString).map((HasCopyrightHolder, _)) + val authorship = fileValue.authorship.map(mkJsonLdStringArray).map((HasAuthorship, _)) + val licenseIdentifier = fileValue.licenseIdentifier.map(mkJsonLdString).map((HasLicenseIdentifier, _)) + val licenseUri = fileValue.licenseUri.map(mkJsonLdUri).map((HasLicenseUri, _)) + val licenseDate = fileValue.licenseDate.map(mkJsonLdDate).map((HasLicenseDate, _)) + knownValues ++ copyrightHolder ++ authorship ++ licenseIdentifier ++ licenseUri ++ licenseDate } } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2Optics.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2Optics.scala index 380ab94079..9af9c17137 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2Optics.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2Optics.scala @@ -9,7 +9,7 @@ import monocle.* import monocle.macros.* import org.knora.webapi.slice.admin.domain.model.CopyrightHolder -import org.knora.webapi.slice.admin.domain.model.LicenseText +import org.knora.webapi.slice.admin.domain.model.LicenseIdentifier import org.knora.webapi.slice.admin.domain.model.LicenseUri object ValueMessagesV2Optics { @@ -19,7 +19,8 @@ object ValueMessagesV2Optics { val copyrightHolderOption: Lens[FileValueV2, Option[CopyrightHolder]] = GenLens[FileValueV2](_.copyrightHolder) - val licenseTextOption: Lens[FileValueV2, Option[LicenseText]] = GenLens[FileValueV2](_.licenseText) + val licenseIdentifierOption: Lens[FileValueV2, Option[LicenseIdentifier]] = + GenLens[FileValueV2](_.licenseIdentifier) val licenseUriOption: Lens[FileValueV2, Option[LicenseUri]] = GenLens[FileValueV2](_.licenseUri) @@ -39,8 +40,8 @@ object ValueMessagesV2Optics { }) val copyrightHolderOption: Lens[FileValueContentV2, Option[CopyrightHolder]] = fileValueV2.andThen(FileValueV2Optics.copyrightHolderOption) - val licenseTextOption: Lens[FileValueContentV2, Option[LicenseText]] = - fileValueV2.andThen(FileValueV2Optics.licenseTextOption) + val licenseIdentifierOption: Lens[FileValueContentV2, Option[LicenseIdentifier]] = + fileValueV2.andThen(FileValueV2Optics.licenseIdentifierOption) val licenseUriOption: Lens[FileValueContentV2, Option[LicenseUri]] = fileValueV2.andThen(FileValueV2Optics.licenseUriOption) } diff --git a/webapi/src/main/scala/org/knora/webapi/slice/admin/api/Codecs.scala b/webapi/src/main/scala/org/knora/webapi/slice/admin/api/Codecs.scala index 25a5785e1c..d3c01e4e4c 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/admin/api/Codecs.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/admin/api/Codecs.scala @@ -108,7 +108,7 @@ object Codecs { implicit val sparqlEncodedString: StringCodec[SparqlEncodedString] = stringCodec(SparqlEncodedString.from) implicit val status: StringCodec[Status] = booleanCodec(Status.from) implicit val copyrightHolder: StringCodec[CopyrightHolder] = stringCodec(CopyrightHolder.from) - implicit val licenseText: StringCodec[LicenseText] = stringCodec(LicenseText.from) + implicit val licenseIdentifier: StringCodec[LicenseIdentifier] = stringCodec(LicenseIdentifier.from) implicit val licenseUri: StringCodec[LicenseUri] = stringCodec(LicenseUri.from) // user diff --git a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala index fb3426aadf..f113da73f5 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala @@ -31,10 +31,10 @@ object Authorship extends StringValueCompanion[Authorship] { fromValidations("Authorship", Authorship.apply, List(nonEmpty, noLineBreaks, maxLength(1_000)))(str) } -final case class LicenseText private (override val value: String) extends StringValue -object LicenseText extends StringValueCompanion[LicenseText] { - def from(str: String): Either[String, LicenseText] = - fromValidations("License text", LicenseText.apply, List(nonEmpty, maxLength(100_000)))(str) +final case class LicenseIdentifier private (override val value: String) extends StringValue +object LicenseIdentifier extends StringValueCompanion[LicenseIdentifier] { + def from(str: String): Either[String, LicenseIdentifier] = + fromValidations("License Identifier", LicenseIdentifier.apply, List(nonEmpty, noLineBreaks, maxLength(100_000)))(str) } final case class LicenseUri private (override val value: String) extends StringValue diff --git a/webapi/src/main/scala/org/knora/webapi/slice/common/repo/rdf/Vocabulary.scala b/webapi/src/main/scala/org/knora/webapi/slice/common/repo/rdf/Vocabulary.scala index 59602b6a78..3be6abf3fc 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/common/repo/rdf/Vocabulary.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/common/repo/rdf/Vocabulary.scala @@ -110,7 +110,7 @@ object Vocabulary { val valueHasStandoff: Iri = iri(kb + "valueHasStandoff") val hasCopyrightHolder: Iri = iri(kb + "hasCopyrightHolder") val hasAuthorship: Iri = iri(kb + "hasAuthorship") - val hasLicenseText: Iri = iri(kb + "hasLicenseText") + val hasLicenseIdentifier: Iri = iri(kb + "hasLicenseIdentifier") val hasLicenseUri: Iri = iri(kb + "hasLicenseUri") val hasLicenseDate: Iri = iri(kb + "hasLicenseDate") diff --git a/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLive.scala b/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLive.scala index 4d830db8f3..086f8b1c94 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLive.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/resources/repo/service/ResourcesRepoLive.scala @@ -282,7 +282,7 @@ object ResourcesRepoLive { .andHasOptional(KB.originalFilename, v.fileValue.originalFilename.map(literalOf)) .andHasOptional(KB.originalMimeType, v.fileValue.originalMimeType.map(literalOf)) .andHasOptional(KB.hasCopyrightHolder, v.fileValue.copyrightHolder.map(_.value).map(literalOf)) - .andHasOptional(KB.hasLicenseText, v.fileValue.licenseText.map(_.value).map(literalOf)) + .andHasOptional(KB.hasLicenseIdentifier, v.fileValue.licenseIdentifier.map(_.value).map(literalOf)) .andHasOptional(KB.hasLicenseUri, v.fileValue.licenseUri.map(_.value).map(literalOfType(_, XSD.ANYURI))) .andHasOptional( KB.hasLicenseDate, diff --git a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/addValueVersion.scala.txt b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/addValueVersion.scala.txt index 076b52a632..78f6dcfd22 100644 --- a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/addValueVersion.scala.txt +++ b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/addValueVersion.scala.txt @@ -250,9 +250,9 @@ DELETE { case None => {} } - @fileValueContentV2.fileValue.licenseText match { + @fileValueContentV2.fileValue.licenseIdentifier match { case Some(text) => { - <@newValueIri> knora-base:hasLicenseText """@text.value""" . + <@newValueIri> knora-base:hasLicenseIdentifier """@text.value""" . } case None => {} } diff --git a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/createValue.scala.txt b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/createValue.scala.txt index 8f69892265..3dd94ffe30 100644 --- a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/createValue.scala.txt +++ b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/createValue.scala.txt @@ -244,9 +244,9 @@ DELETE { case None => {} } - @fileValueContentV2.fileValue.licenseText match { + @fileValueContentV2.fileValue.licenseIdentifier match { case Some(text) => { - <@newValueIri> knora-base:hasLicenseText """@text.value""" . + <@newValueIri> knora-base:hasLicenseIdentifier """@text.value""" . } case None => {} } 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 index ebe4015b25..c75331fc8c 100644 --- a/webapi/src/test/scala/org/knora/webapi/slice/common/ApiComplexV2JsonLdRequestParserSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/slice/common/ApiComplexV2JsonLdRequestParserSpec.scala @@ -457,7 +457,7 @@ object ApiComplexV2JsonLdRequestParserSpec extends ZIOSpecDefault { | "ka:fileValueHasFilename": "internalFilename.ext", | "ka:hasCopyrightHolder" : "Jane Doe", | "ka:hasAuthorship" : [ "Mr. Smith", "Author McAuthorface" ], - | "ka:hasLicenseText" : "CC-BY-4.0", + | "ka:hasLicenseIdentifier" : "CC-BY-4.0", | "ka:hasLicenseUri" : { | "@value" : "http://creativecommons.org/licenses/by/4.0/", | "@type" : "xsd:anyURI" @@ -477,7 +477,7 @@ object ApiComplexV2JsonLdRequestParserSpec extends ZIOSpecDefault { expectedFileValue.copy( copyrightHolder = Some(CopyrightHolder.unsafeFrom("Jane Doe")), authorship = Some(List(Authorship.unsafeFrom("Author McAuthorface"), Authorship.unsafeFrom("Mr. Smith"))), - licenseText = Some(LicenseText.unsafeFrom("CC-BY-4.0")), + licenseIdentifier = Some(LicenseIdentifier.unsafeFrom("CC-BY-4.0")), licenseUri = Some(LicenseUri.unsafeFrom("http://creativecommons.org/licenses/by/4.0/")), licenseDate = Some(LicenseDate.makeNew), ), From 2e54510e5d4db5377dc559b3e857d99359862919 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Mon, 20 Jan 2025 19:24:55 +0100 Subject: [PATCH 30/31] fmt --- .../slice/admin/domain/model/CopyrightAndLicenses.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala index f113da73f5..ca348a7110 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/admin/domain/model/CopyrightAndLicenses.scala @@ -34,7 +34,9 @@ object Authorship extends StringValueCompanion[Authorship] { final case class LicenseIdentifier private (override val value: String) extends StringValue object LicenseIdentifier extends StringValueCompanion[LicenseIdentifier] { def from(str: String): Either[String, LicenseIdentifier] = - fromValidations("License Identifier", LicenseIdentifier.apply, List(nonEmpty, noLineBreaks, maxLength(100_000)))(str) + fromValidations("License Identifier", LicenseIdentifier.apply, List(nonEmpty, noLineBreaks, maxLength(100_000)))( + str, + ) } final case class LicenseUri private (override val value: String) extends StringValue From c3c19552b41c2137f595444a7411f4ead9e303ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Mon, 20 Jan 2025 19:50:43 +0100 Subject: [PATCH 31/31] rm redundant braces --- .../main/scala/org/knora/webapi/slice/common/ValueTypes.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapi/src/main/scala/org/knora/webapi/slice/common/ValueTypes.scala b/webapi/src/main/scala/org/knora/webapi/slice/common/ValueTypes.scala index a18d09e0b1..7be9686a01 100644 --- a/webapi/src/main/scala/org/knora/webapi/slice/common/ValueTypes.scala +++ b/webapi/src/main/scala/org/knora/webapi/slice/common/ValueTypes.scala @@ -32,7 +32,7 @@ trait WithFrom[-I, +A] { from(in).fold(e => throw new IllegalArgumentException(e), identity) } -trait StringValueCompanion[A <: StringValue] extends WithFrom[String, A] {} +trait StringValueCompanion[A <: StringValue] extends WithFrom[String, A] object StringValueCompanion { def nonEmpty: String => Validation[String, String] =