Skip to content

Commit

Permalink
feat(core): adds hook function
Browse files Browse the repository at this point in the history
  • Loading branch information
rafamel committed Sep 30, 2019
1 parent b30f91a commit 7faac2e
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 0 deletions.
155 changes: 155 additions & 0 deletions packages/core/src/create/hook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import {
EnvelopeElement,
Envelope,
InputHook,
ServiceImplementation,
Observable,
ResponseTypeImplementation,
CollectionTreeImplementation
} from '~/types';
import clone from 'lodash.clonedeep';
import { isEnvelope, mergeTypes, traverse } from '~/utils';
import camelcase from 'camelcase';
import { from } from 'rxjs';
import { mergeMap } from 'rxjs/operators';

export default function hook<T extends EnvelopeElement, N extends string>(
envelope: Envelope<T, N>,
hooks: InputHook | InputHook[]
): Envelope<T, N> {
if (Array.isArray(hooks)) {
return hooks.reverse().reduce((acc, fn) => hook(acc, fn), envelope);
}

switch (envelope.item.kind) {
case 'collection': {
const collection = clone(envelope) as Envelope<
T & CollectionTreeImplementation,
N
>;
traverse(
{ tree: collection.item, deep: true, children: false },
(element, path) => {
const key = path.slice(-1)[0] || '';
const elementEnvelope = hook({ name: key, item: element }, hooks);
Object.assign(element, elementEnvelope.item);
if (elementEnvelope.types) {
collection.types = mergeTypes(
collection.types || {},
elementEnvelope.types
);
}
if (elementEnvelope.inline) {
collection.inline = mergeTypes(
collection.inline || {},
elementEnvelope.inline
);
}
}
);
return collection;
}
case 'query':
case 'mutation':
case 'subscription': {
const service = clone(envelope) as Envelope<T & ServiceImplementation, N>;
if (hooks.errors) {
for (const error of hooks.errors) {
if (typeof error === 'string') {
service.item.types.errors.push(error);
} else if (isEnvelope(error)) {
if (error.types) {
service.types = mergeTypes(service.types || {}, error.types);
}
if (error.inline) {
service.inline = mergeTypes(service.inline || {}, error.inline);
}
service.types = mergeTypes(service.types || {}, {
[error.name]: error.item
});
service.item.types.errors.push(error.name);
} else {
const name = camelcase(error.name, { pascalCase: true });
service.inline = mergeTypes(service.inline || {}, {
[name]: { kind: 'error', code: error.code }
});
service.item.types.errors.push(name);
}
}
}
if (hooks.before) {
const beforeFn = hooks.before;
const resolveFn = service.item.resolve;
service.item.resolve = async function resolve(...args: any[]) {
await beforeFn.apply(this, args as any);
return resolveFn.apply(this, args as any);
};
}
if (hooks.after) {
const afterFn = hooks.after;
const resolveFn = service.item.resolve;
if (service.item.kind !== 'subscription') {
service.item.resolve = async function resolve(...args: any[]) {
const response = await resolveFn.apply(this, args as any);
await afterFn.apply(this, [response].concat(args.slice(1)) as any);
return response;
};
} else {
service.item.resolve = async function resolve(...args: any[]) {
const observable: Observable<any> = await resolveFn.apply(
this,
args as any
);
return observable.pipe(
mergeMap((response) => {
const fn = async (): Promise<typeof response> => {
await afterFn.apply(this, [response].concat(
args.slice(1)
) as any);
return response;
};
return from(fn());
})
);
};
}
}
return service;
}
case 'error':
case 'request': {
return envelope;
}
case 'response': {
const response = clone(envelope) as Envelope<
T & ResponseTypeImplementation,
N
>;

if (!response.item.children) return response;

const childrenKeys = Object.keys(response.item.children);
for (const childKey of childrenKeys) {
const child = response.item.children[childKey];
const childEnvelope = hook({ name: childKey, item: child }, hooks);
response.item.children[childKey] = childEnvelope.item;
if (childEnvelope.types) {
response.types = mergeTypes(
response.types || {},
childEnvelope.types
);
}
if (childEnvelope.inline) {
response.inline = mergeTypes(
response.inline || {},
childEnvelope.inline
);
}
}
return response;
}
default: {
throw Error(`Invalid item kind for Envelope`);
}
}
}
1 change: 1 addition & 0 deletions packages/core/src/create/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { default as collection } from './collection';
export { default as group } from './group';
export { default as hook } from './hook';
export { default as root } from './root';
export { default as schema } from './schema';
export { default as scope } from './scope';
Expand Down

0 comments on commit 7faac2e

Please sign in to comment.