diff --git a/.gitignore b/.gitignore index ac8300205..d8d44e3b7 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ /tests/ui/node_modules /tests/ui/out /tests/ui/test-results +/rdf/target/ diff --git a/CLAUDE.md b/CLAUDE.md index 723a28009..c2e17250f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -39,8 +39,9 @@ make down # Stop the services make down -- -v # Stop and remove volumes make drop # Complete reset (down -v, then wipe local dirs) -make cli # Build the ldh CLI, print the PATH export to run -make cli-version # Set cli/pom.xml to the platform version in pom.xml +make rdf # Install the linkeddatahub-rdf library +make cli # Build the ldh CLI (installs rdf first), print the PATH export +make cli-version # Set rdf/ and cli/ poms to the platform version in pom.xml ``` ### Testing @@ -155,15 +156,42 @@ into a shaded `cli/target/ldh.jar` that `cli/bin/ldh` launches. See `cli/README. script → command table and the behavioral differences from the scripts. ```bash -cd cli && mvn package && export PATH="$PWD/bin:$PATH" +make cli && export PATH="$PWD/cli/bin:$PATH" ldh create container --parent "$LDH_BASE" --title "Some" --slug some ldh admin add agent --agent "$AGENT_URI" "${ADMIN_BASE}acl/groups/writers/" ``` -`cli/` is not a module of the platform reactor (the root pom is the webapp artifact, so it cannot -carry ``), but it shares the platform's version: `release.sh` runs `versions:set` on it -around both release bumps, and `make cli-version` re-aligns it if it drifts. +## The RDF library + +`rdf/` builds `com.atomgraph:linkeddatahub-rdf` — the vocabularies (`com.atomgraph.linkeddatahub.rdf.vocabulary`) +and the document shapes the HTTP API accepts (`Documents`, `Blocks`, `Views`, `Queries`, `Services`, +`Imports`, `Acl`, `Ontologies`, plus the SPARQL `Updates`). Jena and nothing else: no Jersey, no +picocli. Every consumer talks to the platform over HTTP its own way, so what is shared is the shape +of the request body, not how it is sent. + +Two consumers today — the CLI, and Web-Algebra's `ldh-*` operations in `../REST-VKG` — which is why +the builders live here rather than on the picocli command classes that used to own them. A shape +described in one place cannot drift between them. + +The platform keeps its own `com.atomgraph.linkeddatahub.vocabulary`, still duplicated with this +library's. Collapsing them would make the platform depend on `rdf/`, and the Dockerfile builds the +webapp from `COPY src` + `COPY pom.xml` alone — so the dependency has to point away from the +platform, not at it. + +Neither `rdf/` nor `cli/` is a module of the platform reactor (the root pom is the webapp artifact, +so it cannot carry ``), but both share the platform's version: `release.sh` runs +`versions:set` on them around both release bumps, and `make cli-version` re-aligns them if they +drift. `cli/pom.xml` resolves the library by `${project.version}`, so the two move together. +`make cli` installs `rdf/` first; building `cli/` on its own needs `make rdf` to have run at least +once since the last version bump. + +`linkeddatahub-rdf` publishes to Maven Central on its own, since `release:perform` only carries +reactor modules: `release.sh` deploys it right after the platform, extracting `rdf/` from the release +tag with `git archive` because the working tree has already moved on to the next SNAPSHOT by then. +A snapshot can be published by hand with `cd rdf && mvn -Prelease clean deploy` — the `release` +profile attaches the sources and javadoc jars and signs them, which is what +`release` gets the platform. `LDH_CERT_FILE`, `LDH_CERT_PASSWORD`, `LDH_BASE` and `LDH_PROXY` supply defaults for `-c`, `-p`, `-b` and `--proxy`. `-c/--cert` takes either format the agent's credential comes in — a PKCS12 diff --git a/cli/README.md b/cli/README.md index cef2eb3a7..08d8bfbfb 100644 --- a/cli/README.md +++ b/cli/README.md @@ -46,11 +46,17 @@ make cli which prints the `export PATH=...` line to run afterwards. It is the equivalent of: ```bash +cd rdf && mvn install && cd .. cd cli mvn package export PATH="$PWD/bin:$PATH" ``` +The `rdf/` step installs `com.atomgraph:linkeddatahub-rdf`, which holds the vocabularies and the +document shapes the commands build — shared with Web-Algebra's `ldh-*` operations, so the two +cannot drift. It is resolved by `${project.version}`, so it has to be installed again after a +version bump; `make cli` does that for you. + This produces the self-contained `target/ldh.jar`, which the `cli/bin/ldh` launcher runs. The launcher prefers `LDH_JAR`, then a jar sitting beside it (the release archive layout), then `../target/ldh.jar` (the source checkout layout). diff --git a/cli/pom.xml b/cli/pom.xml index 4b7b1a567..72fc6d4e4 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -17,7 +17,32 @@ 3.1.11 + + + + central-portal-snapshots + https://central.sonatype.com/repository/maven-snapshots/ + + false + + + true + + + + + + + com.atomgraph + linkeddatahub-rdf + ${project.version} + info.picocli picocli diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/BaseCommand.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/BaseCommand.java index 972860578..19e4a0f50 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/BaseCommand.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/BaseCommand.java @@ -32,7 +32,6 @@ import java.util.concurrent.Callable; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; -import org.apache.jena.rdf.model.Resource; import org.apache.jena.riot.Lang; import org.apache.jena.riot.RDFLanguages; import org.apache.jena.riot.RDFParser; @@ -124,20 +123,6 @@ protected static void put(LDHClient client, URI target, Model model) HttpException.check(target, client.put(target, Entity.entity(model, TEXT_TURTLE_TYPE), ACCEPT_TURTLE)).close(); } - /** - * Returns the subject resource for an appended description: the --uri value - * resolved against the target document URI, or a fresh blank node when not given. - * - * @param model model to create the resource in - * @param target target document URI - * @param uri --uri option value (absolute or relative, can be null) - * @return subject resource - */ - protected static Resource createSubject(Model model, URI target, String uri) - { - return uri != null ? model.createResource(target.resolve(uri).toString()) : model.createResource(); - } - /** * Parses an RDF stream into a model, resolving relative URIs against the base URI * (the equivalent of the scripts' turtle --base piping). diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddConstruct.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddConstruct.java index ec84ddecf..38f5f7eab 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddConstruct.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddConstruct.java @@ -19,16 +19,11 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.http.LDHClient; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.vocab.LDH; -import com.atomgraph.linkeddatahub.cli.vocab.SP; +import com.atomgraph.linkeddatahub.rdf.Queries; +import com.atomgraph.linkeddatahub.rdf.vocabulary.SP; import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.ModelFactory; -import org.apache.jena.rdf.model.Resource; -import org.apache.jena.vocabulary.DCTerms; -import org.apache.jena.vocabulary.RDF; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; import picocli.CommandLine.Option; @@ -88,33 +83,7 @@ public Integer call() throws Exception */ public static void core(LDHClient client, URI target, String uri, String title, String queryText, URI service, String description) { - post(client, target, buildModel(target, uri, SP.Construct, title, queryText, service, description)); - } - - /** - * Builds a SPIN query description. - * - * @param target target document URI - * @param uri query URI (optional) - * @param queryType SPIN query class (sp:Construct or sp:Select) - * @param title query title - * @param queryText query string - * @param service SPARQL service URI (optional) - * @param description query description (optional) - * @return query model - */ - public static Model buildModel(URI target, String uri, Resource queryType, String title, String queryText, URI service, String description) - { - Model model = ModelFactory.createDefaultModel(); - - Resource query = createSubject(model, target, uri). - addProperty(RDF.type, queryType). - addProperty(DCTerms.title, title). - addProperty(SP.text, queryText); - if (service != null) query.addProperty(LDH.service, model.createResource(service.toString())); - if (description != null) query.addProperty(DCTerms.description, description); - - return model; + post(client, target, Queries.query(target, uri, SP.Construct, title, queryText, service, description)); } } diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddFile.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddFile.java index 7872d4529..a3f5df769 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddFile.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddFile.java @@ -20,8 +20,8 @@ import com.atomgraph.linkeddatahub.cli.http.HttpException; import com.atomgraph.linkeddatahub.cli.http.LDHClient; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.util.Digests; -import com.atomgraph.linkeddatahub.cli.vocab.NFO; +import com.atomgraph.linkeddatahub.rdf.Digests; +import com.atomgraph.linkeddatahub.rdf.vocabulary.NFO; import jakarta.ws.rs.client.Entity; import jakarta.ws.rs.core.MediaType; import java.io.IOException; diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddGenericService.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddGenericService.java index 554b41f0c..7952b5307 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddGenericService.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddGenericService.java @@ -18,14 +18,8 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.vocab.A; -import com.atomgraph.linkeddatahub.cli.vocab.SD; +import com.atomgraph.linkeddatahub.rdf.Services; import java.net.URI; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.ModelFactory; -import org.apache.jena.rdf.model.Resource; -import org.apache.jena.vocabulary.DCTerms; -import org.apache.jena.vocabulary.RDF; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; import picocli.CommandLine.Option; @@ -72,41 +66,10 @@ public Integer call() throws Exception { baseMixin.require(getSpec()); // required by the script interface - post(getClient(), target, buildModel(target, uri, title, endpoint, graphStore, authUser, authPwd, description)); + post(getClient(), target, Services.service(target, uri, title, endpoint, graphStore, authUser, authPwd, description)); print(target); return 0; } - /** - * Builds the service description. - * - * @param target target document URI - * @param uri service URI (optional) - * @param title service title - * @param endpoint SPARQL endpoint URI - * @param graphStore Graph Store Protocol endpoint URI (optional) - * @param authUser HTTP Basic auth username (optional) - * @param authPwd HTTP Basic auth password (optional) - * @param description service description (optional) - * @return service model - */ - public static Model buildModel(URI target, String uri, String title, URI endpoint, URI graphStore, String authUser, String authPwd, String description) - { - Model model = ModelFactory.createDefaultModel(); - - Resource service = createSubject(model, target, uri). - addProperty(RDF.type, SD.Service). - addProperty(DCTerms.title, title). - addProperty(SD.endpoint, model.createResource(endpoint.toString())). - addProperty(SD.supportedLanguage, SD.SPARQL11Query). - addProperty(SD.supportedLanguage, SD.SPARQL11Update); - if (graphStore != null) service.addProperty(A.graphStore, model.createResource(graphStore.toString())); - if (authUser != null) service.addProperty(A.authUser, authUser); - if (authPwd != null) service.addProperty(A.authPwd, authPwd); - if (description != null) service.addProperty(DCTerms.description, description); - - return model; - } - } diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddPackageImport.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddPackageImport.java index 698c3652c..189b72270 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddPackageImport.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddPackageImport.java @@ -19,7 +19,7 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.http.HttpException; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.sparql.Updates; +import com.atomgraph.linkeddatahub.rdf.Updates; import java.net.URI; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddResultSetChart.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddResultSetChart.java index ee9ebeb9c..cb3eefb0a 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddResultSetChart.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddResultSetChart.java @@ -18,14 +18,8 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.vocab.LDH; -import com.atomgraph.linkeddatahub.cli.vocab.SPIN; +import com.atomgraph.linkeddatahub.rdf.Views; import java.net.URI; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.ModelFactory; -import org.apache.jena.rdf.model.Resource; -import org.apache.jena.vocabulary.DCTerms; -import org.apache.jena.vocabulary.RDF; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; import picocli.CommandLine.Option; @@ -72,39 +66,10 @@ public Integer call() throws Exception { baseMixin.require(getSpec()); // required by the script interface - post(getClient(), target, buildModel(target, uri, title, query, chartType, categoryVarName, seriesVarName, description)); + post(getClient(), target, Views.resultSetChart(target, uri, title, query, chartType, categoryVarName, seriesVarName, description)); print(target); return 0; } - /** - * Builds the chart description. - * - * @param target target document URI - * @param uri chart URI (optional) - * @param title chart title - * @param query SELECT query URI - * @param chartType chart type URI - * @param categoryVarName category variable name - * @param seriesVarName series variable name - * @param description chart description (optional) - * @return chart model - */ - public static Model buildModel(URI target, String uri, String title, URI query, URI chartType, String categoryVarName, String seriesVarName, String description) - { - Model model = ModelFactory.createDefaultModel(); - - Resource chart = createSubject(model, target, uri). - addProperty(RDF.type, LDH.ResultSetChart). - addProperty(DCTerms.title, title). - addProperty(SPIN.query, model.createResource(query.toString())). - addProperty(LDH.chartType, model.createResource(chartType.toString())). - addProperty(LDH.categoryVarName, categoryVarName). - addProperty(LDH.seriesVarName, seriesVarName); - if (description != null) chart.addProperty(DCTerms.description, description); - - return model; - } - } diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddSelect.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddSelect.java index cbcd857ca..0e3530f48 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddSelect.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddSelect.java @@ -18,7 +18,8 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.vocab.SP; +import com.atomgraph.linkeddatahub.rdf.Queries; +import com.atomgraph.linkeddatahub.rdf.vocabulary.SP; import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; @@ -62,7 +63,7 @@ public Integer call() throws Exception { baseMixin.require(getSpec()); // required by the script interface - post(getClient(), target, AddConstruct.buildModel(target, uri, SP.Select, title, Files.readString(queryFile), service, description)); + post(getClient(), target, Queries.query(target, uri, SP.Select, title, Files.readString(queryFile), service, description)); print(target); return 0; diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddView.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddView.java index ab21f69ce..d6c313370 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddView.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/AddView.java @@ -18,15 +18,8 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.vocab.AC; -import com.atomgraph.linkeddatahub.cli.vocab.LDH; -import com.atomgraph.linkeddatahub.cli.vocab.SPIN; +import com.atomgraph.linkeddatahub.rdf.Views; import java.net.URI; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.ModelFactory; -import org.apache.jena.rdf.model.Resource; -import org.apache.jena.vocabulary.DCTerms; -import org.apache.jena.vocabulary.RDF; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; import picocli.CommandLine.Option; @@ -67,35 +60,10 @@ public Integer call() throws Exception { baseMixin.require(getSpec()); // required by the script interface - post(getClient(), target, buildModel(target, uri, query, title, description, mode)); + post(getClient(), target, Views.view(target, uri, query, title, description, mode)); print(target); return 0; } - /** - * Builds the view description. - * - * @param target target document URI - * @param uri view URI (optional) - * @param query SELECT query URI - * @param title view title (optional) - * @param description view description (optional) - * @param mode layout mode URI (optional) - * @return view model - */ - public static Model buildModel(URI target, String uri, URI query, String title, String description, URI mode) - { - Model model = ModelFactory.createDefaultModel(); - - Resource view = createSubject(model, target, uri). - addProperty(RDF.type, LDH.View). - addProperty(SPIN.query, model.createResource(query.toString())); - if (title != null) view.addProperty(DCTerms.title, title); - if (description != null) view.addProperty(DCTerms.description, description); - if (mode != null) view.addProperty(AC.mode, model.createResource(mode.toString())); - - return model; - } - } diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/CreateContainer.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/CreateContainer.java index 1a61d2014..b1ec21a46 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/CreateContainer.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/CreateContainer.java @@ -18,19 +18,10 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.util.Slugs; -import com.atomgraph.linkeddatahub.cli.util.URIRewriter; -import com.atomgraph.linkeddatahub.cli.vocab.AC; -import com.atomgraph.linkeddatahub.cli.vocab.DH; -import com.atomgraph.linkeddatahub.cli.vocab.LDH; -import com.atomgraph.linkeddatahub.cli.vocab.SPIN; +import com.atomgraph.linkeddatahub.rdf.Documents; +import com.atomgraph.linkeddatahub.rdf.Slugs; +import com.atomgraph.linkeddatahub.rdf.URIs; import java.net.URI; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.ModelFactory; -import org.apache.jena.rdf.model.Resource; -import org.apache.jena.sparql.vocabulary.FOAF; -import org.apache.jena.vocabulary.DCTerms; -import org.apache.jena.vocabulary.RDF; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; import picocli.CommandLine.Option; @@ -73,49 +64,11 @@ public Integer call() throws Exception { baseMixin.require(getSpec()); // required by the script interface - URI doc = URIRewriter.childURI(parent, slug != null ? slug : Slugs.defaultSlug()); - put(getClient(), doc, buildModel(doc, title, description, block, mode, primaryTopic)); + URI doc = URIs.childURI(parent, slug != null ? slug : Slugs.defaultSlug()); + put(getClient(), doc, Documents.container(doc, title, description, block, mode, primaryTopic)); print(doc); return 0; } - /** - * Builds the container document model with its first content block: the given block URI, - * a children view with an explicit mode, or the default children view. - * - * @param doc document URI - * @param title document title - * @param description document description (optional) - * @param block content block URI (optional) - * @param mode children view mode URI (optional, ignored when block is given) - * @param primaryTopic URI of the document's primary topic, relative or absolute (optional) - * @return document model - */ - public static Model buildModel(URI doc, String title, String description, URI block, URI mode, String primaryTopic) - { - Model model = ModelFactory.createDefaultModel(); - - Resource container = model.createResource(doc.toString()). - addProperty(RDF.type, DH.Container). - addProperty(DCTerms.title, title); - - if (block != null) container.addProperty(RDF.li(1), model.createResource(block.toString())); - else if (mode != null) container.addProperty(RDF.li(1), model.createResource(). - addProperty(RDF.type, LDH.Object). - addProperty(RDF.value, model.createResource(). - addProperty(RDF.type, LDH.View). - addProperty(SPIN.query, LDH.SelectChildren). - addProperty(AC.mode, model.createResource(mode.toString())))); - else container.addProperty(RDF.li(1), model.createResource(). - addProperty(RDF.type, LDH.Object). - addProperty(RDF.value, LDH.ChildrenView)); - - if (description != null) container.addProperty(DCTerms.description, description); - // See CreateItem.buildModel: resolved against the document, and singular by the vocabulary. - if (primaryTopic != null) container.addProperty(FOAF.primaryTopic, model.createResource(doc.resolve(primaryTopic).toString())); - - return model; - } - } diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/CreateItem.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/CreateItem.java index 40b3dd3f7..526342dc3 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/CreateItem.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/CreateItem.java @@ -18,16 +18,10 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.util.Slugs; -import com.atomgraph.linkeddatahub.cli.util.URIRewriter; -import com.atomgraph.linkeddatahub.cli.vocab.DH; +import com.atomgraph.linkeddatahub.rdf.Documents; +import com.atomgraph.linkeddatahub.rdf.Slugs; +import com.atomgraph.linkeddatahub.rdf.URIs; import java.net.URI; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.ModelFactory; -import org.apache.jena.rdf.model.Resource; -import org.apache.jena.sparql.vocabulary.FOAF; -import org.apache.jena.vocabulary.DCTerms; -import org.apache.jena.vocabulary.RDF; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; import picocli.CommandLine.Option; @@ -64,37 +58,11 @@ public Integer call() throws Exception { baseMixin.require(getSpec()); // required by the script interface - URI doc = URIRewriter.childURI(container, slug != null ? slug : Slugs.defaultSlug()); - put(getClient(), doc, buildModel(doc, title, description, primaryTopic)); + URI doc = URIs.childURI(container, slug != null ? slug : Slugs.defaultSlug()); + put(getClient(), doc, Documents.item(doc, title, description, primaryTopic)); print(doc); return 0; } - /** - * Builds the item document model. - * - * @param doc document URI - * @param title document title - * @param description document description (optional) - * @param primaryTopic URI of the document's primary topic, relative or absolute (optional) - * @return document model - */ - public static Model buildModel(URI doc, String title, String description, String primaryTopic) - { - Model model = ModelFactory.createDefaultModel(); - - Resource item = model.createResource(doc.toString()). - addProperty(RDF.type, DH.Item). - addProperty(DCTerms.title, title); - if (description != null) item.addProperty(DCTerms.description, description); - // Resolved against the document, so the conventional fragment topic is "#this" and a - // document about something described elsewhere takes that resource's absolute URI. - // Singular because foaf:primaryTopic is an owl:FunctionalProperty: a second value would - // not mean a second topic, it would entail the two topics are the same resource. - if (primaryTopic != null) item.addProperty(FOAF.primaryTopic, model.createResource(doc.resolve(primaryTopic).toString())); - - return model; - } - } diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/ListPackages.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/ListPackages.java index 8823f8549..63713c656 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/ListPackages.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/ListPackages.java @@ -19,7 +19,7 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.http.HttpException; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.vocab.LDH; +import com.atomgraph.linkeddatahub.rdf.vocabulary.LDH; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import java.net.URI; diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/RemovePackageImport.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/RemovePackageImport.java index 191f06b91..ab717ffb8 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/RemovePackageImport.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/RemovePackageImport.java @@ -19,7 +19,7 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.http.HttpException; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.sparql.Updates; +import com.atomgraph.linkeddatahub.rdf.Updates; import java.net.URI; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/AddOntologyImport.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/AddOntologyImport.java index f54d246ec..15e8d939d 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/AddOntologyImport.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/AddOntologyImport.java @@ -18,7 +18,7 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.http.HttpException; -import com.atomgraph.linkeddatahub.cli.sparql.Updates; +import com.atomgraph.linkeddatahub.rdf.Updates; import java.net.URI; import picocli.CommandLine.Command; import picocli.CommandLine.Option; diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/acl/AddAgentToGroup.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/acl/AddAgentToGroup.java index a2a59dce7..fbcbfaedd 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/acl/AddAgentToGroup.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/acl/AddAgentToGroup.java @@ -18,7 +18,7 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.http.HttpException; -import com.atomgraph.linkeddatahub.cli.sparql.Updates; +import com.atomgraph.linkeddatahub.rdf.Updates; import java.net.URI; import picocli.CommandLine.Command; import picocli.CommandLine.Option; diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/acl/CreateAuthorization.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/acl/CreateAuthorization.java index 8fbc01c6d..1fabd986b 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/acl/CreateAuthorization.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/acl/CreateAuthorization.java @@ -18,21 +18,14 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.util.Slugs; -import com.atomgraph.linkeddatahub.cli.util.URIRewriter; -import com.atomgraph.linkeddatahub.cli.vocab.ACL; -import com.atomgraph.linkeddatahub.cli.vocab.DH; +import com.atomgraph.linkeddatahub.rdf.Acl; +import com.atomgraph.linkeddatahub.rdf.Slugs; +import com.atomgraph.linkeddatahub.rdf.URIs; +import com.atomgraph.linkeddatahub.rdf.vocabulary.ACL; import java.net.URI; import java.util.ArrayList; import java.util.List; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.ModelFactory; -import org.apache.jena.rdf.model.Property; import org.apache.jena.rdf.model.Resource; -import org.apache.jena.sparql.vocabulary.FOAF; -import org.apache.jena.vocabulary.DCTerms; -import org.apache.jena.vocabulary.RDF; -import org.apache.jena.vocabulary.RDFS; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; import picocli.CommandLine.Option; @@ -100,7 +93,7 @@ public Integer call() throws Exception if (!append && !control && !read && !write) throw new ParameterException(getSpec().commandLine(), "At least one of '--append', '--control', '--read', '--write' is required"); - URI doc = URIRewriter.childURI(URI.create(base + "acl/authorizations/"), slug != null ? slug : Slugs.defaultSlug()); + URI doc = URIs.childURI(URI.create(base + "acl/authorizations/"), slug != null ? slug : Slugs.defaultSlug()); List modes = new ArrayList<>(); if (append) modes.add(ACL.Append); @@ -108,56 +101,10 @@ public Integer call() throws Exception if (read) modes.add(ACL.Read); if (write) modes.add(ACL.Write); - put(getClient(), doc, buildModel(doc, uri, label, comment, agents, agentClasses, agentGroups, to, toAllIn, modes)); + put(getClient(), doc, Acl.authorization(doc, uri, label, comment, agents, agentClasses, agentGroups, to, toAllIn, modes)); print(doc); return 0; } - /** - * Builds the authorization document model. - * - * @param doc document URI - * @param uri authorization URI (optional, blank node if null) - * @param label authorization label - * @param comment authorization comment (optional) - * @param agents authorized agent URIs - * @param agentClasses authorized agent class URIs - * @param agentGroups authorized agent group URIs - * @param to accessed document URIs - * @param toAllIn accessed document class URIs - * @param modes granted access modes - * @return document model - */ - public static Model buildModel(URI doc, String uri, String label, String comment, - List agents, List agentClasses, List agentGroups, - List to, List toAllIn, List modes) - { - Model model = ModelFactory.createDefaultModel(); - - Resource auth = createSubject(model, doc, uri). - addProperty(RDF.type, ACL.Authorization). - addProperty(RDFS.label, label); - if (comment != null) auth.addProperty(RDFS.comment, comment); - - model.createResource(doc.toString()). - addProperty(RDF.type, DH.Item). - addProperty(FOAF.primaryTopic, auth). - addProperty(DCTerms.title, label); - - addResourceValues(auth, ACL.agent, agents); - addResourceValues(auth, ACL.agentClass, agentClasses); - addResourceValues(auth, ACL.agentGroup, agentGroups); - addResourceValues(auth, ACL.accessTo, to); - addResourceValues(auth, ACL.accessToClass, toAllIn); - modes.forEach(mode -> auth.addProperty(ACL.mode, mode)); - - return model; - } - - static void addResourceValues(Resource subject, Property property, List values) - { - values.forEach(value -> subject.addProperty(property, subject.getModel().createResource(value.toString()))); - } - } diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/acl/CreateGroup.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/acl/CreateGroup.java index ca35618fb..a6988a66a 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/acl/CreateGroup.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/acl/CreateGroup.java @@ -18,17 +18,11 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.util.Slugs; -import com.atomgraph.linkeddatahub.cli.util.URIRewriter; -import com.atomgraph.linkeddatahub.cli.vocab.DH; +import com.atomgraph.linkeddatahub.rdf.Acl; +import com.atomgraph.linkeddatahub.rdf.Slugs; +import com.atomgraph.linkeddatahub.rdf.URIs; import java.net.URI; import java.util.List; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.ModelFactory; -import org.apache.jena.rdf.model.Resource; -import org.apache.jena.sparql.vocabulary.FOAF; -import org.apache.jena.vocabulary.DCTerms; -import org.apache.jena.vocabulary.RDF; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; import picocli.CommandLine.Option; @@ -64,40 +58,12 @@ public class CreateGroup extends BaseCommand public Integer call() throws Exception { URI base = baseMixin.require(getSpec()); - URI doc = URIRewriter.childURI(URI.create(base + "acl/groups/"), slug != null ? slug : Slugs.defaultSlug()); + URI doc = URIs.childURI(URI.create(base + "acl/groups/"), slug != null ? slug : Slugs.defaultSlug()); - put(getClient(), doc, buildModel(doc, uri, name, description, members)); + put(getClient(), doc, Acl.group(doc, uri, name, description, members)); print(doc); return 0; } - /** - * Builds the group document model. - * - * @param doc document URI - * @param uri group URI (optional, blank node if null) - * @param name group name - * @param description group description (optional) - * @param members member agent URIs - * @return document model - */ - public static Model buildModel(URI doc, String uri, String name, String description, List members) - { - Model model = ModelFactory.createDefaultModel(); - - Resource group = createSubject(model, doc, uri). - addProperty(RDF.type, FOAF.Group). - addProperty(FOAF.name, name); - if (description != null) group.addProperty(DCTerms.description, description); - members.forEach(member -> group.addProperty(FOAF.member, model.createResource(member.toString()))); - - model.createResource(doc.toString()). - addProperty(RDF.type, DH.Item). - addProperty(FOAF.primaryTopic, group). - addProperty(DCTerms.title, name); - - return model; - } - } diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/acl/MakePublic.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/acl/MakePublic.java index a041e3521..86f76da27 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/acl/MakePublic.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/acl/MakePublic.java @@ -19,8 +19,8 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.http.HttpException; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.sparql.Updates; -import com.atomgraph.linkeddatahub.cli.util.URIRewriter; +import com.atomgraph.linkeddatahub.rdf.URIs; +import com.atomgraph.linkeddatahub.rdf.Updates; import java.net.URI; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; @@ -41,7 +41,7 @@ public class MakePublic extends BaseCommand public Integer call() throws Exception { URI base = baseMixin.require(getSpec()); - URI adminBase = URIRewriter.adminBase(base); + URI adminBase = URIs.adminBase(base); URI target = URI.create(adminBase + "acl/authorizations/public/"); HttpException.check(target, getClient().patch(target, Updates.makePublic(base, adminBase))).close(); @@ -53,7 +53,7 @@ public Integer call() throws Exception protected URI getEffectiveProxy() { // the request targets the admin app, so the proxy origin gets the admin subdomain too - return getProxyMixin().getProxy() != null ? URIRewriter.adminBase(getProxyMixin().getProxy()) : null; + return getProxyMixin().getProxy() != null ? URIs.adminBase(getProxyMixin().getProxy()) : null; } } diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/AddClass.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/AddClass.java index 830330957..dc579add3 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/AddClass.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/AddClass.java @@ -18,16 +18,10 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.vocab.SPIN; +import com.atomgraph.linkeddatahub.rdf.Ontologies; import java.net.URI; import java.util.ArrayList; import java.util.List; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.ModelFactory; -import org.apache.jena.rdf.model.Resource; -import org.apache.jena.vocabulary.OWL; -import org.apache.jena.vocabulary.RDF; -import org.apache.jena.vocabulary.RDFS; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; import picocli.CommandLine.Option; @@ -71,37 +65,10 @@ public Integer call() throws Exception { baseMixin.require(getSpec()); // required by the script interface - post(getClient(), target, buildModel(target, uri, label, comment, constructor, constraint, superClasses)); + post(getClient(), target, Ontologies.owlClass(target, uri, label, comment, constructor, constraint, superClasses)); print(target); return 0; } - /** - * Builds the class description. - * - * @param target target document URI - * @param uri class URI (optional) - * @param label class label - * @param comment class comment (optional) - * @param constructor constructor query URI (optional) - * @param constraint constraint URI (optional) - * @param superClasses superclass URIs - * @return class model - */ - public static Model buildModel(URI target, String uri, String label, String comment, URI constructor, URI constraint, List superClasses) - { - Model model = ModelFactory.createDefaultModel(); - - Resource cls = createSubject(model, target, uri). - addProperty(RDF.type, OWL.Class). - addProperty(RDFS.label, label); - if (comment != null) cls.addProperty(RDFS.comment, comment); - if (constructor != null) cls.addProperty(SPIN.constructor, model.createResource(constructor.toString())); - if (constraint != null) cls.addProperty(SPIN.constraint, model.createResource(constraint.toString())); - superClasses.forEach(superClass -> cls.addProperty(RDFS.subClassOf, model.createResource(superClass.toString()))); - - return model; - } - } diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/AddConstructor.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/AddConstructor.java index ec1d1eb60..47c63cfc6 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/AddConstructor.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/AddConstructor.java @@ -18,16 +18,11 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.vocab.LDH; -import com.atomgraph.linkeddatahub.cli.vocab.SP; +import com.atomgraph.linkeddatahub.rdf.Ontologies; +import com.atomgraph.linkeddatahub.rdf.vocabulary.SP; import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.ModelFactory; -import org.apache.jena.rdf.model.Resource; -import org.apache.jena.vocabulary.RDF; -import org.apache.jena.vocabulary.RDFS; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; import picocli.CommandLine.Option; @@ -65,37 +60,10 @@ public Integer call() throws Exception { baseMixin.require(getSpec()); // required by the script interface - post(getClient(), target, buildModel(target, uri, SP.Construct, label, Files.readString(queryFile), null, comment)); + post(getClient(), target, Ontologies.constructor(target, uri, SP.Construct, label, Files.readString(queryFile), null, comment)); print(target); return 0; } - /** - * Builds an ontology SPIN query description (labeled with rdfs:label, - * unlike the dct:title-based document queries). - * - * @param target target document URI - * @param uri query URI (optional) - * @param queryType SPIN query class (sp:Construct or sp:Select) - * @param label query label - * @param queryText query string - * @param service SPARQL service URI (optional) - * @param comment query comment (optional) - * @return query model - */ - public static Model buildModel(URI target, String uri, Resource queryType, String label, String queryText, URI service, String comment) - { - Model model = ModelFactory.createDefaultModel(); - - Resource query = createSubject(model, target, uri). - addProperty(RDF.type, queryType). - addProperty(RDFS.label, label). - addProperty(SP.text, queryText); - if (comment != null) query.addProperty(RDFS.comment, comment); - if (service != null) query.addProperty(LDH.service, model.createResource(service.toString())); - - return model; - } - } diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/AddPropertyConstraint.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/AddPropertyConstraint.java index eafc655b2..f33f0608d 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/AddPropertyConstraint.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/AddPropertyConstraint.java @@ -18,14 +18,8 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.vocab.LDH; -import com.atomgraph.linkeddatahub.cli.vocab.SP; +import com.atomgraph.linkeddatahub.rdf.Ontologies; import java.net.URI; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.ModelFactory; -import org.apache.jena.rdf.model.Resource; -import org.apache.jena.vocabulary.RDF; -import org.apache.jena.vocabulary.RDFS; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; import picocli.CommandLine.Option; @@ -63,33 +57,10 @@ public Integer call() throws Exception { baseMixin.require(getSpec()); // required by the script interface - post(getClient(), target, buildModel(target, uri, label, property, comment)); + post(getClient(), target, Ontologies.propertyConstraint(target, uri, label, property, comment)); print(target); return 0; } - /** - * Builds the constraint description. - * - * @param target target document URI - * @param uri constraint URI (optional) - * @param label constraint label - * @param property required property URI - * @param comment constraint comment (optional) - * @return constraint model - */ - public static Model buildModel(URI target, String uri, String label, URI property, String comment) - { - Model model = ModelFactory.createDefaultModel(); - - Resource constraint = createSubject(model, target, uri). - addProperty(RDF.type, LDH.MissingPropertyValue). - addProperty(RDFS.label, label). - addProperty(SP.arg1, model.createResource(property.toString())); - if (comment != null) constraint.addProperty(RDFS.comment, comment); - - return model; - } - } diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/AddRestriction.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/AddRestriction.java index 9ac8ae79c..10cbd2613 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/AddRestriction.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/AddRestriction.java @@ -18,13 +18,9 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; +import com.atomgraph.linkeddatahub.rdf.Ontologies; import java.net.URI; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.ModelFactory; -import org.apache.jena.rdf.model.Resource; import org.apache.jena.vocabulary.OWL; -import org.apache.jena.vocabulary.RDF; -import org.apache.jena.vocabulary.RDFS; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; import picocli.CommandLine.Option; @@ -68,37 +64,10 @@ public Integer call() throws Exception { baseMixin.require(getSpec()); // required by the script interface - post(getClient(), target, buildModel(target, uri, label, comment, onProperty, allValuesFrom, hasValue)); + post(getClient(), target, Ontologies.restriction(target, uri, label, comment, onProperty, allValuesFrom, hasValue)); print(target); return 0; } - /** - * Builds the restriction description. - * - * @param target target document URI - * @param uri restriction URI (optional) - * @param label restriction label - * @param comment restriction comment (optional) - * @param onProperty restricted property URI (optional) - * @param allValuesFrom value class URI (optional) - * @param hasValue value resource URI (optional) - * @return restriction model - */ - public static Model buildModel(URI target, String uri, String label, String comment, URI onProperty, URI allValuesFrom, URI hasValue) - { - Model model = ModelFactory.createDefaultModel(); - - Resource restriction = createSubject(model, target, uri). - addProperty(RDF.type, OWL.Restriction). - addProperty(RDFS.label, label); - if (comment != null) restriction.addProperty(RDFS.comment, comment); - if (onProperty != null) restriction.addProperty(OWL.onProperty, model.createResource(onProperty.toString())); - if (allValuesFrom != null) restriction.addProperty(OWL.allValuesFrom, model.createResource(allValuesFrom.toString())); - if (hasValue != null) restriction.addProperty(OWL.hasValue, model.createResource(hasValue.toString())); - - return model; - } - } diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/AddSelect.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/AddSelect.java index 323e9ab34..10467adfe 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/AddSelect.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/AddSelect.java @@ -18,7 +18,8 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.vocab.SP; +import com.atomgraph.linkeddatahub.rdf.Ontologies; +import com.atomgraph.linkeddatahub.rdf.vocabulary.SP; import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; @@ -62,7 +63,7 @@ public Integer call() throws Exception { baseMixin.require(getSpec()); // required by the script interface - post(getClient(), target, AddConstructor.buildModel(target, uri, SP.Select, label, Files.readString(queryFile), service, comment)); + post(getClient(), target, Ontologies.constructor(target, uri, SP.Select, label, Files.readString(queryFile), service, comment)); print(target); return 0; diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/CreateOntology.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/CreateOntology.java index 9b4312e75..0431d906b 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/CreateOntology.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/CreateOntology.java @@ -18,18 +18,10 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.util.Slugs; -import com.atomgraph.linkeddatahub.cli.util.URIRewriter; -import com.atomgraph.linkeddatahub.cli.vocab.DH; +import com.atomgraph.linkeddatahub.rdf.Ontologies; +import com.atomgraph.linkeddatahub.rdf.Slugs; +import com.atomgraph.linkeddatahub.rdf.URIs; import java.net.URI; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.ModelFactory; -import org.apache.jena.rdf.model.Resource; -import org.apache.jena.sparql.vocabulary.FOAF; -import org.apache.jena.vocabulary.DCTerms; -import org.apache.jena.vocabulary.OWL; -import org.apache.jena.vocabulary.RDF; -import org.apache.jena.vocabulary.RDFS; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; import picocli.CommandLine.Option; @@ -62,38 +54,12 @@ public class CreateOntology extends BaseCommand public Integer call() throws Exception { URI base = baseMixin.require(getSpec()); - URI doc = URIRewriter.childURI(URI.create(base + "ontologies/"), slug != null ? slug : Slugs.defaultSlug()); + URI doc = URIs.childURI(URI.create(base + "ontologies/"), slug != null ? slug : Slugs.defaultSlug()); - put(getClient(), doc, buildModel(doc, uri, label, comment)); + put(getClient(), doc, Ontologies.ontology(doc, uri, label, comment)); print(doc); return 0; } - /** - * Builds the ontology document model. - * - * @param doc document URI - * @param uri ontology URI (optional, blank node if null) - * @param label ontology label - * @param comment ontology comment (optional) - * @return document model - */ - public static Model buildModel(URI doc, String uri, String label, String comment) - { - Model model = ModelFactory.createDefaultModel(); - - Resource ontology = createSubject(model, doc, uri). - addProperty(RDF.type, OWL.Ontology). - addProperty(RDFS.label, label); - if (comment != null) ontology.addProperty(RDFS.comment, comment); - - model.createResource(doc.toString()). - addProperty(RDF.type, DH.Item). - addProperty(FOAF.primaryTopic, ontology). - addProperty(DCTerms.title, label); - - return model; - } - } diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/ImportOntology.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/ImportOntology.java index ebde26ec5..b187dda50 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/ImportOntology.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/admin/ontologies/ImportOntology.java @@ -19,7 +19,8 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.http.HttpException; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.vocab.SP; +import com.atomgraph.linkeddatahub.rdf.Ontologies; +import com.atomgraph.linkeddatahub.rdf.vocabulary.SP; import jakarta.ws.rs.core.Form; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; @@ -27,9 +28,7 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.rdf.model.Resource; -import org.apache.jena.sparql.vocabulary.FOAF; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; import picocli.CommandLine.Option; @@ -77,7 +76,7 @@ public Integer call() throws Exception // the vocabulary goes into the target first, so the CONSTRUCT has it to read and it stays afterwards post(getClient(), graph, vocabulary); post(getClient(), graph, construct(base, query, graph)); - post(getClient(), graph, buildAnnotationModel(graph, source)); + post(getClient(), graph, Ontologies.annotation(graph, source)); print(graph); @@ -141,22 +140,4 @@ protected Model construct(URI base, String query, URI graph) } } - /** - * Builds the arc saying what the document is about. The document is not the ontology, so it takes - * no owl:Ontology type of its own: the vocabulary stored alongside carries that. - * - * @param graph target document URI - * @param source imported ontology URI - * @return primary topic model - */ - public static Model buildAnnotationModel(URI graph, URI source) - { - Model model = ModelFactory.createDefaultModel(); - - model.createResource(graph.toString()). - addProperty(FOAF.primaryTopic, model.createResource(source.toString())); - - return model; - } - } diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/content/AddObjectBlock.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/content/AddObjectBlock.java index 534554861..71a5a1a94 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/content/AddObjectBlock.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/content/AddObjectBlock.java @@ -19,18 +19,13 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.http.HttpException; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.util.SequenceNumbers; -import com.atomgraph.linkeddatahub.cli.vocab.AC; -import com.atomgraph.linkeddatahub.cli.vocab.LDH; +import com.atomgraph.linkeddatahub.rdf.Blocks; +import com.atomgraph.linkeddatahub.rdf.SequenceNumbers; import jakarta.ws.rs.core.Response; import java.net.URI; import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.rdf.model.Property; -import org.apache.jena.rdf.model.Resource; import org.apache.jena.rdf.model.ResourceFactory; -import org.apache.jena.vocabulary.DCTerms; -import org.apache.jena.vocabulary.RDF; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; import picocli.CommandLine.Option; @@ -69,7 +64,7 @@ public class AddObjectBlock extends BaseCommand @Override public Integer call() throws Exception { - post(getClient(), target, buildModel(target, nextSequenceProperty(), uri, value, title, description, mode)); + post(getClient(), target, Blocks.object(target, nextSequenceProperty(), uri, value, title, description, mode)); print(target); return 0; @@ -91,31 +86,4 @@ protected Property nextSequenceProperty() return SequenceNumbers.nextSequenceProperty(current, ResourceFactory.createResource(target.toString())); } - /** - * Builds the object block description. - * - * @param target target document URI - * @param seq membership property (rdf:_N) - * @param uri block URI (optional) - * @param value object resource URI - * @param title block title (optional) - * @param description block description (optional) - * @param mode layout mode URI (optional) - * @return block model - */ - public static Model buildModel(URI target, Property seq, String uri, URI value, String title, String description, URI mode) - { - Model model = ModelFactory.createDefaultModel(); - - Resource block = createSubject(model, target, uri). - addProperty(RDF.type, LDH.Object). - addProperty(RDF.value, model.createResource(value.toString())); - model.createResource(target.toString()).addProperty(seq, block); - if (title != null) block.addProperty(DCTerms.title, title); - if (description != null) block.addProperty(DCTerms.description, description); - if (mode != null) block.addProperty(AC.mode, model.createResource(mode.toString())); - - return model; - } - } diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/content/AddXHTMLBlock.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/content/AddXHTMLBlock.java index 4237b7a98..ebb5c595c 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/content/AddXHTMLBlock.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/content/AddXHTMLBlock.java @@ -19,17 +19,13 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.http.HttpException; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.util.SequenceNumbers; -import com.atomgraph.linkeddatahub.cli.vocab.LDH; +import com.atomgraph.linkeddatahub.rdf.Blocks; +import com.atomgraph.linkeddatahub.rdf.SequenceNumbers; import jakarta.ws.rs.core.Response; import java.net.URI; import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.rdf.model.Property; -import org.apache.jena.rdf.model.Resource; import org.apache.jena.rdf.model.ResourceFactory; -import org.apache.jena.vocabulary.DCTerms; -import org.apache.jena.vocabulary.RDF; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; import picocli.CommandLine.Option; @@ -72,35 +68,10 @@ public Integer call() throws Exception } Property seq = SequenceNumbers.nextSequenceProperty(current, ResourceFactory.createResource(target.toString())); - post(getClient(), target, buildModel(target, seq, uri, value, title, description)); + post(getClient(), target, Blocks.xhtml(target, seq, uri, value, title, description)); print(target); return 0; } - /** - * Builds the XHTML block description. - * - * @param target target document URI - * @param seq membership property (rdf:_N) - * @param uri block URI (optional) - * @param value XHTML content - * @param title block title (optional) - * @param description block description (optional) - * @return block model - */ - public static Model buildModel(URI target, Property seq, String uri, String value, String title, String description) - { - Model model = ModelFactory.createDefaultModel(); - - Resource block = createSubject(model, target, uri). - addProperty(RDF.type, LDH.XHTML). - addProperty(RDF.value, model.createTypedLiteral(value, RDF.dtXMLLiteral)); - model.createResource(target.toString()).addProperty(seq, block); - if (title != null) block.addProperty(DCTerms.title, title); - if (description != null) block.addProperty(DCTerms.description, description); - - return model; - } - } diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/content/RemoveBlock.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/content/RemoveBlock.java index ee61864de..8c8362491 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/content/RemoveBlock.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/content/RemoveBlock.java @@ -18,7 +18,7 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.http.HttpException; -import com.atomgraph.linkeddatahub.cli.sparql.Updates; +import com.atomgraph.linkeddatahub.rdf.Updates; import java.net.URI; import picocli.CommandLine.Command; import picocli.CommandLine.Option; diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/imports/AddCSVImport.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/imports/AddCSVImport.java index 6a60f63e0..d9edc4547 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/imports/AddCSVImport.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/imports/AddCSVImport.java @@ -19,14 +19,8 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.http.LDHClient; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.vocab.LDH; -import com.atomgraph.linkeddatahub.cli.vocab.SPIN; +import com.atomgraph.linkeddatahub.rdf.Imports; import java.net.URI; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.ModelFactory; -import org.apache.jena.rdf.model.Resource; -import org.apache.jena.vocabulary.DCTerms; -import org.apache.jena.vocabulary.RDF; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; import picocli.CommandLine.Option; @@ -90,34 +84,7 @@ public Integer call() throws Exception */ public static void core(LDHClient client, URI target, String uri, String title, URI query, URI file, String delimiter, String description) { - post(client, target, buildModel(target, uri, title, query, file, delimiter, description)); - } - - /** - * Builds the CSV import description. - * - * @param target target document URI - * @param uri import URI (optional) - * @param title import title - * @param query transformation query URI - * @param file uploaded file URI - * @param delimiter CSV delimiter - * @param description import description (optional) - * @return import model - */ - public static Model buildModel(URI target, String uri, String title, URI query, URI file, String delimiter, String description) - { - Model model = ModelFactory.createDefaultModel(); - - Resource csvImport = createSubject(model, target, uri). - addProperty(RDF.type, LDH.CSVImport). - addProperty(DCTerms.title, title). - addProperty(SPIN.query, model.createResource(query.toString())). - addProperty(LDH.file, model.createResource(file.toString())). - addProperty(LDH.delimiter, delimiter); - if (description != null) csvImport.addProperty(DCTerms.description, description); - - return model; + post(client, target, Imports.csv(target, uri, title, query, file, delimiter, description)); } } diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/imports/AddRDFImport.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/imports/AddRDFImport.java index f7d825da2..c57807d6b 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/imports/AddRDFImport.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/imports/AddRDFImport.java @@ -19,14 +19,8 @@ import com.atomgraph.linkeddatahub.cli.BaseCommand; import com.atomgraph.linkeddatahub.cli.http.LDHClient; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.vocab.LDH; -import com.atomgraph.linkeddatahub.cli.vocab.SD; -import com.atomgraph.linkeddatahub.cli.vocab.SPIN; +import com.atomgraph.linkeddatahub.rdf.Imports; import java.net.URI; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.ModelFactory; -import org.apache.jena.rdf.model.Resource; -import org.apache.jena.vocabulary.DCTerms; import org.apache.jena.vocabulary.RDF; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; @@ -91,34 +85,7 @@ public Integer call() throws Exception */ public static void core(LDHClient client, URI target, String uri, String title, URI file, URI query, URI graph, String description) { - post(client, target, buildModel(target, uri, title, file, query, graph, description)); - } - - /** - * Builds the RDF import description. - * - * @param target target document URI - * @param uri import URI (optional) - * @param title import title - * @param file uploaded file URI - * @param query transformation query URI (optional) - * @param graph target named graph URI (optional) - * @param description import description (optional) - * @return import model - */ - public static Model buildModel(URI target, String uri, String title, URI file, URI query, URI graph, String description) - { - Model model = ModelFactory.createDefaultModel(); - - Resource rdfImport = createSubject(model, target, uri). - addProperty(RDF.type, LDH.RDFImport). - addProperty(DCTerms.title, title). - addProperty(LDH.file, model.createResource(file.toString())); - if (graph != null) rdfImport.addProperty(SD.name, model.createResource(graph.toString())); - if (query != null) rdfImport.addProperty(SPIN.query, model.createResource(query.toString())); - if (description != null) rdfImport.addProperty(DCTerms.description, description); - - return model; + post(client, target, Imports.rdf(target, uri, title, file, query, graph, description)); } } diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/imports/ImportCSV.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/imports/ImportCSV.java index b40415c2f..86372a82a 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/imports/ImportCSV.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/imports/ImportCSV.java @@ -20,7 +20,7 @@ import com.atomgraph.linkeddatahub.cli.command.AddConstruct; import com.atomgraph.linkeddatahub.cli.command.AddFile; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.util.Slugs; +import com.atomgraph.linkeddatahub.rdf.Slugs; import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/imports/ImportRDF.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/imports/ImportRDF.java index 08088f41f..c93bde717 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/imports/ImportRDF.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/command/imports/ImportRDF.java @@ -20,7 +20,7 @@ import com.atomgraph.linkeddatahub.cli.command.AddConstruct; import com.atomgraph.linkeddatahub.cli.command.AddFile; import com.atomgraph.linkeddatahub.cli.mixin.BaseMixin; -import com.atomgraph.linkeddatahub.cli.util.Slugs; +import com.atomgraph.linkeddatahub.rdf.Slugs; import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/http/LDHClient.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/http/LDHClient.java index 15c07feec..af71a62fb 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/http/LDHClient.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/http/LDHClient.java @@ -19,7 +19,6 @@ import com.atomgraph.core.MediaTypes; import com.atomgraph.core.client.GraphStoreClient; import com.atomgraph.linkeddatahub.cli.util.URIRewriter; -import org.apache.jena.rdf.model.Model; import jakarta.ws.rs.client.Client; import jakarta.ws.rs.client.Entity; import jakarta.ws.rs.client.WebTarget; @@ -31,6 +30,7 @@ import jakarta.ws.rs.core.MultivaluedMap; import jakarta.ws.rs.core.Response; import java.net.URI; +import org.apache.jena.rdf.model.Model; /** * Graph Store Protocol client for LinkedDataHub documents (direct graph identification), diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/util/PushPlan.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/util/PushPlan.java index a9c5eda4e..3469a1b6d 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/util/PushPlan.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/util/PushPlan.java @@ -16,6 +16,7 @@ package com.atomgraph.linkeddatahub.cli.util; +import com.atomgraph.linkeddatahub.rdf.URIs; import java.io.IOException; import java.net.URI; import java.nio.file.Files; @@ -127,7 +128,7 @@ private static void walk(Path dir, URI url, Path root, Deque ignores, else { String name = doc.getFileName().toString(); - URI docURI = isRootDocument(doc, root) ? url : URIRewriter.childURI(url, stem(name)); + URI docURI = isRootDocument(doc, root) ? url : URIs.childURI(url, stem(name)); steps.add(new Step(Kind.DOCUMENT, doc, relative(doc, root, false), docURI, documentLang(name).getContentType().getContentTypeStr())); } } @@ -138,7 +139,7 @@ private static void walk(Path dir, URI url, Path root, Deque ignores, for (Path subdir : dirs) { if (isIgnored(subdir, true, ignores)) steps.add(skip(subdir, root, true)); - else walk(subdir, URIRewriter.childURI(url, subdir.getFileName().toString()), root, ignores, steps); + else walk(subdir, URIs.childURI(url, subdir.getFileName().toString()), root, ignores, steps); } } finally diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/util/URIRewriter.java b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/util/URIRewriter.java index 593181c03..2bb2a603d 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/util/URIRewriter.java +++ b/cli/src/main/java/com/atomgraph/linkeddatahub/cli/util/URIRewriter.java @@ -17,10 +17,16 @@ package com.atomgraph.linkeddatahub.cli.util; import java.net.URI; -import java.nio.charset.StandardCharsets; /** - * URI manipulation helpers matching the conventions of the bin/ shell scripts. + * Sends a request somewhere other than where its URI says. + *

+ * This is the --proxy option: a document's URI identifies it, but the host that + * answers for it may be a different one - the client-certificate port, say, or a tunnel. The + * logical URI stays in the request; only the origin it is sent to changes. + *

+ * A CLI concern, not a document-shape one, which is why it stays here while the URI conventions + * the platform itself applies live in {@link com.atomgraph.linkeddatahub.rdf.URIs}. * * @author Martynas Jusevičius {@literal } */ @@ -48,57 +54,11 @@ public static URI rewrite(URI uri, URI proxy) * @param uri URI * @return origin string, e.g. https://localhost:4443 */ - public static String origin(URI uri) + private static String origin(URI uri) { if (uri.getScheme() == null || uri.getRawAuthority() == null) throw new IllegalArgumentException("URI '" + uri + "' is not absolute"); return uri.getScheme() + "://" + uri.getRawAuthority(); } - /** - * Converts an end-user application base URI to the base URI of its admin application - * by prefixing the host with the admin. subdomain. - * - * @param base end-user base URI - * @return admin base URI - */ - public static URI adminBase(URI base) - { - return URI.create(base.toString().replaceFirst("://", "://admin.")); - } - - /** - * Percent-encodes a string as a URI path segment. All characters except RFC 3986 - * unreserved ones are encoded, including /. - * - * @param slug path segment - * @return encoded path segment - */ - public static String encodeSlug(String slug) - { - StringBuilder sb = new StringBuilder(); - - for (byte b : slug.getBytes(StandardCharsets.UTF_8)) - { - char c = (char)(b & 0xFF); - if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || - c == '-' || c == '.' || c == '_' || c == '~') sb.append(c); - else sb.append('%').append(String.format("%02X", b & 0xFF)); - } - - return sb.toString(); - } - - /** - * Builds the URI of a child document from the parent container URI and a path segment slug. - * - * @param parent parent container URI (with trailing slash) - * @param slug path segment - * @return child document URI (with trailing slash) - */ - public static URI childURI(URI parent, String slug) - { - return URI.create(parent.toString() + encodeSlug(slug) + "/"); - } - } diff --git a/cli/src/test/java/com/atomgraph/linkeddatahub/cli/PushOutputTest.java b/cli/src/test/java/com/atomgraph/linkeddatahub/cli/PushOutputTest.java index e5ce8fa3b..30ec07e41 100644 --- a/cli/src/test/java/com/atomgraph/linkeddatahub/cli/PushOutputTest.java +++ b/cli/src/test/java/com/atomgraph/linkeddatahub/cli/PushOutputTest.java @@ -16,9 +16,9 @@ package com.atomgraph.linkeddatahub.cli; -import com.atomgraph.linkeddatahub.cli.http.StubServer; import com.atomgraph.linkeddatahub.cli.http.StubServer.Request; -import com.atomgraph.linkeddatahub.cli.util.Digests; +import com.atomgraph.linkeddatahub.cli.http.StubServer; +import com.atomgraph.linkeddatahub.rdf.Digests; import java.io.IOException; import java.io.StringWriter; import java.net.URI; diff --git a/cli/src/test/java/com/atomgraph/linkeddatahub/cli/util/URIRewriterTest.java b/cli/src/test/java/com/atomgraph/linkeddatahub/cli/util/URIRewriterTest.java index 222a90863..276d4b536 100644 --- a/cli/src/test/java/com/atomgraph/linkeddatahub/cli/util/URIRewriterTest.java +++ b/cli/src/test/java/com/atomgraph/linkeddatahub/cli/util/URIRewriterTest.java @@ -48,29 +48,4 @@ public void rewriteRejectsRelativeURI() () -> URIRewriter.rewrite(URI.create("/relative/path"), URI.create("https://localhost:8443"))); } - @Test - public void adminBasePrefixesHostWithAdminSubdomain() - { - assertEquals(URI.create("https://admin.localhost:4443/"), URIRewriter.adminBase(URI.create("https://localhost:4443/"))); - } - - @Test - public void encodeSlugKeepsUnreservedCharacters() - { - assertEquals("abc-._~123", URIRewriter.encodeSlug("abc-._~123")); - } - - @Test - public void encodeSlugEncodesReservedAndNonASCII() - { - assertEquals("a%20b%2F%C4%87", URIRewriter.encodeSlug("a b/ć")); - } - - @Test - public void childURIAppendsEncodedSlugAndSlash() - { - assertEquals(URI.create("https://localhost:4443/some/my%20item/"), - URIRewriter.childURI(URI.create("https://localhost:4443/some/"), "my item")); - } - } diff --git a/make/config.mk b/make/config.mk index 3875143b9..dfa550006 100644 --- a/make/config.mk +++ b/make/config.mk @@ -4,7 +4,7 @@ # has no app to install and compiles its client stylesheet from source rather than from a # published image. Everything it adds lives in make/local.mk. -LOCAL_TARGETS := sef release cli cli-version tests ui-tests ui-tests-install load-tests +LOCAL_TARGETS := sef release rdf cli cli-version tests ui-tests ui-tests-install load-tests # only the deployment configuration is RDF worth parsing here; the rest of the tree is source VALIDATE_PATHS := config datasets diff --git a/make/local.mk b/make/local.mk index 54491bfab..c218bbfe3 100644 --- a/make/local.mk +++ b/make/local.mk @@ -15,17 +15,25 @@ sef: release: ./release.sh -# Set cli/pom.xml to the platform version in pom.xml. The CLI ships with the platform release, so -# the two versions are kept in step; release.sh runs this around the release version bumps, and this -# target is for drift and for manual SNAPSHOT bumps +# Set rdf/pom.xml and cli/pom.xml to the platform version in pom.xml. Both ship with the platform +# release, so all three versions are kept in step; release.sh runs this around the release version +# bumps, and this target is for drift and for manual SNAPSHOT bumps cli-version: @version=$$(mvn -q help:evaluate -Dexpression=project.version -DforceStdout); \ - cd cli && mvn -B -q versions:set -DnewVersion="$$version" -DgenerateBackupPoms=false && \ - echo "cli/pom.xml set to $$version" + for project in rdf cli; do \ + (cd $$project && mvn -B -q versions:set -DnewVersion="$$version" -DgenerateBackupPoms=false) && \ + echo "$$project/pom.xml set to $$version"; \ + done + +# Build the linkeddatahub-rdf library: the vocabularies and the document shapes the API accepts. +# Installed rather than packaged because the CLI - and Web-Algebra's ldh-* operations, in their own +# repository - resolve it as an ordinary Maven dependency. +rdf: + cd rdf && mvn -B install # Build the ldh CLI (requires Java 21 and Maven) and print the line that puts it on $PATH. # Released versions are also attached to the GitHub release, which needs neither. -cli: +cli: rdf cd cli && mvn -B package @echo @echo "Add the ldh launcher to your \$$PATH:" diff --git a/rdf/pom.xml b/rdf/pom.xml new file mode 100644 index 000000000..595a5c1f2 --- /dev/null +++ b/rdf/pom.xml @@ -0,0 +1,191 @@ + + + 4.0.0 + + com.atomgraph + linkeddatahub-rdf + 6.0.1-SNAPSHOT + jar + + LinkedDataHub RDF + The RDF shapes LinkedDataHub documents are made of, and the vocabularies they are written in + https://github.com/AtomGraph/LinkedDataHub + + + + + namedgraph + martynas@atomgraph.com + + Developer + Founder + + AtomGraph + https://atomgraph.com + + + + + + Apache License 2.0 + http://www.apache.org/licenses/LICENSE-2.0.html + repo + + + + + https://github.com/AtomGraph/LinkedDataHub + scm:git:git://github.com/AtomGraph/LinkedDataHub.git + scm:git:git@github.com:AtomGraph/LinkedDataHub.git + + + + + central-portal-snapshots + https://central.sonatype.com/repository/maven-snapshots/ + + + + + + UTF-8 + 21 + + + + + org.apache.jena + jena-arq + 6.1.0 + + + + org.apache.httpcomponents + httpcore + + + org.apache.httpcomponents + httpclient-cache + + + org.apache.httpcomponents + httpclient + + + org.apache.httpcomponents + httpclient-osgi + + + org.apache.httpcomponents + httpcore-osgi + + + + + org.junit.jupiter + junit-jupiter + 5.12.2 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.14.1 + + 21 + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.2 + + + + org.codehaus.mojo + versions-maven-plugin + 2.21.0 + + + org.sonatype.central + central-publishing-maven-plugin + 0.11.0 + true + + central-portal-snapshots + true + + + + + + + + + release + + + + org.apache.maven.plugins + maven-source-plugin + 3.4.0 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.12.0 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 3.2.8 + + + sign-artifacts + verify + + sign + + + + + --pinentry-mode + loopback + + + + + + + + + + + diff --git a/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Acl.java b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Acl.java new file mode 100644 index 000000000..e95f18677 --- /dev/null +++ b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Acl.java @@ -0,0 +1,120 @@ +/* + * Copyright 2026 Martynas Jusevičius . + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.atomgraph.linkeddatahub.rdf; + +import com.atomgraph.linkeddatahub.rdf.vocabulary.ACL; +import com.atomgraph.linkeddatahub.rdf.vocabulary.DH; +import java.net.URI; +import java.util.List; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.Property; +import org.apache.jena.rdf.model.Resource; +import org.apache.jena.sparql.vocabulary.FOAF; +import org.apache.jena.vocabulary.DCTerms; +import org.apache.jena.vocabulary.RDF; +import org.apache.jena.vocabulary.RDFS; + +/** + * Access control: authorizations and the groups they are granted to. + *

+ * Both live in their own document, so each model carries the dh:Item wrapper + * around the resource it is about - the document is not the resource. + * + * @author Martynas Jusevičius {@literal } + */ +public final class Acl +{ + + private Acl() { } + + /** + * Builds an authorization document. Agents, agent classes and agent groups say who; the + * accessTo and accessToClass lists say what; the modes say how. + * + * @param doc document URI + * @param uri authorization URI, relative or absolute (optional, blank node when absent) + * @param label authorization label, also the document title + * @param comment authorization comment (optional) + * @param agents WebIDs the authorization is granted to + * @param agentClasses agent classes the authorization is granted to + * @param agentGroups agent groups the authorization is granted to + * @param to URIs of the documents access is granted on + * @param toAllIn URIs of the classes whose instances access is granted on + * @param modes access modes granted + * @return authorization model + */ + public static Model authorization(URI doc, String uri, String label, String comment, + List agents, List agentClasses, List agentGroups, + List to, List toAllIn, List modes) + { + Model model = ModelFactory.createDefaultModel(); + + Resource auth = Subjects.of(model, doc, uri). + addProperty(RDF.type, ACL.Authorization). + addProperty(RDFS.label, label); + if (comment != null) auth.addProperty(RDFS.comment, comment); + + model.createResource(doc.toString()). + addProperty(RDF.type, DH.Item). + addProperty(FOAF.primaryTopic, auth). + addProperty(DCTerms.title, label); + + addResourceValues(auth, ACL.agent, agents); + addResourceValues(auth, ACL.agentClass, agentClasses); + addResourceValues(auth, ACL.agentGroup, agentGroups); + addResourceValues(auth, ACL.accessTo, to); + addResourceValues(auth, ACL.accessToClass, toAllIn); + modes.forEach(mode -> auth.addProperty(ACL.mode, mode)); + + return model; + } + + /** + * Builds an agent group document. + * + * @param doc document URI + * @param uri group URI, relative or absolute (optional, blank node when absent) + * @param name group name, also the document title + * @param description group description (optional) + * @param members WebIDs of the group's members + * @return group model + */ + public static Model group(URI doc, String uri, String name, String description, List members) + { + Model model = ModelFactory.createDefaultModel(); + + Resource group = Subjects.of(model, doc, uri). + addProperty(RDF.type, FOAF.Group). + addProperty(FOAF.name, name); + if (description != null) group.addProperty(DCTerms.description, description); + members.forEach(member -> group.addProperty(FOAF.member, model.createResource(member.toString()))); + + model.createResource(doc.toString()). + addProperty(RDF.type, DH.Item). + addProperty(FOAF.primaryTopic, group). + addProperty(DCTerms.title, name); + + return model; + } + + private static void addResourceValues(Resource subject, Property property, List values) + { + values.forEach(value -> subject.addProperty(property, subject.getModel().createResource(value.toString()))); + } + +} diff --git a/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Blocks.java b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Blocks.java new file mode 100644 index 000000000..9cf87a4a7 --- /dev/null +++ b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Blocks.java @@ -0,0 +1,96 @@ +/* + * Copyright 2026 Martynas Jusevičius . + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.atomgraph.linkeddatahub.rdf; + +import com.atomgraph.linkeddatahub.rdf.vocabulary.AC; +import com.atomgraph.linkeddatahub.rdf.vocabulary.LDH; +import java.net.URI; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.Property; +import org.apache.jena.rdf.model.Resource; +import org.apache.jena.vocabulary.DCTerms; +import org.apache.jena.vocabulary.RDF; + +/** + * Content blocks, the ordered body of a document. + *

+ * A block is attached to its document by a container membership property + * (rdf:_1, rdf:_2, …) which fixes its position; see + * {@link SequenceNumbers} for reading the next free one off an existing document. + * + * @author Martynas Jusevičius {@literal } + */ +public final class Blocks +{ + + private Blocks() { } + + /** + * Builds an object block: a block whose value is another resource, rendered by the mode given. + * + * @param target target document URI + * @param seq container membership property fixing the block's position + * @param uri block URI, relative or absolute (optional, blank node when absent) + * @param value URI of the resource the block shows + * @param title block title (optional) + * @param description block description (optional) + * @param mode layout mode URI (optional) + * @return block model + */ + public static Model object(URI target, Property seq, String uri, URI value, String title, String description, URI mode) + { + Model model = ModelFactory.createDefaultModel(); + + Resource block = Subjects.of(model, target, uri). + addProperty(RDF.type, LDH.Object). + addProperty(RDF.value, model.createResource(value.toString())); + model.createResource(target.toString()).addProperty(seq, block); + if (title != null) block.addProperty(DCTerms.title, title); + if (description != null) block.addProperty(DCTerms.description, description); + if (mode != null) block.addProperty(AC.mode, model.createResource(mode.toString())); + + return model; + } + + /** + * Builds an XHTML block: a block whose value is markup carried as an + * rdf:XMLLiteral. + * + * @param target target document URI + * @param seq container membership property fixing the block's position + * @param uri block URI, relative or absolute (optional, blank node when absent) + * @param value XHTML markup + * @param title block title (optional) + * @param description block description (optional) + * @return block model + */ + public static Model xhtml(URI target, Property seq, String uri, String value, String title, String description) + { + Model model = ModelFactory.createDefaultModel(); + + Resource block = Subjects.of(model, target, uri). + addProperty(RDF.type, LDH.XHTML). + addProperty(RDF.value, model.createTypedLiteral(value, RDF.dtXMLLiteral)); + model.createResource(target.toString()).addProperty(seq, block); + if (title != null) block.addProperty(DCTerms.title, title); + if (description != null) block.addProperty(DCTerms.description, description); + + return model; + } + +} diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/util/Digests.java b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Digests.java similarity index 97% rename from cli/src/main/java/com/atomgraph/linkeddatahub/cli/util/Digests.java rename to rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Digests.java index dd1638251..e0d02435f 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/util/Digests.java +++ b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Digests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.atomgraph.linkeddatahub.cli.util; +package com.atomgraph.linkeddatahub.rdf; import java.io.IOException; import java.io.InputStream; diff --git a/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Documents.java b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Documents.java new file mode 100644 index 000000000..5912db4f8 --- /dev/null +++ b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Documents.java @@ -0,0 +1,105 @@ +/* + * Copyright 2026 Martynas Jusevičius . + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.atomgraph.linkeddatahub.rdf; + +import com.atomgraph.linkeddatahub.rdf.vocabulary.AC; +import com.atomgraph.linkeddatahub.rdf.vocabulary.DH; +import com.atomgraph.linkeddatahub.rdf.vocabulary.LDH; +import com.atomgraph.linkeddatahub.rdf.vocabulary.SPIN; +import java.net.URI; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.Resource; +import org.apache.jena.sparql.vocabulary.FOAF; +import org.apache.jena.vocabulary.DCTerms; +import org.apache.jena.vocabulary.RDF; + +/** + * The two document kinds of the hierarchy: containers and items. + * + * @author Martynas Jusevičius {@literal } + */ +public final class Documents +{ + + private Documents() { } + + /** + * Builds the container document model with its first content block: the given block URI, + * a children view with an explicit mode, or the default children view. + * + * @param doc document URI + * @param title document title + * @param description document description (optional) + * @param block content block URI (optional) + * @param mode children view mode URI (optional, ignored when block is given) + * @param primaryTopic URI of the document's primary topic, relative or absolute (optional) + * @return document model + */ + public static Model container(URI doc, String title, String description, URI block, URI mode, String primaryTopic) + { + Model model = ModelFactory.createDefaultModel(); + + Resource container = model.createResource(doc.toString()). + addProperty(RDF.type, DH.Container). + addProperty(DCTerms.title, title); + + if (block != null) container.addProperty(RDF.li(1), model.createResource(block.toString())); + else if (mode != null) container.addProperty(RDF.li(1), model.createResource(). + addProperty(RDF.type, LDH.Object). + addProperty(RDF.value, model.createResource(). + addProperty(RDF.type, LDH.View). + addProperty(SPIN.query, LDH.SelectChildren). + addProperty(AC.mode, model.createResource(mode.toString())))); + else container.addProperty(RDF.li(1), model.createResource(). + addProperty(RDF.type, LDH.Object). + addProperty(RDF.value, LDH.ChildrenView)); + + if (description != null) container.addProperty(DCTerms.description, description); + // See item(): resolved against the document, and singular by the vocabulary. + if (primaryTopic != null) container.addProperty(FOAF.primaryTopic, model.createResource(doc.resolve(primaryTopic).toString())); + + return model; + } + + /** + * Builds the item document model. + * + * @param doc document URI + * @param title document title + * @param description document description (optional) + * @param primaryTopic URI of the document's primary topic, relative or absolute (optional) + * @return document model + */ + public static Model item(URI doc, String title, String description, String primaryTopic) + { + Model model = ModelFactory.createDefaultModel(); + + Resource item = model.createResource(doc.toString()). + addProperty(RDF.type, DH.Item). + addProperty(DCTerms.title, title); + if (description != null) item.addProperty(DCTerms.description, description); + // Resolved against the document, so the conventional fragment topic is "#this" and a + // document about something described elsewhere takes that resource's absolute URI. + // Singular because foaf:primaryTopic is an owl:FunctionalProperty: a second value would + // not mean a second topic, it would entail the two topics are the same resource. + if (primaryTopic != null) item.addProperty(FOAF.primaryTopic, model.createResource(doc.resolve(primaryTopic).toString())); + + return model; + } + +} diff --git a/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Imports.java b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Imports.java new file mode 100644 index 000000000..5478b6cd9 --- /dev/null +++ b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Imports.java @@ -0,0 +1,98 @@ +/* + * Copyright 2026 Martynas Jusevičius . + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.atomgraph.linkeddatahub.rdf; + +import com.atomgraph.linkeddatahub.rdf.vocabulary.LDH; +import com.atomgraph.linkeddatahub.rdf.vocabulary.SD; +import com.atomgraph.linkeddatahub.rdf.vocabulary.SPIN; +import java.net.URI; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.Resource; +import org.apache.jena.vocabulary.DCTerms; +import org.apache.jena.vocabulary.RDF; + +/** + * Import descriptions - a file plus the transformation that turns it into RDF. + *

+ * Creating one only describes the import; the platform runs it asynchronously once the + * description lands. + * + * @author Martynas Jusevičius {@literal } + */ +public final class Imports +{ + + private Imports() { } + + /** + * Builds a CSV import description: a CSV file, the delimiter that parses it, and a + * CONSTRUCT query mapping each row to triples. + * + * @param target target document URI + * @param uri import URI, relative or absolute (optional, blank node when absent) + * @param title import title + * @param query URI of the query mapping rows to triples + * @param file URI of the CSV file + * @param delimiter CSV delimiter character + * @param description import description (optional) + * @return import model + */ + public static Model csv(URI target, String uri, String title, URI query, URI file, String delimiter, String description) + { + Model model = ModelFactory.createDefaultModel(); + + Resource csvImport = Subjects.of(model, target, uri). + addProperty(RDF.type, LDH.CSVImport). + addProperty(DCTerms.title, title). + addProperty(SPIN.query, model.createResource(query.toString())). + addProperty(LDH.file, model.createResource(file.toString())). + addProperty(LDH.delimiter, delimiter); + if (description != null) csvImport.addProperty(DCTerms.description, description); + + return model; + } + + /** + * Builds an RDF import description: an RDF file, optionally transformed by a query and + * optionally loaded into a named graph. + * + * @param target target document URI + * @param uri import URI, relative or absolute (optional, blank node when absent) + * @param title import title + * @param file URI of the RDF file + * @param query URI of the query transforming the parsed graph (optional) + * @param graph name of the graph the triples load into (optional) + * @param description import description (optional) + * @return import model + */ + public static Model rdf(URI target, String uri, String title, URI file, URI query, URI graph, String description) + { + Model model = ModelFactory.createDefaultModel(); + + Resource rdfImport = Subjects.of(model, target, uri). + addProperty(RDF.type, LDH.RDFImport). + addProperty(DCTerms.title, title). + addProperty(LDH.file, model.createResource(file.toString())); + if (graph != null) rdfImport.addProperty(SD.name, model.createResource(graph.toString())); + if (query != null) rdfImport.addProperty(SPIN.query, model.createResource(query.toString())); + if (description != null) rdfImport.addProperty(DCTerms.description, description); + + return model; + } + +} diff --git a/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Ontologies.java b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Ontologies.java new file mode 100644 index 000000000..9f98faae9 --- /dev/null +++ b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Ontologies.java @@ -0,0 +1,199 @@ +/* + * Copyright 2026 Martynas Jusevičius . + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.atomgraph.linkeddatahub.rdf; + +import com.atomgraph.linkeddatahub.rdf.vocabulary.DH; +import com.atomgraph.linkeddatahub.rdf.vocabulary.LDH; +import com.atomgraph.linkeddatahub.rdf.vocabulary.SP; +import com.atomgraph.linkeddatahub.rdf.vocabulary.SPIN; +import java.net.URI; +import java.util.List; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.Resource; +import org.apache.jena.sparql.vocabulary.FOAF; +import org.apache.jena.vocabulary.DCTerms; +import org.apache.jena.vocabulary.OWL; +import org.apache.jena.vocabulary.RDF; +import org.apache.jena.vocabulary.RDFS; + +/** + * The terms an application ontology is edited in: the ontology document itself, its classes, + * their restrictions, and the constructors and constraints attached to them. + *

+ * These carry rdfs:label/rdfs:comment rather than + * dct:title/dct:description - ontology terms are annotated the way + * RDFS annotates them, not the way documents are described. + * + * @author Martynas Jusevičius {@literal } + */ +public final class Ontologies +{ + + private Ontologies() { } + + /** + * Builds an ontology document. + * + * @param doc document URI + * @param uri ontology URI, relative or absolute (optional, blank node when absent) + * @param label ontology label, also the document title + * @param comment ontology comment (optional) + * @return ontology model + */ + public static Model ontology(URI doc, String uri, String label, String comment) + { + Model model = ModelFactory.createDefaultModel(); + + Resource ontology = Subjects.of(model, doc, uri). + addProperty(RDF.type, OWL.Ontology). + addProperty(RDFS.label, label); + if (comment != null) ontology.addProperty(RDFS.comment, comment); + + model.createResource(doc.toString()). + addProperty(RDF.type, DH.Item). + addProperty(FOAF.primaryTopic, ontology). + addProperty(DCTerms.title, label); + + return model; + } + + /** + * Builds a class, with its constructor, constraint and superclasses. + * + * @param target target ontology document URI + * @param uri class URI, relative or absolute (optional, blank node when absent) + * @param label class label + * @param comment class comment (optional) + * @param constructor URI of the query constructing instances of the class (optional) + * @param constraint URI of the class's constraint (optional) + * @param superClasses URIs of the class's superclasses + * @return class model + */ + public static Model owlClass(URI target, String uri, String label, String comment, URI constructor, URI constraint, List superClasses) + { + Model model = ModelFactory.createDefaultModel(); + + Resource cls = Subjects.of(model, target, uri). + addProperty(RDF.type, OWL.Class). + addProperty(RDFS.label, label); + if (comment != null) cls.addProperty(RDFS.comment, comment); + if (constructor != null) cls.addProperty(SPIN.constructor, model.createResource(constructor.toString())); + if (constraint != null) cls.addProperty(SPIN.constraint, model.createResource(constraint.toString())); + superClasses.forEach(superClass -> cls.addProperty(RDFS.subClassOf, model.createResource(superClass.toString()))); + + return model; + } + + /** + * Builds a property restriction. + * + * @param target target ontology document URI + * @param uri restriction URI, relative or absolute (optional, blank node when absent) + * @param label restriction label + * @param comment restriction comment (optional) + * @param onProperty URI of the restricted property (optional) + * @param allValuesFrom URI of the class the property's values must come from (optional) + * @param hasValue URI of the value the property must have (optional) + * @return restriction model + */ + public static Model restriction(URI target, String uri, String label, String comment, URI onProperty, URI allValuesFrom, URI hasValue) + { + Model model = ModelFactory.createDefaultModel(); + + Resource restriction = Subjects.of(model, target, uri). + addProperty(RDF.type, OWL.Restriction). + addProperty(RDFS.label, label); + if (comment != null) restriction.addProperty(RDFS.comment, comment); + if (onProperty != null) restriction.addProperty(OWL.onProperty, model.createResource(onProperty.toString())); + if (allValuesFrom != null) restriction.addProperty(OWL.allValuesFrom, model.createResource(allValuesFrom.toString())); + if (hasValue != null) restriction.addProperty(OWL.hasValue, model.createResource(hasValue.toString())); + + return model; + } + + /** + * Builds a SPIN constructor query - the query that builds a new instance of a class. + *

+ * The same shape as {@link Queries#query}, annotated for an ontology rather than described + * as a document. + * + * @param target target ontology document URI + * @param uri query URI, relative or absolute (optional, blank node when absent) + * @param queryType query form, e.g. {@link SP#Construct} + * @param label query label + * @param queryText the SPARQL query string + * @param service URI of the SPARQL service the query runs against (optional) + * @param comment query comment (optional) + * @return constructor model + */ + public static Model constructor(URI target, String uri, Resource queryType, String label, String queryText, URI service, String comment) + { + Model model = ModelFactory.createDefaultModel(); + + Resource query = Subjects.of(model, target, uri). + addProperty(RDF.type, queryType). + addProperty(RDFS.label, label). + addProperty(SP.text, queryText); + if (comment != null) query.addProperty(RDFS.comment, comment); + if (service != null) query.addProperty(LDH.service, model.createResource(service.toString())); + + return model; + } + + /** + * Builds a constraint requiring a property to have a value. + * + * @param target target ontology document URI + * @param uri constraint URI, relative or absolute (optional, blank node when absent) + * @param label constraint label + * @param property URI of the property that must have a value + * @param comment constraint comment (optional) + * @return constraint model + */ + public static Model propertyConstraint(URI target, String uri, String label, URI property, String comment) + { + Model model = ModelFactory.createDefaultModel(); + + Resource constraint = Subjects.of(model, target, uri). + addProperty(RDF.type, LDH.MissingPropertyValue). + addProperty(RDFS.label, label). + addProperty(SP.arg1, model.createResource(property.toString())); + if (comment != null) constraint.addProperty(RDFS.comment, comment); + + return model; + } + + /** + * Builds the annotation naming an imported ontology's source, written into the graph the + * import landed in. + * + * @param graph URI of the graph the ontology was imported into + * @param source URI the ontology was imported from + * @return annotation model + */ + public static Model annotation(URI graph, URI source) + { + Model model = ModelFactory.createDefaultModel(); + + model.createResource(graph.toString()). + addProperty(FOAF.primaryTopic, model.createResource(source.toString())); + + return model; + } + +} diff --git a/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Queries.java b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Queries.java new file mode 100644 index 000000000..60da2763d --- /dev/null +++ b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Queries.java @@ -0,0 +1,65 @@ +/* + * Copyright 2026 Martynas Jusevičius . + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.atomgraph.linkeddatahub.rdf; + +import com.atomgraph.linkeddatahub.rdf.vocabulary.LDH; +import com.atomgraph.linkeddatahub.rdf.vocabulary.SP; +import java.net.URI; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.Resource; +import org.apache.jena.vocabulary.DCTerms; +import org.apache.jena.vocabulary.RDF; + +/** + * Stored SPARQL queries. + * + * @author Martynas Jusevičius {@literal } + */ +public final class Queries +{ + + private Queries() { } + + /** + * Builds a stored query. The query form is the caller's to state - {@link SP#Select} and + * {@link SP#Construct} are stored the same way, and only the type says which one this is. + * + * @param target target document URI + * @param uri query URI, relative or absolute (optional, blank node when absent) + * @param queryType query form, e.g. {@link SP#Select} + * @param title query title + * @param queryText the SPARQL query string + * @param service URI of the SPARQL service the query runs against (optional) + * @param description query description (optional) + * @return query model + */ + public static Model query(URI target, String uri, Resource queryType, String title, String queryText, URI service, String description) + { + Model model = ModelFactory.createDefaultModel(); + + Resource query = Subjects.of(model, target, uri). + addProperty(RDF.type, queryType). + addProperty(DCTerms.title, title). + addProperty(SP.text, queryText); + if (service != null) query.addProperty(LDH.service, model.createResource(service.toString())); + if (description != null) query.addProperty(DCTerms.description, description); + + return model; + } + +} diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/util/SequenceNumbers.java b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/SequenceNumbers.java similarity index 98% rename from cli/src/main/java/com/atomgraph/linkeddatahub/cli/util/SequenceNumbers.java rename to rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/SequenceNumbers.java index 22fcc3738..8da89aed4 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/util/SequenceNumbers.java +++ b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/SequenceNumbers.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.atomgraph.linkeddatahub.cli.util; +package com.atomgraph.linkeddatahub.rdf; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.Property; diff --git a/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Services.java b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Services.java new file mode 100644 index 000000000..f7f496a2d --- /dev/null +++ b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Services.java @@ -0,0 +1,69 @@ +/* + * Copyright 2026 Martynas Jusevičius . + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.atomgraph.linkeddatahub.rdf; + +import com.atomgraph.linkeddatahub.rdf.vocabulary.A; +import com.atomgraph.linkeddatahub.rdf.vocabulary.SD; +import java.net.URI; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.Resource; +import org.apache.jena.vocabulary.DCTerms; +import org.apache.jena.vocabulary.RDF; + +/** + * SPARQL service descriptions - the endpoints a dataspace can query and write. + * + * @author Martynas Jusevičius {@literal } + */ +public final class Services +{ + + private Services() { } + + /** + * Builds a generic SPARQL service description. + * + * @param target target document URI + * @param uri service URI, relative or absolute (optional, blank node when absent) + * @param title service title + * @param endpoint SPARQL endpoint URI + * @param graphStore Graph Store Protocol endpoint URI (optional) + * @param authUser HTTP Basic auth user name (optional) + * @param authPwd HTTP Basic auth password (optional) + * @param description service description (optional) + * @return service model + */ + public static Model service(URI target, String uri, String title, URI endpoint, URI graphStore, String authUser, String authPwd, String description) + { + Model model = ModelFactory.createDefaultModel(); + + Resource service = Subjects.of(model, target, uri). + addProperty(RDF.type, SD.Service). + addProperty(DCTerms.title, title). + addProperty(SD.endpoint, model.createResource(endpoint.toString())). + addProperty(SD.supportedLanguage, SD.SPARQL11Query). + addProperty(SD.supportedLanguage, SD.SPARQL11Update); + if (graphStore != null) service.addProperty(A.graphStore, model.createResource(graphStore.toString())); + if (authUser != null) service.addProperty(A.authUser, authUser); + if (authPwd != null) service.addProperty(A.authPwd, authPwd); + if (description != null) service.addProperty(DCTerms.description, description); + + return model; + } + +} diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/util/Slugs.java b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Slugs.java similarity index 96% rename from cli/src/main/java/com/atomgraph/linkeddatahub/cli/util/Slugs.java rename to rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Slugs.java index 6d77b04ed..3f5b49d56 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/util/Slugs.java +++ b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Slugs.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.atomgraph.linkeddatahub.cli.util; +package com.atomgraph.linkeddatahub.rdf; import java.util.Locale; import java.util.UUID; diff --git a/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Subjects.java b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Subjects.java new file mode 100644 index 000000000..fd62b6686 --- /dev/null +++ b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Subjects.java @@ -0,0 +1,51 @@ +/* + * Copyright 2026 Martynas Jusevičius . + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.atomgraph.linkeddatahub.rdf; + +import java.net.URI; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.Resource; + +/** + * The subject a description appended to a document is about. + * + * @author Martynas Jusevičius {@literal } + */ +public final class Subjects +{ + + private Subjects() { } + + /** + * Returns the subject resource for an appended description: the given URI resolved against + * the target document URI, or a fresh blank node when no URI is given. + *

+ * A blank node is the right default because most appended descriptions - a content block, a + * query, a chart - are only ever referred to from within the document that carries them, so + * minting a URI for them would add an identifier nothing dereferences. + * + * @param model model to create the resource in + * @param target target document URI + * @param uri subject URI, absolute or relative to the target, or null for a blank node + * @return subject resource + */ + public static Resource of(Model model, URI target, String uri) + { + return uri != null ? model.createResource(target.resolve(uri).toString()) : model.createResource(); + } + +} diff --git a/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/URIs.java b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/URIs.java new file mode 100644 index 000000000..915828cc1 --- /dev/null +++ b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/URIs.java @@ -0,0 +1,87 @@ +/* + * Copyright 2026 Martynas Jusevičius . + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.atomgraph.linkeddatahub.rdf; + +import java.net.URI; +import java.nio.charset.StandardCharsets; + +/** + * Where LinkedDataHub puts things: how a child document's URI is derived from its parent and a + * slug, and how an end-user application's base URI relates to its admin application's. + *

+ * Conventions, not manipulation - every method here encodes a rule the platform also applies, so + * a client that follows them addresses documents the platform will agree exist. + * + * @author Martynas Jusevičius {@literal } + */ +public final class URIs +{ + + private URIs() { } + + /** + * Builds the URI of a child document from the parent container URI and a path segment slug. + *

+ * The trailing slash is what makes it a container-addressable document rather than a fragment + * of its parent, so it is added here rather than left to the caller. + * + * @param parent parent container URI (with trailing slash) + * @param slug path segment + * @return child document URI (with trailing slash) + */ + public static URI childURI(URI parent, String slug) + { + return URI.create(parent.toString() + encodeSlug(slug) + "/"); + } + + /** + * Percent-encodes a string as a URI path segment. All characters except RFC 3986 + * unreserved ones are encoded, including / - a slug names one segment, so a + * slash in it is data rather than a further step in the path. + * + * @param slug path segment + * @return encoded path segment + */ + public static String encodeSlug(String slug) + { + StringBuilder sb = new StringBuilder(); + + for (byte b : slug.getBytes(StandardCharsets.UTF_8)) + { + char c = (char)(b & 0xFF); + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || + c == '-' || c == '.' || c == '_' || c == '~') sb.append(c); + else sb.append('%').append(String.format("%02X", b & 0xFF)); + } + + return sb.toString(); + } + + /** + * Converts an end-user application base URI to the base URI of its admin application + * by prefixing the host with the admin. subdomain, which is how nginx's + * wildcard routing tells the two apart. + * + * @param base end-user base URI + * @return admin base URI + */ + public static URI adminBase(URI base) + { + return URI.create(base.toString().replaceFirst("://", "://admin.")); + } + +} diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/sparql/Updates.java b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Updates.java similarity index 99% rename from cli/src/main/java/com/atomgraph/linkeddatahub/cli/sparql/Updates.java rename to rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Updates.java index 160cb6bb6..02cbe6251 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/sparql/Updates.java +++ b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Updates.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.atomgraph.linkeddatahub.cli.sparql; +package com.atomgraph.linkeddatahub.rdf; import java.net.URI; import org.apache.jena.query.ParameterizedSparqlString; diff --git a/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Views.java b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Views.java new file mode 100644 index 000000000..74fa2b5c1 --- /dev/null +++ b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/Views.java @@ -0,0 +1,94 @@ +/* + * Copyright 2026 Martynas Jusevičius . + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.atomgraph.linkeddatahub.rdf; + +import com.atomgraph.linkeddatahub.rdf.vocabulary.AC; +import com.atomgraph.linkeddatahub.rdf.vocabulary.LDH; +import com.atomgraph.linkeddatahub.rdf.vocabulary.SPIN; +import java.net.URI; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.Resource; +import org.apache.jena.vocabulary.DCTerms; +import org.apache.jena.vocabulary.RDF; + +/** + * Views: a query plus how its results are shown. + * + * @author Martynas Jusevičius {@literal } + */ +public final class Views +{ + + private Views() { } + + /** + * Builds a view of a query's results. + * + * @param target target document URI + * @param uri view URI, relative or absolute (optional, blank node when absent) + * @param query URI of the query the view runs + * @param title view title (optional) + * @param description view description (optional) + * @param mode layout mode URI (optional) + * @return view model + */ + public static Model view(URI target, String uri, URI query, String title, String description, URI mode) + { + Model model = ModelFactory.createDefaultModel(); + + Resource view = Subjects.of(model, target, uri). + addProperty(RDF.type, LDH.View). + addProperty(SPIN.query, model.createResource(query.toString())); + if (title != null) view.addProperty(DCTerms.title, title); + if (description != null) view.addProperty(DCTerms.description, description); + if (mode != null) view.addProperty(AC.mode, model.createResource(mode.toString())); + + return model; + } + + /** + * Builds a chart of a SELECT query's result set. The category and series variable names + * pick the result-set columns the chart's axes read. + * + * @param target target document URI + * @param uri chart URI, relative or absolute (optional, blank node when absent) + * @param title chart title + * @param query URI of the query the chart runs + * @param chartType chart type URI + * @param categoryVarName result-set variable name providing the categories + * @param seriesVarName result-set variable name providing the series + * @param description chart description (optional) + * @return chart model + */ + public static Model resultSetChart(URI target, String uri, String title, URI query, URI chartType, String categoryVarName, String seriesVarName, String description) + { + Model model = ModelFactory.createDefaultModel(); + + Resource chart = Subjects.of(model, target, uri). + addProperty(RDF.type, LDH.ResultSetChart). + addProperty(DCTerms.title, title). + addProperty(SPIN.query, model.createResource(query.toString())). + addProperty(LDH.chartType, model.createResource(chartType.toString())). + addProperty(LDH.categoryVarName, categoryVarName). + addProperty(LDH.seriesVarName, seriesVarName); + if (description != null) chart.addProperty(DCTerms.description, description); + + return model; + } + +} diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/A.java b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/A.java similarity index 96% rename from cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/A.java rename to rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/A.java index 73dd5abd3..c37362ccb 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/A.java +++ b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/A.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.atomgraph.linkeddatahub.cli.vocab; +package com.atomgraph.linkeddatahub.rdf.vocabulary; import org.apache.jena.rdf.model.Property; import org.apache.jena.rdf.model.ResourceFactory; diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/AC.java b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/AC.java similarity index 95% rename from cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/AC.java rename to rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/AC.java index 9de56512e..d04870afe 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/AC.java +++ b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/AC.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.atomgraph.linkeddatahub.cli.vocab; +package com.atomgraph.linkeddatahub.rdf.vocabulary; import org.apache.jena.rdf.model.Property; import org.apache.jena.rdf.model.ResourceFactory; diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/ACL.java b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/ACL.java similarity index 97% rename from cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/ACL.java rename to rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/ACL.java index e7f3ff709..b193ce1ba 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/ACL.java +++ b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/ACL.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.atomgraph.linkeddatahub.cli.vocab; +package com.atomgraph.linkeddatahub.rdf.vocabulary; import org.apache.jena.rdf.model.Property; import org.apache.jena.rdf.model.Resource; diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/DH.java b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/DH.java similarity index 96% rename from cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/DH.java rename to rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/DH.java index dcc80f803..92517f2b5 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/DH.java +++ b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/DH.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.atomgraph.linkeddatahub.cli.vocab; +package com.atomgraph.linkeddatahub.rdf.vocabulary; import org.apache.jena.rdf.model.Resource; import org.apache.jena.rdf.model.ResourceFactory; diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/LDH.java b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/LDH.java similarity index 98% rename from cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/LDH.java rename to rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/LDH.java index 397f57f70..c4f2e0637 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/LDH.java +++ b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/LDH.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.atomgraph.linkeddatahub.cli.vocab; +package com.atomgraph.linkeddatahub.rdf.vocabulary; import org.apache.jena.rdf.model.Property; import org.apache.jena.rdf.model.Resource; diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/NFO.java b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/NFO.java similarity index 96% rename from cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/NFO.java rename to rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/NFO.java index de6a53c65..b388abc2f 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/NFO.java +++ b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/NFO.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.atomgraph.linkeddatahub.cli.vocab; +package com.atomgraph.linkeddatahub.rdf.vocabulary; import org.apache.jena.rdf.model.Property; import org.apache.jena.rdf.model.Resource; diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/SD.java b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/SD.java similarity index 97% rename from cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/SD.java rename to rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/SD.java index 4bec9d26f..47eaf2184 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/SD.java +++ b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/SD.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.atomgraph.linkeddatahub.cli.vocab; +package com.atomgraph.linkeddatahub.rdf.vocabulary; import org.apache.jena.rdf.model.Property; import org.apache.jena.rdf.model.Resource; diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/SP.java b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/SP.java similarity index 96% rename from cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/SP.java rename to rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/SP.java index 6634441f9..8217816c8 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/SP.java +++ b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/SP.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.atomgraph.linkeddatahub.cli.vocab; +package com.atomgraph.linkeddatahub.rdf.vocabulary; import org.apache.jena.rdf.model.Property; import org.apache.jena.rdf.model.Resource; diff --git a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/SPIN.java b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/SPIN.java similarity index 96% rename from cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/SPIN.java rename to rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/SPIN.java index a4cbec33d..9c183d737 100644 --- a/cli/src/main/java/com/atomgraph/linkeddatahub/cli/vocab/SPIN.java +++ b/rdf/src/main/java/com/atomgraph/linkeddatahub/rdf/vocabulary/SPIN.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.atomgraph.linkeddatahub.cli.vocab; +package com.atomgraph.linkeddatahub.rdf.vocabulary; import org.apache.jena.rdf.model.Property; import org.apache.jena.rdf.model.ResourceFactory; diff --git a/cli/src/test/java/com/atomgraph/linkeddatahub/cli/command/ModelBuildersTest.java b/rdf/src/test/java/com/atomgraph/linkeddatahub/rdf/ModelBuildersTest.java similarity index 80% rename from cli/src/test/java/com/atomgraph/linkeddatahub/cli/command/ModelBuildersTest.java rename to rdf/src/test/java/com/atomgraph/linkeddatahub/rdf/ModelBuildersTest.java index 770dd454c..88686d467 100644 --- a/cli/src/test/java/com/atomgraph/linkeddatahub/cli/command/ModelBuildersTest.java +++ b/rdf/src/test/java/com/atomgraph/linkeddatahub/rdf/ModelBuildersTest.java @@ -14,22 +14,10 @@ * limitations under the License. */ -package com.atomgraph.linkeddatahub.cli.command; - -import com.atomgraph.linkeddatahub.cli.command.admin.acl.CreateAuthorization; -import com.atomgraph.linkeddatahub.cli.command.admin.acl.CreateGroup; -import com.atomgraph.linkeddatahub.cli.command.admin.ontologies.AddClass; -import com.atomgraph.linkeddatahub.cli.command.admin.ontologies.AddConstructor; -import com.atomgraph.linkeddatahub.cli.command.admin.ontologies.AddPropertyConstraint; -import com.atomgraph.linkeddatahub.cli.command.admin.ontologies.AddRestriction; -import com.atomgraph.linkeddatahub.cli.command.admin.ontologies.CreateOntology; -import com.atomgraph.linkeddatahub.cli.command.admin.ontologies.ImportOntology; -import com.atomgraph.linkeddatahub.cli.command.content.AddObjectBlock; -import com.atomgraph.linkeddatahub.cli.command.content.AddXHTMLBlock; -import com.atomgraph.linkeddatahub.cli.command.imports.AddCSVImport; -import com.atomgraph.linkeddatahub.cli.command.imports.AddRDFImport; -import com.atomgraph.linkeddatahub.cli.vocab.ACL; -import com.atomgraph.linkeddatahub.cli.vocab.SP; +package com.atomgraph.linkeddatahub.rdf; + +import com.atomgraph.linkeddatahub.rdf.vocabulary.ACL; +import com.atomgraph.linkeddatahub.rdf.vocabulary.SP; import java.io.StringReader; import java.net.URI; import java.util.List; @@ -88,7 +76,7 @@ public void createItem() dct:title "My item" ; dct:description "Desc" . """), - CreateItem.buildModel(doc, "My item", "Desc", null)); + Documents.item(doc, "My item", "Desc", null)); } @Test @@ -103,7 +91,7 @@ public void createItemWithFragmentPrimaryTopic() dct:title "My item" ; foaf:primaryTopic . """), - CreateItem.buildModel(doc, "My item", null, "#this")); + Documents.item(doc, "My item", null, "#this")); } @Test @@ -118,7 +106,7 @@ public void createItemWithAbsolutePrimaryTopic() dct:title "About Bob" ; foaf:primaryTopic . """), - CreateItem.buildModel(doc, "About Bob", null, "https://example.org/bob#me")); + Documents.item(doc, "About Bob", null, "https://example.org/bob#me")); } @Test @@ -129,7 +117,7 @@ public void createContainerDefaultChildrenView() dct:title "Some" ; rdf:_1 [ a ldh:Object ; rdf:value ldh:ChildrenView ] . """), - CreateContainer.buildModel(TARGET, "Some", null, null, null, null)); + Documents.container(TARGET, "Some", null, null, null, null)); } @Test @@ -140,7 +128,7 @@ public void createContainerWithMode() dct:title "Some" ; rdf:_1 [ a ldh:Object ; rdf:value [ a ldh:View ; spin:query ldh:SelectChildren ; ac:mode ] ] . """), - CreateContainer.buildModel(TARGET, "Some", null, null, URI.create("https://w3id.org/atomgraph/client#GridMode"), null)); + Documents.container(TARGET, "Some", null, null, URI.create("https://w3id.org/atomgraph/client#GridMode"), null)); } @Test @@ -151,7 +139,7 @@ public void createContainerWithBlock() dct:title "Some" ; rdf:_1 . """), - CreateContainer.buildModel(TARGET, "Some", null, URI.create("https://localhost:4443/some/#block"), null, null)); + Documents.container(TARGET, "Some", null, URI.create("https://localhost:4443/some/#block"), null, null)); } @Test @@ -165,7 +153,7 @@ public void createContainerWithPrimaryTopic() rdf:_1 [ a ldh:Object ; rdf:value ldh:ChildrenView ] ; foaf:primaryTopic <#this> . """), - CreateContainer.buildModel(TARGET, "Some", null, null, null, "#this")); + Documents.container(TARGET, "Some", null, null, null, "#this")); } @Test @@ -177,7 +165,7 @@ public void addViewWithURIAndMode() dct:title "View" ; ac:mode . """), - AddView.buildModel(TARGET, "#view", URI.create("https://localhost:4443/queries/q/#this"), "View", null, + Views.view(TARGET, "#view", URI.create("https://localhost:4443/queries/q/#this"), "View", null, URI.create("https://w3id.org/atomgraph/client#GridMode"))); } @@ -190,7 +178,7 @@ public void addConstructQuery() sp:text \"""CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o }\""" ; ldh:service . """), - AddConstruct.buildModel(TARGET, null, SP.Construct, "Query", "CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o }", + Queries.query(TARGET, null, SP.Construct, "Query", "CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o }", URI.create("https://localhost:4443/services/s/#this"), null)); } @@ -208,7 +196,7 @@ public void createOntology() foaf:primaryTopic _:ontology ; dct:title "My ontology" . """), - CreateOntology.buildModel(doc, null, "My ontology", "Comment")); + Ontologies.ontology(doc, null, "My ontology", "Comment")); } @Test @@ -225,7 +213,7 @@ public void createGroup() foaf:primaryTopic _:group ; dct:title "Editors" . """), - CreateGroup.buildModel(doc, null, "Editors", null, + Acl.group(doc, null, "Editors", null, List.of(URI.create("https://localhost:4443/acl/agents/a/#this"), URI.create("https://localhost:4443/acl/agents/b/#this")))); } @@ -245,7 +233,7 @@ public void createAuthorization() foaf:primaryTopic _:auth ; dct:title "Auth" . """), - CreateAuthorization.buildModel(doc, null, "Auth", null, + Acl.authorization(doc, null, "Auth", null, List.of(URI.create("https://localhost:4443/acl/agents/a/#this")), List.of(), List.of(), List.of(URI.create("https://localhost:4443/some/")), List.of(), List.of(ACL.Read, ACL.Write))); @@ -261,7 +249,7 @@ public void addCSVImport() ldh:file ; ldh:delimiter "," . """), - AddCSVImport.buildModel(TARGET, null, "Cities", URI.create("https://localhost:4443/some/#query"), + Imports.csv(TARGET, null, "Cities", URI.create("https://localhost:4443/some/#query"), URI.create("https://localhost:4443/uploads/abc"), ",", null)); } @@ -274,7 +262,7 @@ public void addRDFImportWithGraph() ldh:file ; sd:name . """), - AddRDFImport.buildModel(TARGET, "#import", "Data", URI.create("https://localhost:4443/uploads/abc"), + Imports.rdf(TARGET, "#import", "Data", URI.create("https://localhost:4443/uploads/abc"), null, URI.create("https://localhost:4443/graphs/g/"), null)); } @@ -286,7 +274,7 @@ public void addXHTMLBlock() _:block a ldh:XHTML ; rdf:value "

Hello

"^^rdf:XMLLiteral . """), - AddXHTMLBlock.buildModel(TARGET, RDF.li(4), null, "

Hello

", null, null)); + Blocks.xhtml(TARGET, RDF.li(4), null, "

Hello

", null, null)); } @Test @@ -301,7 +289,7 @@ public void addGenericServiceWithGraphStoreAndAuth() a:authUser "user" ; a:authPwd "pwd" . """), - AddGenericService.buildModel(TARGET, "#service", "Remote", URI.create("https://remote.example/sparql"), + Services.service(TARGET, "#service", "Remote", URI.create("https://remote.example/sparql"), URI.create("https://remote.example/service"), "user", "pwd", null)); } @@ -314,7 +302,7 @@ public void addGenericServiceMinimal() sd:endpoint ; sd:supportedLanguage sd:SPARQL11Query, sd:SPARQL11Update . """), - AddGenericService.buildModel(TARGET, "#service", "Remote", URI.create("https://remote.example/sparql"), + Services.service(TARGET, "#service", "Remote", URI.create("https://remote.example/sparql"), null, null, null, null)); } @@ -330,7 +318,7 @@ public void addResultSetChart() ldh:categoryVarName "category" ; ldh:seriesVarName "series" . """), - AddResultSetChart.buildModel(TARGET, "#chart", "Chart", URI.create("https://localhost:4443/queries/select/#this"), + Views.resultSetChart(TARGET, "#chart", "Chart", URI.create("https://localhost:4443/queries/select/#this"), URI.create("https://w3id.org/atomgraph/client#BarChart"), "category", "series", "Desc")); } @@ -345,7 +333,7 @@ public void addClassWithSuperClasses() spin:constraint ; rdfs:subClassOf , . """), - AddClass.buildModel(TARGET, "#Concept", "Concept", "A concept", + Ontologies.owlClass(TARGET, "#Concept", "Concept", "A concept", URI.create("https://localhost:4443/queries/construct/#this"), URI.create("https://localhost:4443/constraints/#this"), List.of(URI.create("https://localhost:4443/ns#Thing"), URI.create("https://localhost:4443/ns#Other")))); @@ -358,7 +346,7 @@ public void addClassMinimal() <#Concept> a owl:Class ; rdfs:label "Concept" . """), - AddClass.buildModel(TARGET, "#Concept", "Concept", null, null, null, List.of())); + Ontologies.owlClass(TARGET, "#Concept", "Concept", null, null, null, List.of())); } @Test @@ -371,7 +359,7 @@ public void addConstructor() sp:text "CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o }" ; ldh:service . """), - AddConstructor.buildModel(TARGET, "#constructor", SP.Construct, "Constructor", + Ontologies.constructor(TARGET, "#constructor", SP.Construct, "Constructor", "CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o }", URI.create("https://localhost:4443/services/remote/#this"), "Builds a Concept")); } @@ -384,7 +372,7 @@ public void addPropertyConstraint() rdfs:label "Title required" ; sp:arg1 dct:title . """), - AddPropertyConstraint.buildModel(TARGET, "#constraint", "Title required", + Ontologies.propertyConstraint(TARGET, "#constraint", "Title required", URI.create("http://purl.org/dc/terms/title"), null)); } @@ -399,7 +387,7 @@ public void addRestriction() owl:allValuesFrom rdfs:Literal ; owl:hasValue . """), - AddRestriction.buildModel(TARGET, "#restriction", "Has title", "Every instance carries a title", + Ontologies.restriction(TARGET, "#restriction", "Has title", "Every instance carries a title", URI.create("http://purl.org/dc/terms/title"), URI.create("http://www.w3.org/2000/01/rdf-schema#Literal"), URI.create("https://localhost:4443/values/default/"))); @@ -416,7 +404,7 @@ public void addObjectBlock() dct:description "Desc" ; ac:mode . """), - AddObjectBlock.buildModel(TARGET, RDF.li(2), null, URI.create("https://localhost:4443/other/"), + Blocks.object(TARGET, RDF.li(2), null, URI.create("https://localhost:4443/other/"), "Block", "Desc", URI.create("https://w3id.org/atomgraph/client#ReadMode"))); } @@ -430,7 +418,7 @@ public void importOntologyAnnotation() assertIsomorphic(parse(""" <> foaf:primaryTopic . """), - ImportOntology.buildAnnotationModel(TARGET, URI.create("http://www.w3.org/2004/02/skos/core#"))); + Ontologies.annotation(TARGET, URI.create("http://www.w3.org/2004/02/skos/core#"))); } } diff --git a/cli/src/test/java/com/atomgraph/linkeddatahub/cli/util/SequenceNumbersTest.java b/rdf/src/test/java/com/atomgraph/linkeddatahub/rdf/SequenceNumbersTest.java similarity index 98% rename from cli/src/test/java/com/atomgraph/linkeddatahub/cli/util/SequenceNumbersTest.java rename to rdf/src/test/java/com/atomgraph/linkeddatahub/rdf/SequenceNumbersTest.java index 275030c71..2f50d5994 100644 --- a/cli/src/test/java/com/atomgraph/linkeddatahub/cli/util/SequenceNumbersTest.java +++ b/rdf/src/test/java/com/atomgraph/linkeddatahub/rdf/SequenceNumbersTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.atomgraph.linkeddatahub.cli.util; +package com.atomgraph.linkeddatahub.rdf; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; diff --git a/rdf/src/test/java/com/atomgraph/linkeddatahub/rdf/URIsTest.java b/rdf/src/test/java/com/atomgraph/linkeddatahub/rdf/URIsTest.java new file mode 100644 index 000000000..bd908e58f --- /dev/null +++ b/rdf/src/test/java/com/atomgraph/linkeddatahub/rdf/URIsTest.java @@ -0,0 +1,54 @@ +/* + * Copyright 2026 Martynas Jusevičius . + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.atomgraph.linkeddatahub.rdf; + +import java.net.URI; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Tests for {@link URIs}. + */ +public class URIsTest +{ + + @Test + public void adminBasePrefixesHostWithAdminSubdomain() + { + assertEquals(URI.create("https://admin.localhost:4443/"), URIs.adminBase(URI.create("https://localhost:4443/"))); + } + + @Test + public void encodeSlugKeepsUnreservedCharacters() + { + assertEquals("abc-._~123", URIs.encodeSlug("abc-._~123")); + } + + @Test + public void encodeSlugEncodesReservedAndNonASCII() + { + assertEquals("a%20b%2F%C4%87", URIs.encodeSlug("a b/ć")); + } + + @Test + public void childURIAppendsEncodedSlugAndSlash() + { + assertEquals(URI.create("https://localhost:4443/some/my%20item/"), + URIs.childURI(URI.create("https://localhost:4443/some/"), "my item")); + } + +} diff --git a/cli/src/test/java/com/atomgraph/linkeddatahub/cli/sparql/UpdatesTest.java b/rdf/src/test/java/com/atomgraph/linkeddatahub/rdf/UpdatesTest.java similarity index 98% rename from cli/src/test/java/com/atomgraph/linkeddatahub/cli/sparql/UpdatesTest.java rename to rdf/src/test/java/com/atomgraph/linkeddatahub/rdf/UpdatesTest.java index 79981c84b..20451080c 100644 --- a/cli/src/test/java/com/atomgraph/linkeddatahub/cli/sparql/UpdatesTest.java +++ b/rdf/src/test/java/com/atomgraph/linkeddatahub/rdf/UpdatesTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.atomgraph.linkeddatahub.cli.sparql; +package com.atomgraph.linkeddatahub.rdf; import java.net.URI; import org.apache.jena.query.Syntax; diff --git a/release.sh b/release.sh index addd5b7cf..b9f823a3b 100755 --- a/release.sh +++ b/release.sh @@ -127,20 +127,24 @@ fi print_status "GPG check passed" -# Set cli/pom.xml to the given version and commit it. The CLI is not a module of the platform -# reactor, so maven-release-plugin does not rewrite it - it is kept in step here instead, once for -# the release version and once for the next development version. +# Set rdf/pom.xml and cli/pom.xml to the given version and commit them. Neither is a module of the +# platform reactor, so maven-release-plugin does not rewrite them - they are kept in step here +# instead, once for the release version and once for the next development version. The CLI resolves +# the library by ${project.version}, so the two must move together or the CLI build breaks. sync_cli_version() { local version="$1" + local project - (cd cli && mvn -B -q versions:set -DnewVersion="$version" -DgenerateBackupPoms=false) + for project in rdf cli; do + (cd "$project" && mvn -B -q versions:set -DnewVersion="$version" -DgenerateBackupPoms=false) + done - if git diff --quiet -- cli/pom.xml; then - print_status "cli/pom.xml already at $version" + if git diff --quiet -- rdf/pom.xml cli/pom.xml; then + print_status "rdf/pom.xml and cli/pom.xml already at $version" else - git add cli/pom.xml - git commit -m "Set the CLI version to $version" - print_status "cli/pom.xml set to $version" + git add rdf/pom.xml cli/pom.xml + git commit -m "Set the RDF library and CLI versions to $version" + print_status "rdf/pom.xml and cli/pom.xml set to $version" fi } @@ -228,6 +232,23 @@ print_status "Performing Maven release (deploying to Sonatype)..." mvn release:perform -DlocalCheckout=true PUBLISHED=true +# Publish linkeddatahub-rdf at the same version. It is not a module of the reactor, so +# release:perform does not carry it - but the CLI resolves it by ${project.version}, and Web-Algebra's +# ldh-* operations resolve it from their own repository, so a platform release without it is a release +# whose clients cannot build. +# +# Deployed from the tag rather than the working tree: by this point sync_cli_version has already moved +# rdf/pom.xml on to the next development version, while the tag holds the release one. git archive +# extracts the subtree alone, so nothing here depends on the working tree's state. +# The staging directory is removed inline rather than by a trap: the script's only EXIT trap is +# cleanup_on_failure, and registering a second one would replace it. +print_status "Deploying linkeddatahub-rdf $RELEASE_VERSION..." +RDF_STAGING=$(mktemp -d) +git archive "$RELEASE_TAG" rdf | tar -x -C "$RDF_STAGING" +(cd "$RDF_STAGING/rdf" && mvn -B -Prelease clean deploy) +rm -rf "$RDF_STAGING" +print_status "linkeddatahub-rdf $RELEASE_VERSION deployed" + # Switch to master and merge only the release commit print_status "Merging release commit to master branch..." git checkout master