Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.example.anonymization.dto.AnonymizationJsonLDRequestDto;
import com.example.anonymization.entities.Configuration;
import com.example.anonymization.service.AnonymizationService;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
Expand Down Expand Up @@ -46,6 +47,11 @@ public class AnonymizationRestController {
name = "JSON-LD with two anonymization objects",
summary = "JSON-LD request with two anonymization objects",
externalValue = "/examples/anonymization-request-two-objects.json"
),
@ExampleObject(
name = "JSON-LD of object anonymization (address)",
summary = "JSON-LD request for the anonymization of address objects",
externalValue = "/examples/anonymization-request-object.json"
)
}
)
Expand All @@ -55,7 +61,6 @@ public class AnonymizationRestController {
consumes = {"application/json", "application/ld+json"},
produces = "application/json")
public ResponseEntity<String> anonymization(@RequestBody AnonymizationJsonLDRequestDto anonymizationRequest) {
// logger and service call as in your code
return AnonymizationService.applyAnonymization(anonymizationRequest);
}

Expand Down Expand Up @@ -89,10 +94,10 @@ public ResponseEntity<String> anonymization(@RequestBody AnonymizationJsonLDRequ
)
)
@PutMapping("/api/anonymization/flatjson")
public ResponseEntity<String> anonymizationFlat(@RequestBody AnonymizationFlatJsonRequestDto anonymizationRequest) {

logger.info("Received flat JSON anonymization request. Body: \n" + anonymizationRequest);

public ResponseEntity<String> anonymizationFlat(
@RequestBody AnonymizationFlatJsonRequestDto anonymizationRequest
) throws JsonProcessingException {
logger.info("Received flat JSON anonymization request. Body: \n{}", anonymizationRequest);
return AnonymizationService.applyAnonymizationFlatJson(anonymizationRequest);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.example.anonymization.exceptions.AnonymizationException;
import com.example.anonymization.exceptions.OntologyException;
import com.example.anonymization.exceptions.RequestModelException;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail;
import org.springframework.http.ResponseEntity;
Expand All @@ -20,7 +21,7 @@ public ResponseEntity<ProblemDetail> handleNotFound(OntologyException ex) {
ProblemDetail pd = ProblemDetail.forStatus(HttpStatus.NOT_FOUND);
pd.setTitle("Error in ontology fetching or parsing");
pd.setDetail(ex.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(pd);
return ResponseEntity.status(pd.getStatus()).body(pd);
}

@ExceptionHandler(AnonymizationException.class)
Expand All @@ -29,7 +30,7 @@ public ResponseEntity<ProblemDetail> handleAnonymizationException(AnonymizationE
ProblemDetail pd = ProblemDetail.forStatus(HttpStatus.INTERNAL_SERVER_ERROR);
pd.setTitle("Error during anonymization process");
pd.setDetail(ex.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(pd);
return ResponseEntity.status(pd.getStatus()).body(pd);
}

@ExceptionHandler(RequestModelException.class)
Expand All @@ -38,6 +39,15 @@ public ResponseEntity<ProblemDetail> handleRequestModelException(RequestModelExc
ProblemDetail pd = ProblemDetail.forStatus(HttpStatus.BAD_REQUEST);
pd.setTitle("Invalid request model");
pd.setDetail(ex.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(pd);
return ResponseEntity.status(pd.getStatus()).body(pd);
}

@ExceptionHandler(JsonProcessingException.class)
public ResponseEntity<ProblemDetail> handleJsonException(JsonProcessingException ex) {
log.error("JsonProcessingException: {}", ex.getMessage());
ProblemDetail pd = ProblemDetail.forStatus(HttpStatus.INTERNAL_SERVER_ERROR);
pd.setTitle("Error creation Json output");
pd.setDetail(ex.getMessage());
return ResponseEntity.status(pd.getStatus()).body(pd);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ static String createConfigQuery() {
?property rdfs:domain ?anonymizationObject .
?property rdfs:range ?datatype .
?property <https://w3id.org/soya/ns#classification> ?anonymization .
FILTER(isLiteral(?anonymization))
}
""";
}
Expand All @@ -37,14 +38,53 @@ static ParameterizedSparqlString createDataModelQuery(
q.append(" ?object a <" + anonymizationObject.getURI()+ ">.\n");
for (Property p : properties) {
String local = p.getLocalName();
q.append(" OPTIONAL { ?object ?" + local + " ?_" + local + ".\n");
q.append("FILTER(isLiteral(?_" + local + ")) }\n");
q.append(" OPTIONAL { ?object ?" + local + " ?_" + local + ". }\n");
q.setParam(local, p);
}
q.append("}");
return q;
}

static ParameterizedSparqlString createAttributeOrderQuery(Resource attribute) {
/*
SELECT ?pos ?value
WHERE {
# You know this subject (replace _:b0 with your actual subject IRI/variable)
_:b0 soya:attributeOrder ?head .

# Each cell of the RDF list and its value
?head rdf:rest* ?cell .
?cell rdf:first ?value .

# Compute the position of each cell in the list
{
SELECT ?cell (COUNT(?mid) AS ?pos)
WHERE {
# ?head is taken from the outer query (correlated subquery)
?head rdf:rest* ?mid .
?mid rdf:rest* ?cell .
}
GROUP BY ?cell
}
}
ORDER BY ?pos
*/
String queryString = """
PREFIX soya: <https://w3id.org/soya/ns#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
SELECT ?value
WHERE {
?attribute soya:classification ?o .
?o soya:attributeOrder ?head .
?head rdf:rest* ?cell .
?cell rdf:first ?value .
}
""";
ParameterizedSparqlString q = new ParameterizedSparqlString(queryString);
q.setParam("attribute", attribute);
return q;
}


static ParameterizedSparqlString createKpiDataQuery(Set<Property> properties, Resource kpiObject) {
ParameterizedSparqlString queryString = new ParameterizedSparqlString();
Expand Down Expand Up @@ -83,12 +123,14 @@ static ParameterizedSparqlString deleteOriginalPropertyQuery(Set<Property> prope
queryString.append("DELETE {\n");
for (int i = 0; i < properties.size(); i++) {
queryString.append(" ?object ?p" + i + " ?v" + i + " .\n");
queryString.append(" ?v" + i + " ?p ?o . \n");
}
queryString.append("}\nWHERE {\n");
queryString.append(" ?object a ?type .\n");
int i = 0;
for (Property property : properties) {
queryString.append(" OPTIONAL { ?object ?p" + i + " ?v" + i + " . }\n");
queryString.append(" OPTIONAL { ?object ?p" + i + " ?v" + i + " .\n");
queryString.append(" ?v" + i + " ?p ?o . } \n");
queryString.setParam("p" + i, property);
i++;
}
Expand Down
26 changes: 20 additions & 6 deletions src/main/java/com/example/anonymization/data/QueryService.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public static List<ConfigurationResult> getConfigurations(Model model) {
try (QueryExecution qexec = QueryExecutionFactory.create(QueryBuildingService.createConfigQuery(), model)) {
ResultSet rs = qexec.execSelect();
while (rs.hasNext()) {
// TODO maybe include check for Literal in query
QuerySolution solution = rs.nextSolution();
configurations.add(new ConfigurationResult(
solution.getResource("?anonymizationObject"),
Expand All @@ -40,27 +41,40 @@ public static List<ConfigurationResult> getConfigurations(Model model) {
return configurations;
}

public static List<String> getAttributeOrder(Model model, Resource attribute) {
List<String> attributes = new ArrayList<>();
try (QueryExecution qexec = QueryExecutionFactory
.create(QueryBuildingService.createAttributeOrderQuery(attribute).asQuery(), model)) {
ResultSet rs = qexec.execSelect();
while (rs.hasNext()) {
QuerySolution solution = rs.nextSolution();
attributes.add(solution.get("?value").toString());
}
}
return attributes;
}

/**
* Extracts the data for a given set of attributes and an object type
* @param model the input model
* @param properties attributes for which data should be fetched
* @param objectType the type for which data should be fetched
* @return mapping of resources of the object type with their property data
*/
public static Map<Resource, Map<Property, Literal>> getData(
public static Map<Resource, Map<Property, RDFNode>> getData(
Model model,
Collection<Property> properties,
Resource objectType
) {
Query query = QueryBuildingService.createDataModelQuery(properties, objectType).asQuery();
Map<Resource, Map<Property, Literal>> results = new HashMap<>();
Map<Resource, Map<Property, RDFNode>> results = new HashMap<>();
try (QueryExecution qexec = QueryExecutionFactory.create(query, model)) {
ResultSet resultSet = qexec.execSelect();
while(resultSet.hasNext()) {
QuerySolution solution = resultSet.nextSolution();
Map<Property, Literal> propertyValues = new HashMap<>();
Map<Property, RDFNode> propertyValues = new HashMap<>();
properties.forEach(property -> {
Literal value = solution.getLiteral("_" + property.getLocalName());
RDFNode value = solution.get("_" + property.getLocalName());
if (value != null) {
propertyValues.put(property, value);
}
Expand Down Expand Up @@ -199,7 +213,7 @@ public static List<Set<Resource>> getGeneralizationGroups(Model model, Resource
* @param objectType the object type for which the data is returned
* @return mapping of resources of the object type with their property data
*/
public static Map<Resource, Map<Property, Literal>> getAllData(Model model, Resource objectType) {
public static Map<Resource, Map<Property, RDFNode>> getAllData(Model model, Resource objectType) {
Set<Property> properties = new HashSet<>();
ParameterizedSparqlString pss = QueryBuildingService.createPropertyQuery(objectType);
try (QueryExecution qexec = QueryExecutionFactory.create(pss.asQuery(), model)) {
Expand Down Expand Up @@ -305,7 +319,7 @@ public static Map<Resource, List<QueryService.AttributeInformation>> getAttribut
ParameterizedSparqlString queryString = QueryBuildingService.createAttributeInformationQuery(
objectTypes.stream().map(o -> model.getResource(KPI_OBJECT_URI + o.getLocalName())).toList(),
model.createProperty(HAS_ATTRIBUTE_URI),
model.createProperty(NR_ATTRIBUTES_URI),
model.createProperty(NR_BUCKETS_URI),
model.createProperty(ANONYMIZATION_TYP_URI)
);
Query query = queryString.asQuery();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,48 @@
package com.example.anonymization.entities;


import com.example.anonymization.service.anonymizer.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.Property;
import org.apache.jena.rdf.model.RDFNode;
import org.apache.jena.rdf.model.Resource;

import java.util.Map;

@Data
@Getter
@AllArgsConstructor
public class Configuration {
String dataType;
String anonymization;

public Anonymization<? extends Configuration> createAnonymization(
Model model,
Property property,
Map<Resource, RDFNode> data,
int nrAttr,
Resource anonymizationObject
) {
return switch (anonymization) {
case "generalization" -> switch (dataType) {
case "integer", "double" -> new GeneralizationNumeric(model, property, data, nrAttr, this, anonymizationObject);
case "date" -> new GeneralizationDate(model, property, data, nrAttr, this, anonymizationObject);
case "string" -> throw new IllegalArgumentException("No Generalization possible for type string");
default ->
throw new IllegalArgumentException("Invalid configuration type for object anonymization");
};
case "randomization" -> switch (dataType) {
case "integer", "double" -> new RandomizationNumeric(model, property, data, nrAttr, this, anonymizationObject);
case "date" -> new RandomizationDate(model, property, data, nrAttr, this, anonymizationObject);
default ->
throw new IllegalArgumentException("No Randomization possible for type " + dataType);
};
case "masking" -> new Masking(model, property, data, this, anonymizationObject);
default ->
throw new IllegalArgumentException("No Anonymization implementation for " + anonymization + ": " + dataType);
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.example.anonymization.entities;

import com.example.anonymization.service.anonymizer.GeneralizationObject;
import lombok.Getter;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.Property;
import org.apache.jena.rdf.model.RDFNode;
import org.apache.jena.rdf.model.Resource;

import java.util.List;
import java.util.Map;

@Getter
public class ObjectGeneralizationConfig extends Configuration {

List<String> attributeOrder;

public ObjectGeneralizationConfig(String dataType, List<String> attributeOrder) {
super(dataType, "generalization");
this.attributeOrder = attributeOrder;
}

@Override
public GeneralizationObject createAnonymization(
Model model,
Property property,
Map<Resource, RDFNode> data,
int nrAttr,
Resource anonymizationObject
) {
return new GeneralizationObject(model, property, data, nrAttr, this, anonymizationObject);
}
}
Loading