diff --git a/src/server/ServerStateManager.js b/src/server/ServerStateManager.js index 87b257b4..677d99e4 100644 --- a/src/server/ServerStateManager.js +++ b/src/server/ServerStateManager.js @@ -542,7 +542,7 @@ class ServerStateManager extends BaseStateManager { this.deleteClass(className); } - /** + /** * Register a function for a given class of shared state class to be executed * when a state is created. * @@ -564,7 +564,7 @@ class ServerStateManager extends BaseStateManager { * name: { type: 'string', required: true }, * hookTriggered: { type: 'boolean', default: false }, * }); - * server.stateManager.registerCreateHook('hooked', initValues => { + * server.stateManager.setCreateHook('hooked', initValues => { * return { * ...initValues * hookTriggered: true, @@ -578,13 +578,13 @@ class ServerStateManager extends BaseStateManager { * const values = state.getValues(); * assert.deepEqual(result, { value: 'coucou', hookTriggered: true }); */ - registerCreateHook(className, createHook) { + setCreateHook(className, createHook) { if (!this.#classes.has(className)) { - throw new TypeError(`Cannot execute 'registerCreateHook' on 'BaseStateManager': SharedState class '${className}' does not exists`); + throw new TypeError(`Cannot execute 'setCreateHook' on 'BaseStateManager': SharedState class '${className}' does not exists`); } if (!isFunction(createHook)) { - throw new TypeError(`Cannot execute 'registerCreateHook' on 'BaseStateManager': argument 2 must be a function`); + throw new TypeError(`Cannot execute 'setCreateHook' on 'BaseStateManager': argument 2 must be a function`); } const hooks = this.#createHooksByClassName.get(className); @@ -593,13 +593,43 @@ class ServerStateManager extends BaseStateManager { return () => hooks.delete(createHook); } - registerDeleteHook(className, deleteHook) { + /** + * Register a function for a given class of shared state class to be executed + * when a state is deleted. + * + * For example, this can be usefull to store the values of a given shared state + * in the filesystem. + * + * The hook is associated to each states created from the given class name. + * Note that the hooks are executed server-side regarless the node on which + * `delete` has been called. + * + * @param {string} className - Kind of states on which applying the hook. + * @param {serverStateManagerUpdateHook} createHook - Function called on when + * a state of `className` is created on the network. + * + * @returns {function} deleteHook - Handler that deletes the hook when executed. + * + * @example + * server.stateManager.defineClass('hooked', { + * name: { type: 'string', required: true }, + * hookTriggered: { type: 'boolean', default: false }, + * }); + * server.stateManager.setDeleteHook('hooked', async currentValues => { + * await doSomethingWithValues(currentValues) + * }); + * + * const state = await server.stateManager.create('hooked'); + * // later + * await state.delete(); + */ + setDeleteHook(className, deleteHook) { if (!this.#classes.has(className)) { - throw new TypeError(`Cannot execute 'registerDeleteHook' on 'BaseStateManager': SharedState class '${className}' does not exists`); + throw new TypeError(`Cannot execute 'setDeleteHook' on 'BaseStateManager': SharedState class '${className}' does not exists`); } if (!isFunction(deleteHook)) { - throw new TypeError(`Cannot execute 'registerDeleteHook' on 'BaseStateManager': argument 2 must be a function`); + throw new TypeError(`Cannot execute 'setDeleteHook' on 'BaseStateManager': argument 2 must be a function`); } const hooks = this.#deleteHooksByClassName.get(className); @@ -632,7 +662,7 @@ class ServerStateManager extends BaseStateManager { * value: { type: 'string', default: null, nullable: true }, * numUpdates: { type: 'integer', default: 0 }, * }); - * server.stateManager.registerUpdateHook('hooked', updates => { + * server.stateManager.setUpdateHook('hooked', updates => { * return { * ...updates * numUpdates: currentValues.numUpdates + 1, @@ -645,13 +675,13 @@ class ServerStateManager extends BaseStateManager { * const values = state.getValues(); * assert.deepEqual(result, { value: 'test', numUpdates: 1 }); */ - registerUpdateHook(className, updateHook) { + setUpdateHook(className, updateHook) { if (!this.#classes.has(className)) { - throw new TypeError(`Cannot execute 'registerUpdateHook' on 'BaseStateManager': SharedState class '${className}' does not exists`); + throw new TypeError(`Cannot execute 'setUpdateHook' on 'BaseStateManager': SharedState class '${className}' does not exists`); } if (!isFunction(updateHook)) { - throw new TypeError(`Cannot execute 'registerUpdateHook' on 'BaseStateManager': argument 2 must be a function`); + throw new TypeError(`Cannot execute 'setUpdateHook' on 'BaseStateManager': argument 2 must be a function`); } const hooks = this.#updateHooksByClassName.get(className); @@ -659,6 +689,14 @@ class ServerStateManager extends BaseStateManager { return () => hooks.delete(updateHook); } + + /** + * @deprecated Use {@link ServerStateManager#setUpdateHook} instead. + */ + registerUpdateHook(className, updateHook) { + logger.deprecated('ServerStateManager#registerUpdateHook', 'ServerStateManager#setUpdateHook', '4.0.0-alpha.29'); + return this.setUpdateHook(className, updateHook); + } } export default ServerStateManager; diff --git a/tests/misc/deprecated.spec.js b/tests/misc/deprecated.spec.js index 27e0bebf..31ef2398 100644 --- a/tests/misc/deprecated.spec.js +++ b/tests/misc/deprecated.spec.js @@ -56,7 +56,7 @@ describe('from v4.0.0-alpha.29', () => { await server.stop(); }); - it('SharedStateManager#getSchema()', async () => { + it('StateManager#getSchema()', async () => { const server = new Server(config); server.stateManager.defineClass('a', a); await server.start(); @@ -70,6 +70,18 @@ describe('from v4.0.0-alpha.29', () => { server.stateManager.deleteClass('a'); await server.stop(); }); + + it('ServerStateManager#registerUpdateHook()', async () => { + const server = new Server(config); + server.stateManager.defineClass('a', a); + await server.start(); + + await server.stateManager.setUpdateHook('a', () => {}); // actual + await server.stateManager.registerUpdateHook('a', () => {}); // deprecated + + server.stateManager.deleteClass('a'); + await server.stop(); + }); }); describe('# removed API', () => { diff --git a/tests/states/schema-options.spec.js b/tests/states/SharedStateParameterDescription.spec.js similarity index 99% rename from tests/states/schema-options.spec.js rename to tests/states/SharedStateParameterDescription.spec.js index 533751f1..888d66f3 100644 --- a/tests/states/schema-options.spec.js +++ b/tests/states/SharedStateParameterDescription.spec.js @@ -12,7 +12,7 @@ import { import config from '../utils/config.js'; import { a } from '../utils/class-description.js'; -describe('# SharedState - schema options', () => { +describe('# SharedStateParameterDescription', () => { let server; let client1; let client2; @@ -23,8 +23,6 @@ describe('# SharedState - schema options', () => { // --------------------------------------------------- server = new Server(config); server.stateManager.defineClass('a', a); - // server.stateManager.defineClass('b', b); - await server.start(); // --------------------------------------------------- @@ -172,7 +170,7 @@ describe('# SharedState - schema options', () => { let subscribeCalledBeforeHook = false; - server.stateManager.registerUpdateHook('immediate-test', (updates, currentValues) => { + server.stateManager.setUpdateHook('immediate-test', (updates, currentValues) => { try { // assert.fail(); assert.isTrue(subscribeCalledBeforeHook, 'subscribe should be called before hook'); diff --git a/tests/states/StateManager.spec.js b/tests/states/StateManager.spec.js index 2f5a4a4f..fcbf276e 100644 --- a/tests/states/StateManager.spec.js +++ b/tests/states/StateManager.spec.js @@ -711,7 +711,7 @@ describe(`# StateManager`, () => { }); }); - describe(`## [server] registerCreateHook(className, createHook)`, () => { + describe(`## [server] setCreateHook(className, createHook)`, () => { const hookSchema = { name: { type: 'string', @@ -730,7 +730,7 @@ describe(`# StateManager`, () => { let errored = false; try { - server.stateManager.registerCreateHook('hooked', () => {}); + server.stateManager.setCreateHook('hooked', () => {}); } catch (err) { console.log(err.message); errored = true; @@ -743,10 +743,10 @@ describe(`# StateManager`, () => { server.stateManager.defineClass('hooked', hookSchema); let errored = false; - assert.throws(() => server.stateManager.registerCreateHook('hooked', null)); - assert.throws(() => server.stateManager.registerCreateHook('hooked', {})); - assert.doesNotThrow(() => server.stateManager.registerCreateHook('hooked', () => {})); - assert.doesNotThrow(() => server.stateManager.registerCreateHook('hooked', async () => {})); + assert.throws(() => server.stateManager.setCreateHook('hooked', null)); + assert.throws(() => server.stateManager.setCreateHook('hooked', {})); + assert.doesNotThrow(() => server.stateManager.setCreateHook('hooked', () => {})); + assert.doesNotThrow(() => server.stateManager.setCreateHook('hooked', async () => {})); }); it(`should execute hook on creation of state of given class`, async () => { @@ -756,8 +756,8 @@ describe(`# StateManager`, () => { let hookedCalled = false; let otherCalled = false; - server.stateManager.registerCreateHook('hooked', () => hookedCalled = true);; - server.stateManager.registerCreateHook('other', () => otherCalled = true);; + server.stateManager.setCreateHook('hooked', () => hookedCalled = true);; + server.stateManager.setCreateHook('other', () => otherCalled = true);; const _ = await server.stateManager.create('hooked'); @@ -767,7 +767,7 @@ describe(`# StateManager`, () => { it(`should allow to modify init values`, async () => { server.stateManager.defineClass('hooked', hookSchema); - server.stateManager.registerCreateHook('hooked', (initValues) => { + server.stateManager.setCreateHook('hooked', (initValues) => { if (initValues.name === 'test') { return { value: 'ok', @@ -783,7 +783,7 @@ describe(`# StateManager`, () => { it(`should throw if create hook explicitly return null`, async () => { server.stateManager.defineClass('hooked', hookSchema); - server.stateManager.registerCreateHook('hooked', (initValues) => { + server.stateManager.setCreateHook('hooked', (initValues) => { return null; }); @@ -801,7 +801,7 @@ describe(`# StateManager`, () => { it(`should implicitly continue if hook returns undefined`, async () => { server.stateManager.defineClass('hooked', hookSchema); - server.stateManager.registerCreateHook('hooked', initValues => { + server.stateManager.setCreateHook('hooked', initValues => { if (initValues.name === 'test') { return undefined; } @@ -814,7 +814,7 @@ describe(`# StateManager`, () => { it(`should support async hooks`, async () => { server.stateManager.defineClass('hooked', hookSchema); - server.stateManager.registerCreateHook('hooked', async (initValues) => { + server.stateManager.setCreateHook('hooked', async (initValues) => { await delay(20); if (initValues.name === 'test') { return { @@ -831,7 +831,7 @@ describe(`# StateManager`, () => { it(`should allow to chain hooks`, async () => { server.stateManager.defineClass('hooked', hookSchema); - server.stateManager.registerCreateHook('hooked', async (initValues) => { + server.stateManager.setCreateHook('hooked', async (initValues) => { await delay(20); if (initValues.name === 'test') { return { @@ -841,7 +841,7 @@ describe(`# StateManager`, () => { } }); - server.stateManager.registerCreateHook('hooked', (initValues) => { + server.stateManager.setCreateHook('hooked', (initValues) => { // first callback has been properly executed const expected = { name: 'test', value: 'ok1' }; assert.deepEqual(initValues, expected) @@ -859,7 +859,7 @@ describe(`# StateManager`, () => { }); }); - describe.only(`## [server] registerDeleteHook(className, createHook)`, () => { + describe(`## [server] setDeleteHook(className, createHook)`, () => { const hookSchema = { value: { type: 'integer', @@ -871,7 +871,7 @@ describe(`# StateManager`, () => { let errored = false; try { - server.stateManager.registerDeleteHook('hooked', () => {}); + server.stateManager.setDeleteHook('hooked', () => {}); } catch (err) { console.log(err.message); errored = true; @@ -884,10 +884,10 @@ describe(`# StateManager`, () => { server.stateManager.defineClass('hooked', hookSchema); let errored = false; - assert.throws(() => server.stateManager.registerDeleteHook('hooked', null)); - assert.throws(() => server.stateManager.registerDeleteHook('hooked', {})); - assert.doesNotThrow(() => server.stateManager.registerDeleteHook('hooked', () => {})); - assert.doesNotThrow(() => server.stateManager.registerDeleteHook('hooked', async () => {})); + assert.throws(() => server.stateManager.setDeleteHook('hooked', null)); + assert.throws(() => server.stateManager.setDeleteHook('hooked', {})); + assert.doesNotThrow(() => server.stateManager.setDeleteHook('hooked', () => {})); + assert.doesNotThrow(() => server.stateManager.setDeleteHook('hooked', async () => {})); }); @@ -895,7 +895,7 @@ describe(`# StateManager`, () => { server.stateManager.defineClass('hooked', hookSchema); let called = false; - server.stateManager.registerDeleteHook('hooked', finalValues => called = true); + server.stateManager.setDeleteHook('hooked', finalValues => called = true); const state = await server.stateManager.create('hooked'); assert.equal(called, false); @@ -908,7 +908,7 @@ describe(`# StateManager`, () => { server.stateManager.defineClass('hooked', hookSchema); let called = false; - server.stateManager.registerDeleteHook('hooked', async finalValues => { + server.stateManager.setDeleteHook('hooked', async finalValues => { await delay(20); called = true; }); @@ -923,7 +923,7 @@ describe(`# StateManager`, () => { server.stateManager.defineClass('hooked', hookSchema); let called = false; - server.stateManager.registerDeleteHook('hooked', async finalValues => called = true); + server.stateManager.setDeleteHook('hooked', async finalValues => called = true); const _ = await server.stateManager.create('hooked'); assert.equal(called, false); @@ -935,7 +935,7 @@ describe(`# StateManager`, () => { server.stateManager.defineClass('hooked', hookSchema); let called = false; - server.stateManager.registerDeleteHook('hooked', async finalValues => called = true); + server.stateManager.setDeleteHook('hooked', async finalValues => called = true); const _ = await client.stateManager.create('hooked'); assert.equal(called, false); @@ -950,14 +950,14 @@ describe(`# StateManager`, () => { let called1 = false; let called2 = false; - server.stateManager.registerDeleteHook('hooked', async finalValues => { + server.stateManager.setDeleteHook('hooked', async finalValues => { await delay(20); called1 = true; assert.equal(finalValues.value, 0); return { value: 1 }; }); - server.stateManager.registerDeleteHook('hooked', finalValues => { + server.stateManager.setDeleteHook('hooked', finalValues => { assert.equal(finalValues.value, 1); called2 = true; }); @@ -974,12 +974,12 @@ describe(`# StateManager`, () => { let called1 = false; let called2 = false; - server.stateManager.registerDeleteHook('hooked', async finalValues => { + server.stateManager.setDeleteHook('hooked', async finalValues => { called1 = true; return null; }); - server.stateManager.registerDeleteHook('hooked', finalValues => called2 = true); + server.stateManager.setDeleteHook('hooked', finalValues => called2 = true); const state = await server.stateManager.create('hooked'); await state.delete(); // order is guaranteed @@ -993,11 +993,11 @@ describe(`# StateManager`, () => { let called1 = false; let called2 = false; - server.stateManager.registerDeleteHook('hooked', async finalValues => { + server.stateManager.setDeleteHook('hooked', async finalValues => { called1 = true; }); - server.stateManager.registerDeleteHook('hooked', finalValues => { + server.stateManager.setDeleteHook('hooked', finalValues => { assert.equal(finalValues.value, 42); called2 = true }); @@ -1009,7 +1009,7 @@ describe(`# StateManager`, () => { }); }); - describe('## [server] registerUpdateHook(className, updateHook)', () => { + describe('## [server] setUpdateHook(className, updateHook)', () => { const hookSchema = { name: { type: 'string', @@ -1028,7 +1028,7 @@ describe(`# StateManager`, () => { let errored = false; try { - server.stateManager.registerUpdateHook('hooked', () => {}); + server.stateManager.setUpdateHook('hooked', () => {}); } catch (err) { console.log(err.message); errored = true; @@ -1041,15 +1041,15 @@ describe(`# StateManager`, () => { server.stateManager.defineClass('hooked', hookSchema); let errored = false; - assert.throws(() => server.stateManager.registerUpdateHook('hooked', null)); - assert.throws(() => server.stateManager.registerUpdateHook('hooked', {})); - assert.doesNotThrow(() => server.stateManager.registerUpdateHook('hooked', () => {})); - assert.doesNotThrow(() => server.stateManager.registerUpdateHook('hooked', async () => {})); + assert.throws(() => server.stateManager.setUpdateHook('hooked', null)); + assert.throws(() => server.stateManager.setUpdateHook('hooked', {})); + assert.doesNotThrow(() => server.stateManager.setUpdateHook('hooked', () => {})); + assert.doesNotThrow(() => server.stateManager.setUpdateHook('hooked', async () => {})); }); it('should properly execute hook', async () => { server.stateManager.defineClass('hooked', hookSchema); - server.stateManager.registerUpdateHook('hooked', (updates, currentValues) => { + server.stateManager.setUpdateHook('hooked', (updates, currentValues) => { updates.value = `${updates.name}-value`; return updates; }); @@ -1097,7 +1097,7 @@ describe(`# StateManager`, () => { it('hook API should be `hook(updates, currentValues)`', async () => { return new Promise(async (resolve, reject) => { server.stateManager.defineClass('hooked', hookSchema); - server.stateManager.registerUpdateHook('hooked', (updates, currentValues) => { + server.stateManager.setUpdateHook('hooked', (updates, currentValues) => { try { assert.deepEqual(updates, { name: 'test' }); assert.deepEqual(currentValues, { name: null, value: null }); @@ -1119,7 +1119,7 @@ describe(`# StateManager`, () => { it('should not mess around when several states of same kind are created', async () => { server.stateManager.defineClass('hooked', hookSchema); - server.stateManager.registerUpdateHook('hooked', (updates, currentValues) => { + server.stateManager.setUpdateHook('hooked', (updates, currentValues) => { // just apply name to value updates.value = `${updates.name}-value`; return updates; @@ -1144,7 +1144,7 @@ describe(`# StateManager`, () => { it('should support asynchronous operation', async () => { server.stateManager.defineClass('hooked', hookSchema); - server.stateManager.registerUpdateHook('hooked', async (updates, currentValues) => { + server.stateManager.setUpdateHook('hooked', async (updates, currentValues) => { return new Promise(resolve => { setTimeout(() => { resolve({ @@ -1171,11 +1171,11 @@ describe(`# StateManager`, () => { it('should apply several hooks in registation order', async () => { server.stateManager.defineClass('hooked', hookSchema); - server.stateManager.registerUpdateHook('hooked', (updates, currentValues) => { + server.stateManager.setUpdateHook('hooked', (updates, currentValues) => { return { ...updates, value: 'ok-1' }; }); - server.stateManager.registerUpdateHook('hooked', (updates, currentValues) => { + server.stateManager.setUpdateHook('hooked', (updates, currentValues) => { return { ...updates, value: 'ok-2' }; }); @@ -1195,11 +1195,11 @@ describe(`# StateManager`, () => { it('should unregister hooks properly', async () => { server.stateManager.defineClass('hooked', hookSchema); - server.stateManager.registerUpdateHook('hooked', (updates, currentValues) => { + server.stateManager.setUpdateHook('hooked', (updates, currentValues) => { return { ...updates, value: 'ok-1' }; }); - const unregister = server.stateManager.registerUpdateHook('hooked', (updates, currentValues) => { + const unregister = server.stateManager.setUpdateHook('hooked', (updates, currentValues) => { return { ...updates, value: 'ok-2' }; }); @@ -1223,12 +1223,12 @@ describe(`# StateManager`, () => { it('should abort when explicitly returning `null`', async () => { server.stateManager.defineClass('hooked', hookSchema); - server.stateManager.registerUpdateHook('hooked', (updates, currentValues) => { + server.stateManager.setUpdateHook('hooked', (updates, currentValues) => { assert.ok('hook called'); return null; }); - server.stateManager.registerUpdateHook('hooked', (updates, currentValues) => { + server.stateManager.setUpdateHook('hooked', (updates, currentValues) => { assert.fail('should not be called', updates); return { ...updates, value: 'ok-2' }; }); @@ -1257,7 +1257,7 @@ describe(`# StateManager`, () => { }, }); - server.stateManager.registerUpdateHook('hooked', (updates, currentValues) => { + server.stateManager.setUpdateHook('hooked', (updates, currentValues) => { assert.ok('hook called'); return null; }); @@ -1279,7 +1279,7 @@ describe(`# StateManager`, () => { it('should continue when implicitly returning `undefined`', async () => { server.stateManager.defineClass('hooked', hookSchema); - server.stateManager.registerUpdateHook('hooked', (updates, currentValues) => { + server.stateManager.setUpdateHook('hooked', (updates, currentValues) => { if (updates.name === 'test-1') { return { ...updates,