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

common-workflow-language / cwlviewer / #1998

13 May 2026 06:03PM UTC coverage: 70.05% (-0.3%) from 70.334%
#1998

Pull #751

github

kinow
Remove custom implementations with native queries, now Hibernate + JPA works!
Pull Request #751: Bump org.springframework.boot:spring-boot-starter-parent from 3.1.4 to 4.1.0-RC1

117 of 194 new or added lines in 30 files covered. (60.31%)

20 existing lines in 4 files now uncovered.

1691 of 2414 relevant lines covered (70.05%)

0.7 hits per line

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

50.51
/src/main/java/org/commonwl/view/cwl/RDFService.java
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one
3
 * or more contributor license agreements.  See the NOTICE file
4
 * distributed with this work for additional information
5
 * regarding copyright ownership.  The ASF licenses this file
6
 * to you under the Apache License, Version 2.0 (the
7
 * "License"); you may not use this file except in compliance
8
 * with the License.  You may obtain a copy of the License at
9
 *
10
 *   http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing,
13
 * software distributed under the License is distributed on an
14
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
 * KIND, either express or implied.  See the License for the
16
 * specific language governing permissions and limitations
17
 * under the License.
18
 */
19

20
package org.commonwl.view.cwl;
21

22
import org.apache.commons.io.output.ByteArrayOutputStream;
23
import org.apache.jena.graph.Node;
24
import org.apache.jena.graph.NodeFactory;
25
import org.apache.jena.query.DatasetAccessor;
26
import org.apache.jena.query.DatasetAccessorFactory;
27
import org.apache.jena.query.ParameterizedSparqlString;
28
import org.apache.jena.query.Query;
29
import org.apache.jena.query.QueryExecution;
30
import org.apache.jena.query.QueryExecutionFactory;
31
import org.apache.jena.query.QueryFactory;
32
import org.apache.jena.query.ResultSet;
33
import org.apache.jena.query.ResultSetFactory;
34
import org.apache.jena.rdf.model.Model;
35
import org.apache.jena.riot.RDFFormat;
36
import org.apache.jena.web.DatasetGraphAccessorHTTP;
37
import org.springframework.beans.factory.annotation.Autowired;
38
import org.springframework.beans.factory.annotation.Value;
39
import org.springframework.stereotype.Service;
40

41
/** Handles the parsing of CWL RDF files */
42
@Service
43
public class RDFService {
44

45
  // Context for SPARQL queries
46
  private final String queryCtx =
1✔
47
      """
48
          PREFIX cwl: <https://w3id.org/cwl/cwl#>
49
          PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
50
          PREFIX sld: <https://w3id.org/cwl/salad#>
51
          PREFIX dct: <http://purl.org/dc/terms/>
52
          PREFIX doap: <http://usefulinc.com/ns/doap#>
53
          PREFIX Workflow: <https://w3id.org/cwl/cwl#Workflow/>
54
          PREFIX DockerRequirement: <https://w3id.org/cwl/cwl#DockerRequirement/>
55
          PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
56
          PREFIX s: <http://schema.org/>""";
57

58
  private final String rdfService;
59

60
  /**
61
   * Create the RDFService with configuration
62
   *
63
   * @param rdfService The SPARQL endpoint from configuration
64
   */
65
  @Autowired
66
  public RDFService(@Value("${sparql.endpoint}") String rdfService) {
1✔
67
    this.rdfService = rdfService;
1✔
68
  }
1✔
69

70
  /**
71
   * Add to ontologies in the triple store
72
   *
73
   * @param model The model to be stored
74
   */
75
  public void addToOntologies(Model model) {
76
    DatasetAccessor accessor = DatasetAccessorFactory.createHTTP(rdfService);
×
77
    accessor.add("ontologies", model);
×
78
  }
×
79

80
  /**
81
   * Get a model from the triple store in a given format
82
   *
83
   * @param graphName The name of the graph for the model
84
   * @param format The name of the writer (format to be written)
85
   * @return A byte array representing the model in the given format
86
   */
87
  public byte[] getModel(String graphName, String format) {
88
    DatasetAccessor accessor = DatasetAccessorFactory.createHTTP(rdfService);
×
89
    Model model = accessor.getModel(graphName);
×
90
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
×
91
    model.write(outputStream, format);
×
92
    return outputStream.toByteArray();
×
93
  }
94

95
  /**
96
   * Store a model with triples in the triple store
97
   *
98
   * @param graphName The name of the graph to store the model in
99
   * @param model The model to be stored
100
   */
101
  public void storeModel(String graphName, Model model) {
102
    DatasetGraphAccessorHTTP accessor = new DatasetGraphAccessorHTTP(rdfService);
×
103
    accessor.setOutboundSyntax(RDFFormat.TURTLE);
×
104
    Node name = NodeFactory.createURI(graphName);
×
105
    accessor.httpPut(name, model.getGraph());
×
106
  }
×
107

108
  /**
109
   * Check if a graph exists within the triple store
110
   *
111
   * @param graphName The name of the graph
112
   * @return Whether the graph exists
113
   */
114
  public boolean graphExists(String graphName) {
115
    ParameterizedSparqlString graphQuery = new ParameterizedSparqlString();
×
116
    graphQuery.setCommandText("ASK WHERE { GRAPH ?graphName { ?s ?p ?o } }");
×
117
    graphQuery.setIri("graphName", graphName);
×
118
    Query query = QueryFactory.create(graphQuery.toString());
×
119
    try (QueryExecution qexec = QueryExecutionFactory.createServiceRequest(rdfService, query)) {
×
120
      return qexec.execAsk();
×
121
    }
122
  }
123

124
  /**
125
   * Check if a property of the ontology exists within the triple store
126
   *
127
   * @param ontUri The URI of the property
128
   * @return Whether the graph exists
129
   */
130
  public boolean ontPropertyExists(String ontUri) {
131
    ParameterizedSparqlString graphQuery = new ParameterizedSparqlString();
×
132
    graphQuery.setCommandText(
×
133
        "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>"
134
            + "ASK WHERE { GRAPH ?graphName { ?ont rdfs:label ?label } }");
135
    graphQuery.setIri("ont", ontUri);
×
136
    graphQuery.setIri("graphName", rdfService + "ontologies");
×
137
    Query query = QueryFactory.create(graphQuery.toString());
×
138
    try (QueryExecution qexec = QueryExecutionFactory.createServiceRequest(rdfService, query)) {
×
139
      return qexec.execAsk();
×
140
    }
141
  }
142

143
  /**
144
   * Get the label and doc strings for a workflow resource
145
   *
146
   * @param workflowURI The URI of the workflow
147
   * @return Result set with label and doc strings
148
   */
149
  public ResultSet getLabelAndDoc(String workflowURI) {
150
    ParameterizedSparqlString labelQuery = new ParameterizedSparqlString();
1✔
151
    labelQuery.setCommandText(
1✔
152
        queryCtx
153
            + "SELECT ?label ?doc\n"
154
            + "WHERE {\n"
155
            + "  GRAPH ?wf {"
156
            + "    ?wf rdf:type ?type .\n"
157
            + "    OPTIONAL { ?wf sld:label|rdfs:label ?label }\n"
158
            + "    OPTIONAL { ?wf sld:doc|rdfs:comment ?doc }\n"
159
            + "  }"
160
            + "}");
161
    labelQuery.setIri("wf", workflowURI);
1✔
162
    return runQuery(labelQuery);
1✔
163
  }
164

165
  /**
166
   * Get the label for an ontology URL TODO: can be merged with getLabelAndDoc when
167
   * common-workflow-language/cwltool#427 is resolved
168
   *
169
   * @param ontologyURI The format URI for the ontology
170
   * @return Result set with label and doc strings
171
   */
172
  public String getOntLabel(String ontologyURI) {
173
    ParameterizedSparqlString labelQuery = new ParameterizedSparqlString();
×
174
    labelQuery.setCommandText(
×
175
        """
176
            PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\
177
            SELECT ?label
178
            WHERE {
179
              GRAPH ?graphName {
180
                ?ont rdfs:label ?label
181
              }
182
            }
183
            """);
184
    labelQuery.setIri("ont", ontologyURI);
×
185
    labelQuery.setIri("graphName", rdfService + "ontologies");
×
186
    ResultSet result = runQuery(labelQuery);
×
187
    if (result.hasNext()) {
×
188
      return result.next().get("label").toString();
×
189
    }
190
    return null;
×
191
  }
192

193
  /**
194
   * Get the inputs for the workflow in the model
195
   *
196
   * @param workflowURI URI of the workflow
197
   * @return The result set of inputs
198
   */
199
  public ResultSet getInputs(String workflowURI) {
200
    ParameterizedSparqlString inputsQuery = new ParameterizedSparqlString();
1✔
201
    inputsQuery.setCommandText(
1✔
202
        queryCtx
203
            + "SELECT ?name ?type ?items ?null ?format ?label ?doc\n"
204
            + "WHERE {\n"
205
            + "  GRAPH ?wf {"
206
            + "    ?wf rdf:type cwl:Workflow .\n"
207
            + "    ?wf cwl:inputs ?name .\n"
208
            + "    OPTIONAL {\n"
209
            + "      { \n"
210
            + "        ?name sld:type ?type\n"
211
            + "        FILTER(?type != sld:null) \n"
212
            + "        FILTER (!isBlank(?type))\n"
213
            + "      } UNION { \n"
214
            + "        ?name sld:type ?arraytype .\n"
215
            + "        ?arraytype sld:type ?type .\n"
216
            + "        ?arraytype sld:items ?items \n"
217
            + "      }\n"
218
            + "    }\n"
219
            + "    OPTIONAL { \n"
220
            + "      ?name sld:type ?null\n"
221
            + "      FILTER(?null = sld:null)\n"
222
            + "    }\n"
223
            + "    OPTIONAL { ?name cwl:format ?format }\n"
224
            + "    OPTIONAL { ?name sld:label|rdfs:label ?label }\n"
225
            + "    OPTIONAL { ?name sld:doc|rdfs:comment ?doc }"
226
            + "  }"
227
            + "}");
228
    inputsQuery.setIri("wf", workflowURI);
1✔
229
    return runQuery(inputsQuery);
1✔
230
  }
231

232
  /**
233
   * Get the outputs for the workflow in the model
234
   *
235
   * @param workflowURI URI of the workflow
236
   * @return The result set of outputs
237
   */
238
  public ResultSet getOutputs(String workflowURI) {
239
    ParameterizedSparqlString outputsQuery = new ParameterizedSparqlString();
1✔
240
    outputsQuery.setCommandText(
1✔
241
        queryCtx
242
            + "SELECT ?name ?type ?items ?null ?format ?label ?doc\n"
243
            + "WHERE {\n"
244
            + "  GRAPH ?wf {"
245
            + "    ?wf rdf:type cwl:Workflow .\n"
246
            + "    ?wf cwl:outputs ?name .\n"
247
            + "    OPTIONAL {\n"
248
            + "      { \n"
249
            + "        ?name sld:type ?type\n"
250
            + "        FILTER(?type != sld:null) \n"
251
            + "        FILTER (!isBlank(?type))\n"
252
            + "      } UNION { \n"
253
            + "        ?name sld:type ?arraytype .\n"
254
            + "        ?arraytype sld:type ?type .\n"
255
            + "        ?arraytype sld:items ?items \n"
256
            + "      }\n"
257
            + "    }\n"
258
            + "    OPTIONAL { \n"
259
            + "      ?name sld:type ?null\n"
260
            + "      FILTER(?null = sld:null)\n"
261
            + "    }\n"
262
            + "    OPTIONAL { ?name cwl:format ?format }\n"
263
            + "    OPTIONAL { ?name sld:label|rdfs:label ?label }\n"
264
            + "    OPTIONAL { ?name sld:doc|rdfs:comment ?doc }"
265
            + "  }"
266
            + "}");
267
    outputsQuery.setIri("wf", workflowURI);
1✔
268
    return runQuery(outputsQuery);
1✔
269
  }
270

271
  /**
272
   * Get the steps for the workflow in the model
273
   *
274
   * @param workflowURI URI of the workflow
275
   * @return The result set of steps
276
   */
277
  public ResultSet getSteps(String workflowURI) {
278
    ParameterizedSparqlString stepQuery = new ParameterizedSparqlString();
1✔
279
    stepQuery.setCommandText(
1✔
280
        queryCtx
281
            + "SELECT ?step ?run ?runtype ?label ?doc ?stepinput ?default ?src\n"
282
            + "WHERE {\n"
283
            + "  GRAPH ?wf {"
284
            + "    ?wf Workflow:steps ?step .\n"
285
            + "    ?step cwl:run ?run .\n"
286
            + "    ?run rdf:type ?runtype .\n"
287
            + "    OPTIONAL { \n"
288
            + "        ?step cwl:in ?stepinput .\n"
289
            + "        { ?stepinput cwl:source ?src } UNION { ?stepinput cwl:default ?default }\n"
290
            + "    }\n"
291
            + "    OPTIONAL { ?run sld:label|rdfs:label ?label }\n"
292
            + "    OPTIONAL { ?run sld:doc|rdfs:comment ?doc }\n"
293
            + "  }"
294
            + "}");
295
    stepQuery.setIri("wf", workflowURI);
1✔
296
    return runQuery(stepQuery);
1✔
297
  }
298

299
  /**
300
   * Get links between steps for the workflow in the model
301
   *
302
   * @param workflowURI URI of the workflow
303
   * @return The result set of step links
304
   */
305
  public ResultSet getStepLinks(String workflowURI) {
306
    ParameterizedSparqlString linkQuery = new ParameterizedSparqlString();
1✔
307
    linkQuery.setCommandText(
1✔
308
        queryCtx
309
            + "SELECT ?src ?dest ?default\n"
310
            + "WHERE {\n"
311
            + "  GRAPH ?wf {"
312
            + "    ?wf Workflow:steps ?step .\n"
313
            + "    ?step cwl:in ?dest .\n"
314
            + "    { ?dest cwl:source ?src } UNION { ?dest cwl:default ?default }\n"
315
            + "  }"
316
            + "}");
317
    linkQuery.setIri("wf", workflowURI);
1✔
318
    return runQuery(linkQuery);
1✔
319
  }
320

321
  /**
322
   * Get links between steps and outputs for the workflow in the model
323
   *
324
   * @param workflowURI URI of the workflow
325
   * @return The result set of steps
326
   */
327
  public ResultSet getOutputLinks(String workflowURI) {
328
    ParameterizedSparqlString linkQuery = new ParameterizedSparqlString();
1✔
329
    linkQuery.setCommandText(
1✔
330
        queryCtx
331
            + "SELECT ?src ?dest\n"
332
            + "WHERE {\n"
333
            + "  GRAPH ?wf {"
334
            + "    ?wf rdf:type cwl:Workflow .\n"
335
            + "    ?wf cwl:outputs ?dest .\n"
336
            + "    ?dest cwl:outputSource ?src\n"
337
            + "  }"
338
            + "}");
339
    linkQuery.setIri("wf", workflowURI);
1✔
340
    return runQuery(linkQuery);
1✔
341
  }
342

343
  /**
344
   * Gets the docker requirement and pull link for a workflow
345
   *
346
   * @param workflowURI URI of the workflow
347
   * @return Result set of docker hint and pull link
348
   */
349
  public ResultSet getDockerLink(String workflowURI) {
350
    ParameterizedSparqlString dockerQuery = new ParameterizedSparqlString();
1✔
351
    dockerQuery.setCommandText(
1✔
352
        queryCtx
353
            + "SELECT ?docker ?pull\n"
354
            + "WHERE {\n"
355
            + "  GRAPH ?wf {"
356
            + "    ?wf rdf:type cwl:Workflow .\n"
357
            + "    { ?wf cwl:requirements ?docker } UNION { ?wf cwl:hints ?docker} .\n"
358
            + "    ?docker rdf:type cwl:DockerRequirement\n"
359
            + "    OPTIONAL { ?docker DockerRequirement:dockerPull ?pull }\n"
360
            + "  }"
361
            + "}");
362
    dockerQuery.setIri("wf", workflowURI);
1✔
363
    return runQuery(dockerQuery);
1✔
364
  }
365

366
  /**
367
   * Get authors from schema.org creator fields for a file
368
   *
369
   * @param path The path within the Git repository to the file
370
   * @param fileUri URI of the file
371
   * @return The result set of step links
372
   */
373
  public ResultSet getAuthors(String path, String fileUri) {
374
    ParameterizedSparqlString linkQuery = new ParameterizedSparqlString();
×
375
    linkQuery.setCommandText(
×
376
        queryCtx
377
            + "SELECT ?email ?name ?orcid\n"
378
            + "WHERE {\n"
379
            + "  GRAPH ?graphName {"
380
            + "    ?file s:author|s:contributor|s:creator ?author .\n"
381
            + "    {\n"
382
            + "      ?creator rdf:type s:Person .\n"
383
            + "      OPTIONAL { ?author s:email ?email }\n"
384
            + "      OPTIONAL { ?author s:name ?name }\n"
385
            + "      OPTIONAL { ?author s:id|s:sameAs ?orcid }\n"
386
            + "    } UNION {\n"
387
            + "      ?author rdf:type s:Organization .\n"
388
            + "      ?author s:department* ?dept .\n"
389
            + "      ?dept s:member ?member\n"
390
            + "      OPTIONAL { ?member s:email ?email }\n"
391
            + "      OPTIONAL { ?member s:name ?name }\n"
392
            + "      OPTIONAL { ?member s:id|s:sameAs ?orcid }\n"
393
            + "    }\n"
394
            + "    FILTER(regex(str(?orcid), \"^https?://orcid.org/\" ))\n"
395
            + "    FILTER(regex(str(?file), ?wfFilter, \"i\" ))\n"
396
            + "  }"
397
            + "}");
398
    linkQuery.setLiteral("wfFilter", path + "$");
×
399
    linkQuery.setIri("graphName", fileUri);
×
400
    return runQuery(linkQuery);
×
401
  }
402

403
  /**
404
   * Gets the step name from a full URI
405
   *
406
   * @param baseUrl the URL of the workflow
407
   * @param uri The URI
408
   * @return The step ID
409
   */
410
  public String stepNameFromURI(String baseUrl, String uri) {
411
    uri = uri.substring(uri.indexOf(baseUrl));
1✔
412
    uri = uri.replace(baseUrl, "");
1✔
413
    uri = uri.replace("#", "/");
1✔
414
    uri = uri.substring(1);
1✔
415
    if (uri.indexOf("/") > 0) {
1✔
416
      return uri.substring(0, uri.indexOf("/"));
1✔
417
    }
418
    return uri;
1✔
419
  }
420

421
  /**
422
   * Format a default value
423
   *
424
   * @param defaultVal The default value
425
   * @return Default value suitable for a node label
426
   */
427
  public String formatDefault(String defaultVal) {
428
    int lastCaret = defaultVal.indexOf("^^");
1✔
429
    if (lastCaret != -1) {
1✔
430
      return defaultVal.substring(0, lastCaret);
1✔
431
    }
432
    return "\\\"" + defaultVal + "\\\"";
1✔
433
  }
434

435
  /**
436
   * Convert an RDF type to cwl process
437
   *
438
   * @param runtype The string from the RDF
439
   * @return CWL process the string refers to
440
   */
441
  public CWLProcess strToRuntype(String runtype) {
442
    return switch (runtype) {
1✔
NEW
443
      case "https://w3id.org/cwl/cwl#Workflow" -> CWLProcess.WORKFLOW;
×
444
      case "https://w3id.org/cwl/cwl#CommandLineTool" -> CWLProcess.COMMANDLINETOOL;
1✔
NEW
445
      case "https://w3id.org/cwl/cwl#ExpressionTool" -> CWLProcess.EXPRESSIONTOOL;
×
NEW
446
      default -> null;
×
447
    };
448
  }
449

450
  /**
451
   * Get the label for the node from its name
452
   *
453
   * @param name The name in the form filename#step
454
   * @return The second part of the name, just the step
455
   */
456
  public String labelFromName(String name) {
457
    name = name.substring(name.indexOf('#') + 1);
1✔
458
    int slashIndex = name.indexOf("/");
1✔
459
    if (slashIndex > 0) {
1✔
460
      name = name.substring(slashIndex + 1);
1✔
461
    }
462
    return name;
1✔
463
  }
464

465
  /**
466
   * Run a SPARQL query on a given model
467
   *
468
   * @param queryString The query to be run
469
   * @return The result set of the query
470
   */
471
  ResultSet runQuery(ParameterizedSparqlString queryString) {
472
    Query query = QueryFactory.create(queryString.toString());
×
473
    try (QueryExecution qexec = QueryExecutionFactory.createServiceRequest(rdfService, query)) {
×
474
      return ResultSetFactory.copyResults(qexec.execSelect());
×
475
    }
476
  }
477

478
  public ResultSet getLicense(String workflowURI) {
479
    ParameterizedSparqlString licenseQuery = new ParameterizedSparqlString();
×
480
    licenseQuery.setCommandText(
×
481
        queryCtx
482
            + "SELECT ?license \n"
483
            + "WHERE {\n"
484
            + "  GRAPH ?wf {"
485
            + "    ?wf rdf:type cwl:Workflow .\n"
486
            + "    { ?wf s:license ?license } \n"
487
            + "UNION { ?wf doap:license ?license } \n"
488
            + "UNION { ?wf dct:license ?license } \n"
489
            + "  }"
490
            + "}");
491
    licenseQuery.setIri("wf", workflowURI);
×
492
    return runQuery(licenseQuery);
×
493
  }
494
}
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