diff --git a/src/storage/MonitoringStore.ts b/src/storage/MonitoringStore.ts index a4bd1237b..69189a205 100644 --- a/src/storage/MonitoringStore.ts +++ b/src/storage/MonitoringStore.ts @@ -3,6 +3,7 @@ import type { Patch } from '../ldp/http/Patch'; import type { Representation } from '../ldp/representation/Representation'; import type { RepresentationPreferences } from '../ldp/representation/RepresentationPreferences'; import type { ResourceIdentifier } from '../ldp/representation/ResourceIdentifier'; +import { getParentContainer } from '../util/PathUtil'; import type { Conditions } from './Conditions'; import type { ResourceStore } from './ResourceStore'; @@ -22,12 +23,24 @@ export class MonitoringStore public async addResource(container: ResourceIdentifier, representation: Representation, conditions?: Conditions): Promise { const identifier = await this.source.addResource(container, representation, conditions); + + // Both the container contents and the resource itself have changed + this.emit('changed', container); this.emit('changed', identifier); + return identifier; } public async deleteResource(identifier: ResourceIdentifier, conditions?: Conditions): Promise { await this.source.deleteResource(identifier, conditions); + + // Both the container contents and the resource itself have changed + try { + const container = getParentContainer(identifier); + this.emit('changed', container); + } catch { + // Parent container not found + } this.emit('changed', identifier); } diff --git a/test/unit/storage/MonitoringStore.test.ts b/test/unit/storage/MonitoringStore.test.ts index cfd61bcfc..81531f7dc 100644 --- a/test/unit/storage/MonitoringStore.test.ts +++ b/test/unit/storage/MonitoringStore.test.ts @@ -10,8 +10,8 @@ describe('A MonitoringStore', (): void => { beforeEach(async(): Promise => { source = { - getRepresentation: jest.fn(async(): Promise => 'get'), - addResource: jest.fn(async(): Promise => ({ path: 'newResource' })), + getRepresentation: jest.fn(async(): Promise => ({ success: true })), + addResource: jest.fn(async(): Promise => ({ path: 'http://example.org/foo/bar/new' })), setRepresentation: jest.fn(async(): Promise => undefined), deleteResource: jest.fn(async(): Promise => undefined), modifyResource: jest.fn(async(): Promise => undefined), @@ -26,69 +26,83 @@ describe('A MonitoringStore', (): void => { }); it('calls getRepresentation directly from the source.', async(): Promise => { - await expect(store.getRepresentation({ path: 'getPath' }, {})).resolves.toBe('get'); + await expect(store.getRepresentation({ path: 'getPath' }, {})).resolves.toEqual({ success: true }); expect(source.getRepresentation).toHaveBeenCalledTimes(1); expect(source.getRepresentation).toHaveBeenLastCalledWith({ path: 'getPath' }, {}, undefined); }); it('does not fire a change event after completing getRepresentation.', async(): Promise => { expect(changedCallback).toHaveBeenCalledTimes(0); - await store.getRepresentation({ path: 'getPath' }, {}); + await store.getRepresentation({ path: 'http://example.org/foo/bar' }, {}); expect(changedCallback).toHaveBeenCalledTimes(0); }); it('calls addResource directly from the source.', async(): Promise => { - await expect(store.addResource({ path: 'addPath' }, {} as Representation)).resolves - .toStrictEqual({ path: 'newResource' }); + await expect(store.addResource({ path: 'http://example.org/foo/bar' }, {} as Representation)).resolves + .toStrictEqual({ path: 'http://example.org/foo/bar/new' }); expect(source.addResource).toHaveBeenCalledTimes(1); - expect(source.addResource).toHaveBeenLastCalledWith({ path: 'addPath' }, {}, undefined); + expect(source.addResource).toHaveBeenLastCalledWith({ path: 'http://example.org/foo/bar' }, {}, undefined); }); - it('fires a change event after completing addResource.', async(): Promise => { - const result = store.addResource({ path: 'addPath' }, {} as Representation); + it('fires resource and container change events after completing addResource.', async(): Promise => { + const result = store.addResource({ path: 'http://example.org/foo/bar' }, {} as Representation); expect(changedCallback).toHaveBeenCalledTimes(0); await result; - expect(changedCallback).toHaveBeenCalledTimes(1); - expect(changedCallback).toHaveBeenCalledWith({ path: 'newResource' }); + expect(changedCallback).toHaveBeenCalledTimes(2); + expect(changedCallback).toHaveBeenCalledWith({ path: 'http://example.org/foo/bar' }); + expect(changedCallback).toHaveBeenCalledWith({ path: 'http://example.org/foo/bar/new' }); }); it('calls setRepresentation directly from the source.', async(): Promise => { - await expect(store.setRepresentation({ path: 'setPath' }, {} as Representation)).resolves.toBeUndefined(); + await expect(store.setRepresentation({ path: 'http://example.org/foo/bar' }, {} as Representation)) + .resolves.toBeUndefined(); expect(source.setRepresentation).toHaveBeenCalledTimes(1); - expect(source.setRepresentation).toHaveBeenLastCalledWith({ path: 'setPath' }, {}, undefined); + expect(source.setRepresentation).toHaveBeenLastCalledWith({ path: 'http://example.org/foo/bar' }, {}, undefined); }); - it('fires a change event after completing setRepresentation.', async(): Promise => { - const result = store.setRepresentation({ path: 'setPath' }, {} as Representation); + it('fires a resource change event after completing setRepresentation.', async(): Promise => { + const result = store.setRepresentation({ path: 'http://example.org/foo/bar' }, {} as Representation); expect(changedCallback).toHaveBeenCalledTimes(0); await result; expect(changedCallback).toHaveBeenCalledTimes(1); - expect(changedCallback).toHaveBeenCalledWith({ path: 'setPath' }); + expect(changedCallback).toHaveBeenCalledWith({ path: 'http://example.org/foo/bar' }); }); it('calls deleteResource directly from the source.', async(): Promise => { - await expect(store.deleteResource({ path: 'deletePath' })).resolves.toBeUndefined(); + await expect(store.deleteResource({ path: 'http://example.org/foo/bar' })).resolves.toBeUndefined(); expect(source.deleteResource).toHaveBeenCalledTimes(1); - expect(source.deleteResource).toHaveBeenLastCalledWith({ path: 'deletePath' }, undefined); + expect(source.deleteResource).toHaveBeenLastCalledWith({ path: 'http://example.org/foo/bar' }, undefined); }); - it('fires a change event after completing deleteResource.', async(): Promise => { - const result = store.deleteResource({ path: 'deletePath' }); + it('fires resource and container change events after completing deleteResource.', async(): Promise => { + const result = store.deleteResource({ path: 'http://example.org/foo/bar' }); + expect(changedCallback).toHaveBeenCalledTimes(0); + await result; + expect(changedCallback).toHaveBeenCalledTimes(2); + expect(changedCallback).toHaveBeenCalledWith({ path: 'http://example.org/foo/' }); + expect(changedCallback).toHaveBeenCalledWith({ path: 'http://example.org/foo/bar' }); + }); + + it('fires a resource change event after completing deleteResource on the root.', async(): Promise => { + const result = store.deleteResource({ path: 'http://example.org/' }); expect(changedCallback).toHaveBeenCalledTimes(0); await result; expect(changedCallback).toHaveBeenCalledTimes(1); + expect(changedCallback).toHaveBeenCalledWith({ path: 'http://example.org/' }); }); it('calls modifyResource directly from the source.', async(): Promise => { - await expect(store.modifyResource({ path: 'modifyPath' }, {} as Patch)).resolves.toBeUndefined(); + await expect(store.modifyResource({ path: 'http://example.org/foo/bar' }, {} as Patch)) + .resolves.toBeUndefined(); expect(source.modifyResource).toHaveBeenCalledTimes(1); - expect(source.modifyResource).toHaveBeenLastCalledWith({ path: 'modifyPath' }, {}, undefined); + expect(source.modifyResource).toHaveBeenLastCalledWith({ path: 'http://example.org/foo/bar' }, {}, undefined); }); - it('fires a change event after completing modifyResource.', async(): Promise => { - const result = store.modifyResource({ path: 'modifyPath' }, {} as Patch); + it('fires a resource change event after completing modifyResource.', async(): Promise => { + const result = store.modifyResource({ path: 'http://example.org/foo/bar' }, {} as Patch); expect(changedCallback).toHaveBeenCalledTimes(0); await result; expect(changedCallback).toHaveBeenCalledTimes(1); + expect(changedCallback).toHaveBeenCalledWith({ path: 'http://example.org/foo/bar' }); }); });