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

knowledgepixels / nanodash / 19265479213

11 Nov 2025 12:22PM UTC coverage: 13.89% (+0.1%) from 13.756%
19265479213

push

github

tkuhn
fix(QueryResultTableBuilder): check should be (space != null)

515 of 4722 branches covered (10.91%)

Branch coverage included in aggregate %.

1350 of 8705 relevant lines covered (15.51%)

0.69 hits per line

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

80.53
src/main/java/com/knowledgepixels/nanodash/QueryApiAccess.java
1
package com.knowledgepixels.nanodash;
2

3
import org.apache.commons.lang3.tuple.Pair;
4
import org.eclipse.rdf4j.model.IRI;
5
import org.nanopub.extra.services.*;
6
import org.slf4j.Logger;
7
import org.slf4j.LoggerFactory;
8

9
import java.util.concurrent.ConcurrentHashMap;
10
import java.util.concurrent.ConcurrentMap;
11

12
/**
13
 * Utility class for accessing and managing API queries.
14
 * Provides methods to retrieve query results, manage query IDs, and fetch the latest versions of nanopublications.
15
 */
16
public class QueryApiAccess {
17

18
    private QueryApiAccess() {
19
    }  // no instances allowed
20

21
    private static ConcurrentMap<String, String> queryIds = new ConcurrentHashMap<>();
4✔
22
    private static final Logger logger = LoggerFactory.getLogger(QueryApiAccess.class);
3✔
23

24
    private static ConcurrentMap<String, Pair<Long, String>> latestVersionMap = new ConcurrentHashMap<>();
4✔
25

26
    private static final String queryIriPattern = "^(.*[^A-Za-z0-9-_])(RA[A-Za-z0-9-_]{43})[/#]([^/#]+)$";
27

28
    static {
29
        // TODO Load this dynamically somehow at some point:
30
        load("RAe-oA5eSmkCXCALZ99-0k4imnlI74KPqURfhHOmnzo6A/get-latest-nanopubs-from-pubkeys");
2✔
31
        load("RAuy4N1h4vZ1wgBUMvTiWw2y_Y0_5oFYRTwdq-xj2qqNM/get-latest-nanopubs-from-userid");
2✔
32
        load("RAiCBvPL2hRGzI8g5L68O-C9yEXryC_vG35GdEm5jtH_s/get-user-stats-from-pubkeys");  // Deactivated for now...
2✔
33
        load("RA3U23LL3xbNwsu92fAqsKb0kagOud4f9TlRQq3evNJck/get-user-stats-from-userid");  // Deactivated for now...
2✔
34
        load("RAcNvmEiUNUb2a7O4fwRvy2x2BCN640AC880fTzFworr8/get-top-creators-last30d");
2✔
35
        load("RAr27GmRUKQmvPbfmB34N9l9lX-xYK7nQhvOMbQCk3byI/get-latest-users");
2✔
36
        load("RAYNg6rfvXIVvJY2u8oS0EEjxnVvimLLVZG1rOar_nWIY/get-most-recent-nanopubs");
2✔
37
        load("RAPGhXDRzeGu-Qk0AkjleEtxMxqAvJ-dZn7985gzAbyhs/get-publisher-version");
2✔
38
        load("RAvL7pe2ppsfq4mVWTdJjssYGsjrmliNd_sZO2ytLvg1Y/get-most-used-templates-last30d");
2✔
39
        load("RANn4Mu8r8bqJA9KJMGXTQAEGAEvtNKGFsuhRIC6BRIOo/get-latest-nanopubs-by-type");
2✔
40
        load("RAiRsB2YywxjsBMkVRTREJBooXhf2ZOHoUs5lxciEl37I/get-latest-version-of-np");
2✔
41
        load("RA0aZxyh_I0rCJyBepXOWC2tGdI5YYHORFCC-qBR8xHZA/get-all-user-intros");
2✔
42
        load("RA-tlMmQA7iT2wR2aS3PlONrepX7vdXbkzeWluea7AECg/get-suggested-templates-to-get-started");
2✔
43
        load("RAhI-C2KsqS_IvnxwyBrbMFsoj65dhLWE_CBo_KtcVEVA/get-monthly-type-overview-by-pubkeys");
2✔
44
        load("RAn3agwsH2yk-8132RJApGYxdPSHHCXDAIYiCaSBBo6tg/get-approved-nanopubs");
2✔
45
        load("RAz1ogtMxSTKSOYwHAfD5M3Y-vd1vd46OZta_vvbqh8kY/find-uri-references");
2✔
46
        load("RAE35dYJQlpnqim7VeKuu07E9I1LQUZpkdYQR4RvU3KMU/get-nanopubs-by-type");
2✔
47
        load("RALZXWg5lZoJoQ0VHL5mpDgNxYpqU6FoDLWGp4rs8A6b8/get-introducing-nanopub");
2✔
48
        load("RAWruhiSmyzgZhVRs8QY8YQPAgHzTfl7anxII1de-yaCs/fulltext-search-on-labels");
2✔
49
        load("RAyMrQ89RECTi9gZK5q7gjL1wKTiP8StkLy0NIkkCiyew/find-things");
2✔
50
        load("RAjt1H9rCSr6A9VGzlhye00zPdH69JdGc3kd_2VjDmzVg/get-instances");
2✔
51
        load("RAH06iUwnvj_pRARY15ayJAY5tuJau3rCvHhPPhe49fVI/get-classes-for-thing");
2✔
52
        load("RAJStXEm1wZcg34ZLPqe00VPSzIVCwC2rrxdj_JR8v5DY/find-referencing-nanopubs");  // not yet used...
2✔
53
        load("RAtftxAXJubB4rlm9fOvvHNVIkXvWQLC6Ag_MiV7HL0ow/get-labels-for-thing");  // not yet used...
2✔
54
        load("RARtWHRzNY5hh31X2VB5eOCJAdp9Cjv4CakA0Idqz69MI/get-templates-with-uri");
2✔
55
        load("RAIn9NTsWE0qrpKiK3nOmZRXVzwv0qnfbm7dR_CUnp4aA/get-newer-versions-of-np");
2✔
56
        load("RAQqjXQYlxYQeI4Y3UQy9OrD5Jx1E3PJ8KwKKQlWbiYSw/get-queries");
2✔
57
        load("RAzXDzCHoZmJITgYYquLwDDkSyNf3eKKQz9NfQPYB1cyE/get-latest-thing-nanopub");
2✔
58
        load("RAnpimW7SPwaum2fefdS6_jpzYxcTRGjE-pmgNTL_BBJU/get-projects");
2✔
59
        load("RApiw7Z0NeP3RaLiqX6Q7Ml5CfEWbt-PysUbMNljuiLJw/get-owners");
2✔
60
        load("RASyFJyADTtG-l_Qe3a5PE_e2yUJR-PydXfkZjjrBuV7U/get-members");
2✔
61
        load("RAf0Apox1sbJRC0ZBrqS9wtYccLFJ_5VLq-u4rJy5WbnA/get-spaces");
2✔
62
        load("RAJmZoM0xCGE8OL6EgmQBOd1M58ggNkwZ0IUqHOAPRfvE/get-parts");
2✔
63
        load("RA6bgrU3Ezfg5VAiLru0BFYHaSj6vZU6jJTscxNl8Wqvc/get-assertion-templates");
2✔
64
        load("RA4bt3MQRnEPC2nSsdbCJc74wT-e1w68dSCpYVyvG0274/get-provenance-templates");
2✔
65
        load("RAMcdiJpvvk8424AJIH1jsDUQVcPYOLRw0DNnZt_ND_LQ/get-pubinfo-templates");
2✔
66
        load("RAOpebLWcp0G6D9U8ZhfYGOkz-kObkytDsM6rCQlIi_j0/get-admins");
2✔
67
        load("RA_Xf7mfC2PAohGYktJEXjkbzxPhkfX4DE1bQKFqkxrRg/get-space-members");
2✔
68
        load("RAbq1a1FwRFAZPDde3Sy4GqNUQ2TmaKOWLydJPOyCKc0w/get-filtered-nanopub-list");
2✔
69
        load("RA_F6C2DtlL4EXSF2vzU1KLVUiRLjPAM-r1Wy32JIeSYI/get-pinned-templates");
2✔
70
        load("RAH5jJUo8ukFkPP1xghveSNPze3pmSK69zri5PPM6HEbg/get-pinned-queries");
2✔
71
        load("RAF-LNZPrT11euM9NQGOLRrJg1pD02oXM5Cxb9XP7tBkU/get-space-member-roles");
2✔
72
        //load("RAuwOBxwXal5NTW_rl9WtUA38IJbbT8q_Vp6vq4F6Pu9k/get-views-for-space");  // to be deprecated
73
        load("RAkoDiXZG_CYt978-dZ_vffK-UTbN6e1bmtFy6qdmFzC4/get-latest-accepted-bdj");
2✔
74
        load("RAgnLJH8kcI_e488VdoyQ0g3-wcumj4mSiusxPmeAYsSI/get-latest-biodiv-candidates");
2✔
75
        load("RATpsBysLf8yXeMpY7PHKj-aKNCa4-4Okg1hi97OLDXIo/get-latest-accepted-ds");
2✔
76
        load("RAFNTW3jhWKnNvhMSOfYvG53ZAurxrFv_-vnIJkZyfAuo/get-latest-ds-candidates");
2✔
77
        load("RA0FiH8gukovvEHPBMn72zUDdMQylQmUwtIGNLYBZXGfk/get-ds-reactions");
2✔
78
        load("RAAXmnJdXHO86GqJs8VTdqapUWqCrHKRgRT2b4NfjAfgk/get-latest-accepted-rio");
2✔
79
        load("RAehKOCOnZ3uDBmI0kkCNTh5k9Nl6YYNj7tyc20tVymxY/get-latest-rio-candidates");
2✔
80
        load("RAe7k3L0oElPOrFoUMkUhqU9dGUqfBaUSw3cVplOUn3Fk/get-reactions");
2✔
81
        load("RAZUsK7jU85oUYEVKvMPFlqbwn19oR55IQuFkXuiS_Tkg/get-term-definitions");
2✔
82
        load("RA8RsuKt6HzCXGmw9wMeN4rwSm456bohOuJ0h-jEgXKmM/get-maintained-resources");
2✔
83
        load("RASCMqOpiavtozM3JLKyoU-oZv7MPrWR_Hrn5t0SRlK2M/get-view-displays");
2✔
84
    }
1✔
85

86
    /**
87
     * Loads a query ID into the queryIds map.
88
     *
89
     * @param queryId The query ID to load.
90
     */
91
    static void load(String queryId) {
92
        queryIds.put(queryId.substring(46), queryId);
7✔
93
    }
1✔
94

95
    /**
96
     * Forces the retrieval of an API response for a given query name and parameters.
97
     * Retries until a valid response is received.
98
     *
99
     * @param queryRef The query reference
100
     * @return The API response.
101
     */
102
    public static ApiResponse forcedGet(QueryRef queryRef) {
103
        while (true) {
104
            ApiResponse resp = null;
2✔
105
            try {
106
                resp = QueryApiAccess.get(queryRef);
3✔
107
            } catch (Exception ex) {
×
108
                // TODO We should be more specific about which exceptions we catch here
109
                //      and generally improve this, as this could hang forever.
110
                logger.error("Error while forcing API get for query {}", queryRef, ex);
×
111
            }
1✔
112
            if (resp != null) return resp;
4✔
113
            try {
114
                Thread.sleep(100);
2✔
115
            } catch (InterruptedException ex) {
×
116
                logger.error("Interrupted while forcing API get for query {}", queryRef, ex);
×
117
            }
1✔
118
        }
1✔
119
    }
120

121
    /**
122
     * Retrieves an API response for a given query reference.
123
     *
124
     * @param queryRef The query reference
125
     * @return The API response.
126
     * @throws org.nanopub.extra.services.FailedApiCallException         If the API call fails.
127
     * @throws org.nanopub.extra.services.APINotReachableException       If the API is not reachable.
128
     * @throws org.nanopub.extra.services.NotEnoughAPIInstancesException If there are not enough API instances.
129
     */
130
    public static ApiResponse get(QueryRef queryRef) throws FailedApiCallException, APINotReachableException, NotEnoughAPIInstancesException {
131
        if (queryRef.getName().matches("^RA[A-Za-z0-9-_]{43}/.*$")) {
6✔
132
            // All good
133
        } else if (queryIds.containsKey(queryRef.getName())) {
5✔
134
            queryRef = new QueryRef(queryIds.get(queryRef.getName()), queryRef.getParams());
12✔
135
        } else {
136
            throw new IllegalArgumentException("Query name not known: " + queryRef.getName());
7✔
137
        }
138
        return QueryAccess.get(queryRef);
3✔
139
    }
140

141
    /**
142
     * Retrieves the latest version ID of a given nanopublication.
143
     *
144
     * @param nanopubId The ID of the nanopublication.
145
     * @return The latest version ID.
146
     */
147
    public static String getLatestVersionId(String nanopubId) {
148
        long currentTime = System.currentTimeMillis();
2✔
149
        if (!latestVersionMap.containsKey(nanopubId) || currentTime - latestVersionMap.get(nanopubId).getLeft() > 1000 * 60) {
4!
150
            // Re-fetch if existing value is older than 1 minute
151
            try {
152
                ApiResponse r = get(new QueryRef("get-latest-version-of-np", "np", nanopubId));
8✔
153
                if (r.getData().size() != 1) return nanopubId;
7!
154
                String l = r.getData().get(0).get("latest");
×
155
                latestVersionMap.put(nanopubId, Pair.of(currentTime, l));
×
156
            } catch (Exception ex) {
×
157
                logger.error("Error while getting latest version of nanopub '{}'", nanopubId, ex);
×
158
                return nanopubId;
×
159
            }
×
160
        }
161
        return latestVersionMap.get(nanopubId).getRight();
×
162
    }
163

164
    /**
165
     * Extracts the query ID from a given query IRI.
166
     *
167
     * @param queryIri The query IRI.
168
     * @return The query ID, or null if the IRI is invalid.
169
     */
170
    public static String getQueryId(IRI queryIri) {
171
        if (queryIri == null) return null;
×
172
        if (!queryIri.stringValue().matches(queryIriPattern)) return null;
×
173
        return queryIri.stringValue().replaceFirst(queryIriPattern, "$2/$3");
×
174
    }
175

176
    /**
177
     * Retrieves the query ID for a given query name.
178
     *
179
     * @param queryName The name of the query.
180
     * @return The query ID, or null if the query name is unknown.
181
     */
182
    public static String getQueryId(String queryName) {
183
        return queryIds.get(queryName);
5✔
184
    }
185

186
    /**
187
     * Extracts the query name from a given query IRI.
188
     *
189
     * @param queryIri The query IRI.
190
     * @return The query name, or null if the IRI is invalid.
191
     */
192
    public static String getQueryName(IRI queryIri) {
193
        if (queryIri == null) return null;
4✔
194
        if (!queryIri.stringValue().matches(queryIriPattern)) return null;
7✔
195
        return queryIri.stringValue().replaceFirst(queryIriPattern, "$3");
6✔
196
    }
197

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