mirror of
https://github.com/strapi/strapi.git
synced 2025-11-16 18:19:34 +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 { QueryClientProvider, QueryClient } from 'react-query';
|
||||||
import { ThemeProvider } from 'styled-components';
|
import { ThemeProvider } from 'styled-components';
|
||||||
import { LibraryProvider, StrapiAppProvider } from '@strapi/helper-plugin';
|
import { LibraryProvider, StrapiAppProvider } from '@strapi/helper-plugin';
|
||||||
|
import createHook from '@strapi/hooks';
|
||||||
import configureStore from './core/store/configureStore';
|
import configureStore from './core/store/configureStore';
|
||||||
import { Plugin } from './core/apis';
|
import { Plugin } from './core/apis';
|
||||||
import basename from './utils/basename';
|
import basename from './utils/basename';
|
||||||
@ -12,7 +13,6 @@ import LanguageProvider from './components/LanguageProvider';
|
|||||||
import AutoReloadOverlayBlockerProvider from './components/AutoReloadOverlayBlockerProvider';
|
import AutoReloadOverlayBlockerProvider from './components/AutoReloadOverlayBlockerProvider';
|
||||||
import OverlayBlocker from './components/OverlayBlocker';
|
import OverlayBlocker from './components/OverlayBlocker';
|
||||||
import Fonts from './components/Fonts';
|
import Fonts from './components/Fonts';
|
||||||
|
|
||||||
import GlobalStyle from './components/GlobalStyle';
|
import GlobalStyle from './components/GlobalStyle';
|
||||||
import Notifications from './components/Notifications';
|
import Notifications from './components/Notifications';
|
||||||
import themes from './themes';
|
import themes from './themes';
|
||||||
@ -42,6 +42,7 @@ class StrapiApp {
|
|||||||
this.plugins = {};
|
this.plugins = {};
|
||||||
this.reducers = reducers;
|
this.reducers = reducers;
|
||||||
this.translations = translations;
|
this.translations = translations;
|
||||||
|
this.hooksDict = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
addComponents = components => {
|
addComponents = components => {
|
||||||
@ -143,6 +144,24 @@ class StrapiApp {
|
|||||||
this.plugins[plugin.pluginId] = plugin;
|
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() {
|
render() {
|
||||||
const store = this.createStore();
|
const store = this.createStore();
|
||||||
|
|
||||||
|
|||||||
@ -49,4 +49,92 @@ describe('ADMIN | StrapiApp', () => {
|
|||||||
|
|
||||||
expect(store.getState()).toEqual(fixtures.store.state);
|
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/free-solid-svg-icons": "^5.15.3",
|
||||||
"@fortawesome/react-fontawesome": "^0.1.14",
|
"@fortawesome/react-fontawesome": "^0.1.14",
|
||||||
"@strapi/helper-plugin": "3.6.0",
|
"@strapi/helper-plugin": "3.6.0",
|
||||||
|
"@strapi/hooks": "^0.1.0",
|
||||||
"@strapi/utils": "3.6.0",
|
"@strapi/utils": "3.6.0",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"babel-loader": "8.2.2",
|
"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