Skip to content
Snippets Groups Projects
Commit c6a106c3 authored by Justin Dex's avatar Justin Dex Committed by Paul Moosmann
Browse files

implement communication to frontend when getting a .json file

parent 66feb501
No related branches found
No related tags found
No related merge requests found
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
Backend service to convert an input shacl shape into JSON form for the frontend service. Backend service to convert an input shacl shape into JSON form for the frontend service.
- Input is expected to be a MultipartFile file. - Input is expected to be a MultipartFile file.
- Output object is ShaclModel.java. It consists of a list of json objects for prefixes in the input file and another list of json objects for the shapes listed in the input file. - Output object is ShaclModel.java. It consists of a list of json objects for prefixes in the input file and another list of json objects for the shapes listed in the input file.
- Alternatively input can be two MultipartFiles (one .ttl SHACL Shape, one .json Claim-File), behaviour is the same, but the answer would be a ResponseShaclJsonPair.java, which consists out of a ShaclModel.java and a Map of Strings containing overlapping attributes. This would be done such that the Frontend can prefill the displayed FormFields.
- Each shape is depicted in VicShape.java. Shape name is stored in variable schema. Properties of the shape are stored in variable called 'constraints' which is of type ShapeProperties.java. - Each shape is depicted in VicShape.java. Shape name is stored in variable schema. Properties of the shape are stored in variable called 'constraints' which is of type ShapeProperties.java.
- Test instances can be found under src/test/resources. - Test instances can be found under src/test/resources.
- JUnit test cases can be found under src/test/java. - JUnit test cases can be found under src/test/java.
......
package eu.gaiax.sdcreationwizard.api; package eu.gaiax.sdcreationwizard.api;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import eu.gaiax.sdcreationwizard.api.dto.ResponseShaclJsonPair;
import eu.gaiax.sdcreationwizard.api.dto.ShaclFileUtils; import eu.gaiax.sdcreationwizard.api.dto.ShaclFileUtils;
import eu.gaiax.sdcreationwizard.api.dto.ShaclModel; import eu.gaiax.sdcreationwizard.api.dto.ShaclModel;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
...@@ -182,6 +184,29 @@ public class ConversionController { ...@@ -182,6 +184,29 @@ public class ConversionController {
? HttpStatus.BAD_REQUEST : HttpStatus.OK); ? HttpStatus.BAD_REQUEST : HttpStatus.OK);
} }
/**
* Converts a given SHACL file in conjunction with a json file to a JSON object (ResponseShaclJsonPair) including
* Shacl Model and a Map of Subjects and their values, which are present in both files
*
* @param shaclFile a multipart SHACL file with *.ttl extension
* @param jsonFile a multipart json file with *.json extension
* @return a JSON serialization of the processed content
*/
@PostMapping(value = "/convertAndPrefillFile", produces = "application/json")
@CrossOrigin(origins = "*")
public ResponseEntity<Object> convertShaclAndJson(@RequestParam("file") MultipartFile shaclFile, @RequestParam("jsonFile") MultipartFile jsonFile) {
logger.info("Converting JSON and SHACL file: {}, {}", jsonFile.getOriginalFilename(), shaclFile.getOriginalFilename());
ResponseShaclJsonPair response;
try {
response = ConversionService.checkShaclForJson(shaclFile, jsonFile);
} catch (IOException e) {
return new ResponseEntity<>("Shacl conversion failed <-> Bad Input", HttpStatus.BAD_REQUEST);
} catch (Exception e) {
return new ResponseEntity<>("Error: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<>(response, HttpStatus.OK);
}
/** /**
* To be used after /getAvailableShapes. Returns the content of a predefined JSON file. * To be used after /getAvailableShapes. Returns the content of a predefined JSON file.
...@@ -277,4 +302,4 @@ public class ConversionController { ...@@ -277,4 +302,4 @@ public class ConversionController {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST); return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
} }
} }
} }
\ No newline at end of file
...@@ -3,6 +3,9 @@ package eu.gaiax.sdcreationwizard.api; ...@@ -3,6 +3,9 @@ package eu.gaiax.sdcreationwizard.api;
import eu.gaiax.sdcreationwizard.api.dto.*; import eu.gaiax.sdcreationwizard.api.dto.*;
import org.apache.jena.rdf.model.*; import org.apache.jena.rdf.model.*;
import org.apache.jena.vocabulary.SKOS; import org.apache.jena.vocabulary.SKOS;
import org.apache.jena.riot.Lang;
import org.apache.jena.riot.RDFDataMgr;
import org.apache.jena.shacl.vocabulary.SHACL;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
...@@ -63,6 +66,39 @@ public class ConversionService { ...@@ -63,6 +66,39 @@ public class ConversionService {
return new ShaclModel(prefixList, shapesSorted); return new ShaclModel(prefixList, shapesSorted);
} }
public static ResponseShaclJsonPair checkShaclForJson(final MultipartFile shaclFile, final MultipartFile jsonFile) throws IOException {
Map<String, String> matchedSubjects = new HashMap<>();
try(InputStream inputStreamJson = jsonFile.getInputStream();
InputStream inputStreamShacl = shaclFile.getInputStream()) {
Model modelJson = ModelFactory.createDefaultModel();
Model modelShacl = ModelFactory.createDefaultModel();
RDFDataMgr.read(modelShacl, inputStreamShacl, Lang.TTL);
RDFDataMgr.read(modelJson, inputStreamJson, Lang.JSONLD);
// Extract paths from SHACL shapes
Set<String> shaclPaths = new HashSet<>();
Property shaclPath = modelShacl.createProperty(SHACL.path.getURI());
StmtIterator it = modelShacl.listStatements((Resource) null, shaclPath, (RDFNode) null);
while (it.hasNext()) {
Statement stmt = it.nextStatement();
shaclPaths.add(stmt.getResource().getURI());
}
// Check, if any subject in JSON-LD matches a SHACL path and add matches to a list
StmtIterator jsonldIt = modelJson.listStatements();
while (jsonldIt.hasNext()) {
Statement stmt = jsonldIt.nextStatement();
Property predicate = stmt.getPredicate();
if (shaclPaths.contains(predicate.getURI())) {
matchedSubjects.put(predicate.getURI(), stmt.getObject().toString());
}
}
}
return new ResponseShaclJsonPair(convertToJson(shaclFile.getInputStream()), matchedSubjects);
}
private static int calculateDepth(VicShape shape, List<VicShape> shapes, Map<VicShape, Integer> visitedShapes) { private static int calculateDepth(VicShape shape, List<VicShape> shapes, Map<VicShape, Integer> visitedShapes) {
if (visitedShapes.containsKey(shape)) { if (visitedShapes.containsKey(shape)) {
return visitedShapes.get(shape); return visitedShapes.get(shape);
......
package eu.gaiax.sdcreationwizard.api.dto;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResponseShaclJsonPair{
private final ShaclModel shaclModel;
private final Map<String, String> matchedSubjects;
public ResponseShaclJsonPair(ShaclModel shaclModel, Map<String, String> matchedSubjects) {
this.shaclModel = shaclModel;
this.matchedSubjects = matchedSubjects;
}
public ShaclModel getShaclModel() {
return this.shaclModel;
}
public Map<String, String> getMatchedSubjects() {
return this.matchedSubjects;
}
}
...@@ -28,10 +28,16 @@ import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder; ...@@ -28,10 +28,16 @@ import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.WebApplicationContext;
import eu.gaiax.sdcreationwizard.api.dto.ResponseShaclJsonPair;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
@RunWith(Parameterized.class) @RunWith(Parameterized.class)
@WebAppConfiguration @WebAppConfiguration
...@@ -191,4 +197,40 @@ public class ConversionServicePositiveTests { ...@@ -191,4 +197,40 @@ public class ConversionServicePositiveTests {
JSONAssert.assertEquals(outputFile, mvcResult.getResponse().getContentAsString(), strictComparison); JSONAssert.assertEquals(outputFile, mvcResult.getResponse().getContentAsString(), strictComparison);
} }
/**
* individual test to test "checkShaclForJson" method
* takes two files (test-checkShaclForJson.ttl and test-checkShaclForJson.json)
* checks whether all the keyvalue pairs that match are indeed included in the result
*/
@Test
public void testCheckShaclForJson() throws Exception {
// prepare
Map<String, String> expectedMap = new HashMap<>();
expectedMap.put("http://schema.org/name", "msg Systems hospital");
expectedMap.put("http://schema.org/streetAddress", "Robert-Bürkle-Straße 1");
expectedMap.put("http://w3id.org/gaia-x/ex#locality", "Ismaning");
expectedMap.put("http://schema.org/postalCode", "85737^^http://www.w3.org/2001/XMLSchema#integer");
expectedMap.put("http://w3id.org/gaia-x/ex#countryCode", "DE");
expectedMap.put("http://w3id.org/gaia-x/ex#number", "123456789^^http://www.w3.org/2001/XMLSchema#integer");
String individualInputFilename = "test-checkShaclForJson.ttl";
String individualInputJsonFilname = "test-checkShaclForJson.json";
ClassLoader classloader = Thread.currentThread().getContextClassLoader();
InputStream inputFile = classloader.getResourceAsStream(individualInputFilename);
InputStream inputJsonFile = classloader.getResourceAsStream(individualInputJsonFilname);
MockMultipartFile mockMultipartFileTtl = new MockMultipartFile("fileTtl", individualInputFilename,
MediaType.MULTIPART_FORM_DATA_VALUE, inputFile);
MockMultipartFile mockMultipartFileJson = new MockMultipartFile("fileJson", individualInputFilename,
MediaType.MULTIPART_FORM_DATA_VALUE, inputJsonFile);
// action
ResponseShaclJsonPair responseShaclJsonPairFromTestFiles = ConversionService.checkShaclForJson(mockMultipartFileTtl, mockMultipartFileJson);
Map<String, String> actualMap = responseShaclJsonPairFromTestFiles.getMatchedSubjects();
// test
assertTrue(actualMap.entrySet().containsAll(expectedMap.entrySet()));
}
} }
{
"@context": {
"schema": "http://schema.org/",
"xsd": "http://www.w3.org/2001/XMLSchema#",
"ex": "http://w3id.org/gaia-x/ex#",
"sh": "http://www.w3.org/ns/shacl#"
},
"@id": "did:example:1234",
"@type": "schema:Hospital",
"schema:name": {
"@value": "msg Systems hospital",
"@type": "xsd:string"
},
"schema:address": {
"@type": "schema:address",
"schema:streetAddress": {
"@value": "Robert-Bürkle-Straße 1",
"@type": "xsd:string"
},
"ex:locality": {
"@value": "Ismaning",
"@type": "xsd:string"
},
"schema:postalCode": 85737
},
"schema:telephone": {
"@type": "ex:phone",
"ex:number": 123456789,
"ex:countryCode": "DE"
}
}
\ No newline at end of file
@prefix schema: <http://schema.org/> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix ex: <http://w3id.org/gaia-x/ex#> .
ex:HospitalShape
a sh:NodeShape;
sh:targetClass schema:Hospital;
sh:property [
sh:path schema:name ;
sh:name "name" ;
sh:datatype xsd:string ;
sh:minCount 1;
sh:description "Name of the hospital" ;
sh:order 1
];
sh:property [
sh:path schema:address ;
sh:node ex:AddressShape ;
sh:minCount 1;
sh:order 2;
] ;
sh:property [
sh:path schema:telephone ;
sh:node ex:TelephoneShape ;
sh:minCount 1;
sh:order 3;
];
sh:property [
sh:path schema:Person ;
sh:node ex:PersonShape ;
sh:order 4 ;
].
ex:PersonShape
a sh:NodeShape ;
sh:targetClass schema:Person ;
sh:property [
sh:path schema:givenName ;
sh:name "given name" ;
sh:datatype xsd:string ;
sh:minCount 1;
sh:maxCount 1;
sh:minLength 3;
sh:maxLength 8;
sh:order 31
] ;
sh:property [
sh:path schema:gender ;
sh:in ( "female" "male" ) ;
sh:minCount 1;
sh:maxCount 1;
sh:order 32;
] ;
sh:property [
sh:path schema:birthDate ;
sh:datatype xsd:date ;
sh:minCount 1;
sh:maxCount 1;
sh:order 33;
] ;
sh:property [
sh:path ex:age ;
sh:datatype xsd:integer ;
sh:minCount 1;
sh:maxCount 1;
sh:minInclusive 18 ;
sh:maxInclusive 100 ;
sh:order 34;
] ;
sh:property [
sh:path schema:address ;
sh:node ex:AddressShape ;
sh:minCount 1;
sh:order 35;
] ;
sh:property [
sh:path schema:telephone ;
sh:node ex:TelephoneShape ;
sh:minCount 1;
sh:order 36;
].
ex:AddressShape
a sh:NodeShape ;
sh:targetClass schema:address ;
sh:property [
sh:path schema:streetAddress ;
sh:name "street address" ;
sh:datatype xsd:string ;
sh:order 11 ;
sh:minLength 3;
sh:description "The street address including number" ;
sh:group ex:AddressGroup ;
] ;
sh:property [
sh:path ex:locality ;
sh:name "locality" ;
sh:datatype xsd:string ;
sh:order 12 ;
sh:description "The suburb, city or town of the address" ;
sh:group ex:AddressGroup ;
];
sh:property [
sh:path schema:postalCode ;
sh:name "postal code" ;
sh:datatype xsd:integer ;
sh:order 13 ;
sh:description "The postal code of the locality" ;
sh:group ex:AddressGroup ;
] .
ex:TelephoneShape
a sh:NodeShape;
sh:targetClass ex:phone ;
sh:property [
sh:path ex:countryCode ;
sh:name "country code" ;
sh:minCount 1;
sh:maxCount 1;
sh:description "Country code" ;
sh:group ex:PhoneGroup;
];
sh:property [
sh:path ex:number ;
sh:name "number" ;
sh:datatype xsd:integer ;
sh:minCount 1;
sh:description "The phone number" ;
sh:group ex:PhoneGroup;
].
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment