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

knowledgepixels / nanopub-registry / 28113618611

24 Jun 2026 04:28PM UTC coverage: 31.926% (-0.2%) from 32.089%
28113618611

Pull #116

github

web-flow
Merge d931f8afc into eebd16ba4
Pull Request #116: Enhance and standardize logging across multiple components

313 of 1106 branches covered (28.3%)

Branch coverage included in aggregate %.

1048 of 3157 relevant lines covered (33.2%)

5.51 hits per line

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

0.0
src/main/java/com/knowledgepixels/registry/ListPage.java
1
package com.knowledgepixels.registry;
2

3
import com.google.gson.Gson;
4
import com.mongodb.client.ClientSession;
5
import com.mongodb.client.MongoCursor;
6
import io.vertx.ext.web.RoutingContext;
7
import org.bson.Document;
8
import org.bson.conversions.Bson;
9
import org.nanopub.jelly.NanopubStream;
10
import org.slf4j.Logger;
11
import org.slf4j.LoggerFactory;
12

13
import java.io.IOException;
14
import java.net.URLEncoder;
15
import java.util.List;
16

17
import static com.knowledgepixels.registry.RegistryDB.collection;
18
import static com.knowledgepixels.registry.RegistryDB.unhash;
19
import static com.knowledgepixels.registry.Utils.*;
20
import static com.mongodb.client.model.Aggregates.*;
21
import static com.mongodb.client.model.Filters.gt;
22
import static com.mongodb.client.model.Indexes.ascending;
23
import static com.mongodb.client.model.Indexes.descending;
24
import static com.mongodb.client.model.Projections.exclude;
25
import static com.mongodb.client.model.Projections.include;
26

27
public class ListPage extends Page {
28

29
    private static final Gson gson = new Gson();
×
30
    private static final Logger logger = LoggerFactory.getLogger(ListPage.class);
×
31

32
    public static void show(RoutingContext context) {
33
        ListPage page;
34
        logger.info("Received list request: {}", context.request().path());
×
35
        try (ClientSession s = RegistryDB.getClient().startSession()) {
×
36
            // No transaction here: the nanopubs.jelly endpoint streams large result sets
37
            // that would exceed MongoDB's transaction timeout.
38
            page = new ListPage(s, context);
×
39
            page.show();
×
40
        } catch (IOException ex) {
×
41
            logger.warn("Failed to show list for request {}: {} ({})", context.request().path(), ex.getMessage(), ex.getClass().getSimpleName(), ex);
×
42
        } finally {
43
            logger.debug("Ending response for list request: {}", context.request().path());
×
44
            context.response().end();
×
45
        }
46
    }
×
47

48
    private ListPage(ClientSession mongoSession, RoutingContext context) {
49
        super(mongoSession, context);
×
50
    }
×
51

52
    protected void show() throws IOException {
53
        RoutingContext context = getContext();
×
54
        String format;
55
        String ext = getExtension();
×
56
        final String req = getRequestString();
×
57

58
        logger.debug("Preparing list response for request: {} (ext={})", getFullRequest(), ext);
×
59

60
        if ("json".equals(ext)) {
×
61
            format = TYPE_JSON;
×
62
        } else if ("jelly".equals(ext)) {
×
63
            format = TYPE_JELLY;
×
64
        } else if (ext == null || "html".equals(ext)) {
×
65
            format = Utils.getMimeType(context, SUPPORTED_TYPES_LIST);
×
66
        } else {
67
            logger.warn("Invalid list request (unsupported extension) for {}: {}", getFullRequest(), ext);
×
68
            context.response().setStatusCode(400).setStatusMessage("Invalid request: " + getFullRequest());
×
69
            return;
×
70
        }
71

72
        if (getPresentationFormat() != null) {
×
73
            setRespContentType(getPresentationFormat());
×
74
            logger.debug("Overriding response content type with presentation format: {}", getPresentationFormat());
×
75
        } else {
76
            setRespContentType(format);
×
77
            logger.debug("Set response content type: {}", format);
×
78
        }
79

80
        if (req.matches("/list/[0-9a-f]{64}/([0-9a-f]{64}|\\$)")) {
×
81
            String pubkey = req.replaceFirst("/list/([0-9a-f]{64})/([0-9a-f]{64}|\\$)", "$1");
×
82
            String type = req.replaceFirst("/list/([0-9a-f]{64})/([0-9a-f]{64}|\\$)", "$2");
×
83

84
            logger.info("Serving list for pubkey={} type={} format={}", getLabel(pubkey), getLabel(type), format);
×
85

86
            if (TYPE_JELLY.equals(format)) {
×
87
                // Determine start position from afterChecksums parameter (comma-separated, geometric fallback)
88
                long afterPosition = -1;
×
89
                String afterChecksums = getParam("afterChecksums", null);
×
90
                if (afterChecksums != null) {
×
91
                    for (String checksum : afterChecksums.split(",")) {
×
92
                        checksum = checksum.trim();
×
93
                        if (checksum.isEmpty()) {
×
94
                            continue;
×
95
                        }
96
                        Document match = collection("listEntries").find(mongoSession,
×
97
                                new Document("pubkey", pubkey).append("type", type).append("checksum", checksum)).first();
×
98
                        if (match != null) {
×
99
                            long matchPos = match.getLong("position");
×
100
                            if (matchPos > afterPosition) {
×
101
                                afterPosition = matchPos;
×
102
                            }
103
                        }
104
                    }
105
                }
106

107
                // Build pipeline with optional position filter
108
                Document matchFilter = new Document("pubkey", pubkey).append("type", type);
×
109
                if (afterPosition >= 0) {
×
110
                    matchFilter.append("position", new Document("$gt", afterPosition));
×
111
                }
112
                List<Bson> pipeline = List.of(match(matchFilter), sort(ascending("position")),
×
113
                        lookup("nanopubs", "np", "_id", "nanopub"), project(new Document("jelly", "$nanopub.jelly")), unwind("$jelly"));
×
114
                try (var result = collection("listEntries").aggregate(mongoSession, pipeline).cursor()) {
×
115
                    logger.info("Streaming Jelly nanopubs for pubkey={} type={} afterPosition={}", getLabel(pubkey), getLabel(type), afterPosition);
×
116
                    NanopubStream npStream = NanopubStream.fromMongoCursor(result);
×
117
                    BufferOutputStream outputStream = new BufferOutputStream();
×
118
                    npStream.writeToByteStream(outputStream);
×
119
                    context.response().write(outputStream.getBuffer());
×
120
                    logger.info("Finished streaming Jelly nanopubs for pubkey={} type={}", getLabel(pubkey), getLabel(type));
×
121
                }
122
            } else {
×
123
                MongoCursor<Document> c = collection("listEntries").find(mongoSession, new Document("pubkey", pubkey).append("type", type)).projection(exclude("_id")).sort(ascending("position")).cursor();
×
124

125
                if (TYPE_JSON.equals(format)) {
×
126
                    int count = 0;
×
127
                    println("[");
×
128
                    while (c.hasNext()) {
×
129
                        Document d = c.next();
×
130
                        // Transforming long to int, so the JSON output looks nice:
131
                        // TODO Make this scale beyond the int range
132
                        d.replace("position", d.getLong("position").intValue());
×
133
                        print(d.toJson());
×
134
                        println(c.hasNext() ? "," : "");
×
135
                        count++;
×
136
                    }
×
137
                    println("]");
×
138
                    logger.info("Served {} list entries for pubkey={} type={} (format=json)", count, getLabel(pubkey), getLabel(type));
×
139
                } else {
×
140
                    int listed = 0;
×
141
                    printHtmlHeader("List for pubkey " + getLabel(pubkey) + " / type " + getLabel(type) + " - Nanopub Registry");
×
142
                    println("<h1>List</h1>");
×
143
                    println("<p><a href=\"/list/" + pubkey + "\">&lt; Pubkey</a></p>");
×
144
                    println("<h3>Formats</h3>");
×
145
                    println("<p>");
×
146
                    println("<a href=\"/list/" + pubkey + "/" + type + ".json\">.json</a> |");
×
147
                    println("<a href=\"/list/" + pubkey + "/" + type + ".json.txt\">.json.txt</a>");
×
148
                    println("</p>");
×
149
                    println("<h3>Pubkey Hash</h3>");
×
150
                    println("<p><code>" + pubkey + "</code></p>");
×
151
                    println("<h3>Type Hash</h3>");
×
152
                    println("<p><code>" + type + "</code></p>");
×
153
                    println("<h3>Entries</h3>");
×
154
                    println("<ol>");
×
155
                    while (c.hasNext()) {
×
156
                        Document d = c.next();
×
157
                        println("<li><a href=\"/np/" + d.getString("np") + "\"><code>" + getLabel(d.getString("np")) + "</code></a></li>");
×
158
                        listed++;
×
159
                    }
×
160
                    println("</ol>");
×
161
                    printHtmlFooter();
×
162
                    logger.info("Listed {} entries for pubkey={} type={} (format=html)", listed, getLabel(pubkey), getLabel(type));
×
163
                }
164
            }
165
        } else if (req.matches("/list/[0-9a-f]{64}")) {
×
166
            String pubkey = req.replaceFirst("/list/([0-9a-f]{64})", "$1");
×
167
            MongoCursor<Document> c = collection("lists").find(mongoSession, new Document("pubkey", pubkey)).projection(exclude("_id")).cursor();
×
168
            if (TYPE_JSON.equals(format)) {
×
169
                int count = 0;
×
170
                println("[");
×
171
                while (c.hasNext()) {
×
172
                    print(c.next().toJson());
×
173
                    println(c.hasNext() ? "," : "");
×
174
                    count++;
×
175
                }
176
                println("]");
×
177
                logger.info("Served {} account documents for pubkey={} (format=json)", count, getLabel(pubkey));
×
178
            } else {
×
179
                int listed = 0;
×
180
                printHtmlHeader("Accounts for Pubkey " + getLabel(pubkey) + " - Nanopub Registry");
×
181
                println("<h1>Accounts for Pubkey " + getLabel(pubkey) + "</h1>");
×
182
                println("<p><a href=\"/list\">&lt; Current Trust State</a></p>");
×
183
                println("<h3>Formats</h3>");
×
184
                println("<p>");
×
185
                println("<a href=\"/list/" + pubkey + ".json\">.json</a> |");
×
186
                println("<a href=\"/list/" + pubkey + ".json.txt\">.json.txt</a>");
×
187
                println("</p>");
×
188
                println("<h3>Pubkey Hash</h3>");
×
189
                println("<p><code>" + pubkey + "</code></p>");
×
190
                println("<h3>Entry Lists</h3>");
×
191
                println("<ol>");
×
192
                while (c.hasNext()) {
×
193
                    Document d = c.next();
×
194
                    String type = d.getString("type");
×
195
                    println("<li>");
×
196
                    println("<a href=\"/list/" + pubkey + "/" + type + "\"><code>" + getLabel(type) + "</code></a> ");
×
197
                    if (type.equals("$")) {
×
198
                        println("(all types)");
×
199
                    } else {
200
                        String typeUri = unhash(type);
×
201
                        println("(type " + (typeUri != null ? typeUri : type) + ")");
×
202
                    }
203
                    println("</li>");
×
204
                    listed++;
×
205
                }
×
206
                println("</ol>");
×
207
                printHtmlFooter();
×
208
                logger.info("Listed {} entry lists for pubkey={} (format=html)", listed, getLabel(pubkey));
×
209
            }
210
        } else if (req.equals("/list")) {
×
211
            try (var c = collection(Collection.ACCOUNTS.toString()).find(mongoSession).sort(ascending("pubkey")).projection(exclude("_id")).cursor()) {
×
212
                if (TYPE_JSON.equals(format)) {
×
213
                    int count = 0;
×
214
                    println("[");
×
215
                    while (c.hasNext()) {
×
216
                        print(c.next().toJson());
×
217
                        println(c.hasNext() ? "," : "");
×
218
                        count++;
×
219
                    }
220
                    println("]");
×
221
                    logger.info("Served {} accounts (format=json) for {}", count, getFullRequest());
×
222
                } else {
×
223
                    int listed = 0;
×
224
                    printHtmlHeader("Current Trust State - Nanopub Registry");
×
225
                    println("<h1>Current Trust State</h1>");
×
226
                    println("<p><a href=\"/\">&lt; Home</a></p>");
×
227
                    println("<h3>Formats</h3>");
×
228
                    println("<p>");
×
229
                    println("<a href=\"list.json\">.json</a> |");
×
230
                    println("<a href=\"list.json.txt\">.json.txt</a>");
×
231
                    println("</p>");
×
232
                    println("<h3>Accounts</h3>");
×
233
                    println("<ol>");
×
234
                    while (c.hasNext()) {
×
235
                        Document d = c.next();
×
236
                        String pubkey = d.getString("pubkey");
×
237
                        if (!pubkey.equals("$")) {
×
238
                            println("<li>");
×
239
                            println("<a href=\"/list/" + pubkey + "\"><code>" + getLabel(pubkey) + "</code></a>");
×
240
                            String a = d.getString("agent");
×
241
                            if (a != null && !a.isBlank()) {
×
242
                                print(" by <a href=\"/agent?id=" + URLEncoder.encode(a, "UTF-8") + "\">" + Utils.getAgentLabel(a) + "</a>");
×
243
                                String name = d.getString("name");
×
244
                                if (name != null && !name.isBlank()) {
×
245
                                    print(" (" + name + ")");
×
246
                                }
247
                            }
248
                            print(", status: " + d.get("status"));
×
249
                            print(", depth: " + d.get("depth"));
×
250
                            if (d.get("pathCount") != null) {
×
251
                                print(", pathCount: " + d.get("pathCount"));
×
252
                            }
253
                            if (d.get("ratio") != null) {
×
254
                                print(", ratio: " + df8.format(d.get("ratio")));
×
255
                            }
256
                            Document dollarList = RegistryDB.getOne(mongoSession, "lists",
×
257
                                    new Document("pubkey", pubkey).append("type", "$"));
×
258
                            if (dollarList != null && dollarList.get("maxPosition") != null) {
×
259
                                print(", count: " + (dollarList.getLong("maxPosition") + 1));
×
260
                            }
261
                            if (d.get("quota") != null) {
×
262
                                print(", quota: " + d.get("quota"));
×
263
                            }
264
                            println("");
×
265
                            println("</li>");
×
266
                            listed++;
×
267
                        }
268
                    }
×
269
                    println("</ol>");
×
270
                    printHtmlFooter();
×
271
                    logger.info("Listed {} accounts (format=html) for {}", listed, getFullRequest());
×
272
                }
273
            }
274
        } else if (req.equals("/agent") && context.request().getParam("id") != null) {
×
275
            String agentId = context.request().getParam("id");
×
276
            logger.info("Serving agent detail for id={} format={}", Utils.getAgentLabel(agentId), format);
×
277
            if (TYPE_JSON.equals(format)) {
×
278
                print(AgentInfo.get(mongoSession, agentId).asJson());
×
279
            } else {
280
                Document agentDoc = RegistryDB.getOne(mongoSession, Collection.AGENTS.toString(), new Document("agent", agentId));
×
281
                String agentName = (agentDoc != null) ? agentDoc.getString("name") : null;
×
282
                String headingSuffix = (agentName != null && !agentName.isBlank()) ? " (" + agentName + ")" : "";
×
283
                printHtmlHeader("Agent " + Utils.getAgentLabel(agentId) + headingSuffix + " - Nanopub Registry");
×
284
                println("<h1>Agent " + Utils.getAgentLabel(agentId) + headingSuffix + "</h1>");
×
285
                println("<p><a href=\"/agents\">&lt; Agent List</a></p>");
×
286
                println("<h3>Formats</h3>");
×
287
                println("<p>");
×
288
                println("<a href=\"agent.json?id=" + URLEncoder.encode(agentId, "UTF-8") + "\">.json</a> |");
×
289
                println("<a href=\"agent.json.txt?id=" + URLEncoder.encode(agentId, "UTF-8") + "\">.json.txt</a>");
×
290
                println("</p>");
×
291
                println("<h3>ID</h3>");
×
292
                println("<p><a href=\"" + agentId + "\"><code>" + agentId + "</code></a></p>");
×
293
                println("<h3>Properties</h3>");
×
294
                println("<ul>");
×
295
                if (agentName != null && !agentName.isBlank()) {
×
296
                    println("<li>Name: " + agentName + "</li>");
×
297
                }
298
                println("<li>Average path count: " + agentDoc.get("avgPathCount") + "</li>");
×
299
                println("<li>Total ratio: " + agentDoc.get("totalRatio") + "</li>");
×
300
                println("</ul>");
×
301
                println("<h3>Accounts</h3>");
×
302
                println("<p>Count: " + agentDoc.get("accountCount") + "</p>");
×
303
                println("<p><a href=\"agentAccounts?id=" + URLEncoder.encode(agentId, "UTF-8") + "\">&gt; agentAccounts</a></p>");
×
304
                printHtmlFooter();
×
305
            }
306
        } else if (req.equals("/agentAccounts") && context.request().getParam("id") != null) {
×
307
            String agentId = context.request().getParam("id");
×
308
            logger.info("Serving agent accounts for id={} format={}", Utils.getAgentLabel(agentId), format);
×
309
            MongoCursor<Document> c = collection(Collection.ACCOUNTS.toString()).find(mongoSession, new Document("agent", agentId)).projection(exclude("_id")).cursor();
×
310
            if (TYPE_JSON.equals(format)) {
×
311
                int count = 0;
×
312
                println("[");
×
313
                while (c.hasNext()) {
×
314
                    print(c.next().toJson());
×
315
                    println(c.hasNext() ? "," : "");
×
316
                    count++;
×
317
                }
318
                println("]");
×
319
                logger.info("Served {} agent accounts for id={} (format=json)", count, Utils.getAgentLabel(agentId));
×
320
            } else {
×
321
                Document agentDoc = RegistryDB.getOne(mongoSession, Collection.AGENTS.toString(), new Document("agent", agentId));
×
322
                String agentName = (agentDoc != null) ? agentDoc.getString("name") : null;
×
323
                String headingSuffix = (agentName != null && !agentName.isBlank()) ? " (" + agentName + ")" : "";
×
324
                printHtmlHeader("Accounts of Agent " + Utils.getAgentLabel(agentId) + headingSuffix + " - Nanopub Registry");
×
325
                println("<h1>Accounts of Agent " + Utils.getAgentLabel(agentId) + headingSuffix + "</h1>");
×
326
                println("<p><a href=\"/agent?id=" + URLEncoder.encode(agentId, "UTF-8") + "\">&lt; Agent</a></p>");
×
327
                println("<h3>Formats</h3>");
×
328
                println("<p>");
×
329
                println("<a href=\"agentAccounts.json?id=" + URLEncoder.encode(agentId, "UTF-8") + "\">.json</a> |");
×
330
                println("<a href=\"agentAccounts.json.txt?id=" + URLEncoder.encode(agentId, "UTF-8") + "\">.json.txt</a>");
×
331
                println("</p>");
×
332
                println("<h3>Account List</h3>");
×
333
                println("<ul>");
×
334
                int listed = 0;
×
335
                while (c.hasNext()) {
×
336
                    Document d = c.next();
×
337
                    String pubkey = d.getString("pubkey");
×
338
                    //                                Object iCount = getMaxValue("listEntries", new Document("pubkey", pubkey).append("type", INTRO_TYPE_HASH), "position");
339
                    //                                Object eCount = getMaxValue("listEntries", new Document("pubkey", pubkey).append("type", ENDORSE_TYPE), "position");
340
                    //                                Object fCount = getMaxValue("listEntries", new Document("pubkey", pubkey).append("type", "$"), "position");
341
                    Document dollarList = RegistryDB.getOne(mongoSession, "lists",
×
342
                            new Document("pubkey", pubkey).append("type", "$"));
×
343
                    long npCount = (dollarList != null && dollarList.get("maxPosition") != null)
×
344
                            ? dollarList.getLong("maxPosition") + 1 : 0;
×
345
                    String accountName = d.getString("name");
×
346
                    String nameSuffix = (accountName != null && !accountName.isBlank()) ? " (" + accountName + ")" : "";
×
347
                    println("<li><a href=\"/list/" + pubkey + "\"><code>" + getLabel(pubkey) + "</code></a>" + nameSuffix + " (" + d.get("status") + "), " + "count " + npCount + ", " + "quota " + d.get("quota") + ", " + "ratio " + df8.format(d.get("ratio")) + ", " + "path count " + d.get("pathCount") + "</li>");
×
348
                    listed++;
×
349
                }
×
350
                println("</ul>");
×
351
                printHtmlFooter();
×
352
                logger.info("Listed {} accounts for agent id={} (format=html)", listed, Utils.getAgentLabel(agentId));
×
353
            }
354
        } else if (req.equals("/agents")) {
×
355
            MongoCursor<Document> c = collection(Collection.AGENTS.toString()).find(mongoSession).sort(descending("totalRatio")).projection(exclude("_id")).cursor();
×
356
            if (TYPE_JSON.equals(format)) {
×
357
                int count = 0;
×
358
                println("[");
×
359
                while (c.hasNext()) {
×
360
                    print(c.next().toJson());
×
361
                    println(c.hasNext() ? "," : "");
×
362
                    count++;
×
363
                }
364
                println("]");
×
365
                logger.info("Served {} agents (format=json)", count);
×
366
            } else {
×
367
                int listed = 0;
×
368
                printHtmlHeader("Agent List - Nanopub Registry");
×
369
                println("<h1>Agent List</h1>");
×
370
                println("<p><a href=\"/\">&lt; Home</a></p>");
×
371
                println("<h3>Formats</h3>");
×
372
                println("<p>");
×
373
                println("<a href=\"agents.json\">.json</a> |");
×
374
                println("<a href=\"agents.json.txt\">.json.txt</a>");
×
375
                println("</p>");
×
376
                println("<h3>Agents</h3>");
×
377
                println("<ol>");
×
378
                while (c.hasNext()) {
×
379
                    Document d = c.next();
×
380
                    if (d.get("agent").equals("$")) {
×
381
                        continue;
×
382
                    }
383
                    String a = d.getString("agent");
×
384
                    int accountCount = d.getInteger("accountCount");
×
385
                    String name = d.getString("name");
×
386
                    String nameSuffix = (name != null && !name.isBlank()) ? " (" + name + ")" : "";
×
387
                    println("<li><a href=\"/agent?id=" + URLEncoder.encode(a, "UTF-8") + "\">" + Utils.getAgentLabel(a) + "</a>" + nameSuffix + ", " + accountCount + " account" + (accountCount == 1 ? "" : "s") + ", " + "ratio " + df8.format(d.get("totalRatio")) + ", " + "avg. path count " + df1.format(d.get("avgPathCount")) + "</li>");
×
388
                    listed++;
×
389
                }
×
390
                println("</ol>");
×
391
                printHtmlFooter();
×
392
                logger.info("Listed {} agents (format=html)", listed);
×
393
            }
394
        } else if (req.equals("/nanopubs")) {
×
395
            if (TYPE_JELLY.equals(format)) {
×
396
                // Return all nanopubs after counter X (-1 by default)
397
                long afterCounter;
398
                try {
399
                    afterCounter = Long.parseLong(getParam("afterCounter", "-1"));
×
400
                } catch (NumberFormatException ex) {
×
401
                    logger.warn("Invalid afterCounter parameter for {}: {} ({})", getFullRequest(), getParam("afterCounter", ""), ex);
×
402
                    context.response().setStatusCode(400).setStatusMessage("Invalid afterCounter parameter.");
×
403
                    return;
×
404
                }
×
405
                logger.info("Streaming nanopubs.jelly afterCounter={}", afterCounter);
×
406
                var pipeline = collection(Collection.NANOPUBS.toString()).find(mongoSession).filter(gt("counter", afterCounter)).sort(ascending("counter"))
×
407
                        .projection(include("jelly", "counter"));
×
408

409
                try (var result = pipeline.cursor()) {
×
410
                    NanopubStream npStream = NanopubStream.fromMongoCursorWithCounter(result);
×
411
                    BufferOutputStream outputStream = new BufferOutputStream();
×
412
                    npStream.writeToByteStream(outputStream);
×
413
                    context.response().write(outputStream.getBuffer());
×
414
                }
415
                logger.info("Finished streaming nanopubs.jelly for {}", getFullRequest());
×
416
            } else {
×
417
                // Return nanopubs as streamed JSON or HTML
418
                String sortParam = getParam("sort", "date");
×
419

420
                if (TYPE_JSON.equals(format)) {
×
421
                    Bson filter;
422
                    Bson sort;
423
                    if ("id".equals(sortParam)) {
×
424
                        String afterId = getParam("after", "");
×
425
                        filter = afterId.isEmpty() ? new Document() : gt("_id", afterId);
×
426
                        sort = ascending("_id");
×
427
                    } else {
×
428
                        // sort=date (default): latest first, using indexed counter field
429
                        filter = new Document();
×
430
                        sort = descending("counter");
×
431
                    }
432
                    int count = 0;
×
433
                    try (MongoCursor<Document> c = collection(Collection.NANOPUBS.toString()).find(mongoSession)
×
434
                            .filter(filter).sort(sort)
×
435
                            .projection(include("_id")).cursor()) {
×
436
                        println("[");
×
437
                        boolean first = true;
×
438
                        while (c.hasNext()) {
×
439
                            if (!first) {
×
440
                                println(",");
×
441
                            }
442
                            first = false;
×
443
                            print(gson.toJson(c.next().getString("_id")));
×
444
                            count++;
×
445
                        }
446
                        println("\n]");
×
447
                    }
448
                    logger.info("Served {} nanopub ids (format=json, sort={})", count, sortParam);
×
449
                } else {
×
450
                    printHtmlHeader("Nanopubs - Nanopub Registry");
×
451
                    println("<h1>Nanopubs</h1>");
×
452
                    println("<p><a href=\"/\">&lt; Home</a></p>");
×
453
                    println("<h3>All Nanopub IDs (JSON, latest first)</h3>");
×
454
                    println("<p>");
×
455
                    println("<a href=\"nanopubs.json\">.json</a> |");
×
456
                    println("<a href=\"nanopubs.json.txt\">.json.txt</a>");
×
457
                    println("</p>");
×
458
                    println("<h3>All Nanopub IDs (JSON, sorted by artifact code)</h3>");
×
459
                    println("<p>");
×
460
                    println("<a href=\"nanopubs.json?sort=id\">.json</a> |");
×
461
                    println("<a href=\"nanopubs.json.txt?sort=id\">.json.txt</a>");
×
462
                    println("</p>");
×
463
                    println("<h3>All Nanopubs (Jelly)</h3>");
×
464
                    println("<p><a href=\"nanopubs.jelly\">.jelly</a></p>");
×
465
                    println("<h3>Latest Nanopubs (max. 1000)</h3>");
×
466
                    println("<ol>");
×
467
                    int listed = 0;
×
468
                    try (MongoCursor<Document> c = collection(Collection.NANOPUBS.toString()).find(mongoSession)
×
469
                            .sort(descending("counter")).limit(1000).cursor()) {
×
470
                        while (c.hasNext()) {
×
471
                            String npId = c.next().getString("_id");
×
472
                            println("<li><a href=\"/np/" + npId + "\"><code>" + getLabel(npId) + "</code></a></li>");
×
473
                            listed++;
×
474
                        }
×
475
                    }
476
                    println("</ol>");
×
477
                    printHtmlFooter();
×
478
                    logger.info("Listed {} latest nanopubs (format=html)", listed);
×
479
                }
480
            }
×
481
        } else if (req.equals("/pubkeys")) {
×
482
            try (var c = collection("lists").distinct(mongoSession, "pubkey", String.class).cursor()) {
×
483
                if (TYPE_JSON.equals(format)) {
×
484
                    int count = 0;
×
485
                    println("[");
×
486
                    while (c.hasNext()) {
×
487
                        print(gson.toJson(c.next()));
×
488
                        println(c.hasNext() ? "," : "");
×
489
                        count++;
×
490
                    }
491
                    println("]");
×
492
                    logger.info("Served {} pubkeys (format=json)", count);
×
493
                } else {
×
494
                    int listed = 0;
×
495
                    printHtmlHeader("Pubkey List - Nanopub Registry");
×
496
                    println("<h1>Pubkey List</h1>");
×
497
                    println("<p><a href=\"/\">&lt; Home</a></p>");
×
498
                    println("<h3>Formats</h3>");
×
499
                    println("<p>");
×
500
                    println("<a href=\"pubkeys.json\">.json</a> |");
×
501
                    println("<a href=\"pubkeys.json.txt\">.json.txt</a>");
×
502
                    println("</p>");
×
503
                    println("<h3>Pubkeys</h3>");
×
504
                    println("<ol>");
×
505
                    while (c.hasNext()) {
×
506
                        String pubkey = c.next();
×
507
                        if (!pubkey.equals("$")) {
×
508
                            println("<li>");
×
509
                            println("<a href=\"/list/" + pubkey + "\"><code>" + getLabel(pubkey) + "</code></a>");
×
510
                            println("</li>");
×
511
                            listed++;
×
512
                        }
513
                    }
×
514
                    println("</ol>");
×
515
                    printHtmlFooter();
×
516
                    logger.info("Listed {} pubkeys (format=html)", listed);
×
517
                }
518
            }
519
        } else {
520
            logger.warn("Invalid list request path: {}", getFullRequest());
×
521
            context.response().setStatusCode(400).setStatusMessage("Invalid request: " + getFullRequest());
×
522
        }
523
    }
×
524

525
    private static String getLabel(Object obj) {
526
        if (obj == null) {
×
527
            return null;
×
528
        }
529
        if (obj.toString().length() < 10) {
×
530
            return obj.toString();
×
531
        }
532
        return obj.toString().substring(0, 10);
×
533
    }
534

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