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

knowledgepixels / nanodash / 27811003513

19 Jun 2026 07:06AM UTC coverage: 26.612% (-0.4%) from 26.963%
27811003513

Pull #484

github

web-flow
Merge 2b4ad3339 into 0f6281554
Pull Request #484: Space-ref disambiguation: conflict notice, claimants overview, ref-pinned pages

1552 of 6853 branches covered (22.65%)

Branch coverage included in aggregate %.

3420 of 11830 relevant lines covered (28.91%)

4.26 hits per line

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

63.93
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
    // Query IDs (full id = RA.../query-name)
22
    public static final String GET_LATEST_NANOPUBS_FROM_PUBKEYS = "RAe-oA5eSmkCXCALZ99-0k4imnlI74KPqURfhHOmnzo6A/get-latest-nanopubs-from-pubkeys";
23
    public static final String GET_LATEST_NANOPUBS_FROM_USERID = "RAuy4N1h4vZ1wgBUMvTiWw2y_Y0_5oFYRTwdq-xj2qqNM/get-latest-nanopubs-from-userid";
24
    public static final String GET_USER_STATS_FROM_PUBKEYS = "RAiCBvPL2hRGzI8g5L68O-C9yEXryC_vG35GdEm5jtH_s/get-user-stats-from-pubkeys";
25
    public static final String GET_USER_STATS_FROM_USERID = "RA3U23LL3xbNwsu92fAqsKb0kagOud4f9TlRQq3evNJck/get-user-stats-from-userid";
26
    public static final String GET_TOP_CREATORS_LAST30D = "RAcNvmEiUNUb2a7O4fwRvy2x2BCN640AC880fTzFworr8/get-top-creators-last30d";
27
    public static final String GET_LATEST_USERS = "RAr27GmRUKQmvPbfmB34N9l9lX-xYK7nQhvOMbQCk3byI/get-latest-users";
28
    public static final String GET_MOST_RECENT_NANOPUBS = "RAYNg6rfvXIVvJY2u8oS0EEjxnVvimLLVZG1rOar_nWIY/get-most-recent-nanopubs";
29
    public static final String GET_PUBLISHER_VERSION = "RAPGhXDRzeGu-Qk0AkjleEtxMxqAvJ-dZn7985gzAbyhs/get-publisher-version";
30
    public static final String GET_MOST_USED_TEMPLATES_LAST30D = "RAvL7pe2ppsfq4mVWTdJjssYGsjrmliNd_sZO2ytLvg1Y/get-most-used-templates-last30d";
31
    public static final String GET_LATEST_NANOPUBS_BY_TYPE = "RANn4Mu8r8bqJA9KJMGXTQAEGAEvtNKGFsuhRIC6BRIOo/get-latest-nanopubs-by-type";
32
    public static final String GET_LATEST_VERSION_OF_NP = "RAiRsB2YywxjsBMkVRTREJBooXhf2ZOHoUs5lxciEl37I/get-latest-version-of-np";
33
    public static final String GET_ALL_USER_INTROS = "RAjHh6P11QFUaoPiMRBavdAnTq4YMJW4PB85oVFSBfYjU/get-all-user-intros";
34
    public static final String GET_ALL_USER_PROFILE_PICS= "RAtcodMPmTrmBvdOqwYIrNNFDO74f8B_xo0qsOcKlCwTA/get-all-user-profile-pics";
35
    public static final String GET_ALL_USER_DEFAULT_LICENSE = "RA-_IwzReR2_HfTLz4YcNM6Mh3Vt16y0RUS12tpJTN9FI/get-all-user-default-license";
36
    public static final String GET_SUGGESTED_TEMPLATES_TO_GET_STARTED = "RA-tlMmQA7iT2wR2aS3PlONrepX7vdXbkzeWluea7AECg/get-suggested-templates-to-get-started";
37
    public static final String GET_MONTHLY_TYPE_OVERVIEW_BY_PUBKEYS = "RAhI-C2KsqS_IvnxwyBrbMFsoj65dhLWE_CBo_KtcVEVA/get-monthly-type-overview-by-pubkeys";
38
    public static final String GET_APPROVED_NANOPUBS = "RAn3agwsH2yk-8132RJApGYxdPSHHCXDAIYiCaSBBo6tg/get-approved-nanopubs";
39
    public static final String FIND_URI_REFERENCES = "RAz1ogtMxSTKSOYwHAfD5M3Y-vd1vd46OZta_vvbqh8kY/find-uri-references";
40
    public static final String GET_NANOPUBS_BY_TYPE = "RAE35dYJQlpnqim7VeKuu07E9I1LQUZpkdYQR4RvU3KMU/get-nanopubs-by-type";
41
    public static final String GET_INTRODUCING_NANOPUB = "RALZXWg5lZoJoQ0VHL5mpDgNxYpqU6FoDLWGp4rs8A6b8/get-introducing-nanopub";
42
    public static final String FULLTEXT_SEARCH = "RAxdh5xkc6K6SMLY23yKu__zTWJPXeRFc0qgNNxkbOkpY/fulltext-search";
43
    public static final String FIND_THINGS = "RAyMrQ89RECTi9gZK5q7gjL1wKTiP8StkLy0NIkkCiyew/find-things";
44
    public static final String GET_INSTANCES = "RAjt1H9rCSr6A9VGzlhye00zPdH69JdGc3kd_2VjDmzVg/get-instances";
45
    public static final String GET_CLASSES_FOR_THING = "RAH06iUwnvj_pRARY15ayJAY5tuJau3rCvHhPPhe49fVI/get-classes-for-thing";
46
    public static final String FIND_REFERENCING_NANOPUBS = "RAJStXEm1wZcg34ZLPqe00VPSzIVCwC2rrxdj_JR8v5DY/find-referencing-nanopubs";
47
    public static final String GET_LABELS_FOR_THING = "RAtftxAXJubB4rlm9fOvvHNVIkXvWQLC6Ag_MiV7HL0ow/get-labels-for-thing";
48
    public static final String GET_TEMPLATES_WITH_URI = "RARtWHRzNY5hh31X2VB5eOCJAdp9Cjv4CakA0Idqz69MI/get-templates-with-uri";
49
    public static final String GET_NEWER_VERSIONS_OF_NP = "RAqmmNSxQaRNWRYH0o4Da3GSOwvoFLObhXfAGUCOqEtfw/get-newer-versions-of-np";
50
    public static final String GET_QUERIES = "RAQqjXQYlxYQeI4Y3UQy9OrD5Jx1E3PJ8KwKKQlWbiYSw/get-queries";
51
    public static final String GET_LATEST_THING_NANOPUB = "RAzXDzCHoZmJITgYYquLwDDkSyNf3eKKQz9NfQPYB1cyE/get-latest-thing-nanopub";
52
    public static final String GET_PROJECTS = "RAnpimW7SPwaum2fefdS6_jpzYxcTRGjE-pmgNTL_BBJU/get-projects";
53
    public static final String GET_OWNERS = "RApiw7Z0NeP3RaLiqX6Q7Ml5CfEWbt-PysUbMNljuiLJw/get-owners";
54
    public static final String GET_MEMBERS = "RASyFJyADTtG-l_Qe3a5PE_e2yUJR-PydXfkZjjrBuV7U/get-members";
55
    public static final String GET_PARTS = "RAJmZoM0xCGE8OL6EgmQBOd1M58ggNkwZ0IUqHOAPRfvE/get-parts";
56
    public static final String GET_ASSERTION_TEMPLATES = "RA6bgrU3Ezfg5VAiLru0BFYHaSj6vZU6jJTscxNl8Wqvc/get-assertion-templates";
57
    public static final String GET_PROVENANCE_TEMPLATES = "RA4bt3MQRnEPC2nSsdbCJc74wT-e1w68dSCpYVyvG0274/get-provenance-templates";
58
    public static final String GET_PUBINFO_TEMPLATES = "RAMcdiJpvvk8424AJIH1jsDUQVcPYOLRw0DNnZt_ND_LQ/get-pubinfo-templates";
59
    public static final String GET_FILTERED_NANOPUB_LIST = "RAeoXI4vBzLV_BM2lfI5DWkFSfm6y1z3fOk4E1IncXWUo/get-filtered-nanopub-list";
60
    public static final String GET_LATEST_ACCEPTED_BDJ = "RAkoDiXZG_CYt978-dZ_vffK-UTbN6e1bmtFy6qdmFzC4/get-latest-accepted-bdj";
61
    public static final String GET_LATEST_BIODIV_CANDIDATES = "RAgnLJH8kcI_e488VdoyQ0g3-wcumj4mSiusxPmeAYsSI/get-latest-biodiv-candidates";
62
    public static final String GET_LATEST_ACCEPTED_DS = "RATpsBysLf8yXeMpY7PHKj-aKNCa4-4Okg1hi97OLDXIo/get-latest-accepted-ds";
63
    public static final String GET_LATEST_DS_CANDIDATES = "RAFNTW3jhWKnNvhMSOfYvG53ZAurxrFv_-vnIJkZyfAuo/get-latest-ds-candidates";
64
    public static final String GET_DS_REACTIONS = "RA0FiH8gukovvEHPBMn72zUDdMQylQmUwtIGNLYBZXGfk/get-ds-reactions";
65
    public static final String GET_LATEST_ACCEPTED_RIO = "RAAXmnJdXHO86GqJs8VTdqapUWqCrHKRgRT2b4NfjAfgk/get-latest-accepted-rio";
66
    public static final String GET_LATEST_RIO_CANDIDATES = "RAehKOCOnZ3uDBmI0kkCNTh5k9Nl6YYNj7tyc20tVymxY/get-latest-rio-candidates";
67
    public static final String GET_REACTIONS = "RAe7k3L0oElPOrFoUMkUhqU9dGUqfBaUSw3cVplOUn3Fk/get-reactions";
68
    public static final String GET_TERM_DEFINITIONS = "RAZUsK7jU85oUYEVKvMPFlqbwn19oR55IQuFkXuiS_Tkg/get-term-definitions";
69
    // v10 (issue #302): standalone + preset-supplied views (unbound ?display), gated to
70
    // admins/maintainers of the owning space or the affected user themselves. Each
71
    // referenced view is resolved to its latest version server-side: the version tree's
72
    // most recent current head (a nanopub itself neither superseded nor validly retracted
73
    // via npx:invalidates), robust to backdated supersedes and retracted versions, so
74
    // ?view is already the latest and needs no separate per-view lookup. v10 wraps that
75
    // resolution in a run-once sub-SELECT so the cross-repo lookup federates once for the
76
    // whole view set instead of once per referenced view -- cut a 44-display page from
77
    // ~4.5s to ~1.7s (the per-view federation round-trips were the dominant cost).
78
    public static final String GET_VIEW_DISPLAYS = "RAy49uUd2fPLHJAZ_7QKDtIDVgqaQ589OgQhMwNamKy-4/get-view-displays";
79
    // Ref-scoped get-view-displays (the Content-tab renderer query): takes the space IRI (resource)
80
    // AND the ref's root nanopub (root_np) as two concrete params, gating the authorised signers on
81
    // that ref's admins/maintainers (npa:forSpaceRef) instead of the IRI merged across refs, so the
82
    // rendered Content views match the space ref shown. Both params concrete so the 4-SERVICE
83
    // federation propagates. Column-identical to get-view-displays. Source at
84
    // docs/queries/get-view-displays-ref.trig. See docs/space-ref-identity.md.
85
    public static final String GET_VIEW_DISPLAYS_REF = "RA8iqtdTrZGOWX1Yf7w_8RgB9brG3COMw9wkbjfl6VeHc/get-view-displays";
86

87
    // Spaces-repo queries (endpoint: nanopub-query .../repo/spaces)
88
    // v2: IRI-keyed get-spaces. Prior client head, retained for reference; deployments up
89
    // to this release stay pinned on it, and the roll-out fork-merge will supersede both
90
    // it and v3. No longer fetched by SpaceRepository (now uses GET_SPACES_REF).
91
    public static final String GET_SPACES = "RAxGboS_juHuMyJQghGV3elEgZmQTew5oyw_aC9O9FFQI/get-spaces";
92
    // v3: ref-aware get-spaces (adds ?ref + ?root so the client can key one space per
93
    // ref). Published as an independent nanopub (no npx:supersedes). Active query used by
94
    // SpaceRepository. Source at docs/queries/get-spaces-ref.trig. See
95
    // docs/space-ref-identity.md.
96
    public static final String GET_SPACES_REF = "RAD5KmWO6uqjM04tK7tb2IREgbxA1GTGyRhaRjjaVIKPw/get-spaces";
97
    // Disambiguation claimants: one row per ref (root definition) claiming a space IRI, with that
98
    // ref's validated admins (admins_multi_iri). Pass the space IRI; replaces the per-ref
99
    // get-space-admins fan-out with a single fetch. Which ref is the representative (default) is
100
    // decided client-side. Source at docs/queries/list-space-claimants.trig.
101
    public static final String LIST_SPACE_CLAIMANTS = "RApsQhJnK7MV5fHzFQe4GsnsUdf_HvPT186E02JE-4CTY/list-space-claimants";
102
    public static final String GET_SUB_SPACE_LINKS = "RAWgoQbP9_B9h3Bnwd1FGYX1gLYPyZFOxaeqIeA3TTPSU/get-sub-space-links";
103
    public static final String GET_MAINTAINED_RESOURCES = "RAOOq81R84exTUKUBQT3BbgCaSJyC2lqPDXIP2XaDTosM/get-maintained-resources";
104
    public static final String GET_SPACE_ADMINS = "RAaHOXMQ7Kq37T9syR9at0RqushclHenlPOFRwFDn0Cfs/get-space-admins";
105
    // Ref-scoped admins (Stage 2): takes the ref's root nanopub (root_np), matches admins
106
    // on npa:forSpaceRef, so multi-ref spaces don't merge admin sets across refs. Published
107
    // independently. Source at docs/queries/get-space-admins-ref.trig. See
108
    // docs/space-ref-identity.md.
109
    public static final String GET_SPACE_ADMINS_REF = "RAWM8qlKbV3DEH_NsPJ6hIyTrBwIp8sNeg9MGDgu8la1o/get-space-admins";
110
    public static final String GET_SPACE_ADMIN_PUBKEY_HASHES = "RAJvvNY6KXqveJivZKh-chTCntrsY_KJSGLVNRQdi0pUc/get-space-admin-pubkey-hashes";
111
    // Ref-scoped admin pubkey hashes (Stage 2): takes the ref's root nanopub (root_np),
112
    // matches admins on npa:forSpaceRef, so multi-ref spaces don't merge admin keys across
113
    // refs. Published independently. Source at docs/queries/get-space-admin-pubkey-hashes-ref.trig.
114
    public static final String GET_SPACE_ADMIN_PUBKEY_HASHES_REF = "RAO8KDdS4_Z0-R1qCSKqWcewg0WUSaiQDh_p1N1Bg-zic/get-space-admin-pubkey-hashes";
115
    public static final String GET_SPACE_ROLES = "RAKJFw-xIQ2r_aSKT4-6Pm3JkeqlWC_wmypfpA1JWPJl8/get-space-roles";
116
    // Ref-scoped roles (Stage 2): takes the ref's root nanopub (root_np), matches
117
    // RoleAssignments on npa:forSpaceRef, so multi-ref spaces don't merge role sets across
118
    // refs. Published independently. Source at docs/queries/get-space-roles-ref.trig.
119
    public static final String GET_SPACE_ROLES_REF = "RAqUWUfmEmzxpkeuXek7oEiVSnwjuzRfV8kRe7pQSpe4c/get-space-roles";
120
    public static final String GET_SPACE_MEMBERS = "RAo0c4UNoD-uTP3xATU_-TB6vO-nMO4Ya-mvdaGjX5qVE/get-space-members";
121
    // Ref-scoped members (Stage 2): takes the ref's root nanopub (root_np), resolves the
122
    // ref + its space IRI, and returns ALL non-admin RoleInstantiations naming that IRI
123
    // (raw npa:spacesGraph, matching the looser pre-migration semantic), each with a
124
    // ?validated flag = whether it is also in the trust-state-validated current-state graph
125
    // (i.e. the agent's key has a trust-approved AccountState from an accepted intro). Shows
126
    // every self-declared member while flagging the un-introduced ones, rather than hiding
127
    // them. Published independently. Source at docs/queries/get-space-members-ref.trig.
128
    public static final String GET_SPACE_MEMBERS_REF = "RAqp9TSM4oAwvJ0UQrvZ-qzEuS4R8zpsuD_lw1lyW5MOw/get-space-members";
129
    // Ref-scoped observers (Stage 2): takes the ref's root nanopub (root_np), lists observers
130
    // INCLUDING un-introduced self-declared ones (not in the validated state), each flagged
131
    // via a headerless ?unverified_noheader column (⚠️ when unvalidated). Drives the existing
132
    // Observers view's table (the view nanopub is left untouched). Published independently.
133
    // v3 (RAZ41V9K, supersedes RA58KSjh) (a) resolves owl:sameAs space aliases via the ref's
134
    // validated npa:sameAsSpace edges, so observer roles declared against an alias IRI of the
135
    // space are included, and (b) lists EVERY observer-tier association — no longer hiding users
136
    // who also hold a higher-tier (admin/maintainer/member) role, so an admin who is also a
137
    // participant appears here for that participant role. The built-in admin property and
138
    // genuine higher-tier role declarations are still excluded; non-approved higher-tier claims
139
    // go to LIST_SPACE_NON_APPROVED_REF. v4 (RAobkcQi, supersedes RAZ41V9K) gates the
140
    // npx:invalidates filter on a shared signing pubkey (npa:hasValidSignatureForPublicKeyHash)
141
    // between invalidator and target, so a foreign-key retraction can no longer hide an observer
142
    // (issue #487; mirrors the materializer's #112 same-publisher gate). Source at
143
    // docs/queries/list-space-observers-ref-v4.trig.
144
    public static final String LIST_SPACE_OBSERVERS_REF = "RAobkcQijd4C02lIu-NRz-dhlp8MzPKFau3EO7r3-7hSo/list-space-observers";
145

146
    // Ref-scoped non-approved role claims (root_np): agents holding a higher-tier role
147
    // instantiation (admin/maintainer/member) that is NOT in the validated state — a
148
    // self-assigned or otherwise ungranted claim awaiting approval by an equal-or-higher-tier
149
    // member. Observer-tier roles are excluded (self-assignable, so they need no approval and
150
    // are listed by LIST_SPACE_OBSERVERS_REF). Only admin claims are detectable today (the live
151
    // repo materialises every declaration as ObserverRole). Drives the "❓ Pending
152
    // Admins/Maintainers/Members" view. v3 (RA2BnCGv, supersedes RAZMAChi) resolves owl:sameAs
153
    // space aliases via the ref's validated npa:sameAsSpace edges, so a higher-tier claim made
154
    // against an alias IRI of the space is detected. v4 (RAwv7GRc, supersedes RA2BnCGv) gates the
155
    // npx:invalidates filter on a shared signing pubkey between invalidator and target, so a
156
    // foreign-key retraction can no longer suppress a pending claim (issue #487; mirrors the
157
    // materializer's #112 same-publisher gate). Source at
158
    // docs/queries/list-space-non-approved-ref-v4.trig.
159
    public static final String LIST_SPACE_NON_APPROVED_REF = "RAwv7GRcjaiG3tcrtWiayQdwqOS2qeatEh4qiOGvB7pNg/list-space-non-approved";
160

161
    // Ref-scoped variants of the four About-tab *view* display queries (distinct from the
162
    // GET_SPACE_*_REF client-authority queries above). Each takes the ref's root nanopub
163
    // (root_np), resolves the ref via npa:rootNanopub, and scopes by npa:forSpaceRef (members,
164
    // roles) or the ref-level npa:hasSubSpace / npa:hasMaintainedResource edge (sub-spaces,
165
    // maintained resources), so a ?root=-pinned space page shows only that one ref's listings
166
    // rather than merging all refs claiming the IRI. Column-compatible with the IRI-keyed view
167
    // queries, so they drive the existing view nanopubs unchanged (the observers pattern). Used
168
    // by AboutSpacePanel with an IRI-keyed fallback when the ref root is unknown. Published
169
    // independently (no npx:supersedes). Sources at docs/queries/list-*-ref.trig. See
170
    // docs/space-ref-identity.md.
171
    public static final String LIST_SPACE_MEMBERS_REF = "RAXhi9zBXJ2mZVmjBm_MZk1xM6OCxxVyr5B3xNYdy6boQ/list-space-members";
172
    public static final String LIST_SPACE_ROLES_REF = "RAYrSRARuWV2iTWVe6tKDgkaED8ztlr1q5Z5QBIDV4a-Q/list-space-roles";
173
    public static final String LIST_SUB_SPACES_REF = "RA-j0DFqkNUHxF_WIds8wWJix6DkDFBmUBWmKXfG24XYQ/list-sub-spaces";
174
    public static final String LIST_MAINTAINED_RESOURCES_REF = "RAPthUMRDXiJeD2BrOsZigTsbA0LktBc-HC4alDSfVNKM/list-maintained-resources";
175

176
    // Ref-scoped view displays for a space. Unlike the LIST_*_REF above this is NOT a single-param
177
    // root_np query: view displays aren't materialised into the spaces repo, so the query is a
178
    // 4-SERVICE federated join, and federation only propagates CONCRETE (VALUES) bindings into the
179
    // sub-services. A single auto-detecting param fails (a service-derived resource IRI doesn't push
180
    // into the branches → 0 rows). So this takes TWO concrete params — the space IRI (resource) and
181
    // the ref's root nanopub (root_np) — and resolves the ref + scopes the authority gate to
182
    // npa:forSpaceRef INSIDE the /repo/spaces service. Column-identical to list-view-displays (its
183
    // spaces-only companion), so it drives the existing view-displays view unchanged. Used by
184
    // AboutSpacePanel with both params; falls back to the IRI-keyed query when the ref root is
185
    // unknown. Source at docs/queries/list-view-displays-ref.trig (regenerate from the latest
186
    // list-view-displays by adding the root_np VALUES + ?passedRef resolution + forSpaceRef gate).
187
    public static final String LIST_VIEW_DISPLAYS_REF = "RAKfr2_TxPCXE-u-Ol0UDb2-8U8sej8aqKhD32EtDTob0/list-view-displays";
188

189
    private static final Logger logger = LoggerFactory.getLogger(QueryApiAccess.class);
9✔
190

191
    private static ConcurrentMap<String, Pair<Long, String>> latestVersionMap = new ConcurrentHashMap<>();
15✔
192

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

195
    /**
196
     * Forces the retrieval of an API response for a given query name and parameters.
197
     * Retries until a valid response is received.
198
     *
199
     * @param queryRef The query reference
200
     * @return The API response.
201
     */
202
    public static ApiResponse forcedGet(QueryRef queryRef) {
203
        long deadline = System.currentTimeMillis() + 30_000;
12✔
204
        long sleepMs = 1000;
6✔
205
        while (System.currentTimeMillis() < deadline) {
12!
206
            try {
207
                ApiResponse resp = QueryApiAccess.get(queryRef);
9✔
208
                if (resp != null) return resp;
12✔
209
            } catch (Exception ex) {
1✔
210
                logger.error("Error while forcing API get for query {}", queryRef, ex);
5✔
211
            }
3✔
212
            try {
213
                Thread.sleep(Math.min(sleepMs, Math.max(0, deadline - System.currentTimeMillis())));
24✔
214
                sleepMs = Math.min(sleepMs * 2, 16_000);
18✔
215
            } catch (InterruptedException ex) {
×
216
                Thread.currentThread().interrupt();
×
217
                break;
×
218
            }
3✔
219
        }
220
        throw new RuntimeException("Timed out forcing API get for query: " + queryRef);
×
221
    }
222

223
    /**
224
     * Retrieves an API response for a given query reference.
225
     *
226
     * @param queryRef The query reference
227
     * @return The API response.
228
     * @throws org.nanopub.extra.services.FailedApiCallException         If the API call fails.
229
     * @throws org.nanopub.extra.services.APINotReachableException       If the API is not reachable.
230
     * @throws org.nanopub.extra.services.NotEnoughAPIInstancesException If there are not enough API instances.
231
     */
232
    public static ApiResponse get(QueryRef queryRef) throws FailedApiCallException, APINotReachableException, NotEnoughAPIInstancesException {
233
        if (!queryRef.getQueryId().matches("^RA[A-Za-z0-9-_]{43}/.*$")) {
15!
234
            throw new IllegalArgumentException("QueryRef name must be full query ID: " + queryRef.getQueryId());
×
235
        }
236
        return QueryAccess.get(queryRef);
9✔
237
    }
238

239
    /**
240
     * Retrieves the latest version ID of a given nanopublication.
241
     *
242
     * @param nanopubId The ID of the nanopublication.
243
     * @return The latest version ID.
244
     */
245
    public static String getLatestVersionId(String nanopubId) {
246
        long currentTime = System.currentTimeMillis();
6✔
247
        if (!latestVersionMap.containsKey(nanopubId) || currentTime - latestVersionMap.get(nanopubId).getLeft() > 1000 * 60) {
12!
248
            // Re-fetch if existing value is older than 1 minute
249
            try {
250
                ApiResponse r = ApiCache.retrieveResponseSync(new QueryRef(GET_LATEST_VERSION_OF_NP, "np", nanopubId), false);
27✔
251
                if (r != null && r.getData().size() == 1) {
21!
252
                    String l = r.getData().get(0).get("latest");
24✔
253
                    latestVersionMap.put(nanopubId, Pair.of(currentTime, l));
24✔
254
                }
255
            } catch (Exception ex) {
×
256
                logger.error("Error while getting latest version of nanopub '{}'", nanopubId, ex);
×
257
            }
3✔
258
        }
259
        Pair<Long, String> cached = latestVersionMap.get(nanopubId);
15✔
260
        return cached != null ? cached.getRight() : nanopubId;
21!
261
    }
262

263
    /**
264
     * Extracts the query ID from a given query IRI.
265
     *
266
     * @param queryIri The query IRI.
267
     * @return The query ID, or null if the IRI is invalid.
268
     */
269
    public static String getQueryId(IRI queryIri) {
270
        if (queryIri == null) return null;
×
271
        if (!queryIri.stringValue().matches(queryIriPattern)) return null;
×
272
        return queryIri.stringValue().replaceFirst(queryIriPattern, "$2/$3");
×
273
    }
274

275
    /**
276
     * Extracts the query name from a given query IRI.
277
     *
278
     * @param queryIri The query IRI.
279
     * @return The query name, or null if the IRI is invalid.
280
     */
281
    public static String getQueryName(IRI queryIri) {
282
        if (queryIri == null) return null;
12✔
283
        if (!queryIri.stringValue().matches(queryIriPattern)) return null;
21✔
284
        return queryIri.stringValue().replaceFirst(queryIriPattern, "$3");
18✔
285
    }
286

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