mirror of
https://github.com/strapi/strapi.git
synced 2025-11-02 02:44:55 +00:00
Implementing the hooks API
This commit is contained in:
parent
8295a086e6
commit
c6f188d5ae
@ -4,6 +4,7 @@ import { BrowserRouter } from 'react-router-dom';
|
||||
import { QueryClientProvider, QueryClient } from 'react-query';
|
||||
import { ThemeProvider } from 'styled-components';
|
||||
import { LibraryProvider, StrapiAppProvider } from '@strapi/helper-plugin';
|
||||
import createHook from '@strapi/hooks';
|
||||
import configureStore from './core/store/configureStore';
|
||||
import { Plugin } from './core/apis';
|
||||
import basename from './utils/basename';
|
||||
@ -12,7 +13,6 @@ import LanguageProvider from './components/LanguageProvider';
|
||||
import AutoReloadOverlayBlockerProvider from './components/AutoReloadOverlayBlockerProvider';
|
||||
import OverlayBlocker from './components/OverlayBlocker';
|
||||
import Fonts from './components/Fonts';
|
||||
|
||||
import GlobalStyle from './components/GlobalStyle';
|
||||
import Notifications from './components/Notifications';
|
||||
import themes from './themes';
|
||||
@ -42,6 +42,7 @@ class StrapiApp {
|
||||
this.plugins = {};
|
||||
this.reducers = reducers;
|
||||
this.translations = translations;
|
||||
this.hooksDict = {};
|
||||
}
|
||||
|
||||
addComponents = components => {
|
||||
@ -143,6 +144,24 @@ class StrapiApp {
|
||||
this.plugins[plugin.pluginId] = plugin;
|
||||
};
|
||||
|
||||
createHook = name => {
|
||||
this.hooksDict[name] = createHook();
|
||||
};
|
||||
|
||||
registerHook = (name, fn) => {
|
||||
this.hooksDict[name].register(fn);
|
||||
};
|
||||
|
||||
runHookSeries = (name, asynchronous = false) =>
|
||||
asynchronous ? this.hooksDict[name].runSeriesAsync() : this.hooksDict[name].runSeries();
|
||||
|
||||
runHookWaterfall = (name, initialValue, asynchronous = false) =>
|
||||
asynchronous
|
||||
? this.hooksDict[name].runWaterfallAsync(initialValue)
|
||||
: this.hooksDict[name].runWaterfall(initialValue);
|
||||
|
||||
runHookParallel = name => this.hooksDict[name].runParallel();
|
||||
|
||||
render() {
|
||||
const store = this.createStore();
|
||||
|
||||
|
||||
@ -49,4 +49,92 @@ describe('ADMIN | StrapiApp', () => {
|
||||
|
||||
expect(store.getState()).toEqual(fixtures.store.state);
|
||||
});
|
||||
|
||||
describe('Hook api', () => {
|
||||
it('runs the "moto" hooks in series', () => {
|
||||
const app = StrapiApp({ middlewares, reducers, library });
|
||||
|
||||
app.createHook('hello');
|
||||
app.createHook('moto');
|
||||
|
||||
app.registerHook('hello', () => 5);
|
||||
app.registerHook('moto', () => 1);
|
||||
app.registerHook('moto', () => 2);
|
||||
app.registerHook('moto', () => 3);
|
||||
|
||||
const [a, b, c] = app.runHookSeries('moto');
|
||||
|
||||
expect(a).toBe(1);
|
||||
expect(b).toBe(2);
|
||||
expect(c).toBe(3);
|
||||
});
|
||||
|
||||
it('runs the "moto" hooks in series asynchronously', async () => {
|
||||
const app = StrapiApp({ middlewares, reducers, library });
|
||||
|
||||
app.createHook('hello');
|
||||
app.createHook('moto');
|
||||
|
||||
app.registerHook('hello', () => Promise.resolve(5));
|
||||
app.registerHook('moto', () => 1);
|
||||
app.registerHook('moto', () => 2);
|
||||
app.registerHook('moto', () => 3);
|
||||
|
||||
const [a, b, c] = await app.runHookSeries('moto', true);
|
||||
|
||||
expect(a).toBe(1);
|
||||
expect(b).toBe(2);
|
||||
expect(c).toBe(3);
|
||||
});
|
||||
|
||||
it('runs the "moto" hooks in waterfall', () => {
|
||||
const app = StrapiApp({ middlewares, reducers, library });
|
||||
|
||||
app.createHook('hello');
|
||||
app.createHook('moto');
|
||||
|
||||
app.registerHook('hello', () => 5);
|
||||
app.registerHook('moto', n => n + 1);
|
||||
app.registerHook('moto', n => n + 2);
|
||||
app.registerHook('moto', n => n + 3);
|
||||
|
||||
const res = app.runHookWaterfall('moto', 1);
|
||||
|
||||
expect(res).toBe(7);
|
||||
});
|
||||
|
||||
it('runs the "moto" hooks in waterfall asynchronously', async () => {
|
||||
const app = StrapiApp({ middlewares, reducers, library });
|
||||
|
||||
app.createHook('hello');
|
||||
app.createHook('moto');
|
||||
|
||||
app.registerHook('hello', () => 5);
|
||||
app.registerHook('moto', n => n + 1);
|
||||
app.registerHook('moto', n => Promise.resolve(n + 2));
|
||||
app.registerHook('moto', n => n + 3);
|
||||
|
||||
const res = await app.runHookWaterfall('moto', 1, true);
|
||||
|
||||
expect(res).toBe(7);
|
||||
});
|
||||
|
||||
it('runs the "moto" hooks in parallel', async () => {
|
||||
const app = StrapiApp({ middlewares, reducers, library });
|
||||
|
||||
app.createHook('hello');
|
||||
app.createHook('moto');
|
||||
|
||||
app.registerHook('hello', () => 5);
|
||||
app.registerHook('moto', () => 1);
|
||||
app.registerHook('moto', () => 2);
|
||||
app.registerHook('moto', () => 3);
|
||||
|
||||
const [a, b, c] = await app.runHookParallel('moto');
|
||||
|
||||
expect(a).toBe(1);
|
||||
expect(b).toBe(2);
|
||||
expect(c).toBe(3);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -38,6 +38,7 @@
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.3",
|
||||
"@fortawesome/react-fontawesome": "^0.1.14",
|
||||
"@strapi/helper-plugin": "3.6.0",
|
||||
"@strapi/hooks": "^0.1.0",
|
||||
"@strapi/utils": "3.6.0",
|
||||
"axios": "^0.21.1",
|
||||
"babel-loader": "8.2.2",
|
||||
|
||||
47
packages/utils/hooks/lib/index.js
Normal file
47
packages/utils/hooks/lib/index.js
Normal file
@ -0,0 +1,47 @@
|
||||
'use strict';
|
||||
|
||||
const createHook = () => {
|
||||
const _handlers = [];
|
||||
|
||||
return {
|
||||
register(fn) {
|
||||
_handlers.push(fn);
|
||||
},
|
||||
delete: handler => {
|
||||
_handlers.splice(_handlers.indexOf(handler), 1);
|
||||
},
|
||||
runWaterfall(args) {
|
||||
return _handlers.reduce((acc, fn) => fn(acc), args);
|
||||
},
|
||||
async runWaterfallAsync(args) {
|
||||
let result = args;
|
||||
|
||||
for (const fn of _handlers) {
|
||||
result = await fn(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
runSeries(...args) {
|
||||
return _handlers.map(fn => fn(...args));
|
||||
},
|
||||
async runSeriesAsync(...args) {
|
||||
const result = [];
|
||||
|
||||
for (const fn of _handlers) {
|
||||
result.push(await fn(...args));
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
runParallel(...args) {
|
||||
return Promise.all(
|
||||
_handlers.map(fn => {
|
||||
return fn(...args);
|
||||
})
|
||||
);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = createHook;
|
||||
89
packages/utils/hooks/lib/tests/index.test.js
Normal file
89
packages/utils/hooks/lib/tests/index.test.js
Normal file
@ -0,0 +1,89 @@
|
||||
'use strict';
|
||||
|
||||
const createHook = require('../');
|
||||
|
||||
describe('createHook', () => {
|
||||
let hooksContainer;
|
||||
|
||||
beforeEach(() => {
|
||||
hooksContainer = createHook();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('calls all of the mocks sequentially', () => {
|
||||
hooksContainer.register(() => 1);
|
||||
hooksContainer.register(() => 2);
|
||||
hooksContainer.register(() => 3);
|
||||
|
||||
const [a, b, c] = hooksContainer.runSeries();
|
||||
|
||||
expect(a).toBe(1);
|
||||
expect(b).toBe(2);
|
||||
expect(c).toBe(3);
|
||||
});
|
||||
|
||||
it('calls all of the mocks sequentially when they resolve async code', async () => {
|
||||
hooksContainer.register(() => Promise.resolve(1));
|
||||
hooksContainer.register(() => Promise.resolve(2));
|
||||
hooksContainer.register(() => Promise.resolve(3));
|
||||
|
||||
const [a, b, c] = await hooksContainer.runSeriesAsync();
|
||||
|
||||
expect(a).toBe(1);
|
||||
expect(b).toBe(2);
|
||||
expect(c).toBe(3);
|
||||
});
|
||||
|
||||
it('calls all of the mocks in a waterfall fashion', () => {
|
||||
hooksContainer.register(n => n + 1);
|
||||
hooksContainer.register(n => n + 2);
|
||||
hooksContainer.register(n => n * 3);
|
||||
|
||||
const res = hooksContainer.runWaterfall(1);
|
||||
|
||||
expect(res).toBe(12);
|
||||
});
|
||||
|
||||
it('calls all of the mocks in a waterfall fashion when they resolve async code', async () => {
|
||||
hooksContainer.register(n => Promise.resolve(n + 1));
|
||||
hooksContainer.register(n => n + 2);
|
||||
hooksContainer.register(n => Promise.resolve(n * 3));
|
||||
|
||||
const res = await hooksContainer.runWaterfallAsync(1);
|
||||
|
||||
expect(res).toBe(12);
|
||||
});
|
||||
|
||||
it('calls all of the mocks in parallel', async () => {
|
||||
hooksContainer.register(() => Promise.resolve(1));
|
||||
hooksContainer.register(() => Promise.resolve(2));
|
||||
hooksContainer.register(() => Promise.resolve(3));
|
||||
|
||||
const [a, b, c] = await hooksContainer.runParallel();
|
||||
|
||||
expect(a).toBe(1);
|
||||
expect(b).toBe(2);
|
||||
expect(c).toBe(3);
|
||||
});
|
||||
|
||||
it('removes a hook when calling delete', () => {
|
||||
const fn1 = () => 1;
|
||||
const fn2 = () => 2;
|
||||
const fn3 = () => 3;
|
||||
|
||||
hooksContainer.register(fn1);
|
||||
hooksContainer.register(fn2);
|
||||
hooksContainer.register(fn3);
|
||||
|
||||
hooksContainer.delete(fn3);
|
||||
|
||||
const [a, b, c] = hooksContainer.runSeries();
|
||||
|
||||
expect(a).toBe(1);
|
||||
expect(b).toBe(2);
|
||||
expect(c).toBe(undefined);
|
||||
});
|
||||
});
|
||||
12
packages/utils/hooks/package.json
Normal file
12
packages/utils/hooks/package.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "@strapi/hooks",
|
||||
"main": "./lib/index.js",
|
||||
"version": "0.1.0",
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"scripts": {
|
||||
"test": "jest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jest": "^27.0.3"
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user