Skip to content

Commit

Permalink
fix: Allow StringLiterals with language tag for properties when creat…
Browse files Browse the repository at this point in the history
…ing a class (DEV-4555) (#3486)
  • Loading branch information
seakayone authored Jan 31, 2025
1 parent 7fd7f46 commit 8df9737
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import java.net.URI
import scala.util.Try
import scala.util.matching.Regex

import dsp.valueobjects.Iri
import dsp.valueobjects.Iri.isIri
import dsp.valueobjects.IriErrorMessages
import dsp.valueobjects.UuidUtil
import org.knora.webapi.messages.OntologyConstants.KnoraAdmin.KnoraAdminPrefixExpansion
import org.knora.webapi.messages.StringFormatter.IriDomain
import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2
import org.knora.webapi.slice.admin.domain.model.KnoraProject.*
Expand Down Expand Up @@ -48,15 +48,18 @@ object KnoraProject {
object ProjectIri extends StringValueCompanion[ProjectIri] {

private val BuiltInProjects: Seq[String] =
Seq("SystemProject", "DefaultSharedOntologiesProject").map(KnoraAdminPrefixExpansion + _)
Seq(
"http://www.knora.org/ontology/knora-admin#SystemProject",
"http://www.knora.org/ontology/knora-admin#DefaultSharedOntologiesProject",
)

/**
* Explanation of the project IRI regex:
* * `^` asserts the start of the string.
* * `http://rdfh\.ch/projects/` matches the specified prefix.
* * `[a-zA-Z0-9_-]{4,40}` matches any alphanumeric character, hyphen, or underscore between 4 and 40 times.
*/
private val projectIriRegEx = """^http://rdfh\.ch/projects/[a-zA-Z0-9_-]{4,40}$""".r
private lazy val projectIriRegEx = """^http://rdfh\.ch/projects/[a-zA-Z0-9_-]{4,40}$""".r

/**
* Returns `true` if an IRI string looks like a Knora project IRI
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ import java.nio.charset.StandardCharsets
object DatasetOps { self =>

extension (ds: Dataset) {
def printTrig: UIO[Unit] = as(Lang.TRIG).flatMap(Console.printLine(_)).logError.ignore

def printTrig: UIO[Unit] =
Console.printLine("/// DS TRIG START ///").ignore *>
as(Lang.TRIG).flatMap(Console.printLine(_)).logError.ignore *>
Console.printLine("/// DS TRIG END ///").ignore

def asTriG: Task[String] = as(Lang.TRIG)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ object ModelOps { self =>
asTurtle.flatMap(Console.printLine(_)).logError.ignore

def printTriG: UIO[Unit] =
asTriG.flatMap(Console.printLine(_)).logError.ignore
Console.printLine("/// Model TRIG START ///").ignore *>
asTriG.flatMap(Console.printLine(_)).logError.ignore *>
Console.printLine("/// Model TRIG END ///").ignore

def asTurtle: Task[String] = as(Lang.TURTLE)
def asTriG: Task[String] = as(Lang.TRIG)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,27 +150,31 @@ final case class OntologyV2RequestParser(iriConverter: IriConverter) {
ZIO.fromOption(r.uri).orElseFail("No class IRI found").flatMap(str => iriConverter.asResourceClassIri(str))

private def extractPredicates(r: Resource): ZIO[Scope, String, Map[SmartIri, PredicateInfoV2]] =
val propertyIter = r
val propertyIter: Map[String, List[Statement]] = r
.listProperties()
.asScala
.filterNot(_.predicateUri == null)
.filterNot(_.predicateUri == RDFS.subPropertyOf.toString)
.filterNot(_.predicateUri == RDFS.subClassOf.toString)
.toList
ZIO.foreach(propertyIter)(extractPredicateInfo).map(_.toMap).logError
.groupBy(_.predicateUri)
ZIO.foreach(propertyIter) { case (iri, stmts) => extractPredicateInfo(iri, stmts) }.map(_.toMap).logError

private def extractPredicateInfo(stmt: Statement): ZIO[Scope, String, (SmartIri, PredicateInfoV2)] =
private def extractPredicateInfo(
iri: String,
stmts: List[Statement],
): ZIO[Scope, String, (SmartIri, PredicateInfoV2)] =
for {
propertyIri <- iriConverter.asSmartIri(stmt.predicateUri).mapError(_.getMessage)
objects <- asPredicateInfoV2(stmt.getObject)
} yield (propertyIri, PredicateInfoV2(propertyIri, List(objects)))
propertyIri <- iriConverter.asSmartIri(iri).mapError(_.getMessage)
objects <- ZIO.foreach(stmts)(asOntologyLiteralV2)
} yield (propertyIri, PredicateInfoV2(propertyIri, objects))

private def asPredicateInfoV2(node: RDFNode): ZIO[Scope, String, OntologyLiteralV2] =
node match
private def asOntologyLiteralV2(stmt: Statement): ZIO[Scope, String, OntologyLiteralV2] =
stmt.getObject match
case res: Resource => iriConverter.asSmartIri(res.getURI).mapBoth(_.getMessage, SmartIriLiteralV2.apply)
case literal: Literal => {
literal.getValue match
case str: String => ZIO.succeed(StringLiteralV2.from(str, Option(literal.getLanguage)))
case str: String => ZIO.succeed(StringLiteralV2.from(str, Option(literal.getLanguage).filter(_.nonEmpty)))
case b: java.lang.Boolean => ZIO.succeed(BooleanLiteralV2(b))
case _ => ZIO.fail(s"Unsupported literal type: ${literal.getValue.getClass}")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import zio.test.check
import java.time.Instant

import org.knora.webapi.ApiV2Complex
import org.knora.webapi.LanguageCode
import org.knora.webapi.TestDataFactory
import org.knora.webapi.messages.IriConversions.ConvertibleIri
import org.knora.webapi.messages.StringFormatter
Expand Down Expand Up @@ -157,11 +158,17 @@ object OntologyV2RequestParserSpec extends ZIOSpecDefault {
),
"http://www.w3.org/2000/01/rdf-schema#label".toSmartIri -> PredicateInfoV2(
predicateIri = "http://www.w3.org/2000/01/rdf-schema#label".toSmartIri,
objects = Seq(StringLiteralV2.from("wild thing", Some("en"))),
objects = Seq(
StringLiteralV2.from("wild thing", LanguageCode.EN),
StringLiteralV2.from("Wildes Ding", LanguageCode.DE),
),
),
"http://www.w3.org/2000/01/rdf-schema#comment".toSmartIri -> PredicateInfoV2(
predicateIri = "http://www.w3.org/2000/01/rdf-schema#comment".toSmartIri,
objects = Seq(StringLiteralV2.from("A thing that is wild", Some("en"))),
objects = Seq(
StringLiteralV2.from("A thing that is wild", LanguageCode.EN),
StringLiteralV2.from("Ein valides Ding", LanguageCode.DE),
),
),
),
classIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#WildThing".toSmartIri,
Expand All @@ -188,14 +195,26 @@ object OntologyV2RequestParserSpec extends ZIOSpecDefault {
| "@graph" : [ {
| "@id" : "anything:WildThing",
| "@type" : "owl:Class",
| "rdfs:label" : {
| "@language" : "en",
| "@value" : "wild thing"
| },
| "rdfs:comment" : {
| "@language" : "en",
| "@value" : "A thing that is wild"
| },
| "rdfs:label" : [
| {
| "@language" : "en",
| "@value" : "wild thing"
| },
| {
| "@language" : "de",
| "@value" : "Wildes Ding"
| }
| ],
| "rdfs:comment" : [
| {
| "@language" : "en",
| "@value" : "A thing that is wild"
| },
| {
| "@language" : "de",
| "@value" : "Ein valides Ding"
| }
| ],
| "rdfs:subClassOf" : [ {
| "@id" : "anything:Thing"
| }, {
Expand Down

0 comments on commit 8df9737

Please sign in to comment.