Skip to content

Commit 60c8ffc

Browse files
authored
Merge branch 'main' into feature/RDFA-243-toast-event-notifications
2 parents 9ce770a + 4a2676f commit 60c8ffc

13 files changed

Lines changed: 868 additions & 4 deletions

File tree

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright (c) 2024-2026 SOPTIM AG
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package org.rdfarchitect.api.controller.datasets.graphs.classes;
19+
20+
import io.swagger.v3.oas.annotations.Operation;
21+
import io.swagger.v3.oas.annotations.Parameter;
22+
import io.swagger.v3.oas.annotations.media.Content;
23+
import io.swagger.v3.oas.annotations.media.Schema;
24+
25+
import lombok.RequiredArgsConstructor;
26+
27+
import org.rdfarchitect.api.dto.ClassDTO;
28+
import org.rdfarchitect.api.dto.attributes.AttributeDTO;
29+
import org.rdfarchitect.database.GraphIdentifier;
30+
import org.rdfarchitect.services.ClassExtensionUseCase;
31+
import org.rdfarchitect.services.ExpandURIUseCase;
32+
import org.slf4j.Logger;
33+
import org.slf4j.LoggerFactory;
34+
import org.springframework.http.HttpHeaders;
35+
import org.springframework.web.bind.annotation.PathVariable;
36+
import org.springframework.web.bind.annotation.PostMapping;
37+
import org.springframework.web.bind.annotation.RequestBody;
38+
import org.springframework.web.bind.annotation.RequestHeader;
39+
import org.springframework.web.bind.annotation.RequestMapping;
40+
import org.springframework.web.bind.annotation.RestController;
41+
42+
@RestController
43+
@RequestMapping("api/datasets/{datasetName}/graphs/{graphURI}/classes/{classUUID}/extend")
44+
@RequiredArgsConstructor
45+
public class ClassExtensionRESTController {
46+
private static final Logger logger =
47+
LoggerFactory.getLogger(ClassExtensionRESTController.class);
48+
49+
private final ExpandURIUseCase expandURIUseCase;
50+
private final ClassExtensionUseCase classExtensionUseCase;
51+
52+
@Operation(
53+
summary = "Extend class",
54+
description = "extends a class in another graph",
55+
tags = {"class"})
56+
@PostMapping
57+
public ClassDTO extendClass(
58+
@Parameter(description = "The name/url of the inquirer.")
59+
@RequestHeader(
60+
value = HttpHeaders.ORIGIN,
61+
required = false,
62+
defaultValue = "unknown")
63+
String originURL,
64+
@Parameter(description = "The literal name of the dataset.") @PathVariable
65+
String datasetName,
66+
@Parameter(
67+
description =
68+
"The url encoded uri of the graph, or \"default\" to access the default graph.")
69+
@PathVariable
70+
String graphURI,
71+
@Parameter(description = "The uuid of the class.") @PathVariable String classUUID,
72+
@io.swagger.v3.oas.annotations.parameters.RequestBody(
73+
required = true,
74+
description = "The new attribute",
75+
content =
76+
@Content(schema = @Schema(implementation = AttributeDTO.class)))
77+
@RequestBody
78+
GraphIdentifier newGraphIdentifier) {
79+
logger.info(
80+
"Received POST request: \"/api/datasets/{{}}/graphs/{{}}/classes/{{}}/extend\" from \"{}\".",
81+
datasetName,
82+
graphURI,
83+
classUUID,
84+
originURL);
85+
86+
var extendedGraphURI = expandURIUseCase.expandUri(datasetName, graphURI);
87+
var graphIdentifier = new GraphIdentifier(datasetName, extendedGraphURI);
88+
89+
var newClass =
90+
classExtensionUseCase.extendClass(graphIdentifier, classUUID, newGraphIdentifier);
91+
92+
logger.info(
93+
"Sending response to POST request: \"/api/datasets/{{}}/graphs/{{}}/classes/{{}}/extend\" to \"{}\".",
94+
datasetName,
95+
graphURI,
96+
classUUID,
97+
originURL);
98+
return newClass;
99+
}
100+
}
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
/*
2+
* Copyright (c) 2024-2026 SOPTIM AG
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package org.rdfarchitect.services;
19+
20+
import lombok.AllArgsConstructor;
21+
22+
import org.apache.jena.graph.Graph;
23+
import org.apache.jena.query.TxnType;
24+
import org.apache.jena.rdf.model.Model;
25+
import org.apache.jena.rdf.model.ModelFactory;
26+
import org.apache.jena.vocabulary.RDF;
27+
import org.apache.jena.vocabulary.RDFS;
28+
import org.rdfarchitect.api.dto.ClassDTO;
29+
import org.rdfarchitect.api.dto.ClassMapper;
30+
import org.rdfarchitect.database.DatabasePort;
31+
import org.rdfarchitect.database.GraphIdentifier;
32+
import org.rdfarchitect.models.changelog.ChangeLogEntry;
33+
import org.rdfarchitect.models.cim.data.CIMObjectFetcher;
34+
import org.rdfarchitect.models.cim.data.dto.CIMClass;
35+
import org.rdfarchitect.models.cim.data.dto.relations.CIMSBelongsToCategory;
36+
import org.rdfarchitect.models.cim.data.dto.relations.RDFSLabel;
37+
import org.rdfarchitect.models.cim.data.dto.relations.uri.URI;
38+
import org.rdfarchitect.models.cim.queries.update.CIMUpdates;
39+
import org.rdfarchitect.models.cim.rdf.resources.CIMS;
40+
import org.rdfarchitect.models.cim.rdf.resources.CIMStereotypes;
41+
import org.rdfarchitect.models.cim.rdf.resources.RDFA;
42+
import org.rdfarchitect.models.cim.relations.CIMClassRelationFinder;
43+
import org.rdfarchitect.rdf.graph.wrapper.GraphRewindable;
44+
import org.springframework.stereotype.Service;
45+
46+
import java.util.List;
47+
import java.util.UUID;
48+
49+
@Service
50+
@AllArgsConstructor
51+
public class ClassExtensionService implements ClassExtensionUseCase {
52+
53+
private DatabasePort databasePort;
54+
private ClassMapper classMapper;
55+
private ChangeLogUseCase changeLogUseCase;
56+
57+
@Override
58+
public ClassDTO extendClass(
59+
GraphIdentifier graphIdentifier, String classUUID, GraphIdentifier newGraphIdentifier) {
60+
var graph = databasePort.getGraphWithContext(graphIdentifier);
61+
CIMClass classCopy;
62+
List<CIMClass> superClasses;
63+
64+
GraphRewindable rdfGraph = null;
65+
try {
66+
rdfGraph = graph.getRdfGraph();
67+
rdfGraph.begin(TxnType.READ);
68+
69+
classCopy = fetchStubbedClassCopy(graphIdentifier, classUUID);
70+
superClasses = fetchStubbedSuperClasses(rdfGraph, UUID.fromString(classUUID));
71+
72+
} finally {
73+
if (rdfGraph != null) {
74+
rdfGraph.end();
75+
}
76+
}
77+
78+
var newGraph = databasePort.getGraphWithContext(newGraphIdentifier).getRdfGraph();
79+
insertNewClasses(newGraph, newGraphIdentifier, classCopy, superClasses);
80+
changeLogUseCase.recordChange(
81+
newGraphIdentifier,
82+
new ChangeLogEntry(
83+
"Added "
84+
+ classCopy.getLabel().getValue()
85+
+ " and its superclasses to graph "
86+
+ newGraphIdentifier.graphUri(),
87+
newGraph.getLastDelta()));
88+
89+
return classMapper.toDTO(classCopy);
90+
}
91+
92+
private CIMClass fetchStubbedClassCopy(GraphIdentifier graphIdentifier, String classUUID) {
93+
var cimObjectFetcher =
94+
new CIMObjectFetcher(
95+
databasePort.getGraphWithContext(graphIdentifier).getRdfGraph(),
96+
graphIdentifier.graphUri(),
97+
databasePort.getPrefixMapping(graphIdentifier.datasetName()));
98+
99+
var classCopy = cimObjectFetcher.fetchCIMClass(classUUID);
100+
if (classCopy == null) {
101+
throw new IllegalArgumentException(
102+
"Class with UUID "
103+
+ classUUID
104+
+ " not found in graph "
105+
+ graphIdentifier.graphUri());
106+
}
107+
108+
// set new UUID and remove concrete stereotype
109+
var filteredStereotypes =
110+
classCopy.getStereotypes().stream()
111+
.filter(s -> !s.getStereotype().equals(CIMStereotypes.concreteString))
112+
.toList();
113+
classCopy.setUuid(UUID.randomUUID());
114+
classCopy.setStereotypes(filteredStereotypes);
115+
116+
return classCopy;
117+
}
118+
119+
private List<CIMClass> fetchStubbedSuperClasses(Graph graph, UUID classUUID) {
120+
var model = ModelFactory.createModelForGraph(graph);
121+
var relationFinder = new CIMClassRelationFinder(model);
122+
123+
var superClasses = relationFinder.findSuperClasses(classUUID);
124+
for (var superClass : superClasses) {
125+
// set new UUID and remove concrete stereotype
126+
var filteredStereotypes =
127+
superClass.getStereotypes().stream()
128+
.filter(s -> !s.getStereotype().equals(CIMStereotypes.concreteString))
129+
.toList();
130+
superClass.setUuid(UUID.randomUUID());
131+
superClass.setStereotypes(filteredStereotypes);
132+
}
133+
134+
return superClasses.stream().toList();
135+
}
136+
137+
private void insertNewClasses(
138+
GraphRewindable newGraph,
139+
GraphIdentifier newGraphIdentifier,
140+
CIMClass classCopy,
141+
List<CIMClass> superClasses) {
142+
try {
143+
var model = ModelFactory.createModelForGraph(newGraph);
144+
newGraph.begin(TxnType.WRITE);
145+
var prefixMapping = databasePort.getPrefixMapping(newGraphIdentifier.datasetName());
146+
147+
var newPackage = fetchNewPackage(model);
148+
149+
for (var cls : superClasses) {
150+
if (!model.contains(model.createResource(cls.getUri().toString()), null)) {
151+
cls.setBelongsToCategory(newPackage);
152+
CIMUpdates.insertClass(newGraph, prefixMapping, cls);
153+
}
154+
}
155+
classCopy.setBelongsToCategory(newPackage);
156+
CIMUpdates.insertClass(newGraph, prefixMapping, classCopy);
157+
158+
newGraph.commit();
159+
} finally {
160+
if (newGraph != null) {
161+
newGraph.end();
162+
}
163+
}
164+
}
165+
166+
private CIMSBelongsToCategory fetchNewPackage(Model model) {
167+
var it =
168+
model.listSubjectsWithProperty(RDF.type, CIMS.classCategory)
169+
.filterKeep(s -> s.toString().contains("Core"));
170+
171+
if (!it.hasNext()) {
172+
return null;
173+
}
174+
175+
var corePackage = it.next();
176+
CIMSBelongsToCategory pack = new CIMSBelongsToCategory();
177+
pack.setUri(new URI(corePackage.getURI()));
178+
pack.setLabel(new RDFSLabel(corePackage.getProperty(RDFS.label).getString(), "en"));
179+
pack.setUuid(UUID.fromString(corePackage.getProperty(RDFA.uuid).getString()));
180+
return pack;
181+
}
182+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright (c) 2024-2026 SOPTIM AG
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package org.rdfarchitect.services;
19+
20+
import org.rdfarchitect.api.dto.ClassDTO;
21+
import org.rdfarchitect.database.GraphIdentifier;
22+
23+
public interface ClassExtensionUseCase {
24+
/**
25+
* enables extension of a class by creating an abstract stub of the class and all its
26+
* superclasses in the new graph
27+
*
28+
* @param graphIdentifier the dataset name and graph URI of the class to extend
29+
* @param classUUID the uuid of the class to be extended
30+
* @param newGraph the identifier of the new graph, where the class is to be extended
31+
* @return the uuid of the newly created class stub in the new graph
32+
*/
33+
ClassDTO extendClass(
34+
GraphIdentifier graphIdentifier, String classUUID, GraphIdentifier newGraph);
35+
}

0 commit comments

Comments
 (0)