mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Rework ResourceStore to return extra info
* feat: change return types in ResourceStore.ts
* feat: change return types in BaseResourceStore.ts
* feat: change return types in LockingResourceStore.ts
* feat: change return types in RoutingResourceStore.ts
* feat: change return types in MonitoringStore.ts
* feat: change return types in PassthroughStore.ts
* feat: change return types in ReadOnlyStore.ts
* feat: change return types in PatchHandler.ts
* feat: change return types in PatchingStore.ts
* feat: change return types in RepresentationPatchHandler.ts
* feat: create createResourceIdentifier() function for convenience
* feat: adapt PostOperationHandler.ts to new typing
* feat: change return types in RepresentationConvertingStore.ts
* feat: adapt DataAccessorBasedStore.ts implementation to new typings
* feat: adapt UnsecureWebSocketsProtocol.ts to new typing
* chore: add temporary comments
* fix: return correct Location header on POST request with slug
* fix: npm run lint command needs more packages
* fix: linting errors
* chore: revert ed9952b
* test: adapt PostOperationHandler tests
* test: adapt UnsecureWebSocketsProtocol tests
* test: adapt DataAccessorBasedStore tests
* fix: linting errors
* feat: emit specific created, deleted, updated events in MonitoringStore
* test: adapt RepresentationPatchHandler tests
* fix: revert UnsecureWebSocketsProtocol changes
* feat: emit extra parameter on changed
* test: adapt MonitoringStore tests
* fix: linting errors
* test: add test to MonitorStore.test for coverage
* fix: linting error
* chore: update doc in ResourceStore.ts
* test: improve MonitoringStore tests
* chore: update RELEASE_NOTES.md
* chore: add extra info about the MonitoringStore to documentation/resource-store.md
* chore: Update RELEASE_NOTES.md
Co-authored-by: Anton Wiklund <ixuz07@gmail.com>
* chore: Update documentation/resource-store.md
Co-authored-by: Anton Wiklund <ixuz07@gmail.com>
* chore: very small changes
* chore: simplify metadata creation
* fix: DataAccessorBasedStore improvement and bugfix
* chore: improve resource-store.md
* chore: adapt MonitoringStore event names, update docs and apply code suggestion
* chore: use ResourceStoreResponse type
* fix: typo
* chore: rename ResourceStoreResponse type to ChangeMap
* chore: adapt .gitignore to name change
Co-authored-by: Anton Wiklund <ixuz07@gmail.com>
This commit is contained in:
@@ -23,7 +23,7 @@ import type { Guarded } from '../../../src/util/GuardedStream';
|
||||
import { SingleRootIdentifierStrategy } from '../../../src/util/identifiers/SingleRootIdentifierStrategy';
|
||||
import { trimTrailingSlashes } from '../../../src/util/PathUtil';
|
||||
import { guardedStreamFrom } from '../../../src/util/StreamUtil';
|
||||
import { CONTENT_TYPE, SOLID_HTTP, LDP, PIM, RDF, SOLID_META, DC } from '../../../src/util/Vocabularies';
|
||||
import { CONTENT_TYPE, SOLID_HTTP, LDP, PIM, RDF, SOLID_META, DC, SOLID_AS, AS } from '../../../src/util/Vocabularies';
|
||||
const { namedNode, quad } = DataFactory;
|
||||
|
||||
const GENERATED_PREDICATE = namedNode('generated');
|
||||
@@ -265,11 +265,22 @@ describe('A DataAccessorBasedStore', (): void => {
|
||||
const resourceID = { path: root };
|
||||
representation.metadata.removeAll(RDF.terms.type);
|
||||
const result = await store.addResource(resourceID, representation);
|
||||
expect(result).toEqual({
|
||||
path: expect.stringMatching(new RegExp(`^${root}[^/]+$`, 'u')),
|
||||
});
|
||||
await expect(arrayifyStream(accessor.data[result.path].data)).resolves.toEqual([ resourceData ]);
|
||||
expect(accessor.data[result.path].metadata.get(DC.terms.modified)?.value).toBe(now.toISOString());
|
||||
|
||||
expect(Object.keys(result)).toEqual([
|
||||
root,
|
||||
expect.stringMatching(new RegExp(`^${root}[^/]+$`, 'u')),
|
||||
]);
|
||||
|
||||
expect(result[root].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Update);
|
||||
|
||||
const generatedID = Object.keys(result).find((key): boolean => key !== root)!;
|
||||
expect(generatedID).toBeDefined();
|
||||
expect(generatedID).toMatch(new RegExp(`^${root}[^/]+$`, 'u'));
|
||||
|
||||
await expect(arrayifyStream(accessor.data[generatedID].data)).resolves.toEqual([ resourceData ]);
|
||||
expect(accessor.data[generatedID]).toBeTruthy();
|
||||
expect(accessor.data[generatedID].metadata.get(DC.terms.modified)?.value).toBe(now.toISOString());
|
||||
expect(result[generatedID].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Create);
|
||||
});
|
||||
|
||||
it('can write containers.', async(): Promise<void> => {
|
||||
@@ -278,16 +289,25 @@ describe('A DataAccessorBasedStore', (): void => {
|
||||
representation.metadata.contentType = 'text/turtle';
|
||||
representation.data = guardedStreamFrom([ '<> a <http://test.com/coolContainer>.' ]);
|
||||
const result = await store.addResource(resourceID, representation);
|
||||
expect(result).toEqual({
|
||||
path: expect.stringMatching(new RegExp(`^${root}[^/]+/$`, 'u')),
|
||||
});
|
||||
expect(accessor.data[result.path]).toBeTruthy();
|
||||
expect(accessor.data[result.path].metadata.contentType).toBeUndefined();
|
||||
|
||||
const { data, metadata } = await store.getRepresentation(result);
|
||||
expect(Object.keys(result)).toEqual([
|
||||
root,
|
||||
expect.stringMatching(new RegExp(`^${root}[^/]+?/$`, 'u')),
|
||||
]);
|
||||
|
||||
expect(result[root].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Update);
|
||||
|
||||
const generatedID = Object.keys(result).find((key): boolean => key !== root)!;
|
||||
expect(generatedID).toBeDefined();
|
||||
expect(generatedID).toMatch(new RegExp(`^${root}[^/]+?/$`, 'u'));
|
||||
expect(accessor.data[generatedID]).toBeTruthy();
|
||||
expect(accessor.data[generatedID].metadata.contentType).toBeUndefined();
|
||||
expect(result[generatedID].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Create);
|
||||
|
||||
const { data, metadata } = await store.getRepresentation({ path: generatedID });
|
||||
const quads = await arrayifyStream<Quad>(data);
|
||||
expect(metadata.get(DC.terms.modified)?.value).toBe(now.toISOString());
|
||||
expect(quads.some((entry): boolean => entry.subject.value === result.path &&
|
||||
expect(quads.some((entry): boolean => entry.subject.value === generatedID &&
|
||||
entry.object.value === 'http://test.com/coolContainer')).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -295,10 +315,14 @@ describe('A DataAccessorBasedStore', (): void => {
|
||||
const resourceID = { path: root };
|
||||
representation.metadata.removeAll(RDF.terms.type);
|
||||
representation.metadata.add(SOLID_HTTP.terms.slug, 'newName');
|
||||
const result = await store.addResource(resourceID, representation);
|
||||
expect(result).toEqual({
|
||||
path: `${root}newName`,
|
||||
|
||||
const result = store.addResource(resourceID, representation);
|
||||
await expect(result).resolves.toEqual({
|
||||
[root]: expect.any(RepresentationMetadata),
|
||||
[`${root}newName`]: expect.any(RepresentationMetadata),
|
||||
});
|
||||
expect((await result)[root].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Update);
|
||||
expect((await result)[`${root}newName`].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Create);
|
||||
});
|
||||
|
||||
it('errors on a slug ending on / without Link rel:type Container header.', async(): Promise<void> => {
|
||||
@@ -320,10 +344,14 @@ describe('A DataAccessorBasedStore', (): void => {
|
||||
representation.metadata.add(RDF.terms.type, LDP.terms.Container);
|
||||
representation.metadata.add(SOLID_HTTP.terms.slug, 'newContainer');
|
||||
representation.data = guardedStreamFrom([ `` ]);
|
||||
|
||||
const result = await store.addResource(resourceID, representation);
|
||||
expect(result).toEqual({
|
||||
path: `${root}newContainer/`,
|
||||
[root]: expect.any(RepresentationMetadata),
|
||||
[`${root}newContainer/`]: expect.any(RepresentationMetadata),
|
||||
});
|
||||
expect(result[root].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Update);
|
||||
expect(result[`${root}newContainer/`].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Create);
|
||||
});
|
||||
|
||||
it('generates a new URI if adding the slug would create an existing URI.', async(): Promise<void> => {
|
||||
@@ -331,20 +359,26 @@ describe('A DataAccessorBasedStore', (): void => {
|
||||
representation.metadata.add(SOLID_HTTP.terms.slug, 'newName');
|
||||
accessor.data[`${root}newName`] = representation;
|
||||
const result = await store.addResource(resourceID, representation);
|
||||
expect(result).not.toEqual({
|
||||
path: `${root}newName`,
|
||||
});
|
||||
expect(result).not.toEqual({
|
||||
path: expect.stringMatching(new RegExp(`^${root}[^/]+/$`, 'u')),
|
||||
});
|
||||
expect(result).not.toEqual(expect.objectContaining({
|
||||
[`${root}newName`]: expect.any(RepresentationMetadata),
|
||||
}));
|
||||
expect(result).not.toEqual(expect.objectContaining({
|
||||
[expect.any(String)]: expect.stringMatching(new RegExp(`^${root}[^/]+/$`, 'u')),
|
||||
}));
|
||||
});
|
||||
|
||||
it('generates http://test.com/%26%26 when slug is &%26.', async(): Promise<void> => {
|
||||
const resourceID = { path: root };
|
||||
representation.metadata.removeAll(RDF.terms.type);
|
||||
representation.metadata.add(SOLID_HTTP.terms.slug, '&%26');
|
||||
const result = await store.addResource(resourceID, representation);
|
||||
expect(result).toEqual({ path: `${root}%26%26` });
|
||||
|
||||
const result = store.addResource(resourceID, representation);
|
||||
await expect(result).resolves.toEqual({
|
||||
[root]: expect.any(RepresentationMetadata),
|
||||
[`${root}%26%26`]: expect.any(RepresentationMetadata),
|
||||
});
|
||||
expect((await result)[root].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Update);
|
||||
expect((await result)[`${root}%26%26`].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Create);
|
||||
});
|
||||
|
||||
it('errors if the slug contains a slash.', async(): Promise<void> => {
|
||||
@@ -408,8 +442,11 @@ describe('A DataAccessorBasedStore', (): void => {
|
||||
representation.metadata.contentType = 'text/turtle';
|
||||
representation.data = guardedStreamFrom([ `<${root}> a <coolContainer>.` ]);
|
||||
|
||||
await expect(store.setRepresentation(resourceID, representation)).resolves
|
||||
.toEqual([{ path: `${root}` }]);
|
||||
const result = store.setRepresentation(resourceID, representation);
|
||||
await expect(result).resolves.toEqual({
|
||||
[root]: expect.any(RepresentationMetadata),
|
||||
});
|
||||
expect((await result)[root].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Create);
|
||||
expect(mock).toHaveBeenCalledTimes(1);
|
||||
expect(mock).toHaveBeenLastCalledWith(resourceID);
|
||||
|
||||
@@ -438,10 +475,13 @@ describe('A DataAccessorBasedStore', (): void => {
|
||||
|
||||
it('can write resources.', async(): Promise<void> => {
|
||||
const resourceID = { path: `${root}resource` };
|
||||
await expect(store.setRepresentation(resourceID, representation)).resolves.toEqual([
|
||||
{ path: root },
|
||||
{ path: `${root}resource` },
|
||||
]);
|
||||
const result = store.setRepresentation(resourceID, representation);
|
||||
await expect(result).resolves.toEqual({
|
||||
[root]: expect.any(RepresentationMetadata),
|
||||
[resourceID.path]: expect.any(RepresentationMetadata),
|
||||
});
|
||||
expect((await result)[root].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Update);
|
||||
expect((await result)[resourceID.path].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Create);
|
||||
await expect(arrayifyStream(accessor.data[resourceID.path].data)).resolves.toEqual([ resourceData ]);
|
||||
expect(accessor.data[resourceID.path].metadata.get(DC.terms.modified)?.value).toBe(now.toISOString());
|
||||
expect(accessor.data[root].metadata.get(DC.terms.modified)?.value).toBe(now.toISOString());
|
||||
@@ -455,10 +495,13 @@ describe('A DataAccessorBasedStore', (): void => {
|
||||
representation.metadata.removeAll(RDF.terms.type);
|
||||
representation.metadata.contentType = 'text/turtle';
|
||||
representation.data = guardedStreamFrom([ `<${root}resource/> a <coolContainer>.` ]);
|
||||
await expect(store.setRepresentation(resourceID, representation)).resolves.toEqual([
|
||||
{ path: root },
|
||||
{ path: `${root}container/` },
|
||||
]);
|
||||
const result = store.setRepresentation(resourceID, representation);
|
||||
await expect(result).resolves.toEqual({
|
||||
[root]: expect.any(RepresentationMetadata),
|
||||
[resourceID.path]: expect.any(RepresentationMetadata),
|
||||
});
|
||||
expect((await result)[root].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Update);
|
||||
expect((await result)[resourceID.path].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Create);
|
||||
expect(accessor.data[resourceID.path]).toBeTruthy();
|
||||
expect(accessor.data[resourceID.path].metadata.contentType).toBeUndefined();
|
||||
expect(accessor.data[resourceID.path].metadata.get(DC.terms.modified)?.value).toBe(now.toISOString());
|
||||
@@ -468,10 +511,13 @@ describe('A DataAccessorBasedStore', (): void => {
|
||||
|
||||
it('can overwrite resources which does not update parent metadata.', async(): Promise<void> => {
|
||||
const resourceID = { path: `${root}resource` };
|
||||
await expect(store.setRepresentation(resourceID, representation)).resolves.toEqual([
|
||||
{ path: root },
|
||||
{ path: `${root}resource` },
|
||||
]);
|
||||
const result = store.setRepresentation(resourceID, representation);
|
||||
await expect(result).resolves.toEqual({
|
||||
[root]: expect.any(RepresentationMetadata),
|
||||
[resourceID.path]: expect.any(RepresentationMetadata),
|
||||
});
|
||||
expect((await result)[root].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Update);
|
||||
expect((await result)[resourceID.path].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Create);
|
||||
await expect(arrayifyStream(accessor.data[resourceID.path].data)).resolves.toEqual([ resourceData ]);
|
||||
expect(accessor.data[resourceID.path].metadata.get(DC.terms.modified)?.value).toBe(now.toISOString());
|
||||
expect(accessor.data[root].metadata.get(DC.terms.modified)?.value).toBe(now.toISOString());
|
||||
@@ -479,9 +525,11 @@ describe('A DataAccessorBasedStore', (): void => {
|
||||
// Parent metadata does not get updated if the resource already exists
|
||||
representation = new BasicRepresentation('updatedText', 'text/plain');
|
||||
mockDate.mockReturnValue(later);
|
||||
await expect(store.setRepresentation(resourceID, representation)).resolves.toEqual([
|
||||
{ path: `${root}resource` },
|
||||
]);
|
||||
const result2 = store.setRepresentation(resourceID, representation);
|
||||
await expect(result2).resolves.toEqual({
|
||||
[resourceID.path]: expect.any(RepresentationMetadata),
|
||||
});
|
||||
expect((await result2)[resourceID.path].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Update);
|
||||
await expect(arrayifyStream(accessor.data[resourceID.path].data)).resolves.toEqual([ 'updatedText' ]);
|
||||
expect(accessor.data[resourceID.path].metadata.get(DC.terms.modified)?.value).toBe(later.toISOString());
|
||||
expect(accessor.data[root].metadata.get(DC.terms.modified)?.value).toBe(now.toISOString());
|
||||
@@ -492,10 +540,13 @@ describe('A DataAccessorBasedStore', (): void => {
|
||||
const resourceID = { path: `${root}resource` };
|
||||
representation.metadata.add(namedNode('notGen'), 'value');
|
||||
representation.metadata.add(namedNode('gen'), 'value', SOLID_META.terms.ResponseMetadata);
|
||||
await expect(store.setRepresentation(resourceID, representation)).resolves.toEqual([
|
||||
{ path: root },
|
||||
{ path: `${root}resource` },
|
||||
]);
|
||||
const result = store.setRepresentation(resourceID, representation);
|
||||
await expect(result).resolves.toEqual({
|
||||
[root]: expect.any(RepresentationMetadata),
|
||||
[resourceID.path]: expect.any(RepresentationMetadata),
|
||||
});
|
||||
expect((await result)[root].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Update);
|
||||
expect((await result)[resourceID.path].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Create);
|
||||
await expect(arrayifyStream(accessor.data[resourceID.path].data)).resolves.toEqual([ resourceData ]);
|
||||
expect(accessor.data[resourceID.path].metadata.get(namedNode('notGen'))?.value).toBe('value');
|
||||
expect(accessor.data[resourceID.path].metadata.get(namedNode('gen'))).toBeUndefined();
|
||||
@@ -505,10 +556,13 @@ describe('A DataAccessorBasedStore', (): void => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete accessor.data[root];
|
||||
const resourceID = { path: `${root}resource` };
|
||||
await expect(store.setRepresentation(resourceID, representation)).resolves.toEqual([
|
||||
{ path: `${root}` },
|
||||
{ path: `${root}resource` },
|
||||
]);
|
||||
const result = store.setRepresentation(resourceID, representation);
|
||||
await expect(result).resolves.toEqual({
|
||||
[root]: expect.any(RepresentationMetadata),
|
||||
[resourceID.path]: expect.any(RepresentationMetadata),
|
||||
});
|
||||
expect((await result)[root].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Create);
|
||||
expect((await result)[resourceID.path].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Create);
|
||||
await expect(arrayifyStream(accessor.data[resourceID.path].data)).resolves.toEqual([ resourceData ]);
|
||||
});
|
||||
|
||||
@@ -521,10 +575,13 @@ describe('A DataAccessorBasedStore', (): void => {
|
||||
representation.data = guardedStreamFrom(
|
||||
[ quad(namedNode(`${root}resource/`), namedNode('a'), namedNode('coolContainer')) ],
|
||||
);
|
||||
await expect(store.setRepresentation(resourceID, representation)).resolves.toEqual([
|
||||
{ path: `${root}` },
|
||||
{ path: `${root}container/` },
|
||||
]);
|
||||
const result = store.setRepresentation(resourceID, representation);
|
||||
await expect(result).resolves.toEqual({
|
||||
[root]: expect.any(RepresentationMetadata),
|
||||
[resourceID.path]: expect.any(RepresentationMetadata),
|
||||
});
|
||||
expect((await result)[root].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Update);
|
||||
expect((await result)[resourceID.path].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Create);
|
||||
expect(accessor.data[resourceID.path]).toBeTruthy();
|
||||
expect(accessor.data[resourceID.path].metadata.contentType).toBeUndefined();
|
||||
});
|
||||
@@ -544,11 +601,17 @@ describe('A DataAccessorBasedStore', (): void => {
|
||||
|
||||
it('creates recursive containers when needed.', async(): Promise<void> => {
|
||||
const resourceID = { path: `${root}a/b/resource` };
|
||||
await expect(store.setRepresentation(resourceID, representation)).resolves.toEqual([
|
||||
{ path: `${root}a/` },
|
||||
{ path: `${root}a/b/` },
|
||||
{ path: `${root}a/b/resource` },
|
||||
]);
|
||||
const result = store.setRepresentation(resourceID, representation);
|
||||
await expect(result).resolves.toEqual({
|
||||
[`${root}`]: expect.any(RepresentationMetadata),
|
||||
[`${root}a/`]: expect.any(RepresentationMetadata),
|
||||
[`${root}a/b/`]: expect.any(RepresentationMetadata),
|
||||
[`${root}a/b/resource`]: expect.any(RepresentationMetadata),
|
||||
});
|
||||
expect((await result)[`${root}`].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Update);
|
||||
expect((await result)[`${root}a/`].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Create);
|
||||
expect((await result)[`${root}a/b/`].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Create);
|
||||
expect((await result)[`${root}a/b/resource`].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Create);
|
||||
await expect(arrayifyStream(accessor.data[resourceID.path].data)).resolves.toEqual([ resourceData ]);
|
||||
expect(accessor.data[`${root}a/`].metadata.getAll(RDF.terms.type).map((type): string => type.value))
|
||||
.toContain(LDP.Container);
|
||||
@@ -573,9 +636,11 @@ describe('A DataAccessorBasedStore', (): void => {
|
||||
representation.metadata.removeAll(RDF.terms.type);
|
||||
representation.metadata.contentType = 'text/turtle';
|
||||
representation.data = guardedStreamFrom([]);
|
||||
await expect(store.setRepresentation(resourceID, representation)).resolves.toEqual([
|
||||
{ path: `${root}` },
|
||||
]);
|
||||
const result = store.setRepresentation(resourceID, representation);
|
||||
await expect(result).resolves.toEqual({
|
||||
[root]: expect.any(RepresentationMetadata),
|
||||
});
|
||||
expect((await result)[root].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Create);
|
||||
expect(accessor.data[resourceID.path]).toBeTruthy();
|
||||
expect(Object.keys(accessor.data)).toHaveLength(1);
|
||||
expect(accessor.data[resourceID.path].metadata.contentType).toBeUndefined();
|
||||
@@ -658,51 +723,70 @@ describe('A DataAccessorBasedStore', (): void => {
|
||||
});
|
||||
|
||||
it('will delete resources.', async(): Promise<void> => {
|
||||
accessor.data[`${root}resource`] = representation;
|
||||
await expect(store.deleteResource({ path: `${root}resource` })).resolves.toEqual([
|
||||
{ path: `${root}resource` },
|
||||
{ path: root },
|
||||
]);
|
||||
expect(accessor.data[`${root}resource`]).toBeUndefined();
|
||||
const resourceID = { path: `${root}resource` };
|
||||
accessor.data[resourceID.path] = representation;
|
||||
await expect(store.deleteResource(resourceID)).resolves.toEqual({
|
||||
[root]: expect.any(RepresentationMetadata),
|
||||
[resourceID.path]: expect.any(RepresentationMetadata),
|
||||
});
|
||||
expect(accessor.data[resourceID.path]).toBeUndefined();
|
||||
expect(accessor.data[root].metadata.get(DC.terms.modified)?.value).toBe(now.toISOString());
|
||||
expect(accessor.data[root].metadata.get(GENERATED_PREDICATE)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('will delete root non-storage containers.', async(): Promise<void> => {
|
||||
accessor.data[root] = new BasicRepresentation(representation.data, containerMetadata);
|
||||
await expect(store.deleteResource({ path: root })).resolves.toEqual([
|
||||
{ path: root },
|
||||
]);
|
||||
await expect(store.deleteResource({ path: root })).resolves.toEqual(
|
||||
{ [root]: expect.any(RepresentationMetadata) },
|
||||
);
|
||||
expect(accessor.data[root]).toBeUndefined();
|
||||
});
|
||||
|
||||
it('will delete a root storage auxiliary resource of a non-root container.', async(): Promise<void> => {
|
||||
const resourceID = { path: `${root}container/` };
|
||||
const auxResourceID = { path: `${root}container/.dummy` };
|
||||
const storageMetadata = new RepresentationMetadata(representation.metadata);
|
||||
accessor.data[`${root}container/`] = new BasicRepresentation(representation.data, storageMetadata);
|
||||
accessor.data[`${root}container/.dummy`] = representation;
|
||||
accessor.data[resourceID.path] = new BasicRepresentation(representation.data, storageMetadata);
|
||||
accessor.data[auxResourceID.path] = representation;
|
||||
auxiliaryStrategy.isRequiredInRoot = jest.fn().mockReturnValue(true);
|
||||
await expect(store.deleteResource({ path: `${root}container/.dummy` })).resolves.toEqual([
|
||||
{ path: `${root}container/.dummy` },
|
||||
{ path: `${root}container/` },
|
||||
]);
|
||||
expect(accessor.data[`${root}container/.dummy`]).toBeUndefined();
|
||||
const result = store.deleteResource(auxResourceID);
|
||||
await expect(result).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
[resourceID.path]: expect.any(RepresentationMetadata),
|
||||
[auxResourceID.path]: expect.any(RepresentationMetadata),
|
||||
}),
|
||||
);
|
||||
expect((await result)[resourceID.path].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Update);
|
||||
expect((await result)[auxResourceID.path].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Delete);
|
||||
expect(accessor.data[auxResourceID.path]).toBeUndefined();
|
||||
});
|
||||
|
||||
it('will delete related auxiliary resources.', async(): Promise<void> => {
|
||||
accessor.data[`${root}container/`] = representation;
|
||||
accessor.data[`${root}container/.dummy`] = representation;
|
||||
await expect(store.deleteResource({ path: `${root}container/` })).resolves.toEqual([
|
||||
{ path: `${root}container/` },
|
||||
{ path: `${root}container/.dummy` },
|
||||
{ path: root },
|
||||
]);
|
||||
expect(accessor.data[`${root}container/`]).toBeUndefined();
|
||||
expect(accessor.data[`${root}container/.dummy`]).toBeUndefined();
|
||||
const resourceID = { path: `${root}container/` };
|
||||
const auxResourceID = { path: `${root}container/.dummy` };
|
||||
accessor.data[resourceID.path] = representation;
|
||||
accessor.data[auxResourceID.path] = representation;
|
||||
|
||||
const result = store.deleteResource(resourceID);
|
||||
await expect(result).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
[root]: expect.any(RepresentationMetadata),
|
||||
[resourceID.path]: expect.any(RepresentationMetadata),
|
||||
[auxResourceID.path]: expect.any(RepresentationMetadata),
|
||||
}),
|
||||
);
|
||||
expect((await result)[root].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Update);
|
||||
expect((await result)[resourceID.path].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Delete);
|
||||
expect((await result)[auxResourceID.path].get(SOLID_AS.terms.Activity)?.value).toBe(AS.Delete);
|
||||
expect(accessor.data[resourceID.path]).toBeUndefined();
|
||||
expect(accessor.data[auxResourceID.path]).toBeUndefined();
|
||||
});
|
||||
|
||||
it('will still delete a resource if deleting auxiliary resources causes errors.', async(): Promise<void> => {
|
||||
accessor.data[`${root}resource`] = representation;
|
||||
accessor.data[`${root}resource.dummy`] = representation;
|
||||
const resourceID = { path: `${root}resource` };
|
||||
const auxResourceID = { path: `${root}resource.dummy` };
|
||||
accessor.data[resourceID.path] = representation;
|
||||
accessor.data[auxResourceID.path] = representation;
|
||||
const deleteFn = accessor.deleteResource;
|
||||
accessor.deleteResource = jest.fn(async(identifier: ResourceIdentifier): Promise<void> => {
|
||||
if (auxiliaryStrategy.isAuxiliaryIdentifier(identifier)) {
|
||||
@@ -712,12 +796,14 @@ describe('A DataAccessorBasedStore', (): void => {
|
||||
});
|
||||
const { logger } = store as any;
|
||||
logger.error = jest.fn();
|
||||
await expect(store.deleteResource({ path: `${root}resource` })).resolves.toEqual([
|
||||
{ path: `${root}resource` },
|
||||
{ path: root },
|
||||
]);
|
||||
expect(accessor.data[`${root}resource`]).toBeUndefined();
|
||||
expect(accessor.data[`${root}resource.dummy`]).toBeDefined();
|
||||
const result = store.deleteResource(resourceID);
|
||||
expect(Object.keys(await result)).toHaveLength(2);
|
||||
await expect(result).resolves.toEqual({
|
||||
[root]: expect.any(RepresentationMetadata),
|
||||
[resourceID.path]: expect.any(RepresentationMetadata),
|
||||
});
|
||||
expect(accessor.data[resourceID.path]).toBeUndefined();
|
||||
expect(accessor.data[auxResourceID.path]).toBeDefined();
|
||||
expect(logger.error).toHaveBeenCalledTimes(1);
|
||||
expect(logger.error).toHaveBeenLastCalledWith(
|
||||
'Error deleting auxiliary resource http://test.com/resource.dummy: auxiliary error!',
|
||||
|
||||
Reference in New Issue
Block a user