mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Allow vocabularies to be extended
This commit is contained in:
parent
2e1bae90c7
commit
97f7ca027e
@ -47,8 +47,7 @@ export type VocabularyValue<T> = T extends Vocabulary<any, infer TKey> ? T[TKey]
|
||||
export type VocabularyTerm<T> = T extends Vocabulary<any, infer TKey> ? T['terms'][TKey] : never;
|
||||
|
||||
/**
|
||||
* Creates a function that expands local names from the given base URI,
|
||||
* and exports the given local names as properties on the returned object.
|
||||
* Creates a {@link ValueVocabulary} with the given `baseUri` as namespace and all `localNames` as entries.
|
||||
*/
|
||||
function createValueVocabulary<TBase extends string, TLocal extends string>(baseUri: TBase, localNames: TLocal[]):
|
||||
ValueVocabulary<TBase, TLocal> {
|
||||
@ -64,31 +63,44 @@ ValueVocabulary<TBase, TLocal> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a function that expands local names from the given base URI into named nodes,
|
||||
* and exports the given local names as properties on the returned object.
|
||||
* Creates a {@link TermVocabulary} based on the provided {@link ValueVocabulary}.
|
||||
*/
|
||||
function createTermVocabulary<TBase extends string, TLocal extends string>(namespace: ValueVocabulary<TBase, TLocal>):
|
||||
function createTermVocabulary<TBase extends string, TLocal extends string>(values: ValueVocabulary<TBase, TLocal>):
|
||||
TermVocabulary<ValueVocabulary<TBase, TLocal>> {
|
||||
// Need to cast since `fromEntries` typings aren't strict enough
|
||||
return Object.fromEntries(
|
||||
Object.entries(namespace).map(([ key, value ]): [string, NamedNode] => [ key, DataFactory.namedNode(value) ]),
|
||||
Object.entries(values).map(([ key, value ]): [string, NamedNode] => [ key, DataFactory.namedNode(value) ]),
|
||||
) as TermVocabulary<ValueVocabulary<TBase, TLocal>>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a function that expands local names from the given base URI into string,
|
||||
* and exports the given local names as properties on the returned object.
|
||||
* Under the `terms` property, it exposes the expanded local names as named nodes.
|
||||
* Creates a {@link Vocabulary} with the given `baseUri` as namespace and all `localNames` as entries.
|
||||
* The values are the local names expanded from the given base URI as strings.
|
||||
* The `terms` field contains all the same values but as {@link NamedNode} instead.
|
||||
*/
|
||||
export function createVocabulary<TBase extends string, TLocal extends string>(baseUri: TBase,
|
||||
...localNames: TLocal[]): string extends TLocal ? PartialVocabulary<TBase> : Vocabulary<TBase, TLocal> {
|
||||
const namespace = createValueVocabulary(baseUri, localNames);
|
||||
const values = createValueVocabulary(baseUri, localNames);
|
||||
return {
|
||||
...namespace,
|
||||
terms: createTermVocabulary(namespace),
|
||||
...values,
|
||||
terms: createTermVocabulary(values),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Vocabulary} that extends an existing one by adding new local names.
|
||||
* @param vocabulary - The {@link Vocabulary} to extend.
|
||||
* @param newNames - The new local names that need to be added.
|
||||
*/
|
||||
export function extendVocabulary<TBase extends string, TLocal extends string, TNew extends string>(
|
||||
vocabulary: Vocabulary<TBase, TLocal>, ...newNames: TNew[]):
|
||||
ReturnType<typeof createVocabulary<TBase, TLocal | TNew>> {
|
||||
const localNames = Object.keys(vocabulary)
|
||||
.filter((key): boolean => key !== 'terms' && key !== 'namespace') as TLocal[];
|
||||
const allNames = [ ...localNames, ...newNames ];
|
||||
return createVocabulary(vocabulary.namespace, ...allNames);
|
||||
}
|
||||
|
||||
export const ACL = createVocabulary('http://www.w3.org/ns/auth/acl#',
|
||||
'accessTo',
|
||||
'agent',
|
||||
|
@ -1,22 +1,50 @@
|
||||
import { DataFactory } from 'n3';
|
||||
import { LDP } from '../../../src/util/Vocabularies';
|
||||
import { createVocabulary, extendVocabulary } from '../../../src/util/Vocabularies';
|
||||
|
||||
describe('Vocabularies', (): void => {
|
||||
describe('LDP', (): void => {
|
||||
const vocabulary = createVocabulary('http://www.w3.org/ns/ldp#', 'contains', 'Container');
|
||||
|
||||
describe('createVocabulary', (): void => {
|
||||
it('contains its own URI.', (): void => {
|
||||
expect(LDP.namespace).toBe('http://www.w3.org/ns/ldp#');
|
||||
expect(vocabulary.namespace).toBe('http://www.w3.org/ns/ldp#');
|
||||
});
|
||||
|
||||
it('contains its own URI as a term.', (): void => {
|
||||
expect(LDP.terms.namespace).toEqual(DataFactory.namedNode('http://www.w3.org/ns/ldp#'));
|
||||
expect(vocabulary.terms.namespace).toEqual(DataFactory.namedNode('http://www.w3.org/ns/ldp#'));
|
||||
});
|
||||
|
||||
it('exposes ldp:contains.', (): void => {
|
||||
expect(LDP.contains).toBe('http://www.w3.org/ns/ldp#contains');
|
||||
it('exposes the defined URIs.', (): void => {
|
||||
expect(vocabulary.contains).toBe('http://www.w3.org/ns/ldp#contains');
|
||||
expect(vocabulary.Container).toBe('http://www.w3.org/ns/ldp#Container');
|
||||
});
|
||||
|
||||
it('exposes ldp:contains as a term.', (): void => {
|
||||
expect(LDP.terms.contains).toEqual(DataFactory.namedNode('http://www.w3.org/ns/ldp#contains'));
|
||||
it('exposes the defined URIs as terms.', (): void => {
|
||||
expect(vocabulary.terms.contains).toEqual(DataFactory.namedNode('http://www.w3.org/ns/ldp#contains'));
|
||||
expect(vocabulary.terms.Container).toEqual(DataFactory.namedNode('http://www.w3.org/ns/ldp#Container'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('extendVocabulary', (): void => {
|
||||
const extended = extendVocabulary(vocabulary, 'extended', 'extra');
|
||||
|
||||
it('still contains all the original values.', async(): Promise<void> => {
|
||||
expect(extended.namespace).toBe('http://www.w3.org/ns/ldp#');
|
||||
expect(extended.terms.namespace).toEqual(DataFactory.namedNode('http://www.w3.org/ns/ldp#'));
|
||||
expect(extended.contains).toBe('http://www.w3.org/ns/ldp#contains');
|
||||
expect(extended.Container).toBe('http://www.w3.org/ns/ldp#Container');
|
||||
expect(extended.terms.contains).toEqual(DataFactory.namedNode('http://www.w3.org/ns/ldp#contains'));
|
||||
expect(extended.terms.Container).toEqual(DataFactory.namedNode('http://www.w3.org/ns/ldp#Container'));
|
||||
});
|
||||
|
||||
it('contains the new values.', async(): Promise<void> => {
|
||||
expect(extended.extended).toBe('http://www.w3.org/ns/ldp#extended');
|
||||
expect(extended.extra).toBe('http://www.w3.org/ns/ldp#extra');
|
||||
expect(extended.terms.extended).toEqual(DataFactory.namedNode('http://www.w3.org/ns/ldp#extended'));
|
||||
expect(extended.terms.extra).toEqual(DataFactory.namedNode('http://www.w3.org/ns/ldp#extra'));
|
||||
});
|
||||
|
||||
it('does not modify the original vocabulary.', async(): Promise<void> => {
|
||||
expect((vocabulary as any).extended).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user