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

Nanopublication / nanopub-java / 17852246692

19 Sep 2025 07:56AM UTC coverage: 51.799% (+0.9%) from 50.885%
17852246692

push

github

tkuhn
feat: Add QueryRef and use multimap

- Simplifies usage of query references
- Allows for multi-values per key for new queries with multi-placeholder

996 of 2882 branches covered (34.56%)

Branch coverage included in aggregate %.

5166 of 9014 relevant lines covered (57.31%)

2.69 hits per line

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

32.31
src/main/java/org/nanopub/extra/services/QueryAccess.java
1
package org.nanopub.extra.services;
2

3
import java.io.BufferedReader;
4
import java.io.IOException;
5
import java.io.InputStreamReader;
6
import java.io.Writer;
7
import java.util.ArrayList;
8
import java.util.HashMap;
9
import java.util.List;
10
import java.util.Map;
11

12
import org.apache.commons.lang3.tuple.Pair;
13
import org.apache.http.HttpResponse;
14

15
import com.opencsv.CSVReader;
16
import com.opencsv.CSVWriterBuilder;
17
import com.opencsv.ICSVWriter;
18
import com.opencsv.exceptions.CsvValidationException;
19

20
/**
21
 * Second-generation query API access
22
 */
23
public abstract class QueryAccess {
3✔
24

25
    /**
26
     * Process the header.
27
     *
28
     * @param line the header line from the CSV response
29
     */
30
    protected abstract void processHeader(String[] line);
31

32
    /**
33
     * Process a line of data from the CSV response.
34
     *
35
     * @param line the line of data from the CSV response
36
     */
37
    protected abstract void processLine(String[] line);
38

39
    /**
40
     * Call a query with the given queryId and parameters.
41
     *
42
     * @param queryId the ID of the query to call
43
     * @param params  the parameters to pass to the query
44
     * @throws org.nanopub.extra.services.FailedApiCallException if the API call fails
45
     */
46
    public void call(QueryRef queryRef) throws FailedApiCallException, APINotReachableException, NotEnoughAPIInstancesException {
47
        HttpResponse resp = QueryCall.run(queryRef);
3✔
48
        try (CSVReader csvReader = new CSVReader(new BufferedReader(new InputStreamReader(resp.getEntity().getContent())))) {
13✔
49
            String[] line = null;
2✔
50
            int n = 0;
2✔
51
            while ((line = csvReader.readNext()) != null) {
5✔
52
                n++;
1✔
53
                if (n == 1) {
3!
54
                    processHeader(line);
4✔
55
                } else {
56
                    processLine(line);
×
57
                }
58
            }
59
        } catch (IOException | CsvValidationException ex) {
×
60
            throw new FailedApiCallException(ex);
×
61
        }
1✔
62
    }
1✔
63

64
    /**
65
     * Print the response of a query in CSV format to the given writer.
66
     *
67
     * @param queryRef the query reference
68
     * @param writer  the writer to print the CSV response to
69
     * @throws org.nanopub.extra.services.FailedApiCallException if the API call fails
70
     */
71
    public static void printCvsResponse(QueryRef queryRef, Writer writer) throws FailedApiCallException, APINotReachableException, NotEnoughAPIInstancesException {
72
        ICSVWriter icsvWriter = new CSVWriterBuilder(writer).withSeparator(',').build();
×
73
        QueryAccess a = new QueryAccess() {
×
74

75
            @Override
76
            protected void processHeader(String[] line) {
77
                icsvWriter.writeNext(line);
×
78
            }
×
79

80
            @Override
81
            protected void processLine(String[] line) {
82
                icsvWriter.writeNext(line);
×
83
            }
×
84

85
        };
86
        a.call(queryRef);
×
87
        icsvWriter.flushQuietly();
×
88
    }
×
89

90
    /**
91
     * Get the response of a query as an ApiResponse object.
92
     *
93
     * @param queryRef the query reference
94
     * @return an ApiResponse object containing the response data
95
     * @throws org.nanopub.extra.services.FailedApiCallException if the API call fails
96
     */
97
    public static ApiResponse get(QueryRef queryRef) throws FailedApiCallException, APINotReachableException, NotEnoughAPIInstancesException {
98
        final ApiResponse response = new ApiResponse();
4✔
99
        QueryAccess a = new QueryAccess() {
11✔
100

101
            @Override
102
            protected void processHeader(String[] line) {
103
                response.setHeader(line);
4✔
104
            }
1✔
105

106
            @Override
107
            protected void processLine(String[] line) {
108
                response.add(line);
×
109
            }
×
110

111
        };
112
        a.call(queryRef);
3✔
113
        return response;
2✔
114
    }
115

116
    private static Map<String, Pair<Long, String>> latestVersionMap = new HashMap<>();
5✔
117
    // TODO Make a better query for this, where superseded and rejected are excluded from the start:
118
    private static final String GET_NEWER_VERSIONS = "RA3qSfVzcnAeMOODdpgCg4e-bX6KjZYZ2JQXDsSwluMaI/get-newer-versions-of-np";
119

120
    /**
121
     * Get the latest version ID of a nanopublication.
122
     * If the latest version is not cached or is older than 1 hour, it will re-fetch it.
123
     *
124
     * @param nanopubId the ID of the nanopublication
125
     * @return the latest version ID of the nanopublication
126
     */
127
    // TODO Is this method used anywhere, Nanodash has a copy of this.
128
    public static String getLatestVersionId(String nanopubId) {
129
        long currentTime = System.currentTimeMillis();
×
130
        if (!latestVersionMap.containsKey(nanopubId) || currentTime - latestVersionMap.get(nanopubId).getLeft() > 1000 * 60 * 60) {
×
131
            // Re-fetch if existing value is older than 1 hour
132
            try {
133
                ApiResponse r = QueryAccess.get(new QueryRef(GET_NEWER_VERSIONS, "np", nanopubId));
×
134
                List<String> latestList = new ArrayList<>();
×
135
                for (ApiResponseEntry e : r.getData()) {
×
136
                    if (e.get("retractedBy").isEmpty() && e.get("supersededBy").isEmpty()) {
×
137
                        latestList.add(e.get("newerVersion"));
×
138
                    }
139
                }
×
140
                if (latestList.size() == 1) {
×
141
                    String latest = latestList.getFirst();
×
142
                    latestVersionMap.put(nanopubId, Pair.of(currentTime, latest));
×
143
                    return latest;
×
144
                } else {
145
                    return nanopubId;
×
146
                }
147
            } catch (Exception ex) {
×
148
                ex.printStackTrace();
×
149
                return nanopubId;
×
150
            }
151
        }
152
        return latestVersionMap.get(nanopubId).getRight();
×
153
    }
154

155
}
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