diff --git a/package-lock.json b/package-lock.json index 5ead6dd..87c06d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "solid-logic", - "version": "4.0.3", + "version": "4.0.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "solid-logic", - "version": "4.0.3", + "version": "4.0.4", "license": "MIT", "dependencies": { "@inrupt/solid-client-authn-browser": "^3.1.0", diff --git a/package.json b/package.json index cca7391..4b73f4e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solid-logic", - "version": "4.0.3", + "version": "4.0.4", "description": "Core business logic of SolidOS", "type": "module", "main": "dist/solid-logic.js", diff --git a/src/typeIndex/typeIndexLogic.ts b/src/typeIndex/typeIndexLogic.ts index 0871438..9505f65 100644 --- a/src/typeIndex/typeIndexLogic.ts +++ b/src/typeIndex/typeIndexLogic.ts @@ -7,6 +7,10 @@ import { newThing } from '../util/utils' export function createTypeIndexLogic(store, authn, profileLogic, utilityLogic): TypeIndexLogic { const ns = namespace + function isAbsoluteHttpUri(uri: string | null | undefined): boolean { + return !!uri && (uri.startsWith('https://') || uri.startsWith('http://')) + } + function getRegistrations(instance, theClass) { return store .each(undefined, ns.solid('instance'), instance) @@ -82,7 +86,7 @@ export function createTypeIndexLogic(store, authn, profileLogic, utilityLogic): return scopes } - async function loadCommunityTypeIndexes(user: NamedNode): Promise { + async function loadCommunityTypeIndexes(user: NamedNode): Promise { let preferencesFile try { preferencesFile = await profileLogic.silencedLoadPreferences(user) @@ -96,7 +100,15 @@ export function createTypeIndexLogic(store, authn, profileLogic, utilityLogic): ) let result = [] for (const org of communities) { - result = result.concat(await loadTypeIndexesFor(org as NamedNode) as any) + if (org.termType !== 'NamedNode' || !isAbsoluteHttpUri((org as NamedNode).uri)) { + debug.warn(`Skipping malformed community node for ${user}: ${org}`) + continue + } + try { + result = result.concat(await loadTypeIndexesFor(org as NamedNode) as any) + } catch (err) { + debug.warn(`Skipping community type indexes for ${(org as NamedNode).uri}: ${err}`) + } } return result } @@ -104,7 +116,7 @@ export function createTypeIndexLogic(store, authn, profileLogic, utilityLogic): } async function loadAllTypeIndexes(user: NamedNode) { - return (await loadTypeIndexesFor(user)).concat((await loadCommunityTypeIndexes(user)).flat()) + return (await loadTypeIndexesFor(user)).concat(await loadCommunityTypeIndexes(user)) } async function getScopedAppInstances(klass: NamedNode, user: NamedNode): Promise { @@ -130,10 +142,10 @@ export function createTypeIndexLogic(store, authn, profileLogic, utilityLogic): function docDirUri(node: NamedNode): string | null { const doc = node.doc() const dir = doc.dir() - if (dir?.uri) return dir.uri + if (dir?.uri && isAbsoluteHttpUri(dir.uri)) return dir.uri const docUri = doc.uri - if (!docUri) { - debug.log(`docDirUri: missing doc uri for ${node?.uri}`) + if (!docUri || !isAbsoluteHttpUri(docUri)) { + debug.log(`docDirUri: missing or non-http(s) doc uri for ${node?.uri}`) return null } const withoutFragment = docUri.split('#')[0] diff --git a/src/types.ts b/src/types.ts index 0d145cd..62a6585 100644 --- a/src/types.ts +++ b/src/types.ts @@ -96,7 +96,7 @@ export interface InboxLogic { export interface TypeIndexLogic { getRegistrations: (instance, theClass) => Node[], loadTypeIndexesFor: (user: NamedNode) => Promise>, - loadCommunityTypeIndexes: (user: NamedNode) => Promise, + loadCommunityTypeIndexes: (user: NamedNode) => Promise>, loadAllTypeIndexes: (user: NamedNode) => Promise>, getScopedAppInstances: (klass: NamedNode, user: NamedNode) => Promise, getAppInstances: (klass: NamedNode) => Promise, diff --git a/test/typeIndexLogic.test.ts b/test/typeIndexLogic.test.ts index 18f4338..529deb2 100644 --- a/test/typeIndexLogic.test.ts +++ b/test/typeIndexLogic.test.ts @@ -170,6 +170,27 @@ describe('TypeIndex logic NEW', () => { const result = await typeIndexLogic.loadCommunityTypeIndexes(alice) expect(result).toEqual(ClubScopes) }) + it('skips malformed non-NamedNode community entries', async () => { + web[AlicePreferencesFile.uri] = ` + ${alice} solid:privateTypeIndex ${AlicePrivateTypeIndex}; + solid:community ${club}; + solid:community "not-a-webid" . + ` + + const result = await typeIndexLogic.loadCommunityTypeIndexes(alice) + expect(result).toEqual(ClubScopes) + }) + it('continues when one community fails to load', async () => { + const brokenCommunity = sym('https://broken.example.com/profile/card.ttl#it') + web[AlicePreferencesFile.uri] = ` + ${alice} solid:privateTypeIndex ${AlicePrivateTypeIndex}; + solid:community ${brokenCommunity}; + solid:community ${club} . + ` + + const result = await typeIndexLogic.loadCommunityTypeIndexes(alice) + expect(result).toEqual(ClubScopes) + }) }) const AliceAndClubScopes = [{'instance': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://alice.example.com/publicStuff/actionItems.ttl#this'}, 'scope': {'agent': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://alice.example.com/profile/card.ttl#me'}, 'index': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://alice.example.com/profile/public-type-index.ttl'}, 'label': 'public'}, 'type': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'http://www.w3.org/2005/01/wf/flow#Tracker'}}, {'instance': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://alice.example.com/project4/issues.ttl#this'}, 'scope': {'agent': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://alice.example.com/profile/card.ttl#me'}, 'index': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://alice.example.com/profile/public-type-index.ttl'}, 'label': 'public'}, 'type': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'http://www.w3.org/2005/01/wf/flow#Tracker'}}, {'instance': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://alice.example.com/privateStuff/ToDo.ttl#this'}, 'scope': {'agent': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://alice.example.com/profile/card.ttl#me'}, 'index': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://alice.example.com/settings/private-type-index.ttl'}, 'label': 'private'}, 'type': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'http://www.w3.org/2005/01/wf/flow#Tracker'}}, {'instance': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://alice.example.com/privateStuff/Goals.ttl#this'}, 'scope': {'agent': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://alice.example.com/profile/card.ttl#me'}, 'index': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://alice.example.com/settings/private-type-index.ttl'}, 'label': 'private'}, 'type': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'http://www.w3.org/2005/01/wf/flow#Tracker'}}, {'instance': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://alice.example.com/privateStuff/workingOn.ttl#this'}, 'scope': {'agent': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://alice.example.com/profile/card.ttl#me'}, 'index': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://alice.example.com/settings/private-type-index.ttl'}, 'label': 'private'}, 'type': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'http://www.w3.org/2005/01/wf/flow#Tracker'}}, {'instance': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://club.example.com/publicStuff/actionItems.ttl#this'}, 'scope': {'agent': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://club.example.com/profile/card.ttl#it'}, 'index': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://club.example.com/profile/public-type-index.ttl'}, 'label': 'public'}, 'type': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'http://www.w3.org/2005/01/wf/flow#Tracker'}}, {'instance': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://club.example.com/project4/clubIssues.ttl#this'}, 'scope': {'agent': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://club.example.com/profile/card.ttl#it'}, 'index': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://club.example.com/profile/public-type-index.ttl'}, 'label': 'public'}, 'type': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'http://www.w3.org/2005/01/wf/flow#Tracker'}}, {'instance': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://club.example.com/privateStuff/ToDo.ttl#this'}, 'scope': {'agent': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://club.example.com/profile/card.ttl#it'}, 'index': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://club.example.com/settings/private-type-index.ttl'}, 'label': 'private'}, 'type': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'http://www.w3.org/2005/01/wf/flow#Tracker'}}, {'instance': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://club.example.com/privateStuff/Goals.ttl#this'}, 'scope': {'agent': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://club.example.com/profile/card.ttl#it'}, 'index': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://club.example.com/settings/private-type-index.ttl'}, 'label': 'private'}, 'type': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'http://www.w3.org/2005/01/wf/flow#Tracker'}}, {'instance': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://club.example.com/privateStuff/tasks.ttl#this'}, 'scope': {'agent': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://club.example.com/profile/card.ttl#it'}, 'index': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'https://club.example.com/settings/private-type-index.ttl'}, 'label': 'private'}, 'type': {'classOrder': 5, 'termType': 'NamedNode', 'value': 'http://www.w3.org/2005/01/wf/flow#Tracker'}}]