Skip to content

Commit b8aeaa4

Browse files
committed
Comprehensive URI Template syntax guide
Added a new documentation page explaining RFC 6570 URI Template syntax and its usage throughout Fedify's routing system. The guide covers: - Different expansion types ({var}, {+var}, {/var}, {?var}, {&var}) - When to use each expansion type - Common use cases for actors, collections, inbox listeners, and objects - Common pitfalls like double-encoding with URI identifiers - Decision guide and troubleshooting tips Also added cross-references from actor.md, collections.md, inbox.md, and object.md to help users choose the appropriate expansion syntax for their identifiers. Close #417
1 parent 4bb3969 commit b8aeaa4

6 files changed

Lines changed: 466 additions & 15 deletions

File tree

docs/.vitepress/config.mts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ const MANUAL = {
7070
{ text: "Access control", link: "/manual/access-control.md" },
7171
{ text: "WebFinger", link: "/manual/webfinger.md" },
7272
{ text: "NodeInfo", link: "/manual/nodeinfo.md" },
73+
{ text: "URI Template", link: "/manual/uri-template.md" },
7374
{ text: "Pragmatics", link: "/manual/pragmatics.md" },
7475
{ text: "Key–value store", link: "/manual/kv.md" },
7576
{ text: "Message queue", link: "/manual/mq.md" },

docs/manual/actor.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,13 @@ follows the [URI Template] specification.
6666
> See the [next section](#decoupling-actor-uris-from-webfinger-usernames)
6767
> for details.
6868
69+
> [!NOTE]
70+
> The URI Template syntax supports different expansion types like `{identifier}`
71+
> (simple expansion) and `{+identifier}` (reserved expansion). Choosing the
72+
> right expansion type is important to avoid encoding issues. See the
73+
> [*URI Template* guide](./uri-template.md) for details on when to use
74+
> each type.
75+
6976
[actors]: https://www.w3.org/TR/activitystreams-core/#actors
7077
[activities]: https://www.w3.org/TR/activitystreams-core/#activities
7178
[URI Template]: https://datatracker.ietf.org/doc/html/rfc6570

docs/manual/collections.md

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ Each actor has its own outbox collection, so the URI pattern of the outbox
4747
dispatcher should include the actor's `{identifier}`. The URI pattern syntax
4848
follows the [URI Template] specification.
4949

50+
> [!NOTE]
51+
> The URI Template syntax supports different expansion types like `{identifier}`
52+
> (simple expansion) and `{+identifier}` (reserved expansion). If your
53+
> identifiers contain URIs or special characters, you may need to use
54+
> `{+identifier}` to avoid double-encoding issues. See the
55+
> [*URI Template* guide](./uri-template.md) for details.
56+
5057
Since the outbox is a collection of activities, the outbox dispatcher should
5158
return an array of activities. The following example shows how to construct
5259
an outbox collection:
@@ -1438,14 +1445,14 @@ federation
14381445
async (ctx, values, cursor) => {
14391446
// If a whole collection is requested, return null to use pagination
14401447
if (cursor == null) return null;
1441-
1448+
14421449
// Work with the database to find bookmarked posts
14431450
const { posts, nextCursor } = await getBookmarkedPostsByUserId(
14441451
values.identifier,
14451452
cursor === "" ? null : cursor,
14461453
10
14471454
);
1448-
1455+
14491456
// Convert posts to Article objects
14501457
const items = posts.map(post =>
14511458
new Article({
@@ -1454,7 +1461,7 @@ federation
14541461
content: post.content,
14551462
})
14561463
);
1457-
1464+
14581465
return { items, nextCursor };
14591466
}
14601467
)
@@ -1513,21 +1520,21 @@ federation
15131520
async (ctx, values, cursor) => {
15141521
// Implementation is the same as regular collections
15151522
if (cursor == null) return null;
1516-
1523+
15171524
const { posts, nextCursor } = await getBookmarkedPostsByUserId(
15181525
values.identifier,
15191526
cursor === "" ? null : cursor,
15201527
10
15211528
);
1522-
1529+
15231530
const items = posts.map(post =>
15241531
new Article({
15251532
id: new URL(`/posts/${post.id}`, ctx.url),
15261533
summary: post.title,
15271534
content: post.content,
15281535
})
15291536
);
1530-
1537+
15311538
return { items, nextCursor };
15321539
}
15331540
)
@@ -1578,12 +1585,12 @@ federation
15781585
values.category,
15791586
cursor === "" ? null : cursor
15801587
);
1581-
1588+
15821589
const items = posts.map(post => new Note({
15831590
id: new URL(`/posts/${post.id}`, ctx.url),
15841591
content: post.content,
15851592
}));
1586-
1593+
15871594
return { items, nextCursor };
15881595
}
15891596
)
@@ -1603,9 +1610,9 @@ const ctx = null as unknown as Context<void>;
16031610
ctx.getCollectionUri("bookmarks", { identifier: "alice" })
16041611

16051612
// For a collection with multiple parameters:
1606-
ctx.getCollectionUri("category-posts", {
1607-
identifier: "alice",
1608-
category: "technology"
1613+
ctx.getCollectionUri("category-posts", {
1614+
identifier: "alice",
1615+
category: "technology"
16091616
})
16101617
~~~~
16111618

@@ -1654,28 +1661,28 @@ federation
16541661
"/users/{identifier}/private-bookmarks",
16551662
async (ctx, values, cursor) => {
16561663
if (cursor == null) return null;
1657-
1664+
16581665
const { posts, nextCursor } = await getBookmarkedPostsByUserId(
16591666
values.identifier,
16601667
cursor === "" ? null : cursor
16611668
);
1662-
1669+
16631670
const items = posts.map(post =>
16641671
new Article({
16651672
id: new URL(`/posts/${post.id}`, ctx.url),
16661673
summary: post.title,
16671674
content: post.content,
16681675
})
16691676
);
1670-
1677+
16711678
return { items, nextCursor };
16721679
}
16731680
)
16741681
.setFirstCursor(async (ctx, values) => "")
16751682
.authorize(async (ctx, values, signedKey, signedKeyOwner) => {
16761683
// Only allow access if the viewer is the owner of the bookmarks
16771684
if (signedKeyOwner == null) return false;
1678-
1685+
16791686
const viewerId = await getActorIdentifier(signedKeyOwner.id);
16801687
return await canAccessBookmarks(viewerId, values.identifier);
16811688
});

docs/manual/inbox.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ and a callback function that takes a `Context` object and the activity object.
8282
Note that the `~InboxListenerSetters.on()` method can be chained to register
8383
multiple inbox listeners for different activity types.
8484

85+
> [!NOTE]
86+
> The URI Template syntax supports different expansion types like `{identifier}`
87+
> (simple expansion) and `{+identifier}` (reserved expansion). If your
88+
> identifiers contain URIs or special characters, you may need to use
89+
> `{+identifier}` to avoid double-encoding issues. See the
90+
> [*URI Template* guide](./uri-template.md) for details.
91+
8592
> [!WARNING]
8693
> Activities of any type that are not registered with
8794
> the `~InboxListenerSetters.on()` method are silently ignored.

docs/manual/object.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ an object dispatcher for the `Note` class and
5353
the `/users/{userId}/notes/{noteId}` path. This pattern syntax follows
5454
the [URI Template] specification.
5555

56+
> [!NOTE]
57+
> The URI Template syntax supports different expansion types like `{userId}`
58+
> (simple expansion) and `{+userId}` (reserved expansion). If your
59+
> identifiers contain URIs or special characters, you may need to use
60+
> `{+userId}` to avoid double-encoding issues. See the
61+
> [*URI Template* guide](./uri-template.md) for details.
62+
5663
[objects]: https://www.w3.org/TR/activitystreams-core/#object
5764
[URI Template]: https://datatracker.ietf.org/doc/html/rfc6570
5865

0 commit comments

Comments
 (0)