• 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

29.17
src/main/java/com/knowledgepixels/registry/TrustStatePage.java
1
package com.knowledgepixels.registry;
2

3
import com.mongodb.client.ClientSession;
4
import com.mongodb.client.MongoCursor;
5
import io.vertx.ext.web.RoutingContext;
6
import org.bson.Document;
7
import org.slf4j.Logger;
8
import org.slf4j.LoggerFactory;
9

10
import java.io.IOException;
11
import java.util.List;
12

13
import static com.knowledgepixels.registry.RegistryDB.collection;
14
import static com.knowledgepixels.registry.Utils.TYPE_HTML;
15
import static com.knowledgepixels.registry.Utils.TYPE_JSON;
16
import static com.mongodb.client.model.Projections.exclude;
17
import static com.mongodb.client.model.Sorts.descending;
18

19
/**
20
 * Serves hash-keyed trust state snapshots.
21
 *
22
 * <ul>
23
 *   <li><code>/trust-state</code> — list of retained snapshots (HTML or JSON)</li>
24
 *   <li><code>/trust-state/&lt;hash&gt;</code> — single snapshot (HTML or JSON)</li>
25
 * </ul>
26
 *
27
 * <p>Snapshot content is immutable once a hash is known. Consumers use
28
 * <code>/trust-state/&lt;hash&gt;.json</code> after detecting a hash change (via the
29
 * {@code Nanopub-Registry-Trust-State-Hash} response header) to load the corresponding
30
 * snapshot side-by-side with the previous one.
31
 */
32
public class TrustStatePage extends Page {
33

34
    private static final Logger logger = LoggerFactory.getLogger(TrustStatePage.class);
12✔
35

36
    private static final String SUPPORTED_TYPES = TYPE_JSON + "," + TYPE_HTML;
37

38
    public static void show(RoutingContext context) {
39
        TrustStatePage page;
40
        logger.info("Received trust-state request: {}", context.request().path());
18✔
41
        try (ClientSession s = RegistryDB.getClient().startSession()) {
9✔
42
            s.startTransaction();
6✔
43
            page = new TrustStatePage(s, context);
18✔
44
            page.show();
6✔
45
        } catch (IOException ex) {
×
46
            logger.warn("Failed to show trust-state for request {}: {} ({})", context.request().path(), ex.getMessage(), ex.getClass().getSimpleName(), ex);
×
47
        } finally {
48
            logger.debug("Ending response for trust-state request: {}", context.request().path());
18✔
49
            context.response().end();
12✔
50
        }
51
    }
3✔
52

53
    private TrustStatePage(ClientSession mongoSession, RoutingContext context) {
54
        super(mongoSession, context);
12✔
55
    }
3✔
56

57
    protected void show() throws IOException {
58
        RoutingContext c = getContext();
9✔
59
        String req = getRequestString();
9✔
60
        String ext = getExtension();
9✔
61

62
        logger.debug("Preparing trust-state response for request: {} (ext={})", getFullRequest(), ext);
18✔
63

64
        String format;
65
        if ("json".equals(ext)) {
12✔
66
            format = TYPE_JSON;
9✔
67
        } else if (ext == null || "html".equals(ext)) {
18!
68
            format = Utils.getMimeType(c, SUPPORTED_TYPES);
×
69
        } else {
70
            logger.warn("Invalid trust-state request (unsupported extension) for {}: {}", getFullRequest(), ext);
18✔
71
            c.response().setStatusCode(400).setStatusMessage("Invalid request: " + getFullRequest());
27✔
72
            return;
3✔
73
        }
74

75
        if (getPresentationFormat() != null) {
9!
76
            setRespContentType(getPresentationFormat());
×
77
            logger.debug("Overriding response content type with presentation format: {}", getPresentationFormat());
×
78
        } else {
79
            setRespContentType(format);
9✔
80
            logger.debug("Set response content type: {}", format);
12✔
81
        }
82

83
        if ("/trust-state".equals(req) || "/trust-state/".equals(req)) {
24!
84
            showList(format);
×
85
        } else if (req.matches("/trust-state/[A-Za-z0-9_\\-]+")) {
12✔
86
            String hash = req.substring("/trust-state/".length());
15✔
87
            logger.debug("Resolved snapshot hash '{}' from request path", hash);
12✔
88
            showDetail(hash, format);
12✔
89
        } else {
3✔
90
            logger.warn("Invalid trust-state request path: {}", getFullRequest());
15✔
91
            c.response().setStatusCode(400).setStatusMessage("Invalid request: " + getFullRequest());
27✔
92
        }
93
    }
3✔
94

95
    private void showList(String format) {
96
        int listed = 0;
×
97
        logger.debug("Querying trust-state snapshots collection (format={})", format);
×
98
        // Metadata only — the accounts array is heavy and not needed in the index.
99
        try (MongoCursor<Document> it = collection(Collection.TRUST_STATE_SNAPSHOTS.toString())
×
100
                .find(mongoSession)
×
101
                .projection(exclude("accounts"))
×
102
                .sort(descending("trustStateCounter"))
×
103
                .cursor()) {
×
104
            if (TYPE_JSON.equals(format)) {
×
105
                println("[");
×
106
                while (it.hasNext()) {
×
107
                    Document d = it.next();
×
108
                    Document out = new Document()
×
109
                            .append("trustStateHash", d.getString("_id"))
×
110
                            .append("trustStateCounter", d.get("trustStateCounter"))
×
111
                            .append("createdAt", d.get("createdAt"));
×
112
                    print(out.toJson());
×
113
                    println(it.hasNext() ? "," : "");
×
114
                    listed++;
×
115
                }
×
116
                println("]");
×
117
                logger.info("Listed {} trust-state snapshots (format=json) for {}", listed, getFullRequest());
×
118
            } else {
119
                printHtmlHeader("Trust State History - Nanopub Registry");
×
120
                println("<h1>Trust State History</h1>");
×
121
                println("<p><a href=\"/\">&lt; Home</a></p>");
×
122
                println("<h3>Formats</h3>");
×
123
                println("<p>");
×
124
                println("<a href=\"trust-state.json\">.json</a> |");
×
125
                println("<a href=\"trust-state.json.txt\">.json.txt</a>");
×
126
                println("</p>");
×
127
                println("<h3>Past Trust States</h3>");
×
128
                println("<ol>");
×
129
                while (it.hasNext()) {
×
130
                    Document d = it.next();
×
131
                    String hash = d.getString("_id");
×
132
                    println("<li>");
×
133
                    println("<a href=\"/trust-state/" + hash + "\"><code>" + getLabel(hash) + "</code></a>");
×
134
                    print(", counter " + d.get("trustStateCounter"));
×
135
                    Object createdAt = d.get("createdAt");
×
136
                    if (createdAt != null) {
×
137
                        print(", " + createdAt.toString().replaceFirst("\\.[^.]*$", ""));
×
138
                    }
139
                    println("</li>");
×
140
                }
×
141
                println("</ol>");
×
142
                printHtmlFooter();
×
143
                logger.info("Listed {} trust-state snapshots (format=html) for {}", listed, getFullRequest());
×
144
            }
145
        }
146
    }
×
147

148
    private void showDetail(String hash, String format) {
149
        logger.debug("Looking up trust-state snapshot '{}'", hash);
12✔
150
        Document snapshot = collection(Collection.TRUST_STATE_SNAPSHOTS.toString())
30✔
151
                .find(mongoSession, new Document("_id", hash)).first();
12✔
152
        if (snapshot == null) {
6✔
153
            logger.warn("Trust state snapshot not found: {} (request={})", hash, getFullRequest());
18✔
154
            getContext().response().setStatusCode(404).setStatusMessage("Trust state snapshot not found");
24✔
155
            return;
3✔
156
        }
157
        logger.info("Serving trust state snapshot {} (format={}) for {}", hash, format, getFullRequest());
54✔
158

159
        if (TYPE_JSON.equals(format)) {
12!
160
            // Content is immutable by construction: the URL encodes a hash that never rewrites.
161
            getContext().response().putHeader("Cache-Control", "public, immutable, max-age=31536000");
21✔
162
            logger.debug("Set immutable cache-control for snapshot {}", hash);
12✔
163
            Document output = new Document()
18✔
164
                    .append("trustStateHash", snapshot.getString("_id"))
15✔
165
                    .append("trustStateCounter", snapshot.get("trustStateCounter"))
15✔
166
                    .append("createdAt", snapshot.get("createdAt"))
15✔
167
                    .append("accounts", snapshot.get("accounts"));
9✔
168
            print(output.toJson());
12✔
169
            return;
3✔
170
        }
171

172
        // HTML detail view
173
        printHtmlHeader("Trust State " + getLabel(hash) + " - Nanopub Registry");
×
174
        println("<h1>Trust State <code>" + getLabel(hash) + "</code></h1>");
×
175
        println("<p><a href=\"/trust-state\">&lt; Trust State History</a></p>");
×
176
        println("<h3>Formats</h3>");
×
177
        println("<p>");
×
178
        println("<a href=\"" + hash + ".json\">.json</a> |");
×
179
        println("<a href=\"" + hash + ".json.txt\">.json.txt</a>");
×
180
        println("</p>");
×
181
        println("<h3>Hash</h3>");
×
182
        println("<p><code>" + hash + "</code></p>");
×
183
        println("<h3>Metadata</h3>");
×
184
        println("<ul>");
×
185
        println("<li><em>trustStateCounter:</em> " + snapshot.get("trustStateCounter") + "</li>");
×
186
        Object createdAt = snapshot.get("createdAt");
×
187
        if (createdAt != null) {
×
188
            println("<li><em>createdAt:</em> " + createdAt.toString().replaceFirst("\\.[^.]*$", "") + "</li>");
×
189
        }
190
        Object accountsObj = snapshot.get("accounts");
×
191
        int accountCount = (accountsObj instanceof List) ? ((List<?>) accountsObj).size() : 0;
×
192
        println("<li><em>accountCount:</em> " + accountCount + "</li>");
×
193
        logger.debug("Snapshot {} has {} account entries (accountsObj type: {})", hash, accountCount, accountsObj == null ? "null" : accountsObj.getClass().getSimpleName());
×
194
        println("</ul>");
×
195
        println("<h3>Accounts</h3>");
×
196
        println("<ol>");
×
197
        if (accountsObj instanceof List) {
×
198
            for (Object entry : (List<?>) accountsObj) {
×
199
                if (!(entry instanceof Document)) {
×
200
                    continue;
×
201
                }
202
                Document a = (Document) entry;
×
203
                String pubkey = a.getString("pubkey");
×
204
                String agent = a.getString("agent");
×
205
                String name = a.getString("name");
×
206
                println("<li>");
×
207
                println("<a href=\"/list/" + pubkey + "\"><code>" + getLabel(pubkey) + "</code></a>");
×
208
                if (agent != null && !agent.isBlank()) {
×
209
                    print(" by <a href=\"/agent?id=" + Utils.urlEncode(agent) + "\">" + Utils.getAgentLabel(agent) + "</a>");
×
210
                    if (name != null && !name.isBlank()) {
×
211
                        print(" (" + name + ")");
×
212
                    }
213
                }
214
                print(", status: " + a.get("status"));
×
215
                print(", depth: " + a.get("depth"));
×
216
                if (a.get("pathCount") != null) {
×
217
                    print(", pathCount: " + a.get("pathCount"));
×
218
                }
219
                if (a.get("ratio") != null) {
×
220
                    print(", ratio: " + a.get("ratio"));
×
221
                }
222
                if (a.get("quota") != null) {
×
223
                    print(", quota: " + a.get("quota"));
×
224
                }
225
                println("</li>");
×
226
            }
×
227
        }
228
        println("</ol>");
×
229
        printHtmlFooter();
×
230
    }
×
231

232
    private static String getLabel(String s) {
233
        if (s == null) {
×
234
            return null;
×
235
        }
236
        if (s.length() < 10) {
×
237
            return s;
×
238
        }
239
        return s.substring(0, 10);
×
240
    }
241

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