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

knowledgepixels / nanopub-registry / 24132894881

08 Apr 2026 11:26AM UTC coverage: 32.824% (+0.2%) from 32.613%
24132894881

push

github

tkuhn
perf: batch serverInfo queries and add coverage headers

Fetch all serverInfo key-value pairs in one MongoDB query instead of
7+ separate getValue calls per request. Reuse the result in Page,
MainPage, and RegistryInfo.

Default coverageTypes to "all" and coverageAgents to "viaSetting"
when not configured. Add Nanopub-Registry-Coverage-Types and
Nanopub-Registry-Coverage-Agents HTTP headers to all responses.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

274 of 924 branches covered (29.65%)

Branch coverage included in aggregate %.

801 of 2351 relevant lines covered (34.07%)

5.74 hits per line

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

92.55
src/main/java/com/knowledgepixels/registry/Page.java
1
package com.knowledgepixels.registry;
2

3
import com.mongodb.client.ClientSession;
4
import org.bson.Document;
5
import io.vertx.core.http.HttpMethod;
6
import io.vertx.ext.web.RoutingContext;
7
import org.apache.commons.lang.StringEscapeUtils;
8

9
import java.io.IOException;
10
import java.text.DecimalFormat;
11

12
import static com.knowledgepixels.registry.RegistryDB.*;
13

14
public abstract class Page {
15

16
    protected static final DecimalFormat df8 = new DecimalFormat("0.00000000");
15✔
17
    protected static final DecimalFormat df1 = new DecimalFormat("0.0");
18✔
18

19
    private RoutingContext context;
20
    protected ClientSession mongoSession;
21
    protected Document serverInfo;
22

23
    private String presentationFormat;
24
    private String extension;
25
    private String requestString;
26

27
    /**
28
     * Constructor for the Page class.
29
     *
30
     * @param mongoSession The MongoDB client session.
31
     * @param context      The routing context.
32
     */
33
    public Page(ClientSession mongoSession, RoutingContext context) {
6✔
34
        this.mongoSession = mongoSession;
9✔
35
        this.context = context;
9✔
36
        context.response().setChunked(true);
15✔
37

38
        // Fetch all serverInfo key-value pairs in one query instead of separate getValue calls
39
        serverInfo = new Document();
15✔
40
        for (Document d : collection(Collection.SERVER_INFO.toString()).find(mongoSession)) {
42✔
41
            serverInfo.put(d.getString("_id"), d.get("value"));
30✔
42
        }
3✔
43
        context.response().putHeader("Nanopub-Registry-Status", serverInfo.get("status") + "");
33✔
44
        context.response().putHeader("Nanopub-Registry-Setup-Id", serverInfo.get("setupId") + "");
33✔
45
        context.response().putHeader("Nanopub-Registry-Trust-State-Counter", serverInfo.get("trustStateCounter") + "");
33✔
46
        context.response().putHeader("Nanopub-Registry-Last-Trust-State-Update", serverInfo.get("lastTrustStateUpdate") + "");
33✔
47
        context.response().putHeader("Nanopub-Registry-Trust-State-Hash", serverInfo.get("trustStateHash") + "");
33✔
48
        Object maxSeqNum = getMaxValue(mongoSession, Collection.NANOPUBS.toString(), "seqNum");
18✔
49
        context.response().putHeader("Nanopub-Registry-SeqNum", maxSeqNum + "");
24✔
50
        context.response().putHeader("Nanopub-Registry-Nanopub-Count", collection(Collection.NANOPUBS.toString()).estimatedDocumentCount() + "");
30✔
51
        // TODO(transition): Remove after all peers upgraded
52
        context.response().putHeader("Nanopub-Registry-Load-Counter", maxSeqNum + "");
24✔
53
        context.response().putHeader("Nanopub-Registry-Test-Instance", String.valueOf(serverInfo.get("testInstance") != null && (Boolean) serverInfo.get("testInstance")));
57!
54
        context.response().putHeader("Nanopub-Registry-Coverage-Types", serverInfo.get("coverageTypes") != null ? serverInfo.get("coverageTypes").toString() : "all");
33!
55
        context.response().putHeader("Nanopub-Registry-Coverage-Agents", serverInfo.get("coverageAgents") != null ? serverInfo.get("coverageAgents").toString() : "viaSetting");
33!
56

57
        String r = context.request().path().substring(1);
18✔
58
        if (r.endsWith(".txt")) {
12✔
59
            presentationFormat = "text/plain";
9✔
60
            r = r.replaceFirst("\\.txt$", "");
18✔
61
        } else if (r.endsWith(".html")) {
12✔
62
            presentationFormat = "text/html";
9✔
63
            r = r.replaceFirst("\\.html$", "");
15✔
64
//                } else if (r.endsWith(".gz")) {
65
//                        presentationFormat = "application/x-gzip";
66
//                        r = r.replaceFirst("\\.gz$", "");
67
        }
68
        if (r.matches(".*\\.[a-z]{1,10}")) {
12✔
69
            extension = r.replaceFirst("^.*\\.([a-z]{1,10})$", "$1");
18✔
70
            requestString = r.replaceFirst("^(.*)\\.[a-z]{1,10}$", "$1");
21✔
71
        } else {
72
            requestString = r;
9✔
73
        }
74
    }
3✔
75

76
    /**
77
     * Get the routing context.
78
     *
79
     * @return The routing context.
80
     */
81
    public RoutingContext getContext() {
82
        return context;
9✔
83
    }
84

85
    /**
86
     * Print a line to the response.
87
     *
88
     * @param s The string to print.
89
     */
90
    public void println(String s) {
91
        print(s + "\n");
12✔
92
    }
3✔
93

94
    /**
95
     * Print a string to the response.
96
     *
97
     * @param s The string to print.
98
     */
99
    public void print(String s) {
100
        if (context.request().method() == HttpMethod.HEAD) {
18!
101
            return;
×
102
        }
103
        context.response().write(s);
18✔
104
    }
3✔
105

106
    /**
107
     * Set the response content type.
108
     *
109
     * @param contentType The content type.
110
     */
111
    public void setRespContentType(String contentType) {
112
        if (context.request().method() == HttpMethod.HEAD) {
18✔
113
            return;
3✔
114
        }
115
        context.response().putHeader("Content-Type", contentType);
21✔
116
    }
3✔
117

118
    /**
119
     * Show the page.
120
     *
121
     * @throws IOException
122
     */
123
    protected abstract void show() throws IOException;
124

125
    /**
126
     * Print the HTML header.
127
     *
128
     * @param title The title of the page.
129
     */
130
    public void printHtmlHeader(String title) {
131
        println("<!DOCTYPE HTML>");
9✔
132
        println("<html><head>");
9✔
133
        println("<title>" + title + "</title>");
12✔
134
        println("<meta charset=\"utf-8\"/>");
9✔
135
        println("<script type=\"text/javascript\" src=\"/scripts/nanopub.js\"></script>");
9✔
136
        println("<link rel=\"stylesheet\" href=\"/style.css\" type=\"text/css\" media=\"screen\" title=\"Stylesheet\" />");
9✔
137
        println("</head><body>");
9✔
138
    }
3✔
139

140
    /**
141
     * Print the HTML footer.
142
     */
143
    public void printHtmlFooter() {
144
        println("</body></html>");
9✔
145
    }
3✔
146

147
    /**
148
     * Escape HTML special characters in a string.
149
     *
150
     * @param text The text to escape.
151
     * @return The escaped text.
152
     */
153
    public String escapeHtml(String text) {
154
        return StringEscapeUtils.escapeHtml(text);
×
155
    }
156

157
    /**
158
     * Set the canonical link for this page.
159
     *
160
     * @param url The canonical URL.
161
     */
162
    public void setCanonicalLink(String url) {
163
        context.response().putHeader("Link", "<" + url + ">; rel=\"canonical\"");
24✔
164
    }
3✔
165

166
    /**
167
     * Get the presentation format requested, if any.
168
     *
169
     * @return The presentation format.
170
     */
171
    public String getPresentationFormat() {
172
        return presentationFormat;
9✔
173
    }
174

175
    /**
176
     * Get the extension requested, if any.
177
     *
178
     * @return The extension.
179
     */
180
    public String getExtension() {
181
        return extension;
9✔
182
    }
183

184
    /**
185
     * Get the request string with the leading slash and without extension.
186
     *
187
     * @return The request string.
188
     */
189
    public String getRequestString() {
190
        return "/" + requestString;
12✔
191
    }
192

193
    /**
194
     * Get the full request path.
195
     *
196
     * @return The full request path.
197
     */
198
    public String getFullRequest() {
199
        return context.request().path();
15✔
200
    }
201

202
    /**
203
     * Get a parameter from the request.
204
     *
205
     * @param name         The name of the parameter.
206
     * @param defaultValue The default value of the parameter.
207
     * @return The value of the parameter.
208
     */
209
    public String getParam(String name, String defaultValue) {
210
        String value = context.request().getParam(name);
18✔
211
        if (value == null) value = defaultValue;
12✔
212
        return value;
6✔
213
    }
214

215
    /**
216
     * Check whether the request is empty.
217
     *
218
     * @return True if the request is empty, false otherwise.
219
     */
220
    public boolean isEmpty() {
221
        return requestString.isEmpty();
12✔
222
    }
223

224
    /**
225
     * Check whether the request contains an artifact code.
226
     *
227
     * @return True if the request contains an artifact code, false otherwise.
228
     */
229
    public boolean hasArtifactCode() {
230
        return requestString.matches("RA[A-Za-z0-9\\-_]{43}");
15✔
231
    }
232

233
    /**
234
     * Get the artifact code from the request, if present.
235
     *
236
     * @return The artifact code, or null if not present.
237
     */
238
    public String getArtifactCode() {
239
        if (hasArtifactCode()) {
9✔
240
            return requestString;
9✔
241
        }
242
        return null;
6✔
243
    }
244

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