+14
-3
constellation/src/server/mod.rs
+14
-3
constellation/src/server/mod.rs
···
251
251
///
252
252
/// deprecated: use `did`, which can be repeated multiple times
253
253
from_dids: Option<String>, // comma separated: gross
254
-
limit: Option<u64>,
254
+
#[serde(default = "get_default_limit")]
255
+
limit: u64,
255
256
// TODO: allow reverse (er, forward) order as well
257
+
}
258
+
fn get_default_limit() -> u64 {
259
+
DEFAULT_CURSOR_LIMIT
256
260
}
257
261
#[derive(Template, Serialize)]
258
262
#[template(path = "links.html.j2")]
···
278
282
.transpose()?
279
283
.map(|c| c.next);
280
284
281
-
let limit = query.limit.unwrap_or(DEFAULT_CURSOR_LIMIT);
285
+
let limit = query.limit;
282
286
if limit > DEFAULT_CURSOR_LIMIT_MAX {
283
287
return Err(http::StatusCode::BAD_REQUEST);
284
288
}
285
289
286
-
let mut filter_dids: HashSet<Did> = HashSet::from_iter(query.did.iter().map(|d| Did(d.to_string())));
290
+
let mut filter_dids: HashSet<Did> = HashSet::from_iter(
291
+
query
292
+
.did
293
+
.iter()
294
+
.map(|d| d.trim())
295
+
.filter(|d| !d.is_empty())
296
+
.map(|d| Did(d.to_string())),
297
+
);
287
298
288
299
if let Some(comma_joined) = &query.from_dids {
289
300
if !filter_dids.is_empty() {
+9
-6
constellation/templates/hello.html.j2
+9
-6
constellation/templates/hello.html.j2
···
20
20
21
21
<p>
22
22
This server has indexed <span class="stat">{{ stats.linking_records|human_number }}</span> links between <span class="stat">{{ stats.targetables|human_number }}</span> targets and sources from <span class="stat">{{ stats.dids|human_number }}</span> identities over <span class="stat">{{ days_indexed|human_number }}</span> days.<br/>
23
-
<small>(indexing new records in real time, backfill still TODO)</small>
23
+
<small>(indexing new records in real time, backfill coming soon!)</small>
24
24
</p>
25
25
26
-
<p>The API is currently <strong>unstable</strong>. But feel free to use it! If you want to be nice, put your project name and bsky username (or email) in your user-agent header for api requests.</p>
26
+
<p>But feel free to use it! If you want to be nice, put your project name and bsky username (or email) in your user-agent header for api requests.</p>
27
27
28
28
29
29
<h2>API Endpoints</h2>
···
35
35
<h4>Query parameters:</h4>
36
36
37
37
<ul>
38
-
<li><code>target</code>: required, must url-encode. Example: <code>at://did:plc:vc7f4oafdgxsihk4cry2xpze/app.bsky.feed.post/3lgwdn7vd722r</code></li>
39
-
<li><code>collection</code>: required. Example: <code>app.bsky.feed.like</code></li>
40
-
<li><code>path</code>: required, must url-encode. Example: <code>.subject.uri</code></li>
38
+
<li><p><code>target</code>: required, must url-encode. Example: <code>at://did:plc:vc7f4oafdgxsihk4cry2xpze/app.bsky.feed.post/3lgwdn7vd722r</code></p></li>
39
+
<li><p><code>collection</code>: required. Example: <code>app.bsky.feed.like</code></p></li>
40
+
<li><p><code>path</code>: required, must url-encode. Example: <code>.subject.uri</code></p></li>
41
+
<li><p><code>did</code>: optional, filter links to those from specific users. Include multiple times to filter by multiple users. Example: <code>did=did:plc:vc7f4oafdgxsihk4cry2xpze&did=did:plc:vc7f4oafdgxsihk4cry2xpze</code></p></li>
42
+
<li><p><code>from_dids</code> [deprecated]: optional. Use <code>did</code> instead. Example: <code>from_dids=did:plc:vc7f4oafdgxsihk4cry2xpze,did:plc:vc7f4oafdgxsihk4cry2xpze</code></p></li>
43
+
<li><p><code>limit</code>: optional. Default: <code>16</code>. Maximum: <code>100</code></p></li>
41
44
</ul>
42
45
43
46
<p style="margin-bottom: 0"><strong>Try it:</strong></p>
44
-
{% call try_it::links("at://did:plc:vc7f4oafdgxsihk4cry2xpze/app.bsky.feed.post/3lgwdn7vd722r", "app.bsky.feed.like", ".subject.uri") %}
47
+
{% call try_it::links("at://did:plc:a4pqq234yw7fqbddawjo7y35/app.bsky.feed.post/3m237ilwc372e", "app.bsky.feed.like", ".subject.uri", [""], 16) %}
45
48
46
49
47
50
<h3 class="route"><code>GET /links/distinct-dids</code></h3>
+1
-1
constellation/templates/links.html.j2
+1
-1
constellation/templates/links.html.j2
+20
-2
constellation/templates/try-it-macros.html.j2
+20
-2
constellation/templates/try-it-macros.html.j2
···
1
-
{% macro links(target, collection, path) %}
1
+
{% macro links(target, collection, path, dids, limit) %}
2
2
<form method="get" action="/links">
3
3
<pre class="code"><strong>GET</strong> /links
4
4
?target= <input type="text" name="target" value="{{ target }}" placeholder="target" />
5
5
&collection= <input type="text" name="collection" value="{{ collection }}" placeholder="collection" />
6
-
&path= <input type="text" name="path" value="{{ path }}" placeholder="path" /> <button type="submit">get links</button></pre>
6
+
&path= <input type="text" name="path" value="{{ path }}" placeholder="path" />
7
+
{%- for did in dids %}{% if !did.is_empty() %}
8
+
&did= <input type="text" name="did" value="{{ did }}" placeholder="did:plc:..." />{% endif %}{% endfor %}
9
+
<span id="did-placeholder"></span> <button id="add-did">+ did filter</button>
10
+
&limit= <input type="number" name="limit" value="{{ limit }}" max="100" placeholder="100" /> <button type="submit">get links</button></pre>
7
11
</form>
12
+
<script>
13
+
const addDidButton = document.getElementById('add-did');
14
+
const didPlaceholder = document.getElementById('did-placeholder');
15
+
addDidButton.addEventListener('click', e => {
16
+
e.preventDefault();
17
+
const i = document.createElement('input');
18
+
i.placeholder = 'did:plc:...';
19
+
i.name = "did"
20
+
const p = addDidButton.parentNode;
21
+
p.insertBefore(document.createTextNode('&did= '), didPlaceholder);
22
+
p.insertBefore(i, didPlaceholder);
23
+
p.insertBefore(document.createTextNode('\n '), didPlaceholder);
24
+
});
25
+
</script>
8
26
{% endmacro %}
9
27
10
28