fix: add foaf:isPrimaryTopicOf to WebID profile - #300
Conversation
Per the Solid WebID Profile spec, profiles link the agent to the document via foaf:isPrimaryTopicOf. Empty string is a relative URI reference to the document itself. Keeps schema:mainEntityOfPage for schema.org compatibility. Closes #299
There was a problem hiding this comment.
Pull request overview
Updates the generated WebID JSON-LD profile to include a foaf:isPrimaryTopicOf link from the #me subject to the profile document, aligning output more closely with Solid WebID Profile expectations.
Changes:
- Add
foaf:isPrimaryTopicOf(asisPrimaryTopicOf) to generated profile JSON-LD and set it to""(document-relative IRI). - Change
schema:mainEntityOfPage(asmainEntityOfPage) to also use""instead of an absolute document URL. - Extend WebID profile tests to assert presence of the new field and adjust existing
mainEntityOfPageassertion to allow empty string values.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/webid/profile.js |
Adds isPrimaryTopicOf context + output and changes mainEntityOfPage to be document-relative. |
test/webid.test.js |
Updates assertions for mainEntityOfPage and adds a new test for isPrimaryTopicOf. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| 'foaf:name': name, | ||
| 'mainEntityOfPage': profileDoc, | ||
| 'isPrimaryTopicOf': '', | ||
| 'mainEntityOfPage': '', | ||
| 'inbox': `${pod}inbox/`, |
There was a problem hiding this comment.
mainEntityOfPage was changed from an absolute document IRI to an empty string. While "" can resolve to the document IRI in JSON-LD when a base IRI is provided, many schema.org-oriented consumers treat this value as a plain string and may not resolve relative IRIs, which would be a behavior change/regression versus the previous absolute URL. Consider restoring the absolute document URL for mainEntityOfPage (or explicitly setting @base in the JSON-LD context so relative IRIs are unambiguous) while keeping foaf:isPrimaryTopicOf as needed.
| '@id': webId, | ||
| '@type': ['foaf:Person', 'schema:Person'], | ||
| 'foaf:name': name, | ||
| 'mainEntityOfPage': profileDoc, | ||
| 'isPrimaryTopicOf': '', | ||
| 'mainEntityOfPage': '', |
There was a problem hiding this comment.
This change adds foaf:isPrimaryTopicOf on the #me node, but it still does not describe the profile document itself (e.g., <> a foaf:PersonalProfileDocument; foaf:primaryTopic <#me>; foaf:maker <#me>). If this PR is intended to close #299 and support clients that discover a user by dereferencing the document URL and following foaf:primaryTopic, the profile generator likely needs to emit a document node (typically via @graph) in addition to foaf:isPrimaryTopicOf.
| @@ -87,7 +87,16 @@ describe('WebID Profile', () => { | |||
| const res = await request(profilePath); | |||
| const jsonLd = await res.json(); | |||
|
|
|||
| assert.ok(jsonLd['mainEntityOfPage'], 'Should have mainEntityOfPage'); | |||
| // Empty string is a relative URI reference to the document itself | |||
| assert.ok('mainEntityOfPage' in jsonLd, 'Should have mainEntityOfPage'); | |||
| }); | |||
|
|
|||
| it('should have isPrimaryTopicOf pointing to the document', async () => { | |||
| const res = await request(profilePath); | |||
| const jsonLd = await res.json(); | |||
|
|
|||
| // Empty string is a relative URI reference to the document itself | |||
| assert.ok('isPrimaryTopicOf' in jsonLd, 'Should have foaf:isPrimaryTopicOf'); | |||
There was a problem hiding this comment.
The new assertions only check that mainEntityOfPage / isPrimaryTopicOf keys exist, but not that they actually point to the profile document. Since the implementation relies on "" being interpreted as a relative IRI to the document, add a stronger assertion (e.g., that the value is "", or that it expands/resolves to the request URL) to prevent silent regressions where these fields are present but incorrect.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| it('should have mainEntityOfPage pointing to the document', async () => { | ||
| const res = await request(profilePath); | ||
| const jsonLd = await res.json(); | ||
|
|
||
| assert.ok(jsonLd['mainEntityOfPage'], 'Should have mainEntityOfPage'); | ||
| // Empty string is a relative URI reference to the document itself (JSON-LD) | ||
| assert.strictEqual(jsonLd['mainEntityOfPage'], '', 'mainEntityOfPage should be "" (self)'); | ||
| }); | ||
|
|
||
| it('should have isPrimaryTopicOf pointing to the document', async () => { | ||
| const res = await request(profilePath); | ||
| const jsonLd = await res.json(); | ||
|
|
||
| // Empty string is a relative URI reference to the document itself (JSON-LD) | ||
| assert.strictEqual(jsonLd['isPrimaryTopicOf'], '', 'isPrimaryTopicOf should be "" (self)'); | ||
| }); |
There was a problem hiding this comment.
These tests assert an exact empty-string value for mainEntityOfPage/isPrimaryTopicOf, which tightly couples the tests to a specific JSON-LD serialization choice. If the implementation is adjusted to emit an explicit document IRI (or a different relative form) for compatibility, these tests will fail even though the RDF meaning is unchanged; consider asserting that the fields are present and point to the profile document (e.g., accept "" or the expected absolute URL).
Summary
foaf:isPrimaryTopicOfpointing from#meto the document, per the Solid WebID Profile spec""as a relative URI reference to the document itself (standard JSON-LD pattern)schema:mainEntityOfPagefor schema.org-aware consumersWhy
Apps dereferencing the document URL need a way to find the subject WebID inside it.
foaf:isPrimaryTopicOf(forward direction from agent to doc) is what the Solid WebID Profile spec recommends.Closes #299
Test plan