Skip to content

Commit

Permalink
feat(core): use @feathers/hooks and add async type (#1929)
Browse files Browse the repository at this point in the history
  • Loading branch information
bertho-zero authored Apr 27, 2020
1 parent cbd31c1 commit a5c4756
Show file tree
Hide file tree
Showing 16 changed files with 954 additions and 289 deletions.
2 changes: 1 addition & 1 deletion .github/issue_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ Tell us about the applicable parts of your setup.

**React Native Version**:

**Module Loader**:
**Module Loader**:
5 changes: 4 additions & 1 deletion packages/client/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
function createConfig (name, isProduction = false) {
const output = name === 'index' ? 'feathers' : name;
const commons = {
entry: `./src/${name}.js`,
entry: [
'regenerator-runtime/runtime',
`./src/${name}.js`
],
output: {
library: 'feathers',
libraryTarget: 'umd',
Expand Down
3 changes: 3 additions & 0 deletions packages/commons/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
"publishConfig": {
"access": "public"
},
"dependencies": {
"@feathersjs/hooks": "^0.4.0-alpha.0"
},
"devDependencies": {
"@types/mocha": "^7.0.2",
"@types/node": "^13.11.1",
Expand Down
143 changes: 139 additions & 4 deletions packages/commons/src/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { HookContext } from '@feathersjs/hooks';
import { createSymbol, _ } from './utils';

const { each, pick } = _;
const { each, pick, omit } = _;
const noop = () => {};

export const ACTIVATE_HOOKS = createSymbol('__feathersActivateHooks');

Expand Down Expand Up @@ -87,9 +89,13 @@ export function convertHookData (obj: any) {
// Duck-checks a given object to be a hook object
// A valid hook object has `type` and `method`
export function isHookObject (hookObject: any) {
return typeof hookObject === 'object' &&
typeof hookObject.method === 'string' &&
typeof hookObject.type === 'string';
return (
hookObject instanceof HookContext || (
typeof hookObject === 'object' &&
typeof hookObject.method === 'string' &&
typeof hookObject.type === 'string'
)
);
}

// Returns all service and application hooks combined
Expand Down Expand Up @@ -193,3 +199,132 @@ export function enableHooks (obj: any, methods: string[], types: string[]) {
}
});
}

async function handleError (hook: any, context: any, onError: any) {
try {
const result = await hook.call(context.self, context);
Object.assign(context, omit(result, 'arguments'));
} catch (errorError) {
if (typeof onError === 'function') {
onError(errorError, context);
}
throw errorError;
}

if (typeof context.error !== 'undefined') {
throw context.error;
}
}

export function firstHook (context: any, next: any) {
context.type = 'before';
return next();
}

export function lastHook (context: any, next: any) {
context.type = 'after';
return next();
}

export function toBeforeHook (hook: any) {
return async (context: any, next: any) => {
const result = await hook.call(context.self, context);
Object.assign(context, omit(result, 'arguments'));
await next();
};
}

export function toAfterHook (hook: any) {
return async (context: any, next: any) => {
await next();
const result = await hook.call(context.self, context);
Object.assign(context, omit(result, 'arguments'));
};
}

export function toErrorHook (hook: any, onError: any, control: any) {
return async (context: any, next: any) => {
try {
await next();
} catch (error) {
if (typeof control === 'function') {
control(context);
}

context.error = error;
context.result = undefined;

await handleError(hook, context, onError);
}
};
}

export function toFinallyHook (hook: any, onError: any, control: any) {
return async (context: any, next: any) => {
try {
await next();
} catch (error) {
throw error;
} finally {
if (typeof control === 'function') {
control(context);
}

await handleError(hook, context, onError);
}
};
}

export function beforeWrapper (hooks: any) {
return [firstHook, ...[].concat(hooks).map(toBeforeHook)];
}

export function afterWrapper (hooks: any) {
return [...[].concat(hooks).reverse().map(toAfterHook), lastHook];
}

export function finallyWrapper (hooks: any) {
let errorInFinally: any;

const onError = (error: any, context: any) => {
errorInFinally = error;
context.error = error;
context.result = undefined;
};
const control = () => {
if (errorInFinally) {
throw errorInFinally;
}
};

return [].concat(hooks).reverse().map(hook => toFinallyHook(hook, onError, control));
}

export function errorWrapper (hooks: any) {
let errorInError: any;

const onError = (error: any, context: any) => {
errorInError = error;
context.error = error;
context.result = undefined;
};
const control = (context: any) => {
if (!context.original) {
context.original = { ...context };
}
if (errorInError) {
throw errorInError;
}
context.type = 'error';
};

return [noop].concat(hooks).reverse().map(hook => toErrorHook(hook, onError, control));
}

export function wrap ({ async = [], before = [], after = [] }: any = {}) {
return [
...[].concat(async),
...beforeWrapper(before),
...afterWrapper(after)
];
}
1 change: 1 addition & 0 deletions packages/express/test/rest.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ describe('@feathersjs/express/rest provider', () => {
const convertHook = hook => {
const result = Object.assign({}, hook);

delete result.self;
delete result.service;
delete result.app;
delete result.error;
Expand Down
12 changes: 6 additions & 6 deletions packages/feathers/lib/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ const Proto = require('uberproto');
// Returns a hook that emits service events. Should always be
// used as the very last hook in the chain
const eventHook = exports.eventHook = function eventHook () {
return function (hook) {
const { app, service } = hook;
const eventName = hook.event === null ? hook.event : app.eventMappings[hook.method];
return function (ctx) {
const { app, service, method, event, type, result } = ctx;
const eventName = event === null ? event : app.eventMappings[method];
const isHookEvent = service._hookEvents && service._hookEvents.indexOf(eventName) !== -1;

// If this event is not being sent yet and we are not in an error hook
if (eventName && isHookEvent && hook.type !== 'error') {
const results = Array.isArray(hook.result) ? hook.result : [ hook.result ];
if (eventName && isHookEvent && type !== 'error') {
const results = Array.isArray(result) ? result : [ result ];

results.forEach(element => service.emit(eventName, element, hook));
results.forEach(element => service.emit(eventName, element, ctx));
}
};
};
Expand Down
8 changes: 4 additions & 4 deletions packages/feathers/lib/hooks/base.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const { _ } = require('@feathersjs/commons');

const assignArguments = context => {
const assignArguments = (context, next) => {
const { service, method } = context;
const parameters = service.methods[method];

Expand All @@ -12,10 +12,10 @@ const assignArguments = context => {
context.params = {};
}

return context;
return next();
};

const validate = context => {
const validate = (context, next) => {
const { service, method, path } = context;
const parameters = service.methods[method];

Expand All @@ -27,7 +27,7 @@ const validate = context => {
throw new Error(`A data object must be provided to the '${path}.${method}' method`);
}

return context;
return next();
};

module.exports = [ assignArguments, validate ];
Loading

0 comments on commit a5c4756

Please # to comment.