From e5f6cf69259293ef6d5769888b118827e54759a6 Mon Sep 17 00:00:00 2001 From: Ivan Mrsulja Date: Thu, 26 Sep 2024 15:01:02 +0200 Subject: [PATCH 01/12] Added software controller with fetching operations. --- .../software/AuthorResponseDTO.java | 10 + .../software/FundingResponseDTO.java | 8 + .../software/SoftwareController.java | 345 ++++++++++++++++++ .../software/SoftwareResponseDTO.java | 27 ++ .../resources/rdf/tbox/filegraph/vivo.owl | 16 +- .../rdf/tbox/firsttime/vitroAnnotations.n3 | 12 + 6 files changed, 414 insertions(+), 4 deletions(-) create mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/AuthorResponseDTO.java create mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FundingResponseDTO.java create mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java create mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/AuthorResponseDTO.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/AuthorResponseDTO.java new file mode 100644 index 0000000000..7557e0cbf2 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/AuthorResponseDTO.java @@ -0,0 +1,10 @@ +package edu.cornell.mannlib.vitro.webapp.controller.software; + +public class AuthorResponseDTO { + + public String name; + + public String type; + + public String identifier; +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FundingResponseDTO.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FundingResponseDTO.java new file mode 100644 index 0000000000..412fc1782a --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FundingResponseDTO.java @@ -0,0 +1,8 @@ +package edu.cornell.mannlib.vitro.webapp.controller.software; + +public class FundingResponseDTO { + + public String funding; + + public String funder; +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java new file mode 100644 index 0000000000..6e29e0c672 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java @@ -0,0 +1,345 @@ +package edu.cornell.mannlib.vitro.webapp.controller.software; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; +import edu.cornell.mannlib.vitro.webapp.controller.api.sparqlquery.InvalidQueryTypeException; +import edu.cornell.mannlib.vitro.webapp.controller.api.sparqlquery.SparqlQueryApiExecutor; +import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; +import edu.cornell.mannlib.vitro.webapp.utils.http.AcceptHeaderParsingException; +import edu.cornell.mannlib.vitro.webapp.utils.http.NotAcceptableException; +import org.apache.jena.query.QueryParseException; + +@WebServlet(name = "softwareController", urlPatterns = {"/software", "/software/*"}, loadOnStartup = 5) +public class SoftwareController extends HttpServlet { + + private final String FIND_ALL_QUERY = + "PREFIX rdf: \n" + + "PREFIX rdfs: \n" + + "PREFIX xsd: \n" + + "PREFIX owl: \n" + + "PREFIX swrl: \n" + + "PREFIX swrlb: \n" + + "PREFIX vitro: \n" + + "PREFIX bibo: \n" + + "PREFIX c4o: \n" + + "PREFIX cito: \n" + + "PREFIX dcterms: \n" + + "PREFIX event: \n" + + "PREFIX fabio: \n" + + "PREFIX foaf: \n" + + "PREFIX geo: \n" + + "PREFIX obo: \n" + + "PREFIX vivo: \n" + + "PREFIX vcard: \n" + + "\n" + + "SELECT ?software ?label ?author ?authorType ?authorIdentifier ?datePublished ?funding ?funder ?keywords " + + "?version ?abstract ?identifier ?sameAs ?url\n" + + "WHERE\n" + + "{\n" + + " ?software rdf:type obo:ERO_0000071\n" + + " OPTIONAL { ?software rdfs:label ?label }\n" + + " OPTIONAL { ?software vivo:relatedBy ?relatedObject .\n" + + " ?relatedObject vitro:mostSpecificType vivo:Authorship .\n" + + " OPTIONAL { ?relatedObject vivo:relates ?authorObject .\n" + + " OPTIONAL { ?authorObject rdfs:label ?author }\n" + + " OPTIONAL { ?authorObject vitro:mostSpecificType ?authorType }\n" + + " OPTIONAL { ?authorObject vivo:orcidId ?authorIdentifier }\n" + + " }\n" + + " FILTER (?author != ?label)\n" + + " }\n" + + " OPTIONAL { ?software vivo:dateTimeValue ?dateObject .\n" + + " OPTIONAL { ?dateObject vivo:dateTime ?datePublished }\n" + + " }\n" + + " OPTIONAL { ?software vivo:informationResourceSupportedBy ?funderObject .\n" + + " OPTIONAL { ?funderObject vivo:assignedBy ?funder }\n" + + " OPTIONAL { ?funderObject rdfs:label ?funding }\n" + + " }\n" + + " OPTIONAL { ?software vivo:freetextKeywords ?keywords }\n" + + " OPTIONAL { ?software obo:ERO_0000072 ?version }\n" + + " OPTIONAL { ?software bibo:abstract ?abstract }\n" + + " OPTIONAL { ?software vivo:swhid ?identifier }\n" + + " OPTIONAL { ?software owl:sameAs ?sameAsObject .\n" + + " OPTIONAL { ?sameAsObject rdfs:label ?sameAs }\n" + + " }\n" + + " OPTIONAL { ?software obo:ARG_2000028 ?contactInfo .\n" + + " OPTIONAL { ?contactInfo vcard:hasURL ?url }\n" + + " }\n" + + "}\n"; + + private final String FIND_BY_ID_QUERY_TEMPLATE = + "PREFIX rdf: \n" + + "PREFIX rdfs: \n" + + "PREFIX xsd: \n" + + "PREFIX owl: \n" + + "PREFIX swrl: \n" + + "PREFIX swrlb: \n" + + "PREFIX vitro: \n" + + "PREFIX bibo: \n" + + "PREFIX c4o: \n" + + "PREFIX cito: \n" + + "PREFIX dcterms: \n" + + "PREFIX event: \n" + + "PREFIX fabio: \n" + + "PREFIX foaf: \n" + + "PREFIX geo: \n" + + "PREFIX obo: \n" + + "PREFIX vivo: \n" + + "PREFIX vcard: \n" + + "\n" + + "SELECT ?software ?label ?author ?authorType ?authorIdentifier ?datePublished ?funding ?funder ?keywords " + + "?version ?abstract ?identifier ?sameAs ?url\n" + + "WHERE\n" + + "{\n" + + " BIND (<%s> AS ?software)\n" + + " ?software rdf:type obo:ERO_0000071\n" + + " OPTIONAL { ?software rdfs:label ?label }\n" + + " OPTIONAL { ?software vivo:relatedBy ?relatedObject .\n" + + " ?relatedObject vitro:mostSpecificType vivo:Authorship .\n" + + " OPTIONAL { ?relatedObject vivo:relates ?authorObject .\n" + + " OPTIONAL { ?authorObject rdfs:label ?author }\n" + + " OPTIONAL { ?authorObject vitro:mostSpecificType ?authorType }\n" + + " OPTIONAL { ?authorObject vivo:orcidId ?authorIdentifier }\n" + + " }\n" + + " FILTER (?author != ?label)\n" + + " }\n" + + " OPTIONAL { ?software vivo:dateTimeValue ?dateObject .\n" + + " OPTIONAL { ?dateObject vivo:dateTime ?datePublished }\n" + + " }\n" + + " OPTIONAL { ?software vivo:informationResourceSupportedBy ?funderObject .\n" + + " OPTIONAL { ?funderObject vivo:assignedBy ?funder }\n" + + " OPTIONAL { ?funderObject rdfs:label ?funding }\n" + + " }\n" + + " OPTIONAL { ?software vivo:freetextKeywords ?keywords }\n" + + " OPTIONAL { ?software obo:ERO_0000072 ?version }\n" + + " OPTIONAL { ?software bibo:abstract ?abstract }\n" + + " OPTIONAL { ?software vivo:swhid ?identifier }\n" + + " OPTIONAL { ?software owl:sameAs ?sameAsObject .\n" + + " OPTIONAL { ?sameAsObject rdfs:label ?sameAs }\n" + + " }\n" + + " OPTIONAL { ?software obo:ARG_2000028 ?contactInfo .\n" + + " OPTIONAL { ?contactInfo vcard:hasURL ?url }\n" + + " }\n" + + "}\n"; + + private static List> parseBindings(String jsonResponse) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = objectMapper.readTree(jsonResponse); + + JsonNode bindingsNode = rootNode.path("results").path("bindings"); + + List> recordsList = new ArrayList<>(); + + for (JsonNode bindingNode : bindingsNode) { + Map recordMap = new HashMap<>(); + + Iterator> fieldsIterator = bindingNode.fields(); + while (fieldsIterator.hasNext()) { + Map.Entry field = fieldsIterator.next(); + + String fieldName = field.getKey(); + String fieldValue = field.getValue().path("value").asText(); + + recordMap.put(fieldName, fieldValue); + } + + recordsList.add(recordMap); + } + + return recordsList; + } + + private String serializeToJSON(Object serializationObject) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.enable(SerializationFeature.INDENT_OUTPUT); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + return objectMapper.writeValueAsString(serializationObject); + } + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) + throws IOException, ServletException { + + String pathInfo = req.getPathInfo(); + String queryString; + + if (pathInfo != null && pathInfo.length() > 1) { + String softwareId = pathInfo.substring(1); + String defaultNamespace = + Objects.requireNonNull(ConfigurationProperties.getInstance().getProperty("Vitro.defaultNamespace")); + String softwareUri = defaultNamespace + softwareId; + queryString = String.format(FIND_BY_ID_QUERY_TEMPLATE, softwareUri); + } else { + queryString = FIND_ALL_QUERY; + } + + RDFService rdfService = ModelAccess.on(getServletContext()).getRDFService(); + + try { + String format = "application/sparql-results+json"; + SparqlQueryApiExecutor core = SparqlQueryApiExecutor.instance( + rdfService, queryString, format); + + String acceptHeader = req.getHeader("Accept"); + if (Objects.nonNull(acceptHeader) && !acceptHeader.isEmpty()) { + resp.setContentType(acceptHeader); + } else { + resp.setContentType("application/json"); + } + + if (Objects.nonNull(acceptHeader) && acceptHeader.equals("application/json")) { + List response = handleDTOConversion(core); + if (response.size() == 1) { + resp.getWriter().println(serializeToJSON(response.get(0))); + } else { + resp.getWriter().println(serializeToJSON(response)); + } + + } else { + core.executeAndFormat(resp.getOutputStream()); + } + } catch (InvalidQueryTypeException e) { + do400BadRequest("Query type is not SELECT, ASK, CONSTRUCT, " + + "or DESCRIBE: '" + queryString + "'", resp); + } catch (QueryParseException e) { + do400BadRequest("Failed to parse query: '" + queryString + "''", e, + resp); + } catch (NotAcceptableException | AcceptHeaderParsingException e) { + do500InternalServerError("Problem with the page fields: the " + + "selected fields do not include an " + + "acceptable content type.", e, resp); + } catch (RDFServiceException e) { + do500InternalServerError("Problem executing the query.", e, resp); + } + } + + private List handleDTOConversion(SparqlQueryApiExecutor core) + throws RDFServiceException, IOException { + String sparqlQueryResponse = getSparqlQueryResponse(core); + + List> bindings = parseBindings(sparqlQueryResponse); + + List softwareResponse = new ArrayList<>(); + for (Map binding : bindings) { + Optional existingRecord = + softwareResponse.stream() + .filter(software -> software.internalIdentifier.equals(binding.get("software"))).findFirst(); + if (existingRecord.isPresent()) { + addAuthorToSoftware(binding, existingRecord.get()); + addFundingToSoftware(binding, existingRecord.get()); + continue; + } + + SoftwareResponseDTO software = new SoftwareResponseDTO(); + software.internalIdentifier = binding.getOrDefault("software", null); + software.label = binding.getOrDefault("label", null); + software.description = binding.getOrDefault("abstract", null); + software.version = binding.getOrDefault("version", null); + software.sameAs = binding.getOrDefault("sameAs", null); + software.identifier = binding.getOrDefault("identifier", null); + software.url = binding.getOrDefault("url", null); + + String dateTimeString = binding.getOrDefault("datePublished", null); + if (Objects.nonNull(dateTimeString)) { + software.datePublished = dateTimeString.split("T")[0]; + } + + addAuthorToSoftware(binding, software); + addFundingToSoftware(binding, software); + + softwareResponse.add(software); + } + + return softwareResponse; + } + + private void addAuthorToSoftware(Map binding, SoftwareResponseDTO software) { + if (!Objects.nonNull(binding.get("author"))) { + return; + } + + if (software.authors.stream().anyMatch(author -> author.name.equals(binding.get("author")))) { + return; + } + + AuthorResponseDTO author = new AuthorResponseDTO(); + author.name = binding.getOrDefault("author", null); + author.type = binding.getOrDefault("authorType", null); + author.identifier = binding.getOrDefault("authorIdentifier", null); + software.authors.add(author); + } + + private void addFundingToSoftware(Map binding, SoftwareResponseDTO software) { + if (!Objects.nonNull(binding.get("funding")) && !Objects.nonNull(binding.get("funder"))) { + return; + } + + if (software.funding.stream().anyMatch(funding -> funding.funding.equals(binding.get("funding")))) { + return; + } + + if (software.funding.stream().anyMatch(funding -> funding.funder.equals(binding.get("funder")))) { + return; + } + + FundingResponseDTO funding = new FundingResponseDTO(); + funding.funding = binding.getOrDefault("funding", null); + funding.funder = binding.getOrDefault("funder", null); + software.funding.add(funding); + } + + private String getSparqlQueryResponse(SparqlQueryApiExecutor core) throws IOException, RDFServiceException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + core.executeAndFormat(outputStream); + + String sparqlResponse = outputStream.toString("UTF-8"); + + outputStream.close(); + + return sparqlResponse; + } + + private void do400BadRequest(String message, HttpServletResponse resp) + throws IOException { + resp.setStatus(400); + resp.getWriter().println(message); + } + + private void do400BadRequest(String message, Exception e, + HttpServletResponse resp) throws IOException { + resp.setStatus(400); + PrintWriter w = resp.getWriter(); + w.println(message); + e.printStackTrace(w); + } + + private void do500InternalServerError(String message, Exception e, + HttpServletResponse resp) throws IOException { + resp.setStatus(500); + PrintWriter w = resp.getWriter(); + w.println(message); + e.printStackTrace(w); + } +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java new file mode 100644 index 0000000000..96272fd777 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java @@ -0,0 +1,27 @@ +package edu.cornell.mannlib.vitro.webapp.controller.software; + +import java.util.ArrayList; +import java.util.List; + +public class SoftwareResponseDTO { + + public String internalIdentifier; + + public String label; + + public String datePublished; + + public List authors = new ArrayList<>(); + + public List funding = new ArrayList<>(); + + public String version; + + public String description; + + public String identifier; + + public String sameAs; + + public String url; +} diff --git a/home/src/main/resources/rdf/tbox/filegraph/vivo.owl b/home/src/main/resources/rdf/tbox/filegraph/vivo.owl index 1b9d2de125..83878935ec 100644 --- a/home/src/main/resources/rdf/tbox/filegraph/vivo.owl +++ b/home/src/main/resources/rdf/tbox/filegraph/vivo.owl @@ -6397,15 +6397,24 @@ To enable other Gender/Sex codes to be used, this dataproperty has range URI. Th + + + + SWHID + Home page for SoftWare Heritage persistent IDentifiers : https://docs.softwareheritage.org/devel/swh-model/persistent-identifiers.html + The SoftWare Heritage persistent IDentifier, or SWHID for short, is a PID for software artifacts. It guarantees to remain stable (persistent) over time. + + + + Software - + A general term primarily used for digitally stored data such as computer programs and other kinds of information read and written by computers. - IAO is a planned specification, in SWO is an Information artifact. In eagle-i, we have a need to collect material instances and is it thus currently classified as a material entity. - Microsoft Word is commonly used word processing software. + Aligned with SWO which also defines software and other types of services as subtypes of Information content entity. Microsoft Word is commonly used word processing software. PERSON: Melissa Haendel http://en.wikipedia.org/wiki/Computer_software software @@ -6413,7 +6422,6 @@ To enable other Gender/Sex codes to be used, this dataproperty has range URI. Th - diff --git a/home/src/main/resources/rdf/tbox/firsttime/vitroAnnotations.n3 b/home/src/main/resources/rdf/tbox/firsttime/vitroAnnotations.n3 index 2fcfd609cb..f33e4c0517 100644 --- a/home/src/main/resources/rdf/tbox/firsttime/vitroAnnotations.n3 +++ b/home/src/main/resources/rdf/tbox/firsttime/vitroAnnotations.n3 @@ -1748,6 +1748,18 @@ bibo:doi vitro:selectFromExistingAnnot "true"^^xsd:boolean . +vivo:swhid + vitro:displayLimitAnnot + "1"^^xsd:int ; + vitro:displayRankAnnot + "5"^^xsd:int ; + vitro:hiddenFromDisplayBelowRoleLevelAnnot + ; + vitro:hiddenFromPublishBelowRoleLevelAnnot + ; + vitro:inPropertyGroupAnnot + . + obo:ERO_0000070 ## inverse of ERO_0000031 # vitro:displayLimitAnnot # "2"^^xsd:int ; From 428c4cee14f7fe4bccb77850f50f345e170080eb Mon Sep 17 00:00:00 2001 From: Ivan Mrsulja Date: Thu, 26 Sep 2024 15:21:45 +0200 Subject: [PATCH 02/12] Small refactor. --- .../vitro/webapp/controller/software/SoftwareController.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java index 6e29e0c672..b9634217f7 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java @@ -11,7 +11,6 @@ import java.util.Objects; import java.util.Optional; -import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -180,7 +179,7 @@ private String serializeToJSON(Object serializationObject) throws IOException { @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) - throws IOException, ServletException { + throws IOException { String pathInfo = req.getPathInfo(); String queryString; From 3304969d499831f5dbd7ba9b5cffb80baf186a82 Mon Sep 17 00:00:00 2001 From: Ivan Mrsulja Date: Thu, 26 Sep 2024 15:40:48 +0200 Subject: [PATCH 03/12] Added support for keywords. --- .../vitro/webapp/controller/software/SoftwareController.java | 5 +++-- .../webapp/controller/software/SoftwareResponseDTO.java | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java index b9634217f7..92c8ac6fba 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java @@ -75,7 +75,7 @@ public class SoftwareController extends HttpServlet { " OPTIONAL { ?funderObject vivo:assignedBy ?funder }\n" + " OPTIONAL { ?funderObject rdfs:label ?funding }\n" + " }\n" + - " OPTIONAL { ?software vivo:freetextKeywords ?keywords }\n" + + " OPTIONAL { ?software vivo:freetextKeyword ?keywords }\n" + " OPTIONAL { ?software obo:ERO_0000072 ?version }\n" + " OPTIONAL { ?software bibo:abstract ?abstract }\n" + " OPTIONAL { ?software vivo:swhid ?identifier }\n" + @@ -130,7 +130,7 @@ public class SoftwareController extends HttpServlet { " OPTIONAL { ?funderObject vivo:assignedBy ?funder }\n" + " OPTIONAL { ?funderObject rdfs:label ?funding }\n" + " }\n" + - " OPTIONAL { ?software vivo:freetextKeywords ?keywords }\n" + + " OPTIONAL { ?software vivo:freetextKeyword ?keywords }\n" + " OPTIONAL { ?software obo:ERO_0000072 ?version }\n" + " OPTIONAL { ?software bibo:abstract ?abstract }\n" + " OPTIONAL { ?software vivo:swhid ?identifier }\n" + @@ -259,6 +259,7 @@ private List handleDTOConversion(SparqlQueryApiExecutor cor software.sameAs = binding.getOrDefault("sameAs", null); software.identifier = binding.getOrDefault("identifier", null); software.url = binding.getOrDefault("url", null); + software.keywords = binding.getOrDefault("keywords", null); String dateTimeString = binding.getOrDefault("datePublished", null); if (Objects.nonNull(dateTimeString)) { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java index 96272fd777..290205a11e 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java @@ -24,4 +24,6 @@ public class SoftwareResponseDTO { public String sameAs; public String url; + + public String keywords; } From 9204f511a4428c44836645eef83b7f1f7d0393c6 Mon Sep 17 00:00:00 2001 From: Ivan Mrsulja Date: Thu, 26 Sep 2024 16:51:48 +0200 Subject: [PATCH 04/12] Added support for isPartOf and hasPart. --- .../webapp/controller/software/SoftwareController.java | 6 ++++++ .../webapp/controller/software/SoftwareResponseDTO.java | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java index 92c8ac6fba..75f36d654a 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java @@ -78,6 +78,8 @@ public class SoftwareController extends HttpServlet { " OPTIONAL { ?software vivo:freetextKeyword ?keywords }\n" + " OPTIONAL { ?software obo:ERO_0000072 ?version }\n" + " OPTIONAL { ?software bibo:abstract ?abstract }\n" + + " OPTIONAL { ?software obo:BFO_0000050 ?isPartOf }\n" + + " OPTIONAL { ?software obo:BFO_0000050 ?hasPart }\n" + " OPTIONAL { ?software vivo:swhid ?identifier }\n" + " OPTIONAL { ?software owl:sameAs ?sameAsObject .\n" + " OPTIONAL { ?sameAsObject rdfs:label ?sameAs }\n" + @@ -133,6 +135,8 @@ public class SoftwareController extends HttpServlet { " OPTIONAL { ?software vivo:freetextKeyword ?keywords }\n" + " OPTIONAL { ?software obo:ERO_0000072 ?version }\n" + " OPTIONAL { ?software bibo:abstract ?abstract }\n" + + " OPTIONAL { ?software obo:BFO_0000050 ?isPartOf }\n" + + " OPTIONAL { ?software obo:BFO_0000050 ?hasPart }\n" + " OPTIONAL { ?software vivo:swhid ?identifier }\n" + " OPTIONAL { ?software owl:sameAs ?sameAsObject .\n" + " OPTIONAL { ?sameAsObject rdfs:label ?sameAs }\n" + @@ -260,6 +264,8 @@ private List handleDTOConversion(SparqlQueryApiExecutor cor software.identifier = binding.getOrDefault("identifier", null); software.url = binding.getOrDefault("url", null); software.keywords = binding.getOrDefault("keywords", null); + software.isPartOf = binding.getOrDefault("isPartOf", null); + software.hasPart = binding.getOrDefault("hasPart", null); String dateTimeString = binding.getOrDefault("datePublished", null); if (Objects.nonNull(dateTimeString)) { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java index 290205a11e..677cd2ed77 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java @@ -26,4 +26,8 @@ public class SoftwareResponseDTO { public String url; public String keywords; + + public String isPartOf; + + public String hasPart; } From 0346d49e8facafeed4968e86c2e34a9a7d64c1e4 Mon Sep 17 00:00:00 2001 From: Ivan Mrsulja Date: Fri, 27 Sep 2024 16:06:42 +0200 Subject: [PATCH 05/12] Added support for all software CRUD operations. --- ...{AuthorResponseDTO.java => AuthorDTO.java} | 2 +- ...undingResponseDTO.java => FundingDTO.java} | 2 +- .../software/SoftwareController.java | 456 ++++++++++++++++-- .../software/SoftwareRequestDTO.java | 25 + .../software/SoftwareResponseDTO.java | 4 +- 5 files changed, 439 insertions(+), 50 deletions(-) rename api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/{AuthorResponseDTO.java => AuthorDTO.java} (81%) rename api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/{FundingResponseDTO.java => FundingDTO.java} (77%) create mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareRequestDTO.java diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/AuthorResponseDTO.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/AuthorDTO.java similarity index 81% rename from api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/AuthorResponseDTO.java rename to api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/AuthorDTO.java index 7557e0cbf2..ca845def4e 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/AuthorResponseDTO.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/AuthorDTO.java @@ -1,6 +1,6 @@ package edu.cornell.mannlib.vitro.webapp.controller.software; -public class AuthorResponseDTO { +public class AuthorDTO { public String name; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FundingResponseDTO.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FundingDTO.java similarity index 77% rename from api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FundingResponseDTO.java rename to api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FundingDTO.java index 412fc1782a..aa111ffc98 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FundingResponseDTO.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FundingDTO.java @@ -1,6 +1,6 @@ package edu.cornell.mannlib.vitro.webapp.controller.software; -public class FundingResponseDTO { +public class FundingDTO { public String funding; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java index 75f36d654a..cce0f7ff65 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java @@ -1,5 +1,7 @@ package edu.cornell.mannlib.vitro.webapp.controller.software; +import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintWriter; @@ -10,9 +12,10 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.UUID; +import java.util.function.BiConsumer; import javax.servlet.annotation.WebServlet; -import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -20,18 +23,43 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils; +import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission; +import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.AuthorizationRequest; import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.controller.api.VitroApiServlet; import edu.cornell.mannlib.vitro.webapp.controller.api.sparqlquery.InvalidQueryTypeException; import edu.cornell.mannlib.vitro.webapp.controller.api.sparqlquery.SparqlQueryApiExecutor; +import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceDataset; import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; +import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexer; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; -import edu.cornell.mannlib.vitro.webapp.utils.http.AcceptHeaderParsingException; -import edu.cornell.mannlib.vitro.webapp.utils.http.NotAcceptableException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jena.ontology.OntModel; +import org.apache.jena.query.Dataset; +import org.apache.jena.query.QueryExecution; +import org.apache.jena.query.QueryExecutionFactory; import org.apache.jena.query.QueryParseException; +import org.apache.jena.query.QuerySolution; +import org.apache.jena.query.ReadWrite; +import org.apache.jena.query.ResultSet; +import org.apache.jena.update.GraphStore; +import org.apache.jena.update.GraphStoreFactory; +import org.apache.jena.update.UpdateAction; +import org.apache.jena.update.UpdateFactory; +import org.apache.jena.update.UpdateRequest; @WebServlet(name = "softwareController", urlPatterns = {"/software", "/software/*"}, loadOnStartup = 5) -public class SoftwareController extends HttpServlet { +public class SoftwareController extends VitroApiServlet { + + private static final Log log = LogFactory.getLog(SoftwareController.class); + + private static final String CREATED_GRAPH_BASE_URI = "http://vitro.mannlib.cornell.edu/a/graph/"; + + private static final AuthorizationRequest REQUIRED_ACTIONS = SimplePermission.USE_SPARQL_UPDATE_API.ACTION; private final String FIND_ALL_QUERY = "PREFIX rdf: \n" + @@ -146,6 +174,66 @@ public class SoftwareController extends HttpServlet { " }\n" + "}\n"; + private final String DELETE_SOFTWARE_QUERY_TEMPLATE = + "PREFIX rdf: \n" + + "PREFIX rdfs: \n" + + "PREFIX xsd: \n" + + "PREFIX owl: \n" + + "PREFIX swrl: \n" + + "PREFIX swrlb: \n" + + "PREFIX vitro: \n" + + "PREFIX bibo: \n" + + "PREFIX c4o: \n" + + "PREFIX cito: \n" + + "PREFIX dcterms: \n" + + "PREFIX event: \n" + + "PREFIX fabio: \n" + + "PREFIX foaf: \n" + + "PREFIX geo: \n" + + "PREFIX obo: \n" + + "PREFIX vivo: \n" + + "PREFIX vcard: \n" + + "\n" + + "DELETE\n" + + "{\n" + + " ?software vivo:dateTimeValue ?dateObject .\n" + + " ?dateObject ?p1 ?o1 .\n" + + " ?software vivo:relatedBy ?relatedObject .\n" + + " ?relatedObject rdf:type vivo:Authorship .\n" + + " ?relatedObject ?p2 ?o2 .\n" + + " ?software vivo:informationResourceSupportedBy ?funderObject .\n" + + " ?funderObject ?p3 ?o3 .\n" + + " ?software obo:ARG_2000028 ?contactInfo .\n" + + " ?contactInfo ?p4 ?o4 .\n" + + " ?software rdfs:label ?label .\n" + + " ?software vivo:freetextKeyword ?keywords .\n" + + " ?software obo:ERO_0000072 ?version .\n" + + " ?software bibo:abstract ?abstract .\n" + + " ?software vivo:swhid ?identifier .\n" + + " ?software owl:sameAs ?sameAsObject .\n" + + " ?sameAsObject rdfs:label ?sameAs .\n" + + " ?software obo:ARG_2000028 ?contactInfo .\n" + + " ?contactInfo vcard:hasURL ?url .\n" + + "}\n" + + "WHERE\n" + + "{\n" + + " BIND (<%s> AS ?software)\n" + + " OPTIONAL { ?software vivo:dateTimeValue ?dateObject . OPTIONAL { ?dateObject ?p1 ?o1 } }\n" + + " OPTIONAL { ?software vivo:relatedBy ?relatedObject . ?relatedObject rdf:type vivo:Authorship .\n" + + " OPTIONAL { ?relatedObject ?p2 ?o2 } }\n" + + " OPTIONAL { ?software vivo:informationResourceSupportedBy ?funderObject .\n" + + " OPTIONAL { ?funderObject ?p3 ?o3 } }\n" + + " OPTIONAL { ?software obo:ARG_2000028 ?contactInfo .\n" + + " OPTIONAL { ?contactInfo ?p4 ?o4 } OPTIONAL { ?contactInfo vcard:hasURL ?url } }\n" + + " OPTIONAL { ?software rdfs:label ?label }\n" + + " OPTIONAL { ?software vivo:freetextKeyword ?keywords }\n" + + " OPTIONAL { ?software obo:ERO_0000072 ?version }\n" + + " OPTIONAL { ?software bibo:abstract ?abstract }\n" + + " OPTIONAL { ?software vivo:swhid ?identifier }\n" + + " OPTIONAL { ?software owl:sameAs ?sameAsObject . OPTIONAL { ?sameAsObject rdfs:label ?sameAs } }\n" + + "}\n"; + + private static List> parseBindings(String jsonResponse) throws IOException { ObjectMapper objectMapper = new ObjectMapper(); JsonNode rootNode = objectMapper.readTree(jsonResponse); @@ -182,62 +270,330 @@ private String serializeToJSON(Object serializationObject) throws IOException { } @Override - public void doGet(HttpServletRequest req, HttpServletResponse resp) - throws IOException { - + public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { String pathInfo = req.getPathInfo(); - String queryString; - - if (pathInfo != null && pathInfo.length() > 1) { - String softwareId = pathInfo.substring(1); - String defaultNamespace = - Objects.requireNonNull(ConfigurationProperties.getInstance().getProperty("Vitro.defaultNamespace")); - String softwareUri = defaultNamespace + softwareId; - queryString = String.format(FIND_BY_ID_QUERY_TEMPLATE, softwareUri); - } else { - queryString = FIND_ALL_QUERY; - } + String queryString = (pathInfo != null && pathInfo.length() > 1) + ? String.format(FIND_BY_ID_QUERY_TEMPLATE, buildSoftwareUri(pathInfo.substring(1))) + : FIND_ALL_QUERY; RDFService rdfService = ModelAccess.on(getServletContext()).getRDFService(); try { - String format = "application/sparql-results+json"; - SparqlQueryApiExecutor core = SparqlQueryApiExecutor.instance( - rdfService, queryString, format); + SparqlQueryApiExecutor core = + SparqlQueryApiExecutor.instance(rdfService, queryString, "application/sparql-results+json"); + handleResponseContentType(req, resp); - String acceptHeader = req.getHeader("Accept"); - if (Objects.nonNull(acceptHeader) && !acceptHeader.isEmpty()) { - resp.setContentType(acceptHeader); + if (isJsonRequest(req)) { + List response = handleDTOConversion(core); + resp.getWriter().println(serializeToJSON(response.size() == 1 ? response.get(0) : response)); } else { - resp.setContentType("application/json"); + core.executeAndFormat(resp.getOutputStream()); } + } catch (Exception e) { + handleException(e, queryString, resp); + } + } - if (Objects.nonNull(acceptHeader) && acceptHeader.equals("application/json")) { - List response = handleDTOConversion(core); - if (response.size() == 1) { - resp.getWriter().println(serializeToJSON(response.get(0))); - } else { - resp.getWriter().println(serializeToJSON(response)); + @Override + public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { +// authorise(req); + + executeWithTransaction(req, resp, (graphStore, softwareUri) -> { + + SoftwareRequestDTO softwareDTO = null; + try { + softwareDTO = parseRequestBody(req, SoftwareRequestDTO.class); + } catch (IOException e) { + try { + do400BadRequest("Error while parsing request body.", resp); + } catch (IOException ex) { + log.error("Error while handling exception.", ex); } + } - } else { - core.executeAndFormat(resp.getOutputStream()); + if (Objects.isNull(softwareDTO)) { + return; + } + + StringBuilder insertSoftwareQuery = buildInsertSoftwareQuery(softwareDTO, softwareUri, ModelAccess.on( + new VitroRequest(req)).getOntModel()); + executeUpdate(graphStore, insertSoftwareQuery.toString()); + + softwareDTO.internalIdentifier = softwareUri; + try { + resp.getWriter().println(serializeToJSON(softwareDTO)); + } catch (IOException e) { + try { + do400BadRequest("Error while writing response body.", resp); + } catch (IOException ex) { + log.error("Error while handling exception.", ex); + } } - } catch (InvalidQueryTypeException e) { - do400BadRequest("Query type is not SELECT, ASK, CONSTRUCT, " - + "or DESCRIBE: '" + queryString + "'", resp); - } catch (QueryParseException e) { - do400BadRequest("Failed to parse query: '" + queryString + "''", e, - resp); - } catch (NotAcceptableException | AcceptHeaderParsingException e) { - do500InternalServerError("Problem with the page fields: the " - + "selected fields do not include an " - + "acceptable content type.", e, resp); - } catch (RDFServiceException e) { + resp.setStatus(HttpServletResponse.SC_CREATED); + }); + } + + @Override + public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException { + // authorise(req); + + executeWithTransaction(req, resp, (graphStore, softwareUri) -> { + String deleteQuery = String.format(DELETE_SOFTWARE_QUERY_TEMPLATE, softwareUri); + executeUpdate(graphStore, deleteQuery); + resp.setStatus(HttpServletResponse.SC_NO_CONTENT); + }); + } + + @Override + public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException { + // authorise(req); + + doDelete(req, resp); + + executeWithTransaction(req, resp, (graphStore, softwareUri) -> { + SoftwareRequestDTO softwareDTO; + try { + softwareDTO = parseRequestBody(req, SoftwareRequestDTO.class); + } catch (IOException e) { + throw new RuntimeException(e); + } + StringBuilder insertSoftwareQuery = buildInsertSoftwareQuery(softwareDTO, softwareUri, ModelAccess.on( + new VitroRequest(req)).getOntModel()); + executeUpdate(graphStore, insertSoftwareQuery.toString()); + + softwareDTO.internalIdentifier = softwareUri; + resp.setStatus(HttpServletResponse.SC_NO_CONTENT); + }); + } + + private String buildSoftwareUri(String softwareId) { + String defaultNamespace = + Objects.requireNonNull(ConfigurationProperties.getInstance().getProperty("Vitro.defaultNamespace")); + return defaultNamespace + softwareId; + } + + private void handleResponseContentType(HttpServletRequest req, HttpServletResponse resp) { + String acceptHeader = req.getHeader("Accept"); + if (acceptHeader != null && !acceptHeader.isEmpty()) { + resp.setContentType(acceptHeader); + } else { + resp.setContentType("application/json"); + } + } + + private boolean isJsonRequest(HttpServletRequest req) { + String acceptHeader = req.getHeader("Accept"); + return acceptHeader != null && acceptHeader.equals("application/json"); + } + + private T parseRequestBody(HttpServletRequest req, Class clazz) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.readValue(req.getInputStream(), clazz); + } + + private StringBuilder buildInsertSoftwareQuery(SoftwareRequestDTO softwareDTO, String softwareUri, + OntModel ontModel) { + StringBuilder insertSoftwareQuery = new StringBuilder(); + addPrefixClauses(insertSoftwareQuery); + insertSoftwareQuery.append("\n") + .append("INSERT DATA\n") + .append("{\n") + .append("GRAPH ").append("<" + CREATED_GRAPH_BASE_URI + ">") + .append("\n") + .append("{\n"); + + String defaultNamespace = ConfigurationProperties.getInstance().getProperty("Vitro.defaultNamespace"); + addSoftwareRelatedFields(insertSoftwareQuery, softwareDTO, softwareUri); + addPublicationDate(insertSoftwareQuery, softwareDTO.datePublished, defaultNamespace, softwareUri); + addAuthors(insertSoftwareQuery, softwareDTO.authors, defaultNamespace, softwareUri, ontModel); + addFunding(insertSoftwareQuery, softwareDTO.funding, defaultNamespace, softwareUri); + + insertSoftwareQuery.append(" }\n").append("}\n"); + + return insertSoftwareQuery; + } + + private void executeUpdate(GraphStore graphStore, String query) { + UpdateRequest updateRequest = UpdateFactory.create(query); + UpdateAction.execute(updateRequest, graphStore); + } + + private void handleException(Exception e, String queryString, HttpServletResponse resp) throws IOException { + if (e instanceof InvalidQueryTypeException) { + do400BadRequest("Invalid query type: '" + queryString + "'", resp); + } else if (e instanceof QueryParseException) { + do400BadRequest("Failed to parse query: '" + queryString + "'", resp); + } else if (e instanceof RDFServiceException) { do500InternalServerError("Problem executing the query.", e, resp); } } + private void executeWithTransaction(HttpServletRequest req, HttpServletResponse resp, + BiConsumer action) throws IOException { + VitroRequest vreq = new VitroRequest(req); + SearchIndexer indexer = ApplicationUtils.instance().getSearchIndexer(); + Dataset ds = new RDFServiceDataset(vreq.getUnfilteredRDFService()); + GraphStore graphStore = GraphStoreFactory.create(ds); + + String softwareUri; + if (req.getMethod().equalsIgnoreCase("POST")) { + softwareUri = buildSoftwareUri(UUID.randomUUID().toString()); + } else { + String pathInfo = req.getPathInfo(); + if (Objects.isNull(pathInfo) || pathInfo.isEmpty()) { + do400BadRequest("You have to provide a record identifier.", resp); + return; + } + + softwareUri = buildSoftwareUri(pathInfo.substring(1)); + } + + try { + pauseIndexer(indexer); + beginTransaction(ds); + + action.accept(graphStore, softwareUri); + + } finally { + commitTransaction(ds); + unpauseIndexer(indexer); + } + } + + private void pauseIndexer(SearchIndexer indexer) { + if (indexer != null) { + indexer.pause(); + } + } + + private void unpauseIndexer(SearchIndexer indexer) { + if (indexer != null) { + indexer.unpause(); + } + } + + private void beginTransaction(Dataset ds) { + if (ds.supportsTransactions()) { + ds.begin(ReadWrite.WRITE); + } + } + + private void commitTransaction(Dataset ds) { + if (ds.supportsTransactions()) { + ds.commit(); + ds.end(); + } + } + + private void addSoftwareRelatedFields(StringBuilder query, SoftwareRequestDTO softwareDTO, String softwareUri) { + query + .append("<").append(softwareUri).append("> rdf:type obo:ERO_0000071 ;\n") + .append("rdfs:label \"").append(softwareDTO.label).append("\" ;\n") + .append("vivo:freetextKeyword \"").append(softwareDTO.keywords).append("\" ;\n") + .append("obo:ERO_0000072 \"").append(softwareDTO.version).append("\" ;\n") + .append("bibo:abstract \"").append(softwareDTO.description).append("\" ;\n") + .append("vivo:swhid \"").append(softwareDTO.identifier).append("\" .\n"); + } + + private void addAuthors(StringBuilder query, List authors, String defaultNamespace, String documentUri, + OntModel ontModel) { + for (AuthorDTO author : authors) { + String authorUri = null; + boolean personFound = false; + + if (author.identifier != null && !author.identifier.isEmpty() && author.type.endsWith("Person")) { + String checkAuthorQuery = String.format( + "PREFIX rdf: \n" + + "PREFIX vivo: \n" + + "PREFIX foaf: \n" + + "SELECT ?author WHERE { ?author rdf:type foaf:Person . ?author vivo:orcidId \"%s\" . }", + author.identifier + ); + + try (QueryExecution qe = QueryExecutionFactory.create(checkAuthorQuery, ontModel)) { + ResultSet results = qe.execSelect(); + + if (results.hasNext()) { + QuerySolution solution = results.nextSolution(); + authorUri = solution.getResource("author").getURI(); + personFound = true; + } + } catch (Exception e) { + log.error("Error when looking for existing person.", e); + } + } + + if (!personFound) { + authorUri = defaultNamespace + UUID.randomUUID(); + + query.append("<").append(authorUri).append("> rdf:type <").append(author.type).append("> ;\n") + .append("rdfs:label \"").append(author.name).append("\" ;\n"); + + if (author.identifier != null && !author.identifier.isEmpty() && author.type.endsWith("Person")) { + query.append("vivo:orcidId \"").append(author.identifier).append("\" ;\n"); + } + + query.append(".\n"); + } + + String relatedObjectUri = defaultNamespace + UUID.randomUUID(); + query.append("<").append(documentUri).append("> vivo:relatedBy <").append(relatedObjectUri).append("> .\n") + .append("<").append(relatedObjectUri).append("> rdf:type vivo:Authorship ;\n") + .append("vivo:relates <").append(authorUri).append("> .\n"); + } + } + + + private void addPublicationDate(StringBuilder query, String dateString, String defaultNamespace, + String documentUri) { + if (dateString != null && !dateString.isEmpty()) { + String dateObjectUri = defaultNamespace + UUID.randomUUID(); + + query.append("<").append(documentUri).append("> vivo:dateTimeValue <") + .append(dateObjectUri).append("> .\n") + .append("<").append(dateObjectUri).append("> rdf:type vivo:DateTimeValue ;\n") + .append("vivo:dateTime \"").append(dateString).append("\"^^xsd:date .\n"); + } + } + + private void addFunding(StringBuilder query, List fundings, String defaultNamespace, + String documentUri) { + for (FundingDTO funding : fundings) { + String funderObjectUri = defaultNamespace + UUID.randomUUID(); + + query.append("<").append(documentUri).append("> vivo:informationResourceSupportedBy <") + .append(funderObjectUri).append("> .\n") + .append("<").append(funderObjectUri).append("> rdf:type vivo:Funding ;\n"); + + if (funding.funder != null && !funding.funder.isEmpty()) { + query.append("vivo:assignedBy <").append(funding.funder).append("> ;\n"); + } + + query.append("rdfs:label \"").append(funding.funding).append("\" .\n"); + } + } + + private void addPrefixClauses(StringBuilder sb) { + sb.append("PREFIX rdf: \n") + .append("PREFIX rdfs: \n") + .append("PREFIX xsd: \n") + .append("PREFIX owl: \n") + .append("PREFIX swrl: \n") + .append("PREFIX swrlb: \n") + .append("PREFIX vitro: \n") + .append("PREFIX bibo: \n") + .append("PREFIX c4o: \n") + .append("PREFIX cito: \n") + .append("PREFIX dcterms: \n") + .append("PREFIX event: \n") + .append("PREFIX fabio: \n") + .append("PREFIX foaf: \n") + .append("PREFIX geo: \n") + .append("PREFIX obo: \n") + .append("PREFIX vivo: \n") + .append("PREFIX vcard: \n"); + } + private List handleDTOConversion(SparqlQueryApiExecutor core) throws RDFServiceException, IOException { String sparqlQueryResponse = getSparqlQueryResponse(core); @@ -290,7 +646,7 @@ private void addAuthorToSoftware(Map binding, SoftwareResponseDT return; } - AuthorResponseDTO author = new AuthorResponseDTO(); + AuthorDTO author = new AuthorDTO(); author.name = binding.getOrDefault("author", null); author.type = binding.getOrDefault("authorType", null); author.identifier = binding.getOrDefault("authorIdentifier", null); @@ -310,7 +666,7 @@ private void addFundingToSoftware(Map binding, SoftwareResponseD return; } - FundingResponseDTO funding = new FundingResponseDTO(); + FundingDTO funding = new FundingDTO(); funding.funding = binding.getOrDefault("funding", null); funding.funder = binding.getOrDefault("funder", null); software.funding.add(funding); @@ -348,4 +704,12 @@ private void do500InternalServerError(String message, Exception e, w.println(message); e.printStackTrace(w); } + + private void authorise(HttpServletRequest req) { + try { + confirmAuthorization(req, REQUIRED_ACTIONS); + } catch (AuthException e) { + sendShortResponse(SC_FORBIDDEN, e.getMessage(), resp); + } + } } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareRequestDTO.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareRequestDTO.java new file mode 100644 index 0000000000..1619d0cb28 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareRequestDTO.java @@ -0,0 +1,25 @@ +package edu.cornell.mannlib.vitro.webapp.controller.software; + +import java.util.ArrayList; +import java.util.List; + +public class SoftwareRequestDTO { + + public String internalIdentifier; + + public String label; + + public String datePublished; + + public List authors = new ArrayList<>(); + + public List funding = new ArrayList<>(); + + public String version; + + public String description; + + public String identifier; + + public String keywords; +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java index 677cd2ed77..82894cee53 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java @@ -11,9 +11,9 @@ public class SoftwareResponseDTO { public String datePublished; - public List authors = new ArrayList<>(); + public List authors = new ArrayList<>(); - public List funding = new ArrayList<>(); + public List funding = new ArrayList<>(); public String version; From 6a914004dd4a3aa42e00c4a2d9192a63c69c382f Mon Sep 17 00:00:00 2001 From: Ivan Mrsulja Date: Mon, 30 Sep 2024 09:10:26 +0200 Subject: [PATCH 06/12] Fixed small bug. --- .../vitro/webapp/controller/software/SoftwareController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java index cce0f7ff65..45dfda12c0 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java @@ -705,7 +705,7 @@ private void do500InternalServerError(String message, Exception e, e.printStackTrace(w); } - private void authorise(HttpServletRequest req) { + private void authorise(HttpServletRequest req, HttpServletResponse resp) throws IOException { try { confirmAuthorization(req, REQUIRED_ACTIONS); } catch (AuthException e) { From c7be0cadba8b791de919e1913a907b495faa43a7 Mon Sep 17 00:00:00 2001 From: Ivan Mrsulja Date: Mon, 30 Sep 2024 16:58:45 +0200 Subject: [PATCH 07/12] Aligned CRUD to codemeta standards. Deletion now goes through generic delete endpoint. --- .../webapp/controller/software/FunderDTO.java | 8 + .../controller/software/FundingDTO.java | 8 - .../software/SoftwareController.java | 221 +++++++++++++----- .../software/SoftwareRequestDTO.java | 11 +- .../software/SoftwareResponseDTO.java | 8 +- 5 files changed, 189 insertions(+), 67 deletions(-) create mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FunderDTO.java delete mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FundingDTO.java diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FunderDTO.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FunderDTO.java new file mode 100644 index 0000000000..7a406c38f2 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FunderDTO.java @@ -0,0 +1,8 @@ +package edu.cornell.mannlib.vitro.webapp.controller.software; + +public class FunderDTO { + + public String name; + + public String type; +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FundingDTO.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FundingDTO.java deleted file mode 100644 index aa111ffc98..0000000000 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FundingDTO.java +++ /dev/null @@ -1,8 +0,0 @@ -package edu.cornell.mannlib.vitro.webapp.controller.software; - -public class FundingDTO { - - public String funding; - - public String funder; -} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java index 45dfda12c0..ed108e374a 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java @@ -1,10 +1,11 @@ package edu.cornell.mannlib.vitro.webapp.controller.software; -import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -14,8 +15,10 @@ import java.util.Optional; import java.util.UUID; import java.util.function.BiConsumer; +import java.util.regex.Pattern; import javax.servlet.annotation.WebServlet; +import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -26,16 +29,18 @@ import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils; import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission; import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.AuthorizationRequest; +import edu.cornell.mannlib.vitro.webapp.beans.ApplicationBean; import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; -import edu.cornell.mannlib.vitro.webapp.controller.api.VitroApiServlet; import edu.cornell.mannlib.vitro.webapp.controller.api.sparqlquery.InvalidQueryTypeException; import edu.cornell.mannlib.vitro.webapp.controller.api.sparqlquery.SparqlQueryApiExecutor; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet; import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceDataset; import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexer; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; +import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.jena.ontology.OntModel; @@ -53,14 +58,12 @@ import org.apache.jena.update.UpdateRequest; @WebServlet(name = "softwareController", urlPatterns = {"/software", "/software/*"}, loadOnStartup = 5) -public class SoftwareController extends VitroApiServlet { +public class SoftwareController extends FreemarkerHttpServlet { private static final Log log = LogFactory.getLog(SoftwareController.class); private static final String CREATED_GRAPH_BASE_URI = "http://vitro.mannlib.cornell.edu/a/graph/"; - private static final AuthorizationRequest REQUIRED_ACTIONS = SimplePermission.USE_SPARQL_UPDATE_API.ACTION; - private final String FIND_ALL_QUERY = "PREFIX rdf: \n" + "PREFIX rdfs: \n" + @@ -82,7 +85,7 @@ public class SoftwareController extends VitroApiServlet { "PREFIX vcard: \n" + "\n" + "SELECT ?software ?label ?author ?authorType ?authorIdentifier ?datePublished ?funding ?funder ?keywords " + - "?version ?abstract ?identifier ?sameAs ?url\n" + + "?version ?abstract ?identifier ?doi ?sameAs ?url\n" + "WHERE\n" + "{\n" + " ?software rdf:type obo:ERO_0000071\n" + @@ -100,7 +103,10 @@ public class SoftwareController extends VitroApiServlet { " OPTIONAL { ?dateObject vivo:dateTime ?datePublished }\n" + " }\n" + " OPTIONAL { ?software vivo:informationResourceSupportedBy ?funderObject .\n" + - " OPTIONAL { ?funderObject vivo:assignedBy ?funder }\n" + + " OPTIONAL { ?funderObject vivo:assignedBy ?funderObject ." + + " OPTIONAL { ?dateObject rdfs:label ?funder }\n" + + " OPTIONAL { ?dateObject vitro:mostSpecificType ?funderType }\n" + + " }\n" + " OPTIONAL { ?funderObject rdfs:label ?funding }\n" + " }\n" + " OPTIONAL { ?software vivo:freetextKeyword ?keywords }\n" + @@ -109,6 +115,7 @@ public class SoftwareController extends VitroApiServlet { " OPTIONAL { ?software obo:BFO_0000050 ?isPartOf }\n" + " OPTIONAL { ?software obo:BFO_0000050 ?hasPart }\n" + " OPTIONAL { ?software vivo:swhid ?identifier }\n" + + " OPTIONAL { ?software bibo:doi ?doi }\n" + " OPTIONAL { ?software owl:sameAs ?sameAsObject .\n" + " OPTIONAL { ?sameAsObject rdfs:label ?sameAs }\n" + " }\n" + @@ -138,7 +145,7 @@ public class SoftwareController extends VitroApiServlet { "PREFIX vcard: \n" + "\n" + "SELECT ?software ?label ?author ?authorType ?authorIdentifier ?datePublished ?funding ?funder ?keywords " + - "?version ?abstract ?identifier ?sameAs ?url\n" + + "?version ?abstract ?identifier ?doi ?sameAs ?url\n" + "WHERE\n" + "{\n" + " BIND (<%s> AS ?software)\n" + @@ -157,7 +164,10 @@ public class SoftwareController extends VitroApiServlet { " OPTIONAL { ?dateObject vivo:dateTime ?datePublished }\n" + " }\n" + " OPTIONAL { ?software vivo:informationResourceSupportedBy ?funderObject .\n" + - " OPTIONAL { ?funderObject vivo:assignedBy ?funder }\n" + + " OPTIONAL { ?funderObject vivo:assignedBy ?funderObject ." + + " OPTIONAL { ?dateObject rdfs:label ?funder }\n" + + " OPTIONAL { ?dateObject vitro:mostSpecificType ?funderType }\n" + + " }\n" + " OPTIONAL { ?funderObject rdfs:label ?funding }\n" + " }\n" + " OPTIONAL { ?software vivo:freetextKeyword ?keywords }\n" + @@ -166,6 +176,7 @@ public class SoftwareController extends VitroApiServlet { " OPTIONAL { ?software obo:BFO_0000050 ?isPartOf }\n" + " OPTIONAL { ?software obo:BFO_0000050 ?hasPart }\n" + " OPTIONAL { ?software vivo:swhid ?identifier }\n" + + " OPTIONAL { ?software bibo:doi ?doi }\n" + " OPTIONAL { ?software owl:sameAs ?sameAsObject .\n" + " OPTIONAL { ?sameAsObject rdfs:label ?sameAs }\n" + " }\n" + @@ -296,7 +307,9 @@ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOExc @Override public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { -// authorise(req); + if (!isAuthorizedToDisplayPage(req, resp, requiredActions(new VitroRequest(req)))) { + return; + } executeWithTransaction(req, resp, (graphStore, softwareUri) -> { @@ -321,6 +334,7 @@ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOEx softwareDTO.internalIdentifier = softwareUri; try { + resp.setContentType("application/json"); resp.getWriter().println(serializeToJSON(softwareDTO)); } catch (IOException e) { try { @@ -335,18 +349,61 @@ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOEx @Override public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException { - // authorise(req); + if (!isAuthorizedToDisplayPage(req, resp, requiredActions(new VitroRequest(req)))) { + return; + } - executeWithTransaction(req, resp, (graphStore, softwareUri) -> { - String deleteQuery = String.format(DELETE_SOFTWARE_QUERY_TEMPLATE, softwareUri); - executeUpdate(graphStore, deleteQuery); - resp.setStatus(HttpServletResponse.SC_NO_CONTENT); - }); + String pathInfo = req.getPathInfo(); + if (Objects.isNull(pathInfo) || pathInfo.isEmpty()) { + do400BadRequest("You have to provide a record identifier.", resp); + return; + } + + VitroRequest vreq = new VitroRequest(req); + ApplicationBean appBean = vreq.getAppBean(); + String appName = appBean.getApplicationName().toLowerCase(); + URL url = new URL("http://" + req.getServerName() + ":" + req.getServerPort() + "/" + appName + + "/deleteIndividualController?individualUri=" + + URLEncoder.encode(buildSoftwareUri(pathInfo.substring(1))) + "&redirectUrl=%2F"); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + connection.setRequestProperty("Accept", "application/json"); + + addCookiesToRequest(req, connection); + + connection.getResponseCode(); + + resp.setStatus(HttpServletResponse.SC_NO_CONTENT); + + connection.disconnect(); + +// executeWithTransaction(req, resp, (graphStore, softwareUri) -> { +// String deleteQuery = String.format(DELETE_SOFTWARE_QUERY_TEMPLATE, softwareUri); +// executeUpdate(graphStore, deleteQuery); +// resp.setStatus(HttpServletResponse.SC_NO_CONTENT); +// }); + } + + private void addCookiesToRequest(HttpServletRequest req, HttpURLConnection connection) { + Cookie[] cookies = req.getCookies(); + if (cookies != null) { + StringBuilder cookieHeader = new StringBuilder(); + for (Cookie cookie : cookies) { + cookieHeader.append(cookie.getName()).append("=").append(cookie.getValue()).append("; "); + } + // Remove the trailing "; " at the end of the cookie string + if (cookieHeader.length() > 0) { + cookieHeader.setLength(cookieHeader.length() - 2); + } + connection.setRequestProperty("Cookie", cookieHeader.toString()); + } } @Override public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException { - // authorise(req); + if (!isAuthorizedToDisplayPage(req, resp, requiredActions(new VitroRequest(req)))) { + return; + } doDelete(req, resp); @@ -369,7 +426,7 @@ public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOExc private String buildSoftwareUri(String softwareId) { String defaultNamespace = Objects.requireNonNull(ConfigurationProperties.getInstance().getProperty("Vitro.defaultNamespace")); - return defaultNamespace + softwareId; + return defaultNamespace + StringEscapeUtils.escapeJava(softwareId); } private void handleResponseContentType(HttpServletRequest req, HttpServletResponse resp) { @@ -406,7 +463,8 @@ private StringBuilder buildInsertSoftwareQuery(SoftwareRequestDTO softwareDTO, S addSoftwareRelatedFields(insertSoftwareQuery, softwareDTO, softwareUri); addPublicationDate(insertSoftwareQuery, softwareDTO.datePublished, defaultNamespace, softwareUri); addAuthors(insertSoftwareQuery, softwareDTO.authors, defaultNamespace, softwareUri, ontModel); - addFunding(insertSoftwareQuery, softwareDTO.funding, defaultNamespace, softwareUri); + addFunding(insertSoftwareQuery, softwareDTO.fundings, defaultNamespace, softwareUri); + addFunders(insertSoftwareQuery, softwareDTO.funders, defaultNamespace, softwareUri); insertSoftwareQuery.append(" }\n").append("}\n"); @@ -486,13 +544,34 @@ private void commitTransaction(Dataset ds) { } private void addSoftwareRelatedFields(StringBuilder query, SoftwareRequestDTO softwareDTO, String softwareUri) { - query - .append("<").append(softwareUri).append("> rdf:type obo:ERO_0000071 ;\n") - .append("rdfs:label \"").append(softwareDTO.label).append("\" ;\n") - .append("vivo:freetextKeyword \"").append(softwareDTO.keywords).append("\" ;\n") - .append("obo:ERO_0000072 \"").append(softwareDTO.version).append("\" ;\n") - .append("bibo:abstract \"").append(softwareDTO.description).append("\" ;\n") - .append("vivo:swhid \"").append(softwareDTO.identifier).append("\" .\n"); + query.append("<").append(softwareUri).append("> rdf:type obo:ERO_0000071 ;\n"); + + if (Objects.nonNull(softwareDTO.keywords) && !softwareDTO.keywords.isEmpty()) { + query.append("vivo:freetextKeyword \"").append(StringEscapeUtils.escapeJava(softwareDTO.keywords)) + .append("\" ;\n"); + } + + if (Objects.nonNull(softwareDTO.version) && !softwareDTO.version.isEmpty()) { + query.append("obo:ERO_0000072 \"").append(StringEscapeUtils.escapeJava(softwareDTO.version)) + .append("\" ;\n"); + } + + if (Objects.nonNull(softwareDTO.description) && !softwareDTO.description.isEmpty()) { + query.append("bibo:abstract \"").append(StringEscapeUtils.escapeJava(softwareDTO.description)) + .append("\" ;\n"); + } + + String doiPattern = "^10\\.\\d{4,9}/[-,._;()/:A-Z0-9]+$"; + Pattern compiledPattern = Pattern.compile(doiPattern, Pattern.CASE_INSENSITIVE); + for (String identifier : softwareDTO.identifiers) { + if (compiledPattern.matcher(identifier).matches()) { + query.append("bibo:doi \"").append(StringEscapeUtils.escapeJava(identifier)).append("\" ;\n"); + } else { + query.append("vivo:swhid \"").append(StringEscapeUtils.escapeJava(identifier)).append("\" ;\n"); + } + } + + query.append("rdfs:label \"").append(StringEscapeUtils.escapeJava(softwareDTO.name)).append("\" .\n"); } private void addAuthors(StringBuilder query, List authors, String defaultNamespace, String documentUri, @@ -507,7 +586,7 @@ private void addAuthors(StringBuilder query, List authors, String def "PREFIX vivo: \n" + "PREFIX foaf: \n" + "SELECT ?author WHERE { ?author rdf:type foaf:Person . ?author vivo:orcidId \"%s\" . }", - author.identifier + StringEscapeUtils.escapeJava(author.identifier) ); try (QueryExecution qe = QueryExecutionFactory.create(checkAuthorQuery, ontModel)) { @@ -526,11 +605,13 @@ private void addAuthors(StringBuilder query, List authors, String def if (!personFound) { authorUri = defaultNamespace + UUID.randomUUID(); - query.append("<").append(authorUri).append("> rdf:type <").append(author.type).append("> ;\n") - .append("rdfs:label \"").append(author.name).append("\" ;\n"); + query.append("<").append(authorUri).append("> rdf:type <") + .append(StringEscapeUtils.escapeJava(author.type)).append("> ;\n") + .append("rdfs:label \"").append(StringEscapeUtils.escapeJava(author.name)).append("\" ;\n"); if (author.identifier != null && !author.identifier.isEmpty() && author.type.endsWith("Person")) { - query.append("vivo:orcidId \"").append(author.identifier).append("\" ;\n"); + query.append("vivo:orcidId \"").append(StringEscapeUtils.escapeJava(author.identifier)) + .append("\" ;\n"); } query.append(".\n"); @@ -552,24 +633,41 @@ private void addPublicationDate(StringBuilder query, String dateString, String d query.append("<").append(documentUri).append("> vivo:dateTimeValue <") .append(dateObjectUri).append("> .\n") .append("<").append(dateObjectUri).append("> rdf:type vivo:DateTimeValue ;\n") - .append("vivo:dateTime \"").append(dateString).append("\"^^xsd:date .\n"); + .append("vivo:dateTime \"").append(StringEscapeUtils.escapeJava(dateString)).append("\"^^xsd:date .\n"); } } - private void addFunding(StringBuilder query, List fundings, String defaultNamespace, + private void addFunding(StringBuilder query, List fundings, String defaultNamespace, String documentUri) { - for (FundingDTO funding : fundings) { + for (String funding : fundings) { String funderObjectUri = defaultNamespace + UUID.randomUUID(); query.append("<").append(documentUri).append("> vivo:informationResourceSupportedBy <") .append(funderObjectUri).append("> .\n") .append("<").append(funderObjectUri).append("> rdf:type vivo:Funding ;\n"); - if (funding.funder != null && !funding.funder.isEmpty()) { - query.append("vivo:assignedBy <").append(funding.funder).append("> ;\n"); - } + query.append("rdfs:label \"").append(StringEscapeUtils.escapeJava(funding)).append("\" .\n"); + } + } + + private void addFunders(StringBuilder query, List funders, String defaultNamespace, + String documentUri) { + for (FunderDTO funder : funders) { + String fundingObjectUri = defaultNamespace + UUID.randomUUID(); + String funderObjectUri = defaultNamespace + UUID.randomUUID(); - query.append("rdfs:label \"").append(funding.funding).append("\" .\n"); + query.append("<").append(documentUri).append("> vivo:informationResourceSupportedBy <") + .append(fundingObjectUri).append("> .\n") + .append("<").append(fundingObjectUri).append("> rdf:type vivo:Funding .\n"); + + if (funder.name != null && !funder.name.isEmpty()) { + query + .append("<").append(funderObjectUri).append("> rdf:type <") + .append(StringEscapeUtils.escapeJava(funder.type)).append("> ;\n") + .append("rdfs:label \"").append(StringEscapeUtils.escapeJava(funder.name)).append("\" .\n") + .append("<").append(fundingObjectUri).append("> vivo:assignedBy <") + .append(funderObjectUri).append("> .\n"); + } } } @@ -608,16 +706,27 @@ private List handleDTOConversion(SparqlQueryApiExecutor cor if (existingRecord.isPresent()) { addAuthorToSoftware(binding, existingRecord.get()); addFundingToSoftware(binding, existingRecord.get()); + addFundersToSoftware(binding, existingRecord.get()); continue; } SoftwareResponseDTO software = new SoftwareResponseDTO(); software.internalIdentifier = binding.getOrDefault("software", null); - software.label = binding.getOrDefault("label", null); + software.name = binding.getOrDefault("label", null); software.description = binding.getOrDefault("abstract", null); software.version = binding.getOrDefault("version", null); software.sameAs = binding.getOrDefault("sameAs", null); - software.identifier = binding.getOrDefault("identifier", null); + + String swhid = binding.getOrDefault("identifier", null); + if (Objects.nonNull(swhid)) { + software.identifiers.add(swhid); + } + + String doi = binding.getOrDefault("doi", null); + if (Objects.nonNull(doi)) { + software.identifiers.add(doi); + } + software.url = binding.getOrDefault("url", null); software.keywords = binding.getOrDefault("keywords", null); software.isPartOf = binding.getOrDefault("isPartOf", null); @@ -630,6 +739,7 @@ private List handleDTOConversion(SparqlQueryApiExecutor cor addAuthorToSoftware(binding, software); addFundingToSoftware(binding, software); + addFundersToSoftware(binding, software); softwareResponse.add(software); } @@ -654,22 +764,30 @@ private void addAuthorToSoftware(Map binding, SoftwareResponseDT } private void addFundingToSoftware(Map binding, SoftwareResponseDTO software) { - if (!Objects.nonNull(binding.get("funding")) && !Objects.nonNull(binding.get("funder"))) { + if (!Objects.nonNull(binding.get("funding"))) { return; } - if (software.funding.stream().anyMatch(funding -> funding.funding.equals(binding.get("funding")))) { + if (software.fundings.stream().anyMatch(funding -> funding.equals(binding.get("funding")))) { return; } - if (software.funding.stream().anyMatch(funding -> funding.funder.equals(binding.get("funder")))) { + software.fundings.add(binding.get("funding")); + } + + private void addFundersToSoftware(Map binding, SoftwareResponseDTO software) { + if (!Objects.nonNull(binding.get("funder"))) { + return; + } + + if (software.funders.stream().anyMatch(funder -> funder.name.equals(binding.get("funder")))) { return; } - FundingDTO funding = new FundingDTO(); - funding.funding = binding.getOrDefault("funding", null); - funding.funder = binding.getOrDefault("funder", null); - software.funding.add(funding); + FunderDTO funder = new FunderDTO(); + funder.name = binding.get("funder"); + funder.type = binding.getOrDefault("funderType", null); + software.funders.add(funder); } private String getSparqlQueryResponse(SparqlQueryApiExecutor core) throws IOException, RDFServiceException { @@ -705,11 +823,8 @@ private void do500InternalServerError(String message, Exception e, e.printStackTrace(w); } - private void authorise(HttpServletRequest req, HttpServletResponse resp) throws IOException { - try { - confirmAuthorization(req, REQUIRED_ACTIONS); - } catch (AuthException e) { - sendShortResponse(SC_FORBIDDEN, e.getMessage(), resp); - } + @Override + protected AuthorizationRequest requiredActions(VitroRequest vreq) { + return SimplePermission.USE_SPARQL_UPDATE_API.ACTION; } } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareRequestDTO.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareRequestDTO.java index 1619d0cb28..c0cd79e329 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareRequestDTO.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareRequestDTO.java @@ -3,23 +3,28 @@ import java.util.ArrayList; import java.util.List; +import com.fasterxml.jackson.annotation.JsonProperty; + public class SoftwareRequestDTO { public String internalIdentifier; - public String label; + @JsonProperty(required = true) + public String name; public String datePublished; public List authors = new ArrayList<>(); - public List funding = new ArrayList<>(); + public List fundings = new ArrayList<>(); + + public List funders = new ArrayList<>(); public String version; public String description; - public String identifier; + public List identifiers = new ArrayList<>(); public String keywords; } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java index 82894cee53..4add5f117b 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java @@ -7,19 +7,21 @@ public class SoftwareResponseDTO { public String internalIdentifier; - public String label; + public String name; public String datePublished; public List authors = new ArrayList<>(); - public List funding = new ArrayList<>(); + public List fundings = new ArrayList<>(); + + public List funders = new ArrayList<>(); public String version; public String description; - public String identifier; + public List identifiers = new ArrayList<>(); public String sameAs; From 0839d9be1e980a84a2cc45cf649301926fdd7954 Mon Sep 17 00:00:00 2001 From: Ivan Mrsulja Date: Tue, 1 Oct 2024 14:31:49 +0200 Subject: [PATCH 08/12] Fixed funders support. --- .../software/SoftwareController.java | 124 +++++------------- 1 file changed, 32 insertions(+), 92 deletions(-) diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java index ed108e374a..e113ee85aa 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java @@ -85,7 +85,7 @@ public class SoftwareController extends FreemarkerHttpServlet { "PREFIX vcard: \n" + "\n" + "SELECT ?software ?label ?author ?authorType ?authorIdentifier ?datePublished ?funding ?funder ?keywords " + - "?version ?abstract ?identifier ?doi ?sameAs ?url\n" + + "?version ?abstract ?identifier ?doi ?sameAs ?url ?funderType\n" + "WHERE\n" + "{\n" + " ?software rdf:type obo:ERO_0000071\n" + @@ -102,12 +102,12 @@ public class SoftwareController extends FreemarkerHttpServlet { " OPTIONAL { ?software vivo:dateTimeValue ?dateObject .\n" + " OPTIONAL { ?dateObject vivo:dateTime ?datePublished }\n" + " }\n" + - " OPTIONAL { ?software vivo:informationResourceSupportedBy ?funderObject .\n" + - " OPTIONAL { ?funderObject vivo:assignedBy ?funderObject ." + - " OPTIONAL { ?dateObject rdfs:label ?funder }\n" + - " OPTIONAL { ?dateObject vitro:mostSpecificType ?funderType }\n" + + " OPTIONAL { ?software vivo:informationResourceSupportedBy ?fundingObject .\n" + + " OPTIONAL { ?fundingObject vivo:assignedBy ?funderObject ." + + " OPTIONAL { ?funderObject rdfs:label ?funder }\n" + + " OPTIONAL { ?funderObject vitro:mostSpecificType ?funderType }\n" + " }\n" + - " OPTIONAL { ?funderObject rdfs:label ?funding }\n" + + " OPTIONAL { ?fundingObject rdfs:label ?funding }\n" + " }\n" + " OPTIONAL { ?software vivo:freetextKeyword ?keywords }\n" + " OPTIONAL { ?software obo:ERO_0000072 ?version }\n" + @@ -145,7 +145,7 @@ public class SoftwareController extends FreemarkerHttpServlet { "PREFIX vcard: \n" + "\n" + "SELECT ?software ?label ?author ?authorType ?authorIdentifier ?datePublished ?funding ?funder ?keywords " + - "?version ?abstract ?identifier ?doi ?sameAs ?url\n" + + "?version ?abstract ?identifier ?doi ?sameAs ?url ?funderType\n" + "WHERE\n" + "{\n" + " BIND (<%s> AS ?software)\n" + @@ -163,12 +163,12 @@ public class SoftwareController extends FreemarkerHttpServlet { " OPTIONAL { ?software vivo:dateTimeValue ?dateObject .\n" + " OPTIONAL { ?dateObject vivo:dateTime ?datePublished }\n" + " }\n" + - " OPTIONAL { ?software vivo:informationResourceSupportedBy ?funderObject .\n" + - " OPTIONAL { ?funderObject vivo:assignedBy ?funderObject ." + - " OPTIONAL { ?dateObject rdfs:label ?funder }\n" + - " OPTIONAL { ?dateObject vitro:mostSpecificType ?funderType }\n" + + " OPTIONAL { ?software vivo:informationResourceSupportedBy ?fundingObject .\n" + + " OPTIONAL { ?fundingObject vivo:assignedBy ?funderObject ." + + " OPTIONAL { ?funderObject rdfs:label ?funder }\n" + + " OPTIONAL { ?funderObject vitro:mostSpecificType ?funderType }\n" + " }\n" + - " OPTIONAL { ?funderObject rdfs:label ?funding }\n" + + " OPTIONAL { ?fundingObject rdfs:label ?funding }\n" + " }\n" + " OPTIONAL { ?software vivo:freetextKeyword ?keywords }\n" + " OPTIONAL { ?software obo:ERO_0000072 ?version }\n" + @@ -185,65 +185,6 @@ public class SoftwareController extends FreemarkerHttpServlet { " }\n" + "}\n"; - private final String DELETE_SOFTWARE_QUERY_TEMPLATE = - "PREFIX rdf: \n" + - "PREFIX rdfs: \n" + - "PREFIX xsd: \n" + - "PREFIX owl: \n" + - "PREFIX swrl: \n" + - "PREFIX swrlb: \n" + - "PREFIX vitro: \n" + - "PREFIX bibo: \n" + - "PREFIX c4o: \n" + - "PREFIX cito: \n" + - "PREFIX dcterms: \n" + - "PREFIX event: \n" + - "PREFIX fabio: \n" + - "PREFIX foaf: \n" + - "PREFIX geo: \n" + - "PREFIX obo: \n" + - "PREFIX vivo: \n" + - "PREFIX vcard: \n" + - "\n" + - "DELETE\n" + - "{\n" + - " ?software vivo:dateTimeValue ?dateObject .\n" + - " ?dateObject ?p1 ?o1 .\n" + - " ?software vivo:relatedBy ?relatedObject .\n" + - " ?relatedObject rdf:type vivo:Authorship .\n" + - " ?relatedObject ?p2 ?o2 .\n" + - " ?software vivo:informationResourceSupportedBy ?funderObject .\n" + - " ?funderObject ?p3 ?o3 .\n" + - " ?software obo:ARG_2000028 ?contactInfo .\n" + - " ?contactInfo ?p4 ?o4 .\n" + - " ?software rdfs:label ?label .\n" + - " ?software vivo:freetextKeyword ?keywords .\n" + - " ?software obo:ERO_0000072 ?version .\n" + - " ?software bibo:abstract ?abstract .\n" + - " ?software vivo:swhid ?identifier .\n" + - " ?software owl:sameAs ?sameAsObject .\n" + - " ?sameAsObject rdfs:label ?sameAs .\n" + - " ?software obo:ARG_2000028 ?contactInfo .\n" + - " ?contactInfo vcard:hasURL ?url .\n" + - "}\n" + - "WHERE\n" + - "{\n" + - " BIND (<%s> AS ?software)\n" + - " OPTIONAL { ?software vivo:dateTimeValue ?dateObject . OPTIONAL { ?dateObject ?p1 ?o1 } }\n" + - " OPTIONAL { ?software vivo:relatedBy ?relatedObject . ?relatedObject rdf:type vivo:Authorship .\n" + - " OPTIONAL { ?relatedObject ?p2 ?o2 } }\n" + - " OPTIONAL { ?software vivo:informationResourceSupportedBy ?funderObject .\n" + - " OPTIONAL { ?funderObject ?p3 ?o3 } }\n" + - " OPTIONAL { ?software obo:ARG_2000028 ?contactInfo .\n" + - " OPTIONAL { ?contactInfo ?p4 ?o4 } OPTIONAL { ?contactInfo vcard:hasURL ?url } }\n" + - " OPTIONAL { ?software rdfs:label ?label }\n" + - " OPTIONAL { ?software vivo:freetextKeyword ?keywords }\n" + - " OPTIONAL { ?software obo:ERO_0000072 ?version }\n" + - " OPTIONAL { ?software bibo:abstract ?abstract }\n" + - " OPTIONAL { ?software vivo:swhid ?identifier }\n" + - " OPTIONAL { ?software owl:sameAs ?sameAsObject . OPTIONAL { ?sameAsObject rdfs:label ?sameAs } }\n" + - "}\n"; - private static List> parseBindings(String jsonResponse) throws IOException { ObjectMapper objectMapper = new ObjectMapper(); @@ -376,12 +317,6 @@ public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IO resp.setStatus(HttpServletResponse.SC_NO_CONTENT); connection.disconnect(); - -// executeWithTransaction(req, resp, (graphStore, softwareUri) -> { -// String deleteQuery = String.format(DELETE_SOFTWARE_QUERY_TEMPLATE, softwareUri); -// executeUpdate(graphStore, deleteQuery); -// resp.setStatus(HttpServletResponse.SC_NO_CONTENT); -// }); } private void addCookiesToRequest(HttpServletRequest req, HttpURLConnection connection) { @@ -571,7 +506,7 @@ private void addSoftwareRelatedFields(StringBuilder query, SoftwareRequestDTO so } } - query.append("rdfs:label \"").append(StringEscapeUtils.escapeJava(softwareDTO.name)).append("\" .\n"); + query.append("rdfs:label \"").append(StringEscapeUtils.escapeJava(softwareDTO.name)).append("\"@en-US .\n"); } private void addAuthors(StringBuilder query, List authors, String defaultNamespace, String documentUri, @@ -607,7 +542,7 @@ private void addAuthors(StringBuilder query, List authors, String def query.append("<").append(authorUri).append("> rdf:type <") .append(StringEscapeUtils.escapeJava(author.type)).append("> ;\n") - .append("rdfs:label \"").append(StringEscapeUtils.escapeJava(author.name)).append("\" ;\n"); + .append("rdfs:label \"").append(StringEscapeUtils.escapeJava(author.name)).append("\"@en-US ;\n"); if (author.identifier != null && !author.identifier.isEmpty() && author.type.endsWith("Person")) { query.append("vivo:orcidId \"").append(StringEscapeUtils.escapeJava(author.identifier)) @@ -646,28 +581,33 @@ private void addFunding(StringBuilder query, List fundings, String defau .append(funderObjectUri).append("> .\n") .append("<").append(funderObjectUri).append("> rdf:type vivo:Funding ;\n"); - query.append("rdfs:label \"").append(StringEscapeUtils.escapeJava(funding)).append("\" .\n"); + query.append("rdfs:label \"").append(StringEscapeUtils.escapeJava(funding)).append("\"@en-US .\n"); } } private void addFunders(StringBuilder query, List funders, String defaultNamespace, String documentUri) { for (FunderDTO funder : funders) { - String fundingObjectUri = defaultNamespace + UUID.randomUUID(); + if (Objects.isNull(funder.name) || funder.name.isEmpty()) { + continue; + } + + String grantObjectUri = defaultNamespace + UUID.randomUUID(); String funderObjectUri = defaultNamespace + UUID.randomUUID(); query.append("<").append(documentUri).append("> vivo:informationResourceSupportedBy <") - .append(fundingObjectUri).append("> .\n") - .append("<").append(fundingObjectUri).append("> rdf:type vivo:Funding .\n"); - - if (funder.name != null && !funder.name.isEmpty()) { - query - .append("<").append(funderObjectUri).append("> rdf:type <") - .append(StringEscapeUtils.escapeJava(funder.type)).append("> ;\n") - .append("rdfs:label \"").append(StringEscapeUtils.escapeJava(funder.name)).append("\" .\n") - .append("<").append(fundingObjectUri).append("> vivo:assignedBy <") - .append(funderObjectUri).append("> .\n"); - } + .append(grantObjectUri).append("> .\n") + .append("<").append(grantObjectUri).append("> rdf:type vivo:Grant ;\n") + .append("rdfs:label \"").append(StringEscapeUtils.escapeJava(funder.name)).append(" Grant") + .append("\"@en-US .\n"); + + query + .append("<").append(funderObjectUri).append("> rdf:type <") + .append(StringEscapeUtils.escapeJava(funder.type)).append("> ;\n") + .append("rdfs:label \"").append(StringEscapeUtils.escapeJava(funder.name)).append("\"@en-US .\n") + .append("<").append(grantObjectUri).append("> vivo:assignedBy <") + .append(funderObjectUri).append("> .\n"); + } } From e631cde52ed0774586dc56dff357dce2c3ac75c2 Mon Sep 17 00:00:00 2001 From: Ivan Mrsulja Date: Tue, 1 Oct 2024 15:37:34 +0200 Subject: [PATCH 09/12] Added initial language set for labels. Improved grant persistence. --- .../controller/software/FunderRequestDTO.java | 10 ++++++++++ ...{FunderDTO.java => FunderResponseDTO.java} | 2 +- .../software/SoftwareController.java | 20 +++++++++++++------ .../software/SoftwareRequestDTO.java | 2 +- .../software/SoftwareResponseDTO.java | 2 +- 5 files changed, 27 insertions(+), 9 deletions(-) create mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FunderRequestDTO.java rename api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/{FunderDTO.java => FunderResponseDTO.java} (77%) diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FunderRequestDTO.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FunderRequestDTO.java new file mode 100644 index 0000000000..8b8d7c8c8d --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FunderRequestDTO.java @@ -0,0 +1,10 @@ +package edu.cornell.mannlib.vitro.webapp.controller.software; + +public class FunderRequestDTO { + + public String name; + + public String type; + + public String grantName; +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FunderDTO.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FunderResponseDTO.java similarity index 77% rename from api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FunderDTO.java rename to api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FunderResponseDTO.java index 7a406c38f2..dd7ee79fa4 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FunderDTO.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FunderResponseDTO.java @@ -1,6 +1,6 @@ package edu.cornell.mannlib.vitro.webapp.controller.software; -public class FunderDTO { +public class FunderResponseDTO { public String name; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java index e113ee85aa..217b3605bc 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java @@ -585,10 +585,11 @@ private void addFunding(StringBuilder query, List fundings, String defau } } - private void addFunders(StringBuilder query, List funders, String defaultNamespace, + private void addFunders(StringBuilder query, List funders, String defaultNamespace, String documentUri) { - for (FunderDTO funder : funders) { - if (Objects.isNull(funder.name) || funder.name.isEmpty()) { + for (FunderRequestDTO funder : funders) { + if (Objects.isNull(funder.name) || funder.name.isEmpty() || Objects.isNull(funder.type) || + funder.type.isEmpty()) { continue; } @@ -597,8 +598,15 @@ private void addFunders(StringBuilder query, List funders, String def query.append("<").append(documentUri).append("> vivo:informationResourceSupportedBy <") .append(grantObjectUri).append("> .\n") - .append("<").append(grantObjectUri).append("> rdf:type vivo:Grant ;\n") - .append("rdfs:label \"").append(StringEscapeUtils.escapeJava(funder.name)).append(" Grant") + .append("<").append(grantObjectUri).append("> rdf:type vivo:Grant ;\n"); + + if (Objects.nonNull(funder.grantName) && !funder.grantName.isEmpty()) { + query.append("rdfs:label \"").append(StringEscapeUtils.escapeJava(funder.grantName)); + } else { + query.append("rdfs:label \"").append(StringEscapeUtils.escapeJava(funder.name)).append(" Grant"); + } + + query .append("\"@en-US .\n"); query @@ -724,7 +732,7 @@ private void addFundersToSoftware(Map binding, SoftwareResponseD return; } - FunderDTO funder = new FunderDTO(); + FunderResponseDTO funder = new FunderResponseDTO(); funder.name = binding.get("funder"); funder.type = binding.getOrDefault("funderType", null); software.funders.add(funder); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareRequestDTO.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareRequestDTO.java index c0cd79e329..582bb6716d 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareRequestDTO.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareRequestDTO.java @@ -18,7 +18,7 @@ public class SoftwareRequestDTO { public List fundings = new ArrayList<>(); - public List funders = new ArrayList<>(); + public List funders = new ArrayList<>(); public String version; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java index 4add5f117b..30e727bf8f 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java @@ -15,7 +15,7 @@ public class SoftwareResponseDTO { public List fundings = new ArrayList<>(); - public List funders = new ArrayList<>(); + public List funders = new ArrayList<>(); public String version; From a1738f9c5082870f0c703249bf0479c40c8de97a Mon Sep 17 00:00:00 2001 From: Ivan Mrsulja Date: Wed, 2 Oct 2024 10:48:49 +0200 Subject: [PATCH 10/12] Refactored code. Extracted common logic into utility files. Built InsertQueryBuilder. Fixed deletion bug as well as some minor bugs. --- .../IndividualApiCommonCRUDUtility.java | 126 +++++ .../software/IndividualApiNetworkUtility.java | 74 +++ .../software/IndividualApiSparqlUtility.java | 95 ++++ .../InformationContentEntityResponseDTO.java | 19 + ...formationContentEntityResponseUtility.java | 50 ++ .../software/InsertQueryBuilder.java | 162 ++++++ .../software/SoftwareController.java | 502 ++---------------- .../software/SoftwareResponseDTO.java | 14 +- 8 files changed, 576 insertions(+), 466 deletions(-) create mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/IndividualApiCommonCRUDUtility.java create mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/IndividualApiNetworkUtility.java create mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/IndividualApiSparqlUtility.java create mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/InformationContentEntityResponseDTO.java create mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/InformationContentEntityResponseUtility.java create mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/InsertQueryBuilder.java diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/IndividualApiCommonCRUDUtility.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/IndividualApiCommonCRUDUtility.java new file mode 100644 index 0000000000..b32e0e7219 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/IndividualApiCommonCRUDUtility.java @@ -0,0 +1,126 @@ +package edu.cornell.mannlib.vitro.webapp.controller.software; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.util.Objects; +import java.util.UUID; +import java.util.function.BiConsumer; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils; +import edu.cornell.mannlib.vitro.webapp.beans.ApplicationBean; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceDataset; +import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexer; +import org.apache.jena.query.Dataset; +import org.apache.jena.query.ReadWrite; +import org.apache.jena.update.GraphStore; +import org.apache.jena.update.GraphStoreFactory; + +public class IndividualApiCommonCRUDUtility { + + public static void executeWithTransaction(HttpServletRequest req, HttpServletResponse resp, + BiConsumer action) throws IOException { + VitroRequest vreq = new VitroRequest(req); + SearchIndexer indexer = ApplicationUtils.instance().getSearchIndexer(); + Dataset ds = new RDFServiceDataset(vreq.getUnfilteredRDFService()); + GraphStore graphStore = GraphStoreFactory.create(ds); + + String entityUri; + if (req.getMethod().equalsIgnoreCase("POST")) { + entityUri = IndividualApiSparqlUtility.buildIndividualUri(UUID.randomUUID().toString()); + } else { + String pathInfo = req.getPathInfo(); + if (Objects.isNull(pathInfo) || pathInfo.isEmpty()) { + IndividualApiNetworkUtility.do400BadRequest("You have to provide a record identifier.", resp); + return; + } + + entityUri = IndividualApiSparqlUtility.buildIndividualUri(pathInfo.substring(1)); + } + + try { + pauseIndexer(indexer); + beginTransaction(ds); + + action.accept(graphStore, entityUri); + + } finally { + commitTransaction(ds); + unpauseIndexer(indexer); + } + } + + private static void pauseIndexer(SearchIndexer indexer) { + if (indexer != null) { + indexer.pause(); + } + } + + private static void unpauseIndexer(SearchIndexer indexer) { + if (indexer != null) { + indexer.unpause(); + } + } + + private static void beginTransaction(Dataset ds) { + if (ds.supportsTransactions()) { + ds.begin(ReadWrite.WRITE); + } + } + + private static void commitTransaction(Dataset ds) { + if (ds.supportsTransactions()) { + ds.commit(); + ds.end(); + } + } + + public static void performDeleteOperation(HttpServletRequest req, HttpServletResponse resp) + throws IOException { + String pathInfo = req.getPathInfo(); + if (Objects.isNull(pathInfo) || pathInfo.isEmpty()) { + IndividualApiNetworkUtility.do400BadRequest("You have to provide a record identifier.", resp); + return; + } + + VitroRequest vreq = new VitroRequest(req); + ApplicationBean appBean = vreq.getAppBean(); + String appName = appBean.getApplicationName().toLowerCase(); + URL url = new URL("http://" + req.getServerName() + ":" + req.getServerPort() + "/" + appName + + "/deleteIndividualController?individualUri=" + + URLEncoder.encode(IndividualApiSparqlUtility.buildIndividualUri(pathInfo.substring(1))) + + "&redirectUrl=%2F"); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + connection.setRequestProperty("Accept", "application/json"); + + addCookiesToRequest(req, connection); + + connection.getResponseCode(); + + resp.setStatus(HttpServletResponse.SC_NO_CONTENT); + + connection.disconnect(); + } + + private static void addCookiesToRequest(HttpServletRequest req, HttpURLConnection connection) { + Cookie[] cookies = req.getCookies(); + if (cookies != null) { + StringBuilder cookieHeader = new StringBuilder(); + for (Cookie cookie : cookies) { + cookieHeader.append(cookie.getName()).append("=").append(cookie.getValue()).append("; "); + } + // Remove the trailing "; " at the end of the cookie string + if (cookieHeader.length() > 0) { + cookieHeader.setLength(cookieHeader.length() - 2); + } + connection.setRequestProperty("Cookie", cookieHeader.toString()); + } + } +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/IndividualApiNetworkUtility.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/IndividualApiNetworkUtility.java new file mode 100644 index 0000000000..e498d9a0f7 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/IndividualApiNetworkUtility.java @@ -0,0 +1,74 @@ +package edu.cornell.mannlib.vitro.webapp.controller.software; + +import java.io.IOException; +import java.io.PrintWriter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import edu.cornell.mannlib.vitro.webapp.controller.api.sparqlquery.InvalidQueryTypeException; +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; +import org.apache.jena.query.QueryParseException; + +public class IndividualApiNetworkUtility { + + public static void handleResponseContentType(HttpServletRequest req, HttpServletResponse resp) { + String acceptHeader = req.getHeader("Accept"); + if (acceptHeader != null && !acceptHeader.isEmpty()) { + resp.setContentType(acceptHeader); + } else { + resp.setContentType("application/json"); + } + } + + public static String serializeToJSON(Object serializationObject) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.enable(SerializationFeature.INDENT_OUTPUT); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + return objectMapper.writeValueAsString(serializationObject); + } + + public static boolean isJsonRequest(HttpServletRequest req) { + String acceptHeader = req.getHeader("Accept"); + return acceptHeader != null && acceptHeader.equals("application/json"); + } + + public static T parseRequestBody(HttpServletRequest req, Class clazz) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.readValue(req.getInputStream(), clazz); + } + + public static void handleException(Exception e, String queryString, HttpServletResponse resp) throws IOException { + if (e instanceof InvalidQueryTypeException) { + do400BadRequest("Invalid query type: '" + queryString + "'", resp); + } else if (e instanceof QueryParseException) { + do400BadRequest("Failed to parse query: '" + queryString + "'", resp); + } else if (e instanceof RDFServiceException) { + do500InternalServerError("Problem executing the query.", e, resp); + } + } + + public static void do400BadRequest(String message, HttpServletResponse resp) + throws IOException { + resp.setStatus(400); + resp.getWriter().println(message); + } + + public static void do404NotFound(String message, HttpServletResponse resp) + throws IOException { + resp.setStatus(404); + resp.getWriter().println(message); + } + + public static void do500InternalServerError(String message, Exception e, + HttpServletResponse resp) throws IOException { + resp.setStatus(500); + PrintWriter w = resp.getWriter(); + w.println(message); + e.printStackTrace(w); + } +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/IndividualApiSparqlUtility.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/IndividualApiSparqlUtility.java new file mode 100644 index 0000000000..c1aaac8a2a --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/IndividualApiSparqlUtility.java @@ -0,0 +1,95 @@ +package edu.cornell.mannlib.vitro.webapp.controller.software; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; +import edu.cornell.mannlib.vitro.webapp.controller.api.sparqlquery.SparqlQueryApiExecutor; +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; +import org.apache.commons.lang3.StringEscapeUtils; +import org.apache.jena.update.GraphStore; +import org.apache.jena.update.UpdateAction; +import org.apache.jena.update.UpdateFactory; +import org.apache.jena.update.UpdateRequest; + +public class IndividualApiSparqlUtility { + + public static String getSparqlQueryResponse(SparqlQueryApiExecutor core) throws IOException, RDFServiceException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + core.executeAndFormat(outputStream); + + String sparqlResponse = outputStream.toString("UTF-8"); + + outputStream.close(); + + return sparqlResponse; + } + + public static List> parseBindings(String jsonResponse) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = objectMapper.readTree(jsonResponse); + + JsonNode bindingsNode = rootNode.path("results").path("bindings"); + + List> recordsList = new ArrayList<>(); + + for (JsonNode bindingNode : bindingsNode) { + Map recordMap = new HashMap<>(); + + Iterator> fieldsIterator = bindingNode.fields(); + while (fieldsIterator.hasNext()) { + Map.Entry field = fieldsIterator.next(); + + String fieldName = field.getKey(); + String fieldValue = field.getValue().path("value").asText(); + + recordMap.put(fieldName, fieldValue); + } + + recordsList.add(recordMap); + } + + return recordsList; + } + + public static void addPrefixClauses(StringBuilder queryBuilder) { + queryBuilder + .append("PREFIX rdf: \n") + .append("PREFIX rdfs: \n") + .append("PREFIX xsd: \n") + .append("PREFIX owl: \n") + .append("PREFIX swrl: \n") + .append("PREFIX swrlb: \n") + .append("PREFIX vitro: \n") + .append("PREFIX bibo: \n") + .append("PREFIX c4o: \n") + .append("PREFIX cito: \n") + .append("PREFIX dcterms: \n") + .append("PREFIX event: \n") + .append("PREFIX fabio: \n") + .append("PREFIX foaf: \n") + .append("PREFIX geo: \n") + .append("PREFIX obo: \n") + .append("PREFIX vivo: \n") + .append("PREFIX vcard: \n"); + } + + public static void executeUpdate(GraphStore graphStore, String query) { + UpdateRequest updateRequest = UpdateFactory.create(query); + UpdateAction.execute(updateRequest, graphStore); + } + + public static String buildIndividualUri(String entityId) { + String defaultNamespace = + Objects.requireNonNull(ConfigurationProperties.getInstance().getProperty("Vitro.defaultNamespace")); + return defaultNamespace + StringEscapeUtils.escapeJava(entityId); + } +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/InformationContentEntityResponseDTO.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/InformationContentEntityResponseDTO.java new file mode 100644 index 0000000000..731ce6227b --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/InformationContentEntityResponseDTO.java @@ -0,0 +1,19 @@ +package edu.cornell.mannlib.vitro.webapp.controller.software; + +import java.util.ArrayList; +import java.util.List; + +public class InformationContentEntityResponseDTO { + + public String internalIdentifier; + + public String name; + + public String datePublished; + + public List authors = new ArrayList<>(); + + public List fundings = new ArrayList<>(); + + public List funders = new ArrayList<>(); +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/InformationContentEntityResponseUtility.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/InformationContentEntityResponseUtility.java new file mode 100644 index 0000000000..56d76dccf4 --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/InformationContentEntityResponseUtility.java @@ -0,0 +1,50 @@ +package edu.cornell.mannlib.vitro.webapp.controller.software; + +import java.util.Map; +import java.util.Objects; + +public class InformationContentEntityResponseUtility { + + public static void addAuthorToICE(Map binding, InformationContentEntityResponseDTO entity) { + if (!Objects.nonNull(binding.get("author"))) { + return; + } + + if (entity.authors.stream().anyMatch(author -> author.name.equals(binding.get("author")))) { + return; + } + + AuthorDTO author = new AuthorDTO(); + author.name = binding.getOrDefault("author", null); + author.type = binding.getOrDefault("authorType", null); + author.identifier = binding.getOrDefault("authorIdentifier", null); + entity.authors.add(author); + } + + public static void addFundingToICE(Map binding, InformationContentEntityResponseDTO entity) { + if (!Objects.nonNull(binding.get("funding"))) { + return; + } + + if (entity.fundings.stream().anyMatch(funding -> funding.equals(binding.get("funding")))) { + return; + } + + entity.fundings.add(binding.get("funding")); + } + + public static void addFundersToICE(Map binding, InformationContentEntityResponseDTO entity) { + if (!Objects.nonNull(binding.get("funder"))) { + return; + } + + if (entity.funders.stream().anyMatch(funder -> funder.name.equals(binding.get("funder")))) { + return; + } + + FunderResponseDTO funder = new FunderResponseDTO(); + funder.name = binding.get("funder"); + funder.type = binding.getOrDefault("funderType", null); + entity.funders.add(funder); + } +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/InsertQueryBuilder.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/InsertQueryBuilder.java new file mode 100644 index 0000000000..5eef08169b --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/InsertQueryBuilder.java @@ -0,0 +1,162 @@ +package edu.cornell.mannlib.vitro.webapp.controller.software; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames; +import org.apache.commons.lang3.StringEscapeUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jena.ontology.OntModel; +import org.apache.jena.query.QueryExecution; +import org.apache.jena.query.QueryExecutionFactory; +import org.apache.jena.query.QuerySolution; +import org.apache.jena.query.ResultSet; + +public class InsertQueryBuilder { + + private static final Log log = LogFactory.getLog(InsertQueryBuilder.class); + + private final StringBuilder query = new StringBuilder(); + + public InsertQueryBuilder() { + } + + + public String build() { + return query.toString(); + } + + public InsertQueryBuilder startInsertQuery() { + IndividualApiSparqlUtility.addPrefixClauses(query); + query.append("\n") + .append("INSERT DATA\n") + .append("{\n") + .append("GRAPH ").append("<" + ModelNames.ABOX_ASSERTIONS + ">") + .append("\n") + .append("{\n"); + + return this; + } + + public InsertQueryBuilder addPublicationDate(String dateString, String defaultNamespace, + String documentUri) { + if (dateString != null && !dateString.isEmpty()) { + String dateObjectUri = defaultNamespace + UUID.randomUUID(); + + query.append("<").append(documentUri).append("> vivo:dateTimeValue <") + .append(dateObjectUri).append("> .\n") + .append("<").append(dateObjectUri).append("> rdf:type vivo:DateTimeValue ;\n") + .append("vivo:dateTime \"").append(StringEscapeUtils.escapeJava(dateString)).append("\"^^xsd:date .\n"); + } + + return this; + } + + public InsertQueryBuilder addAuthors(List authors, String defaultNamespace, String documentUri, + OntModel ontModel) { + for (AuthorDTO author : authors) { + String authorUri = null; + boolean personFound = false; + + if (author.identifier != null && !author.identifier.isEmpty() && author.type.endsWith("Person")) { + String checkAuthorQuery = String.format( + "PREFIX rdf: \n" + + "PREFIX vivo: \n" + + "PREFIX foaf: \n" + + "SELECT ?author WHERE { ?author rdf:type foaf:Person . ?author vivo:orcidId \"%s\" . }", + StringEscapeUtils.escapeJava(author.identifier) + ); + + try (QueryExecution qe = QueryExecutionFactory.create(checkAuthorQuery, ontModel)) { + ResultSet results = qe.execSelect(); + + if (results.hasNext()) { + QuerySolution solution = results.nextSolution(); + authorUri = solution.getResource("author").getURI(); + personFound = true; + } + } catch (Exception e) { + log.error("Error when looking for existing person.", e); + } + } + + if (!personFound) { + authorUri = defaultNamespace + UUID.randomUUID(); + + query.append("<").append(authorUri).append("> rdf:type <") + .append(StringEscapeUtils.escapeJava(author.type)).append("> ;\n") + .append("rdfs:label \"").append(StringEscapeUtils.escapeJava(author.name)).append("\"@en-US ;\n"); + + if (author.identifier != null && !author.identifier.isEmpty() && author.type.endsWith("Person")) { + query.append("vivo:orcidId \"").append(StringEscapeUtils.escapeJava(author.identifier)) + .append("\" ;\n"); + } + + query.append(".\n"); + } + + String relatedObjectUri = defaultNamespace + UUID.randomUUID(); + query.append("<").append(documentUri).append("> vivo:relatedBy <").append(relatedObjectUri).append("> .\n") + .append("<").append(relatedObjectUri).append("> rdf:type vivo:Authorship ;\n") + .append("vivo:relates <").append(authorUri).append("> .\n"); + } + + return this; + } + + public InsertQueryBuilder addFunding(List fundings, String defaultNamespace, + String documentUri) { + for (String funding : fundings) { + String funderObjectUri = defaultNamespace + UUID.randomUUID(); + + query.append("<").append(documentUri).append("> vivo:informationResourceSupportedBy <") + .append(funderObjectUri).append("> .\n") + .append("<").append(funderObjectUri).append("> rdf:type vivo:Funding ;\n"); + + query.append("rdfs:label \"").append(StringEscapeUtils.escapeJava(funding)).append("\"@en-US .\n"); + } + + return this; + } + + public InsertQueryBuilder addFunders(List funders, String defaultNamespace, String documentUri) { + for (FunderRequestDTO funder : funders) { + if (Objects.isNull(funder.name) || funder.name.isEmpty() || Objects.isNull(funder.type) || + funder.type.isEmpty()) { + continue; + } + + String grantObjectUri = defaultNamespace + UUID.randomUUID(); + String funderObjectUri = defaultNamespace + UUID.randomUUID(); + + query.append("<").append(documentUri).append("> vivo:informationResourceSupportedBy <") + .append(grantObjectUri).append("> .\n") + .append("<").append(grantObjectUri).append("> rdf:type vivo:Grant ;\n"); + + if (Objects.nonNull(funder.grantName) && !funder.grantName.isEmpty()) { + query.append("rdfs:label \"").append(StringEscapeUtils.escapeJava(funder.grantName)); + } else { + query.append("rdfs:label \"").append(StringEscapeUtils.escapeJava(funder.name)).append(" Grant"); + } + + query + .append("\"@en-US .\n"); + + query + .append("<").append(funderObjectUri).append("> rdf:type <") + .append(StringEscapeUtils.escapeJava(funder.type)).append("> ;\n") + .append("rdfs:label \"").append(StringEscapeUtils.escapeJava(funder.name)).append("\"@en-US .\n") + .append("<").append(grantObjectUri).append("> vivo:assignedBy <") + .append(funderObjectUri).append("> .\n"); + + } + + return this; + } + + public StringBuilder getInsertQuery() { + return query; + } +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java index 217b3605bc..e8852d28cc 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java @@ -1,69 +1,36 @@ package edu.cornell.mannlib.vitro.webapp.controller.software; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.PrintWriter; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLEncoder; import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.UUID; -import java.util.function.BiConsumer; import java.util.regex.Pattern; import javax.servlet.annotation.WebServlet; -import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils; import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission; import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.AuthorizationRequest; -import edu.cornell.mannlib.vitro.webapp.beans.ApplicationBean; import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; -import edu.cornell.mannlib.vitro.webapp.controller.api.sparqlquery.InvalidQueryTypeException; import edu.cornell.mannlib.vitro.webapp.controller.api.sparqlquery.SparqlQueryApiExecutor; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet; -import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceDataset; import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; -import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexer; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.jena.ontology.OntModel; -import org.apache.jena.query.Dataset; -import org.apache.jena.query.QueryExecution; -import org.apache.jena.query.QueryExecutionFactory; -import org.apache.jena.query.QueryParseException; -import org.apache.jena.query.QuerySolution; -import org.apache.jena.query.ReadWrite; -import org.apache.jena.query.ResultSet; -import org.apache.jena.update.GraphStore; -import org.apache.jena.update.GraphStoreFactory; -import org.apache.jena.update.UpdateAction; -import org.apache.jena.update.UpdateFactory; -import org.apache.jena.update.UpdateRequest; @WebServlet(name = "softwareController", urlPatterns = {"/software", "/software/*"}, loadOnStartup = 5) public class SoftwareController extends FreemarkerHttpServlet { private static final Log log = LogFactory.getLog(SoftwareController.class); - private static final String CREATED_GRAPH_BASE_URI = "http://vitro.mannlib.cornell.edu/a/graph/"; - private final String FIND_ALL_QUERY = "PREFIX rdf: \n" + "PREFIX rdfs: \n" + @@ -185,47 +152,12 @@ public class SoftwareController extends FreemarkerHttpServlet { " }\n" + "}\n"; - - private static List> parseBindings(String jsonResponse) throws IOException { - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode rootNode = objectMapper.readTree(jsonResponse); - - JsonNode bindingsNode = rootNode.path("results").path("bindings"); - - List> recordsList = new ArrayList<>(); - - for (JsonNode bindingNode : bindingsNode) { - Map recordMap = new HashMap<>(); - - Iterator> fieldsIterator = bindingNode.fields(); - while (fieldsIterator.hasNext()) { - Map.Entry field = fieldsIterator.next(); - - String fieldName = field.getKey(); - String fieldValue = field.getValue().path("value").asText(); - - recordMap.put(fieldName, fieldValue); - } - - recordsList.add(recordMap); - } - - return recordsList; - } - - private String serializeToJSON(Object serializationObject) throws IOException { - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.enable(SerializationFeature.INDENT_OUTPUT); - objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); - return objectMapper.writeValueAsString(serializationObject); - } - @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { String pathInfo = req.getPathInfo(); String queryString = (pathInfo != null && pathInfo.length() > 1) - ? String.format(FIND_BY_ID_QUERY_TEMPLATE, buildSoftwareUri(pathInfo.substring(1))) + ? String.format(FIND_BY_ID_QUERY_TEMPLATE, + IndividualApiSparqlUtility.buildIndividualUri(pathInfo.substring(1))) : FIND_ALL_QUERY; RDFService rdfService = ModelAccess.on(getServletContext()).getRDFService(); @@ -233,16 +165,22 @@ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOExc try { SparqlQueryApiExecutor core = SparqlQueryApiExecutor.instance(rdfService, queryString, "application/sparql-results+json"); - handleResponseContentType(req, resp); + IndividualApiNetworkUtility.handleResponseContentType(req, resp); + + if (IndividualApiNetworkUtility.isJsonRequest(req)) { + List response = handleDTOConversion(core, resp); + + if (response.isEmpty()) { + return; + } - if (isJsonRequest(req)) { - List response = handleDTOConversion(core); - resp.getWriter().println(serializeToJSON(response.size() == 1 ? response.get(0) : response)); + resp.getWriter().println( + IndividualApiNetworkUtility.serializeToJSON(response.size() == 1 ? response.get(0) : response)); } else { core.executeAndFormat(resp.getOutputStream()); } } catch (Exception e) { - handleException(e, queryString, resp); + IndividualApiNetworkUtility.handleException(e, queryString, resp); } } @@ -252,14 +190,14 @@ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOEx return; } - executeWithTransaction(req, resp, (graphStore, softwareUri) -> { + IndividualApiCommonCRUDUtility.executeWithTransaction(req, resp, (graphStore, softwareUri) -> { SoftwareRequestDTO softwareDTO = null; try { - softwareDTO = parseRequestBody(req, SoftwareRequestDTO.class); + softwareDTO = IndividualApiNetworkUtility.parseRequestBody(req, SoftwareRequestDTO.class); } catch (IOException e) { try { - do400BadRequest("Error while parsing request body.", resp); + IndividualApiNetworkUtility.do400BadRequest("Error while parsing request body.", resp); } catch (IOException ex) { log.error("Error while handling exception.", ex); } @@ -269,17 +207,17 @@ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOEx return; } - StringBuilder insertSoftwareQuery = buildInsertSoftwareQuery(softwareDTO, softwareUri, ModelAccess.on( + String insertSoftwareQuery = buildInsertSoftwareQuery(softwareDTO, softwareUri, ModelAccess.on( new VitroRequest(req)).getOntModel()); - executeUpdate(graphStore, insertSoftwareQuery.toString()); + IndividualApiSparqlUtility.executeUpdate(graphStore, insertSoftwareQuery); softwareDTO.internalIdentifier = softwareUri; try { resp.setContentType("application/json"); - resp.getWriter().println(serializeToJSON(softwareDTO)); + resp.getWriter().println(IndividualApiNetworkUtility.serializeToJSON(softwareDTO)); } catch (IOException e) { try { - do400BadRequest("Error while writing response body.", resp); + IndividualApiNetworkUtility.do400BadRequest("Error while writing response body.", resp); } catch (IOException ex) { log.error("Error while handling exception.", ex); } @@ -294,44 +232,7 @@ public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IO return; } - String pathInfo = req.getPathInfo(); - if (Objects.isNull(pathInfo) || pathInfo.isEmpty()) { - do400BadRequest("You have to provide a record identifier.", resp); - return; - } - - VitroRequest vreq = new VitroRequest(req); - ApplicationBean appBean = vreq.getAppBean(); - String appName = appBean.getApplicationName().toLowerCase(); - URL url = new URL("http://" + req.getServerName() + ":" + req.getServerPort() + "/" + appName + - "/deleteIndividualController?individualUri=" + - URLEncoder.encode(buildSoftwareUri(pathInfo.substring(1))) + "&redirectUrl=%2F"); - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod("GET"); - connection.setRequestProperty("Accept", "application/json"); - - addCookiesToRequest(req, connection); - - connection.getResponseCode(); - - resp.setStatus(HttpServletResponse.SC_NO_CONTENT); - - connection.disconnect(); - } - - private void addCookiesToRequest(HttpServletRequest req, HttpURLConnection connection) { - Cookie[] cookies = req.getCookies(); - if (cookies != null) { - StringBuilder cookieHeader = new StringBuilder(); - for (Cookie cookie : cookies) { - cookieHeader.append(cookie.getName()).append("=").append(cookie.getValue()).append("; "); - } - // Remove the trailing "; " at the end of the cookie string - if (cookieHeader.length() > 0) { - cookieHeader.setLength(cookieHeader.length() - 2); - } - connection.setRequestProperty("Cookie", cookieHeader.toString()); - } + IndividualApiCommonCRUDUtility.performDeleteOperation(req, resp); } @Override @@ -342,140 +243,39 @@ public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOExc doDelete(req, resp); - executeWithTransaction(req, resp, (graphStore, softwareUri) -> { + IndividualApiCommonCRUDUtility.executeWithTransaction(req, resp, (graphStore, softwareUri) -> { SoftwareRequestDTO softwareDTO; try { - softwareDTO = parseRequestBody(req, SoftwareRequestDTO.class); + softwareDTO = IndividualApiNetworkUtility.parseRequestBody(req, SoftwareRequestDTO.class); } catch (IOException e) { throw new RuntimeException(e); } - StringBuilder insertSoftwareQuery = buildInsertSoftwareQuery(softwareDTO, softwareUri, ModelAccess.on( + String insertSoftwareQuery = buildInsertSoftwareQuery(softwareDTO, softwareUri, ModelAccess.on( new VitroRequest(req)).getOntModel()); - executeUpdate(graphStore, insertSoftwareQuery.toString()); + IndividualApiSparqlUtility.executeUpdate(graphStore, insertSoftwareQuery); softwareDTO.internalIdentifier = softwareUri; resp.setStatus(HttpServletResponse.SC_NO_CONTENT); }); } - private String buildSoftwareUri(String softwareId) { - String defaultNamespace = - Objects.requireNonNull(ConfigurationProperties.getInstance().getProperty("Vitro.defaultNamespace")); - return defaultNamespace + StringEscapeUtils.escapeJava(softwareId); - } - - private void handleResponseContentType(HttpServletRequest req, HttpServletResponse resp) { - String acceptHeader = req.getHeader("Accept"); - if (acceptHeader != null && !acceptHeader.isEmpty()) { - resp.setContentType(acceptHeader); - } else { - resp.setContentType("application/json"); - } - } - - private boolean isJsonRequest(HttpServletRequest req) { - String acceptHeader = req.getHeader("Accept"); - return acceptHeader != null && acceptHeader.equals("application/json"); - } - - private T parseRequestBody(HttpServletRequest req, Class clazz) throws IOException { - ObjectMapper objectMapper = new ObjectMapper(); - return objectMapper.readValue(req.getInputStream(), clazz); - } - - private StringBuilder buildInsertSoftwareQuery(SoftwareRequestDTO softwareDTO, String softwareUri, - OntModel ontModel) { - StringBuilder insertSoftwareQuery = new StringBuilder(); - addPrefixClauses(insertSoftwareQuery); - insertSoftwareQuery.append("\n") - .append("INSERT DATA\n") - .append("{\n") - .append("GRAPH ").append("<" + CREATED_GRAPH_BASE_URI + ">") - .append("\n") - .append("{\n"); + private String buildInsertSoftwareQuery(SoftwareRequestDTO softwareDTO, String softwareUri, + OntModel ontModel) { + InsertQueryBuilder queryBuilder = new InsertQueryBuilder(); + queryBuilder.startInsertQuery(); String defaultNamespace = ConfigurationProperties.getInstance().getProperty("Vitro.defaultNamespace"); - addSoftwareRelatedFields(insertSoftwareQuery, softwareDTO, softwareUri); - addPublicationDate(insertSoftwareQuery, softwareDTO.datePublished, defaultNamespace, softwareUri); - addAuthors(insertSoftwareQuery, softwareDTO.authors, defaultNamespace, softwareUri, ontModel); - addFunding(insertSoftwareQuery, softwareDTO.fundings, defaultNamespace, softwareUri); - addFunders(insertSoftwareQuery, softwareDTO.funders, defaultNamespace, softwareUri); + addSoftwareRelatedFields(queryBuilder.getInsertQuery(), softwareDTO, softwareUri); - insertSoftwareQuery.append(" }\n").append("}\n"); + queryBuilder + .addPublicationDate(softwareDTO.datePublished, defaultNamespace, softwareUri) + .addAuthors(softwareDTO.authors, defaultNamespace, softwareUri, ontModel) + .addFunding(softwareDTO.fundings, defaultNamespace, softwareUri) + .addFunders(softwareDTO.funders, defaultNamespace, softwareUri); - return insertSoftwareQuery; - } - - private void executeUpdate(GraphStore graphStore, String query) { - UpdateRequest updateRequest = UpdateFactory.create(query); - UpdateAction.execute(updateRequest, graphStore); - } + queryBuilder.getInsertQuery().append(" }\n").append("}\n"); - private void handleException(Exception e, String queryString, HttpServletResponse resp) throws IOException { - if (e instanceof InvalidQueryTypeException) { - do400BadRequest("Invalid query type: '" + queryString + "'", resp); - } else if (e instanceof QueryParseException) { - do400BadRequest("Failed to parse query: '" + queryString + "'", resp); - } else if (e instanceof RDFServiceException) { - do500InternalServerError("Problem executing the query.", e, resp); - } - } - - private void executeWithTransaction(HttpServletRequest req, HttpServletResponse resp, - BiConsumer action) throws IOException { - VitroRequest vreq = new VitroRequest(req); - SearchIndexer indexer = ApplicationUtils.instance().getSearchIndexer(); - Dataset ds = new RDFServiceDataset(vreq.getUnfilteredRDFService()); - GraphStore graphStore = GraphStoreFactory.create(ds); - - String softwareUri; - if (req.getMethod().equalsIgnoreCase("POST")) { - softwareUri = buildSoftwareUri(UUID.randomUUID().toString()); - } else { - String pathInfo = req.getPathInfo(); - if (Objects.isNull(pathInfo) || pathInfo.isEmpty()) { - do400BadRequest("You have to provide a record identifier.", resp); - return; - } - - softwareUri = buildSoftwareUri(pathInfo.substring(1)); - } - - try { - pauseIndexer(indexer); - beginTransaction(ds); - - action.accept(graphStore, softwareUri); - - } finally { - commitTransaction(ds); - unpauseIndexer(indexer); - } - } - - private void pauseIndexer(SearchIndexer indexer) { - if (indexer != null) { - indexer.pause(); - } - } - - private void unpauseIndexer(SearchIndexer indexer) { - if (indexer != null) { - indexer.unpause(); - } - } - - private void beginTransaction(Dataset ds) { - if (ds.supportsTransactions()) { - ds.begin(ReadWrite.WRITE); - } - } - - private void commitTransaction(Dataset ds) { - if (ds.supportsTransactions()) { - ds.commit(); - ds.end(); - } + return queryBuilder.build(); } private void addSoftwareRelatedFields(StringBuilder query, SoftwareRequestDTO softwareDTO, String softwareUri) { @@ -509,142 +309,14 @@ private void addSoftwareRelatedFields(StringBuilder query, SoftwareRequestDTO so query.append("rdfs:label \"").append(StringEscapeUtils.escapeJava(softwareDTO.name)).append("\"@en-US .\n"); } - private void addAuthors(StringBuilder query, List authors, String defaultNamespace, String documentUri, - OntModel ontModel) { - for (AuthorDTO author : authors) { - String authorUri = null; - boolean personFound = false; - - if (author.identifier != null && !author.identifier.isEmpty() && author.type.endsWith("Person")) { - String checkAuthorQuery = String.format( - "PREFIX rdf: \n" + - "PREFIX vivo: \n" + - "PREFIX foaf: \n" + - "SELECT ?author WHERE { ?author rdf:type foaf:Person . ?author vivo:orcidId \"%s\" . }", - StringEscapeUtils.escapeJava(author.identifier) - ); - - try (QueryExecution qe = QueryExecutionFactory.create(checkAuthorQuery, ontModel)) { - ResultSet results = qe.execSelect(); - - if (results.hasNext()) { - QuerySolution solution = results.nextSolution(); - authorUri = solution.getResource("author").getURI(); - personFound = true; - } - } catch (Exception e) { - log.error("Error when looking for existing person.", e); - } - } - - if (!personFound) { - authorUri = defaultNamespace + UUID.randomUUID(); - - query.append("<").append(authorUri).append("> rdf:type <") - .append(StringEscapeUtils.escapeJava(author.type)).append("> ;\n") - .append("rdfs:label \"").append(StringEscapeUtils.escapeJava(author.name)).append("\"@en-US ;\n"); - - if (author.identifier != null && !author.identifier.isEmpty() && author.type.endsWith("Person")) { - query.append("vivo:orcidId \"").append(StringEscapeUtils.escapeJava(author.identifier)) - .append("\" ;\n"); - } - - query.append(".\n"); - } - - String relatedObjectUri = defaultNamespace + UUID.randomUUID(); - query.append("<").append(documentUri).append("> vivo:relatedBy <").append(relatedObjectUri).append("> .\n") - .append("<").append(relatedObjectUri).append("> rdf:type vivo:Authorship ;\n") - .append("vivo:relates <").append(authorUri).append("> .\n"); - } - } - - - private void addPublicationDate(StringBuilder query, String dateString, String defaultNamespace, - String documentUri) { - if (dateString != null && !dateString.isEmpty()) { - String dateObjectUri = defaultNamespace + UUID.randomUUID(); - - query.append("<").append(documentUri).append("> vivo:dateTimeValue <") - .append(dateObjectUri).append("> .\n") - .append("<").append(dateObjectUri).append("> rdf:type vivo:DateTimeValue ;\n") - .append("vivo:dateTime \"").append(StringEscapeUtils.escapeJava(dateString)).append("\"^^xsd:date .\n"); - } - } - - private void addFunding(StringBuilder query, List fundings, String defaultNamespace, - String documentUri) { - for (String funding : fundings) { - String funderObjectUri = defaultNamespace + UUID.randomUUID(); - - query.append("<").append(documentUri).append("> vivo:informationResourceSupportedBy <") - .append(funderObjectUri).append("> .\n") - .append("<").append(funderObjectUri).append("> rdf:type vivo:Funding ;\n"); - - query.append("rdfs:label \"").append(StringEscapeUtils.escapeJava(funding)).append("\"@en-US .\n"); - } - } - - private void addFunders(StringBuilder query, List funders, String defaultNamespace, - String documentUri) { - for (FunderRequestDTO funder : funders) { - if (Objects.isNull(funder.name) || funder.name.isEmpty() || Objects.isNull(funder.type) || - funder.type.isEmpty()) { - continue; - } - - String grantObjectUri = defaultNamespace + UUID.randomUUID(); - String funderObjectUri = defaultNamespace + UUID.randomUUID(); - - query.append("<").append(documentUri).append("> vivo:informationResourceSupportedBy <") - .append(grantObjectUri).append("> .\n") - .append("<").append(grantObjectUri).append("> rdf:type vivo:Grant ;\n"); - - if (Objects.nonNull(funder.grantName) && !funder.grantName.isEmpty()) { - query.append("rdfs:label \"").append(StringEscapeUtils.escapeJava(funder.grantName)); - } else { - query.append("rdfs:label \"").append(StringEscapeUtils.escapeJava(funder.name)).append(" Grant"); - } - - query - .append("\"@en-US .\n"); - - query - .append("<").append(funderObjectUri).append("> rdf:type <") - .append(StringEscapeUtils.escapeJava(funder.type)).append("> ;\n") - .append("rdfs:label \"").append(StringEscapeUtils.escapeJava(funder.name)).append("\"@en-US .\n") - .append("<").append(grantObjectUri).append("> vivo:assignedBy <") - .append(funderObjectUri).append("> .\n"); - - } - } - - private void addPrefixClauses(StringBuilder sb) { - sb.append("PREFIX rdf: \n") - .append("PREFIX rdfs: \n") - .append("PREFIX xsd: \n") - .append("PREFIX owl: \n") - .append("PREFIX swrl: \n") - .append("PREFIX swrlb: \n") - .append("PREFIX vitro: \n") - .append("PREFIX bibo: \n") - .append("PREFIX c4o: \n") - .append("PREFIX cito: \n") - .append("PREFIX dcterms: \n") - .append("PREFIX event: \n") - .append("PREFIX fabio: \n") - .append("PREFIX foaf: \n") - .append("PREFIX geo: \n") - .append("PREFIX obo: \n") - .append("PREFIX vivo: \n") - .append("PREFIX vcard: \n"); - } - - private List handleDTOConversion(SparqlQueryApiExecutor core) + private List handleDTOConversion(SparqlQueryApiExecutor core, HttpServletResponse resp) throws RDFServiceException, IOException { - String sparqlQueryResponse = getSparqlQueryResponse(core); + String sparqlQueryResponse = IndividualApiSparqlUtility.getSparqlQueryResponse(core); - List> bindings = parseBindings(sparqlQueryResponse); + List> bindings = IndividualApiSparqlUtility.parseBindings(sparqlQueryResponse); + if (bindings.isEmpty()) { + IndividualApiNetworkUtility.do404NotFound("Not found.", resp); + } List softwareResponse = new ArrayList<>(); for (Map binding : bindings) { @@ -652,9 +324,9 @@ private List handleDTOConversion(SparqlQueryApiExecutor cor softwareResponse.stream() .filter(software -> software.internalIdentifier.equals(binding.get("software"))).findFirst(); if (existingRecord.isPresent()) { - addAuthorToSoftware(binding, existingRecord.get()); - addFundingToSoftware(binding, existingRecord.get()); - addFundersToSoftware(binding, existingRecord.get()); + InformationContentEntityResponseUtility.addAuthorToICE(binding, existingRecord.get()); + InformationContentEntityResponseUtility.addFundingToICE(binding, existingRecord.get()); + InformationContentEntityResponseUtility.addFundersToICE(binding, existingRecord.get()); continue; } @@ -685,9 +357,9 @@ private List handleDTOConversion(SparqlQueryApiExecutor cor software.datePublished = dateTimeString.split("T")[0]; } - addAuthorToSoftware(binding, software); - addFundingToSoftware(binding, software); - addFundersToSoftware(binding, software); + InformationContentEntityResponseUtility.addAuthorToICE(binding, software); + InformationContentEntityResponseUtility.addFundingToICE(binding, software); + InformationContentEntityResponseUtility.addFundersToICE(binding, software); softwareResponse.add(software); } @@ -695,82 +367,6 @@ private List handleDTOConversion(SparqlQueryApiExecutor cor return softwareResponse; } - private void addAuthorToSoftware(Map binding, SoftwareResponseDTO software) { - if (!Objects.nonNull(binding.get("author"))) { - return; - } - - if (software.authors.stream().anyMatch(author -> author.name.equals(binding.get("author")))) { - return; - } - - AuthorDTO author = new AuthorDTO(); - author.name = binding.getOrDefault("author", null); - author.type = binding.getOrDefault("authorType", null); - author.identifier = binding.getOrDefault("authorIdentifier", null); - software.authors.add(author); - } - - private void addFundingToSoftware(Map binding, SoftwareResponseDTO software) { - if (!Objects.nonNull(binding.get("funding"))) { - return; - } - - if (software.fundings.stream().anyMatch(funding -> funding.equals(binding.get("funding")))) { - return; - } - - software.fundings.add(binding.get("funding")); - } - - private void addFundersToSoftware(Map binding, SoftwareResponseDTO software) { - if (!Objects.nonNull(binding.get("funder"))) { - return; - } - - if (software.funders.stream().anyMatch(funder -> funder.name.equals(binding.get("funder")))) { - return; - } - - FunderResponseDTO funder = new FunderResponseDTO(); - funder.name = binding.get("funder"); - funder.type = binding.getOrDefault("funderType", null); - software.funders.add(funder); - } - - private String getSparqlQueryResponse(SparqlQueryApiExecutor core) throws IOException, RDFServiceException { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - core.executeAndFormat(outputStream); - - String sparqlResponse = outputStream.toString("UTF-8"); - - outputStream.close(); - - return sparqlResponse; - } - - private void do400BadRequest(String message, HttpServletResponse resp) - throws IOException { - resp.setStatus(400); - resp.getWriter().println(message); - } - - private void do400BadRequest(String message, Exception e, - HttpServletResponse resp) throws IOException { - resp.setStatus(400); - PrintWriter w = resp.getWriter(); - w.println(message); - e.printStackTrace(w); - } - - private void do500InternalServerError(String message, Exception e, - HttpServletResponse resp) throws IOException { - resp.setStatus(500); - PrintWriter w = resp.getWriter(); - w.println(message); - e.printStackTrace(w); - } - @Override protected AuthorizationRequest requiredActions(VitroRequest vreq) { return SimplePermission.USE_SPARQL_UPDATE_API.ACTION; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java index 30e727bf8f..6862948372 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java @@ -3,19 +3,7 @@ import java.util.ArrayList; import java.util.List; -public class SoftwareResponseDTO { - - public String internalIdentifier; - - public String name; - - public String datePublished; - - public List authors = new ArrayList<>(); - - public List fundings = new ArrayList<>(); - - public List funders = new ArrayList<>(); +public class SoftwareResponseDTO extends InformationContentEntityResponseDTO { public String version; From 55c5ce470870c9ec636172344ee596fea1b03418 Mon Sep 17 00:00:00 2001 From: Ivan Mrsulja Date: Wed, 2 Oct 2024 11:34:57 +0200 Subject: [PATCH 11/12] Refactored code. Moved logic that is not related to VIVO in Vitro. --- .../{software => api/dto}/AuthorDTO.java | 2 +- .../dto}/FunderRequestDTO.java | 2 +- .../dto}/FunderResponseDTO.java | 2 +- .../InformationContentEntityResponseDTO.java | 2 +- ...formationContentEntityResponseUtility.java | 2 +- .../dto}/SoftwareRequestDTO.java | 2 +- .../dto}/SoftwareResponseDTO.java | 2 +- .../software/SoftwareController.java | 11 +- .../utility}/InsertQueryBuilder.java | 5 +- .../IndividualApiCommonCRUDUtility.java | 126 ------------------ .../software/IndividualApiNetworkUtility.java | 74 ---------- .../software/IndividualApiSparqlUtility.java | 95 ------------- 12 files changed, 18 insertions(+), 307 deletions(-) rename api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/{software => api/dto}/AuthorDTO.java (63%) rename api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/{software => api/dto}/FunderRequestDTO.java (64%) rename api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/{software => api/dto}/FunderResponseDTO.java (58%) rename api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/{software => api/dto}/InformationContentEntityResponseDTO.java (86%) rename api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/{software => api/dto}/InformationContentEntityResponseUtility.java (96%) rename api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/{software => api/dto}/SoftwareRequestDTO.java (90%) rename api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/{software => api/dto}/SoftwareResponseDTO.java (86%) rename api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/{ => api}/software/SoftwareController.java (97%) rename api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/{software => api/utility}/InsertQueryBuilder.java (95%) delete mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/IndividualApiCommonCRUDUtility.java delete mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/IndividualApiNetworkUtility.java delete mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/IndividualApiSparqlUtility.java diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/AuthorDTO.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/dto/AuthorDTO.java similarity index 63% rename from api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/AuthorDTO.java rename to api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/dto/AuthorDTO.java index ca845def4e..5c25c9d703 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/AuthorDTO.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/dto/AuthorDTO.java @@ -1,4 +1,4 @@ -package edu.cornell.mannlib.vitro.webapp.controller.software; +package edu.cornell.mannlib.vitro.webapp.controller.api.dto; public class AuthorDTO { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FunderRequestDTO.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/dto/FunderRequestDTO.java similarity index 64% rename from api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FunderRequestDTO.java rename to api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/dto/FunderRequestDTO.java index 8b8d7c8c8d..6a07f04f95 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FunderRequestDTO.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/dto/FunderRequestDTO.java @@ -1,4 +1,4 @@ -package edu.cornell.mannlib.vitro.webapp.controller.software; +package edu.cornell.mannlib.vitro.webapp.controller.api.dto; public class FunderRequestDTO { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FunderResponseDTO.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/dto/FunderResponseDTO.java similarity index 58% rename from api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FunderResponseDTO.java rename to api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/dto/FunderResponseDTO.java index dd7ee79fa4..fd7436e381 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/FunderResponseDTO.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/dto/FunderResponseDTO.java @@ -1,4 +1,4 @@ -package edu.cornell.mannlib.vitro.webapp.controller.software; +package edu.cornell.mannlib.vitro.webapp.controller.api.dto; public class FunderResponseDTO { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/InformationContentEntityResponseDTO.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/dto/InformationContentEntityResponseDTO.java similarity index 86% rename from api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/InformationContentEntityResponseDTO.java rename to api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/dto/InformationContentEntityResponseDTO.java index 731ce6227b..6dc344773a 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/InformationContentEntityResponseDTO.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/dto/InformationContentEntityResponseDTO.java @@ -1,4 +1,4 @@ -package edu.cornell.mannlib.vitro.webapp.controller.software; +package edu.cornell.mannlib.vitro.webapp.controller.api.dto; import java.util.ArrayList; import java.util.List; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/InformationContentEntityResponseUtility.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/dto/InformationContentEntityResponseUtility.java similarity index 96% rename from api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/InformationContentEntityResponseUtility.java rename to api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/dto/InformationContentEntityResponseUtility.java index 56d76dccf4..b9de7dddc5 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/InformationContentEntityResponseUtility.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/dto/InformationContentEntityResponseUtility.java @@ -1,4 +1,4 @@ -package edu.cornell.mannlib.vitro.webapp.controller.software; +package edu.cornell.mannlib.vitro.webapp.controller.api.dto; import java.util.Map; import java.util.Objects; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareRequestDTO.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/dto/SoftwareRequestDTO.java similarity index 90% rename from api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareRequestDTO.java rename to api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/dto/SoftwareRequestDTO.java index 582bb6716d..6fe0494055 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareRequestDTO.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/dto/SoftwareRequestDTO.java @@ -1,4 +1,4 @@ -package edu.cornell.mannlib.vitro.webapp.controller.software; +package edu.cornell.mannlib.vitro.webapp.controller.api.dto; import java.util.ArrayList; import java.util.List; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/dto/SoftwareResponseDTO.java similarity index 86% rename from api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java rename to api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/dto/SoftwareResponseDTO.java index 6862948372..ca53382efc 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareResponseDTO.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/dto/SoftwareResponseDTO.java @@ -1,4 +1,4 @@ -package edu.cornell.mannlib.vitro.webapp.controller.software; +package edu.cornell.mannlib.vitro.webapp.controller.api.dto; import java.util.ArrayList; import java.util.List; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/software/SoftwareController.java similarity index 97% rename from api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java rename to api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/software/SoftwareController.java index e8852d28cc..1afe7f276b 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/SoftwareController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/software/SoftwareController.java @@ -1,4 +1,4 @@ -package edu.cornell.mannlib.vitro.webapp.controller.software; +package edu.cornell.mannlib.vitro.webapp.controller.api.software; import java.io.IOException; import java.util.ArrayList; @@ -15,9 +15,13 @@ import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission; import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.AuthorizationRequest; import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; +import edu.cornell.mannlib.vitro.webapp.controller.VitroHttpServlet; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.controller.api.dto.InformationContentEntityResponseUtility; +import edu.cornell.mannlib.vitro.webapp.controller.api.dto.SoftwareRequestDTO; +import edu.cornell.mannlib.vitro.webapp.controller.api.dto.SoftwareResponseDTO; import edu.cornell.mannlib.vitro.webapp.controller.api.sparqlquery.SparqlQueryApiExecutor; -import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet; +import edu.cornell.mannlib.vitro.webapp.controller.api.utility.InsertQueryBuilder; import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; @@ -27,7 +31,7 @@ import org.apache.jena.ontology.OntModel; @WebServlet(name = "softwareController", urlPatterns = {"/software", "/software/*"}, loadOnStartup = 5) -public class SoftwareController extends FreemarkerHttpServlet { +public class SoftwareController extends VitroHttpServlet { private static final Log log = LogFactory.getLog(SoftwareController.class); @@ -367,7 +371,6 @@ private List handleDTOConversion(SparqlQueryApiExecutor cor return softwareResponse; } - @Override protected AuthorizationRequest requiredActions(VitroRequest vreq) { return SimplePermission.USE_SPARQL_UPDATE_API.ACTION; } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/InsertQueryBuilder.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/utility/InsertQueryBuilder.java similarity index 95% rename from api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/InsertQueryBuilder.java rename to api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/utility/InsertQueryBuilder.java index 5eef08169b..a2c85c7f7a 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/InsertQueryBuilder.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/utility/InsertQueryBuilder.java @@ -1,9 +1,12 @@ -package edu.cornell.mannlib.vitro.webapp.controller.software; +package edu.cornell.mannlib.vitro.webapp.controller.api.utility; import java.util.List; import java.util.Objects; import java.util.UUID; +import edu.cornell.mannlib.vitro.webapp.controller.api.dto.AuthorDTO; +import edu.cornell.mannlib.vitro.webapp.controller.api.dto.FunderRequestDTO; +import edu.cornell.mannlib.vitro.webapp.controller.api.software.IndividualApiSparqlUtility; import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames; import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.logging.Log; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/IndividualApiCommonCRUDUtility.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/IndividualApiCommonCRUDUtility.java deleted file mode 100644 index b32e0e7219..0000000000 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/IndividualApiCommonCRUDUtility.java +++ /dev/null @@ -1,126 +0,0 @@ -package edu.cornell.mannlib.vitro.webapp.controller.software; - -import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLEncoder; -import java.util.Objects; -import java.util.UUID; -import java.util.function.BiConsumer; - -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils; -import edu.cornell.mannlib.vitro.webapp.beans.ApplicationBean; -import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; -import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceDataset; -import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexer; -import org.apache.jena.query.Dataset; -import org.apache.jena.query.ReadWrite; -import org.apache.jena.update.GraphStore; -import org.apache.jena.update.GraphStoreFactory; - -public class IndividualApiCommonCRUDUtility { - - public static void executeWithTransaction(HttpServletRequest req, HttpServletResponse resp, - BiConsumer action) throws IOException { - VitroRequest vreq = new VitroRequest(req); - SearchIndexer indexer = ApplicationUtils.instance().getSearchIndexer(); - Dataset ds = new RDFServiceDataset(vreq.getUnfilteredRDFService()); - GraphStore graphStore = GraphStoreFactory.create(ds); - - String entityUri; - if (req.getMethod().equalsIgnoreCase("POST")) { - entityUri = IndividualApiSparqlUtility.buildIndividualUri(UUID.randomUUID().toString()); - } else { - String pathInfo = req.getPathInfo(); - if (Objects.isNull(pathInfo) || pathInfo.isEmpty()) { - IndividualApiNetworkUtility.do400BadRequest("You have to provide a record identifier.", resp); - return; - } - - entityUri = IndividualApiSparqlUtility.buildIndividualUri(pathInfo.substring(1)); - } - - try { - pauseIndexer(indexer); - beginTransaction(ds); - - action.accept(graphStore, entityUri); - - } finally { - commitTransaction(ds); - unpauseIndexer(indexer); - } - } - - private static void pauseIndexer(SearchIndexer indexer) { - if (indexer != null) { - indexer.pause(); - } - } - - private static void unpauseIndexer(SearchIndexer indexer) { - if (indexer != null) { - indexer.unpause(); - } - } - - private static void beginTransaction(Dataset ds) { - if (ds.supportsTransactions()) { - ds.begin(ReadWrite.WRITE); - } - } - - private static void commitTransaction(Dataset ds) { - if (ds.supportsTransactions()) { - ds.commit(); - ds.end(); - } - } - - public static void performDeleteOperation(HttpServletRequest req, HttpServletResponse resp) - throws IOException { - String pathInfo = req.getPathInfo(); - if (Objects.isNull(pathInfo) || pathInfo.isEmpty()) { - IndividualApiNetworkUtility.do400BadRequest("You have to provide a record identifier.", resp); - return; - } - - VitroRequest vreq = new VitroRequest(req); - ApplicationBean appBean = vreq.getAppBean(); - String appName = appBean.getApplicationName().toLowerCase(); - URL url = new URL("http://" + req.getServerName() + ":" + req.getServerPort() + "/" + appName + - "/deleteIndividualController?individualUri=" + - URLEncoder.encode(IndividualApiSparqlUtility.buildIndividualUri(pathInfo.substring(1))) + - "&redirectUrl=%2F"); - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod("GET"); - connection.setRequestProperty("Accept", "application/json"); - - addCookiesToRequest(req, connection); - - connection.getResponseCode(); - - resp.setStatus(HttpServletResponse.SC_NO_CONTENT); - - connection.disconnect(); - } - - private static void addCookiesToRequest(HttpServletRequest req, HttpURLConnection connection) { - Cookie[] cookies = req.getCookies(); - if (cookies != null) { - StringBuilder cookieHeader = new StringBuilder(); - for (Cookie cookie : cookies) { - cookieHeader.append(cookie.getName()).append("=").append(cookie.getValue()).append("; "); - } - // Remove the trailing "; " at the end of the cookie string - if (cookieHeader.length() > 0) { - cookieHeader.setLength(cookieHeader.length() - 2); - } - connection.setRequestProperty("Cookie", cookieHeader.toString()); - } - } -} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/IndividualApiNetworkUtility.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/IndividualApiNetworkUtility.java deleted file mode 100644 index e498d9a0f7..0000000000 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/IndividualApiNetworkUtility.java +++ /dev/null @@ -1,74 +0,0 @@ -package edu.cornell.mannlib.vitro.webapp.controller.software; - -import java.io.IOException; -import java.io.PrintWriter; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import edu.cornell.mannlib.vitro.webapp.controller.api.sparqlquery.InvalidQueryTypeException; -import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; -import org.apache.jena.query.QueryParseException; - -public class IndividualApiNetworkUtility { - - public static void handleResponseContentType(HttpServletRequest req, HttpServletResponse resp) { - String acceptHeader = req.getHeader("Accept"); - if (acceptHeader != null && !acceptHeader.isEmpty()) { - resp.setContentType(acceptHeader); - } else { - resp.setContentType("application/json"); - } - } - - public static String serializeToJSON(Object serializationObject) throws IOException { - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.enable(SerializationFeature.INDENT_OUTPUT); - objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); - return objectMapper.writeValueAsString(serializationObject); - } - - public static boolean isJsonRequest(HttpServletRequest req) { - String acceptHeader = req.getHeader("Accept"); - return acceptHeader != null && acceptHeader.equals("application/json"); - } - - public static T parseRequestBody(HttpServletRequest req, Class clazz) throws IOException { - ObjectMapper objectMapper = new ObjectMapper(); - return objectMapper.readValue(req.getInputStream(), clazz); - } - - public static void handleException(Exception e, String queryString, HttpServletResponse resp) throws IOException { - if (e instanceof InvalidQueryTypeException) { - do400BadRequest("Invalid query type: '" + queryString + "'", resp); - } else if (e instanceof QueryParseException) { - do400BadRequest("Failed to parse query: '" + queryString + "'", resp); - } else if (e instanceof RDFServiceException) { - do500InternalServerError("Problem executing the query.", e, resp); - } - } - - public static void do400BadRequest(String message, HttpServletResponse resp) - throws IOException { - resp.setStatus(400); - resp.getWriter().println(message); - } - - public static void do404NotFound(String message, HttpServletResponse resp) - throws IOException { - resp.setStatus(404); - resp.getWriter().println(message); - } - - public static void do500InternalServerError(String message, Exception e, - HttpServletResponse resp) throws IOException { - resp.setStatus(500); - PrintWriter w = resp.getWriter(); - w.println(message); - e.printStackTrace(w); - } -} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/IndividualApiSparqlUtility.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/IndividualApiSparqlUtility.java deleted file mode 100644 index c1aaac8a2a..0000000000 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/software/IndividualApiSparqlUtility.java +++ /dev/null @@ -1,95 +0,0 @@ -package edu.cornell.mannlib.vitro.webapp.controller.software; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; -import edu.cornell.mannlib.vitro.webapp.controller.api.sparqlquery.SparqlQueryApiExecutor; -import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; -import org.apache.commons.lang3.StringEscapeUtils; -import org.apache.jena.update.GraphStore; -import org.apache.jena.update.UpdateAction; -import org.apache.jena.update.UpdateFactory; -import org.apache.jena.update.UpdateRequest; - -public class IndividualApiSparqlUtility { - - public static String getSparqlQueryResponse(SparqlQueryApiExecutor core) throws IOException, RDFServiceException { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - core.executeAndFormat(outputStream); - - String sparqlResponse = outputStream.toString("UTF-8"); - - outputStream.close(); - - return sparqlResponse; - } - - public static List> parseBindings(String jsonResponse) throws IOException { - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode rootNode = objectMapper.readTree(jsonResponse); - - JsonNode bindingsNode = rootNode.path("results").path("bindings"); - - List> recordsList = new ArrayList<>(); - - for (JsonNode bindingNode : bindingsNode) { - Map recordMap = new HashMap<>(); - - Iterator> fieldsIterator = bindingNode.fields(); - while (fieldsIterator.hasNext()) { - Map.Entry field = fieldsIterator.next(); - - String fieldName = field.getKey(); - String fieldValue = field.getValue().path("value").asText(); - - recordMap.put(fieldName, fieldValue); - } - - recordsList.add(recordMap); - } - - return recordsList; - } - - public static void addPrefixClauses(StringBuilder queryBuilder) { - queryBuilder - .append("PREFIX rdf: \n") - .append("PREFIX rdfs: \n") - .append("PREFIX xsd: \n") - .append("PREFIX owl: \n") - .append("PREFIX swrl: \n") - .append("PREFIX swrlb: \n") - .append("PREFIX vitro: \n") - .append("PREFIX bibo: \n") - .append("PREFIX c4o: \n") - .append("PREFIX cito: \n") - .append("PREFIX dcterms: \n") - .append("PREFIX event: \n") - .append("PREFIX fabio: \n") - .append("PREFIX foaf: \n") - .append("PREFIX geo: \n") - .append("PREFIX obo: \n") - .append("PREFIX vivo: \n") - .append("PREFIX vcard: \n"); - } - - public static void executeUpdate(GraphStore graphStore, String query) { - UpdateRequest updateRequest = UpdateFactory.create(query); - UpdateAction.execute(updateRequest, graphStore); - } - - public static String buildIndividualUri(String entityId) { - String defaultNamespace = - Objects.requireNonNull(ConfigurationProperties.getInstance().getProperty("Vitro.defaultNamespace")); - return defaultNamespace + StringEscapeUtils.escapeJava(entityId); - } -} From d64d5caa59dbb0b1987770e90480ee8655fe93a3 Mon Sep 17 00:00:00 2001 From: Ivan Mrsulja Date: Wed, 2 Oct 2024 12:14:16 +0200 Subject: [PATCH 12/12] Fixed minor security issue. Small refactor. --- .../webapp/controller/api/software/SoftwareController.java | 2 +- .../InformationContentEntityResponseUtility.java | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) rename api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/{dto => utility}/InformationContentEntityResponseUtility.java (84%) diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/software/SoftwareController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/software/SoftwareController.java index 1afe7f276b..eab83afd4f 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/software/SoftwareController.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/software/SoftwareController.java @@ -17,10 +17,10 @@ import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; import edu.cornell.mannlib.vitro.webapp.controller.VitroHttpServlet; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; -import edu.cornell.mannlib.vitro.webapp.controller.api.dto.InformationContentEntityResponseUtility; import edu.cornell.mannlib.vitro.webapp.controller.api.dto.SoftwareRequestDTO; import edu.cornell.mannlib.vitro.webapp.controller.api.dto.SoftwareResponseDTO; import edu.cornell.mannlib.vitro.webapp.controller.api.sparqlquery.SparqlQueryApiExecutor; +import edu.cornell.mannlib.vitro.webapp.controller.api.utility.InformationContentEntityResponseUtility; import edu.cornell.mannlib.vitro.webapp.controller.api.utility.InsertQueryBuilder; import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/dto/InformationContentEntityResponseUtility.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/utility/InformationContentEntityResponseUtility.java similarity index 84% rename from api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/dto/InformationContentEntityResponseUtility.java rename to api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/utility/InformationContentEntityResponseUtility.java index b9de7dddc5..bf3602df31 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/dto/InformationContentEntityResponseUtility.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/api/utility/InformationContentEntityResponseUtility.java @@ -1,8 +1,12 @@ -package edu.cornell.mannlib.vitro.webapp.controller.api.dto; +package edu.cornell.mannlib.vitro.webapp.controller.api.utility; import java.util.Map; import java.util.Objects; +import edu.cornell.mannlib.vitro.webapp.controller.api.dto.AuthorDTO; +import edu.cornell.mannlib.vitro.webapp.controller.api.dto.FunderResponseDTO; +import edu.cornell.mannlib.vitro.webapp.controller.api.dto.InformationContentEntityResponseDTO; + public class InformationContentEntityResponseUtility { public static void addAuthorToICE(Map binding, InformationContentEntityResponseDTO entity) {