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

knowledgepixels / nanopub-registry / 23243177209

18 Mar 2026 11:47AM UTC coverage: 30.21% (+4.3%) from 25.927%
23243177209

push

github

web-flow
Merge pull request #83 from knowledgepixels/feature/get-path-nanodash-forward

Add /get/ path that forwards to Nanodash for HTML requests

185 of 680 branches covered (27.21%)

Branch coverage included in aggregate %.

621 of 1988 relevant lines covered (31.24%)

5.27 hits per line

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

67.13
src/main/java/com/knowledgepixels/registry/NanopubPage.java
1
package com.knowledgepixels.registry;
2

3
import com.github.jsonldjava.shaded.com.google.common.base.Charsets;
4
import com.mongodb.client.ClientSession;
5
import eu.neverblink.jelly.core.utils.IoUtils;
6
import io.vertx.ext.web.RoutingContext;
7
import org.apache.commons.lang.StringEscapeUtils;
8
import org.bson.Document;
9
import org.bson.types.Binary;
10
import org.eclipse.rdf4j.common.exception.RDF4JException;
11
import org.eclipse.rdf4j.rio.RDFFormat;
12
import org.nanopub.MalformedNanopubException;
13
import org.nanopub.Nanopub;
14
import org.nanopub.NanopubImpl;
15
import org.nanopub.NanopubUtils;
16

17
import java.io.IOException;
18

19
import static com.knowledgepixels.registry.RegistryDB.collection;
20
import static com.knowledgepixels.registry.RegistryDB.isSet;
21
import static com.knowledgepixels.registry.Utils.*;
22

23
public class NanopubPage extends Page {
24

25
    static final String NANODASH_BASE_URL_DEFAULT = "https://nanodash.knowledgepixels.com/";
26

27
    static String getNanodashBaseUrl() {
28
        return Utils.getEnv("REGISTRY_NANODASH_BASE_URL", NANODASH_BASE_URL_DEFAULT);
12✔
29
    }
30

31
    private final boolean forwardHtml;
32

33
    public static void show(RoutingContext context) {
34
        show(context, false);
×
35
    }
×
36

37
    public static void show(RoutingContext context, boolean forwardHtml) {
38
        NanopubPage page;
39
        try (ClientSession s = RegistryDB.getClient().startSession()) {
9✔
40
            s.startTransaction();
6✔
41
            page = new NanopubPage(s, context, forwardHtml);
21✔
42
            page.show();
6✔
43
        } catch (IOException ex) {
×
44
            ex.printStackTrace();
×
45
        } finally {
46
            context.response().end();
12✔
47
            // TODO Clean-up here?
48
        }
49
    }
3✔
50

51
    private NanopubPage(ClientSession mongoSession, RoutingContext context, boolean forwardHtml) {
52
        super(mongoSession, context);
12✔
53
        this.forwardHtml = forwardHtml;
9✔
54
    }
3✔
55

56
    protected void show() throws IOException {
57
        RoutingContext c = getContext();
9✔
58
        String ext = getExtension();
9✔
59
        String format = Utils.getType(ext);
9✔
60
        final String req = getRequestString();
9✔
61
        if (format == null) {
6✔
62
            format = Utils.getMimeType(c, SUPPORTED_TYPES_NANOPUB);
12✔
63
        }
64
        if (format == null) {
6!
65
            c.response().setStatusCode(400).setStatusMessage("Invalid request: " + getFullRequest());
×
66
            return;
×
67
        }
68

69
        var presentationFormat = getPresentationFormat();
9✔
70
        if (presentationFormat != null) {
6!
71
            setRespContentType(presentationFormat);
×
72
        } else {
73
            setRespContentType(format);
9✔
74
        }
75

76
        if (req.matches("/(np|get)/RA[a-zA-Z0-9-_]{43}(\\.[a-z]+)?")) {
12!
77
            String ac = req.replaceFirst("/(np|get)/(RA[a-zA-Z0-9-_]{43})(\\.[a-z]+)?", "$2");
15✔
78
            Document npDoc = collection(Collection.NANOPUBS.toString()).find(new Document("_id", ac)).first();
36✔
79
            if (npDoc == null) {
6✔
80
                if (!isSet(mongoSession, Collection.SERVER_INFO.toString(), "testInstance")) {
21!
81
                    //getResp().sendError(404, "Not found: " + ac);
82
                    c.response().setStatusCode(307);
15✔
83
                    c.response().putHeader("Location", "https://np.knowledgepixels.com/" + ac);
21✔
84
                    return;
3✔
85
                } else {
86
                    c.response().setStatusCode(404).setStatusMessage("Not found: " + getFullRequest());
×
87
                    return;
×
88
                }
89
            }
90
            //                String url = ServerConf.getInfo().getPublicUrl();
91
            if (TYPE_TRIG.equals(format)) {
12✔
92
                println(npDoc.getString("content"));
18✔
93
            } else if (TYPE_JELLY.equals(format)) {
12!
94
                if (presentationFormat != null && presentationFormat.startsWith("text")) {
×
95
                    // Parse the Jelly frame and return it as Protobuf Text Format Language
96
                    // https://protobuf.dev/reference/protobuf/textformat-spec/
97
                    // It's better than bombarding the browser with a binary file.
98
                    var frame = eu.neverblink.jelly.core.proto.google.v1.RdfStreamFrame
×
99
                            .parseFrom(((Binary) npDoc.get("jelly")).getData());
×
100
                    println(frame.toString());
×
101
                } else {
×
102
                    // To return this correctly, we would need to prepend the delimiter byte before the Jelly frame
103
                    // (the DB stores is non-delimited and the HTTP response must be delimited).
104
                    BufferOutputStream outputStream = new BufferOutputStream();
×
105
                    IoUtils.writeFrameAsDelimited(
×
106
                            ((Binary) npDoc.get("jelly")).getData(),
×
107
                            outputStream
108
                    );
109
                    c.response().write(outputStream.getBuffer());
×
110
                }
×
111
            } else if (format != null && format.equals(TYPE_NQUADS)) {
18!
112
                outputNanopub(npDoc, RDFFormat.NQUADS);
×
113
            } else if (format != null && format.equals(TYPE_JSONLD)) {
18!
114
                outputNanopub(npDoc, RDFFormat.JSONLD);
×
115
            } else if (format != null && format.equals(TYPE_TRIX)) {
18!
116
                outputNanopub(npDoc, RDFFormat.TRIX);
×
117
            } else if (forwardHtml && isHtmlRequested(c)) {
18✔
118
                String fullId = npDoc.getString("fullId");
12✔
119
                c.response().setStatusCode(302);
15✔
120
                c.response().putHeader("Location", getNanodashBaseUrl() + "explore?id=" + Utils.urlEncode(fullId));
27✔
121
                return;
3✔
122
            } else if (forwardHtml) {
9✔
123
                // Non-HTML default for /get/ path (e.g. Accept: */*): serve as trig
124
                setRespContentType(TYPE_TRIG);
9✔
125
                println(npDoc.getString("content"));
18✔
126
            } else {
127
                printHtmlHeader("Nanopublication " + ac + " - Nanopub Registry");
12✔
128
                println("<h1>Nanopublication</h1>");
9✔
129
                println("<p><a href=\"/\">&lt; Home</a></p>");
9✔
130
                println("<h3>ID</h3>");
9✔
131
                String fullId = npDoc.getString("fullId");
12✔
132
                println("<p><a href=\"" + fullId + "\"><code>" + fullId + "</code></a></p>");
15✔
133
                println("<h3>Formats</h3>");
9✔
134
                println("<p>");
9✔
135
                println("<a href=\"/np/" + ac + ".trig\">.trig</a> |");
12✔
136
                println("<a href=\"/np/" + ac + ".trig.txt\">.trig.txt</a> |");
12✔
137
                println("<a href=\"/np/" + ac + ".jelly\">.jelly</a> |");
12✔
138
                println("<a href=\"/np/" + ac + ".jelly.txt\">.jelly.txt</a> |");
12✔
139
                println("<a href=\"/np/" + ac + ".jsonld\">.jsonld</a> |");
12✔
140
                println("<a href=\"/np/" + ac + ".jsonld.txt\">.jsonld.txt</a> |");
12✔
141
                println("<a href=\"/np/" + ac + ".nq\">.nq</a> |");
12✔
142
                println("<a href=\"/np/" + ac + ".nq.txt\">.nq.txt</a> |");
12✔
143
                println("<a href=\"/np/" + ac + ".xml\">.xml</a> |");
12✔
144
                println("<a href=\"/np/" + ac + ".xml.txt\">.xml.txt</a>");
12✔
145
                println("</p>");
9✔
146
                println("<h3>Content</h3>");
9✔
147
                println("<pre>");
9✔
148
                println(StringEscapeUtils.escapeHtml(npDoc.getString("content")));
18✔
149
                println("</pre>");
9✔
150
                printHtmlFooter();
6✔
151
            }
152
        } else {
3✔
153
            c.response().setStatusCode(400).setStatusMessage("Invalid request: " + getFullRequest());
×
154
        }
155
    }
3✔
156

157
    private static boolean isHtmlRequested(RoutingContext c) {
158
        String accept = c.request().getHeader("Accept");
15✔
159
        return accept != null && accept.contains("text/html");
30!
160
    }
161

162
    private void outputNanopub(Document npDoc, RDFFormat rdfFormat) {
163
        RoutingContext c = getContext();
×
164
        try {
165
            Nanopub np = new NanopubImpl(npDoc.getString("content"), RDFFormat.TRIG);
×
166
            c.response().write(NanopubUtils.writeToString(np, rdfFormat), Charsets.UTF_8.toString());
×
167
        } catch (RDF4JException | MalformedNanopubException | IOException ex) {
×
168
            c.response().setStatusCode(500).setStatusMessage("Failed transforming nanopub: " + getFullRequest());
×
169
            ex.printStackTrace();
×
170
        }
×
171
    }
×
172

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