• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

knowledgepixels / nanopub-query / 17767759901

16 Sep 2025 01:40PM UTC coverage: 71.59% (-0.2%) from 71.741%
17767759901

push

github

Ziroli Plutschow
Replacing newest System.err with slf4j logging

- Note: Also upgraded mockito, since it's transient dependency byte-buddy could not handle a jvm > 21

233 of 350 branches covered (66.57%)

Branch coverage included in aggregate %.

591 of 801 relevant lines covered (73.78%)

3.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

60.0
src/main/java/com/knowledgepixels/query/GrlcSpec.java
1
package com.knowledgepixels.query;
2

3
import java.util.ArrayList;
4
import java.util.Collections;
5
import java.util.Comparator;
6
import java.util.HashSet;
7
import java.util.List;
8
import java.util.Set;
9

10
import org.eclipse.rdf4j.model.IRI;
11
import org.eclipse.rdf4j.model.Statement;
12
import org.eclipse.rdf4j.model.ValueFactory;
13
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
14
import org.eclipse.rdf4j.model.vocabulary.DCTERMS;
15
import org.eclipse.rdf4j.model.vocabulary.RDFS;
16
import org.eclipse.rdf4j.query.algebra.Var;
17
import org.eclipse.rdf4j.query.algebra.helpers.AbstractSimpleQueryModelVisitor;
18
import org.eclipse.rdf4j.query.parser.ParsedQuery;
19
import org.eclipse.rdf4j.query.parser.sparql.SPARQLParser;
20
import org.nanopub.Nanopub;
21
import org.nanopub.SimpleCreatorPattern;
22
import org.nanopub.extra.server.GetNanopub;
23
import org.nanopub.extra.services.QueryAccess;
24

25
import io.vertx.core.MultiMap;
26
import net.trustyuri.TrustyUriUtils;
27
import org.slf4j.Logger;
28
import org.slf4j.LoggerFactory;
29

30
/**
31
 * This class produces a page with the grlc specification. This is needed internally to tell grlc
32
 * how to execute a particular query template.
33
 */
34
public class GrlcSpec {
35

36
    private static final ValueFactory vf = SimpleValueFactory.getInstance();
2✔
37

38
    private static final Logger log = LoggerFactory.getLogger(GrlcSpec.class);
3✔
39

40
    /**
41
     * IRI for relation to link a grlc query instance to its SPARQL template.
42
     */
43
    public static final IRI HAS_SPARQL = vf.createIRI("https://w3id.org/kpxl/grlc/sparql");
4✔
44

45
    /**
46
     * IRI for relation to link a grlc query instance to its SPARQL endpoint URL.
47
     */
48
    public static final IRI HAS_ENDPOINT = vf.createIRI("https://w3id.org/kpxl/grlc/endpoint");
4✔
49

50
    /**
51
     * URL for the given Nanopub Query instance, needed for internal coordination.
52
     */
53
    public static final String nanopubQueryUrl = Utils.getEnvString("NANOPUB_QUERY_URL", "http://query:9393/");
5✔
54

55
    private MultiMap parameters;
56
    private Nanopub np;
57
    private String requestUrlBase;
58
    private String artifactCode, queryPart;
59
    private String queryName;
60
    private String label;
61
    private String desc;
62
    private String license;
63
    private String queryContent;
64
    private String endpoint;
65
    private List<String> placeholdersList;
66

67
    /**
68
     * Creates a new page instance.
69
     *
70
     * @param requestUrl The request URL
71
     * @param parameters The URL request parameters
72
     */
73
    public GrlcSpec(String requestUrl, MultiMap parameters) {
2✔
74
        requestUrl = requestUrl.replaceFirst("\\?.*$", "");
5✔
75
        this.parameters = parameters;
3✔
76
        if (!requestUrl.matches(".*/RA[A-Za-z0-9\\-_]{43}/(.*)?")) {
4✔
77
            // TODO Raise exception
78
            return;
1✔
79
        }
80
        artifactCode = requestUrl.replaceFirst("^(.*/)(RA[A-Za-z0-9\\-_]{43})/(.*)?$", "$2");
6✔
81
        queryPart = requestUrl.replaceFirst("^(.*/)(RA[A-Za-z0-9\\-_]{43}/)(.*)?$", "$3");
6✔
82
        requestUrlBase = requestUrl.replaceFirst("^/(.*/)(RA[A-Za-z0-9\\-_]{43})/(.*)?$", "$1");
6✔
83

84
        // TODO Get the nanopub from the local store:
85
        np = GetNanopub.get(artifactCode);
5✔
86
        if (parameters.get("api-version") != null && parameters.get("api-version").equals("latest")) {
10✔
87
            // TODO Get the latest version from the local store:
88
            np = GetNanopub.get(QueryAccess.getLatestVersionId(np.getUri().stringValue()));
8✔
89
            artifactCode = TrustyUriUtils.getArtifactCode(np.getUri().stringValue());
7✔
90
        }
91
        for (Statement st : np.getAssertion()) {
12✔
92
            if (!st.getSubject().stringValue().startsWith(np.getUri().stringValue())) continue;
9!
93
            String qn = st.getSubject().stringValue().replaceFirst("^.*[#/](.*)$", "$1");
7✔
94
            if (queryName != null && !qn.equals(queryName)) {
8!
95
                np = null;
×
96
                break;
×
97
            }
98
            queryName = qn;
3✔
99
            if (st.getPredicate().equals(RDFS.LABEL)) {
5✔
100
                label = st.getObject().stringValue();
6✔
101
            } else if (st.getPredicate().equals(DCTERMS.DESCRIPTION)) {
5✔
102
                desc = st.getObject().stringValue();
6✔
103
            } else if (st.getPredicate().equals(DCTERMS.LICENSE) && st.getObject() instanceof IRI) {
9!
104
                license = st.getObject().stringValue();
6✔
105
            } else if (st.getPredicate().equals(HAS_SPARQL)) {
5✔
106
                // TODO Improve this:
107
                queryContent = st.getObject().stringValue().replace("https://w3id.org/np/l/nanopub-query-1.1/repo/", nanopubQueryUrl + "repo/");
10✔
108
            } else if (st.getPredicate().equals(HAS_ENDPOINT) && st.getObject() instanceof IRI) {
9!
109
                endpoint = st.getObject().stringValue();
5✔
110
                if (endpoint.startsWith("https://w3id.org/np/l/nanopub-query-1.1/repo/")) {
5!
111
                    endpoint = endpoint.replace("https://w3id.org/np/l/nanopub-query-1.1/repo/", nanopubQueryUrl + "repo/");
9✔
112
                } else {
113
                    // TODO Raise exception
114
                    endpoint = null;
×
115
                }
116
            }
117
        }
1✔
118

119
        final Set<String> placeholders = new HashSet<>();
4✔
120
        // TODO Make sure we properly handle MalformedQueryException thrown here:
121
        ParsedQuery query = new SPARQLParser().parseQuery(queryContent, null);
8✔
122
        query.getTupleExpr().visitChildren(new AbstractSimpleQueryModelVisitor<>() {
14✔
123

124
            @Override
125
            public void meet(Var node) throws RuntimeException {
126
                super.meet(node);
3✔
127
                if (!node.isConstant() && !node.isAnonymous() && node.getName().startsWith("_")) {
11!
128
                    placeholders.add(node.getName());
×
129
                }
130
            }
1✔
131

132
        });
133
        List<String> placeholdersListPre = new ArrayList<>(placeholders);
5✔
134
        Collections.sort(placeholdersListPre);
2✔
135
        placeholdersListPre.sort(Comparator.comparing(String::length));
4✔
136
        placeholdersList = Collections.unmodifiableList(placeholdersListPre);
4✔
137
    }
1✔
138

139
    /**
140
     * Returns the grlc spec as a string.
141
     *
142
     * @return grlc specification string
143
     */
144
    public String getSpec() {
145
        if (np == null) return null;
5✔
146
        String s = "";
2✔
147
        if (queryPart.isEmpty()) {
4✔
148
            if (label == null) {
3!
149
                s += "title: \"untitled query\"\n";
×
150
            } else {
151
                s += "title: \"" + escapeLiteral(label) + "\"\n";
6✔
152
            }
153
            s += "description: \"" + escapeLiteral(desc) + "\"\n";
6✔
154
            StringBuilder userName = new StringBuilder();
4✔
155
            Set<IRI> creators = SimpleCreatorPattern.getCreators(np);
4✔
156
            for (IRI userIri : creators) {
10✔
157
                userName.append(", ").append(userIri);
6✔
158
            }
1✔
159
            if (!userName.isEmpty()) userName = new StringBuilder(userName.substring(2));
10!
160
            String url = "";
2✔
161
            if (!creators.isEmpty()) url = creators.iterator().next().stringValue();
9!
162
            s += "contact:\n";
3✔
163
            s += "  name: \"" + escapeLiteral(userName.toString()) + "\"\n";
6✔
164
            s += "  url: " + url + "\n";
4✔
165
            if (license != null) {
3!
166
                s += "licence: " + license + "\n";
5✔
167
            }
168
            s += "queries:\n";
3✔
169
            s += "  - " + nanopubQueryUrl + requestUrlBase + artifactCode + "/" + queryName + ".rq";
10✔
170
        } else if (queryPart.equals(queryName + ".rq")) {
8✔
171
            if (label != null) {
3!
172
                s += "#+ summary: \"" + escapeLiteral(label) + "\"\n";
6✔
173
            }
174
            if (desc != null) {
3!
175
                s += "#+ description: \"" + escapeLiteral(desc) + "\"\n";
6✔
176
            }
177
            if (license != null) {
3!
178
                s += "#+ licence: " + license + "\n";
5✔
179
            }
180
            if (endpoint != null) {
3!
181
                s += "#+ endpoint: " + endpoint + "\n";
5✔
182
            }
183
            s += "\n";
3✔
184
            s += queryContent;
6✔
185
        } else {
186
            return null;
2✔
187
        }
188
        return s;
2✔
189
    }
190

191
    public String getRepoName() {
192
        return endpoint.replaceAll("/", "_").replaceFirst("^.*_repo_", "");
×
193
    }
194

195
    public String getQueryContent() {
196
        return queryContent;
×
197
    }
198

199
    public String getExpandedQueryContent() {
200
        String expanded = queryContent;
×
201
        for (String ph : placeholdersList) {
×
202
            log.info("ph: ", ph);
×
203
            log.info("getParamName(ph): ", getParamName(ph));
×
204
            if (isMultiPlaceholder(ph)) {
×
205
                // TODO multi placeholders need proper documentation
206
                List<String> val = parameters.getAll(getParamName(ph));
×
207
                if (!isOptionalPlaceholder(ph) && val.isEmpty()) {
×
208
                    // TODO throw exception
209
                    return null;
×
210
                }
211
                if (val.isEmpty()) {
×
212
                    expanded = expanded.replaceAll("values\\s*\\?" + ph + "\\s*\\{\\s*\\}(\\s*\\.)?", "");
×
213
                    continue;
×
214
                }
215
                String valueList = "";
×
216
                for (String v : val) {
×
217
                    if (isIriPlaceholder(ph)) {
×
218
                        valueList += serializeIri(v) + " ";
×
219
                    } else {
220
                        valueList += serializeLiteral(v) + " ";
×
221
                    }
222
                }
×
223
                expanded = expanded.replaceAll("values\\s*\\?" + ph + "\\s*\\{\\s*\\}", "values ?" + ph + " { " + valueList + "}");
×
224
            } else {
×
225
                String val = parameters.get(getParamName(ph));
×
226
                log.info("val: ", val);
×
227
                if (!isOptionalPlaceholder(ph) && val == null) {
×
228
                    // TODO throw exception
229
                    return null;
×
230
                }
231
                if (val == null) continue;
×
232
                if (isIriPlaceholder(ph)) {
×
233
                    expanded = expanded.replaceAll("\\?" + ph, serializeIri(val));
×
234
                } else {
235
                    expanded = expanded.replaceAll("\\?" + ph, serializeLiteral(val));
×
236
                }
237
            }
238
        }
×
239
        log.info("expanded: ", expanded);
×
240
        return expanded;
×
241
    }
242

243
    public static String escapeLiteral(String s) {
244
        return s.replace("\\", "\\\\").replace("\n", "\\n").replace("\"", "\\\"");
11✔
245
    }
246

247
    public static boolean isOptionalPlaceholder(String placeholder) {
248
        return placeholder.startsWith("__");
×
249
    }
250

251
    public static boolean isMultiPlaceholder(String placeholder) {
252
        return placeholder.endsWith("_multi") || placeholder.endsWith("_multi_iri");
×
253
    }
254

255
    public static boolean isIriPlaceholder(String placeholder) {
256
        return placeholder.endsWith("_iri");
×
257
    }
258

259
    public static String getParamName(String placeholder) {
260
        return placeholder.replaceFirst("^_+", "").replaceFirst("_iri$", "").replaceFirst("_multi$", "");
×
261
    }
262

263
    public static String serializeIri(String iriString) {
264
        return "<" + iriString + ">";
×
265
    }
266

267
    public static String serializeLiteral(String literalString) {
268
        return "\"" + escapeLiteral(literalString) + "\"";
×
269
    }
270

271
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc