Skip to content

Commit

Permalink
feat(core): adds create application and allof for intercepts
Browse files Browse the repository at this point in the history
  • Loading branch information
rafamel committed Oct 12, 2019
1 parent 50d8573 commit 5b13c7f
Show file tree
Hide file tree
Showing 16 changed files with 745 additions and 105 deletions.
72 changes: 72 additions & 0 deletions packages/core/src/create/application/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import {
CollectionTreeImplementation,
CollectionTreeApplication,
CollectionTree,
TreeTypes
} from '~/types';
import clone from 'lodash.clonedeep';
import { traverse, isElementType } from '~/utils';
import camelcase from 'camelcase';
import serviceIntercepts from './service-intercepts';
import { mergeServiceTypes } from './merge';

export default function application(
collection: CollectionTree | CollectionTreeImplementation,
options?: {
prefixScope?: boolean;
prefixInlineError?: boolean;
transform?: (str: string, explicit: boolean) => string;
}
): CollectionTreeApplication {
collection = clone(collection);

const opts = Object.assign(
{
prefixScope: true,
prefixInlineError: false,
transform: (str: string) => camelcase(str, { pascalCase: true })
},
options
);

const types = {
source: collection.types,
application: Object.entries(collection.types).reduce(
(acc: TreeTypes, [name, type]) => {
const pascal = opts.transform(name, true);
if (Object.hasOwnProperty.call(acc, pascal)) {
throw Error(`Type name collision: ${pascal}`);
}
acc[pascal] = type;
return acc;
},
{}
)
};

traverse(collection, { children: false, inline: false }, (element, path) => {
const name = opts.transform(path.slice(-1)[0], true);

if (isElementType(element)) {
if (element.kind !== 'response' || !element.children) return;
for (const [key, service] of Object.entries(element.children)) {
const fullName = name + opts.transform(key, false);
serviceIntercepts(fullName, service, types.source);
mergeServiceTypes(fullName, service, types, opts);
}
} else {
const fullName =
opts.prefixScope && path[path.length - 3]
? opts.transform(path[path.length - 3], false) + name
: name;

serviceIntercepts(fullName, element, types.source);
mergeServiceTypes(fullName, element, types, opts);
}
});

return {
...collection,
types: types.application
} as CollectionTreeApplication;
}
108 changes: 108 additions & 0 deletions packages/core/src/create/application/merge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { TreeTypes, Service, ServiceErrors, Type } from '~/types';

export function mergeServiceTypes(
name: string,
service: Service,
types: { source: TreeTypes; application: TreeTypes },
options: {
prefixInlineError: boolean;
transform: (str: string, explicit: boolean) => string;
}
): void {
const { prefixInlineError, transform } = options;

for (const kind of ['request', 'response'] as ['request', 'response']) {
const type = service.types[kind];
if (typeof type === 'string') {
service.types[kind] = checkSourceType(kind, type, types, options);
} else {
const pascal = name + transform('R' + kind.slice(1), false);
mergeServiceType(kind, pascal, type, types, options);
service.types[kind] = pascal;
}
}

const errors: ServiceErrors = {};
for (const [key, error] of Object.entries(service.types.errors)) {
if (typeof error === 'string') {
let id = checkSourceType('error', error, types, options);
if (key !== error) {
id = (prefixInlineError ? name : '') + transform(key, true);
mergeServiceType('error', id, types.source[error], types, options);
}
errors[id] = id;
} else {
const id = (prefixInlineError ? name : '') + transform(key, true);
mergeServiceType('error', id, error, types, options);
errors[id] = id;
}
}
service.types.errors = errors;
}

export function mergeServiceType(
kind: string,
name: string,
type: Type,
types: { source: TreeTypes; application: TreeTypes },
options: {
prefixInlineError: boolean;
transform: (str: string, explicit: boolean) => string;
}
): void {
if (type.kind !== kind) {
throw Error(`Invalid inline type kind: ${name}`);
}

switch (type.kind) {
case 'error':
case 'request': {
if (Object.hasOwnProperty.call(types.application, name)) {
throw Error(`Inline type name collision: ${name}`);
}
types.application[name] = type;
return;
}
case 'response': {
if (Object.hasOwnProperty.call(types.application, name)) {
throw Error(`Inline type name collision: ${name}`);
}
types.application[name] = type;

if (type.children) {
for (const [key, service] of Object.entries(type.children)) {
mergeServiceTypes(
name + options.transform(key, false),
service,
types,
options
);
}
}
return;
}
default: {
throw Error(`Invalid kind for type: ${name}`);
}
}
}

export function checkSourceType(
kind: string,
name: string,
types: { source: TreeTypes; application: TreeTypes },
options: { transform: (str: string, explicit: boolean) => string }
): string {
const pascal = options.transform(name, true);
if (
!Object.hasOwnProperty.call(types.source, name) ||
!Object.hasOwnProperty.call(types.application, pascal)
) {
throw Error(`Collection lacks referenced type: ${name}`);
}
if (types.application[pascal].kind !== kind) {
throw Error(`Invalid type kind reference: ${pascal}`);
}

return pascal;
}
87 changes: 87 additions & 0 deletions packages/core/src/create/application/service-intercepts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import {
Service,
ServiceImplementation,
TreeTypes,
Type,
Observable
} from '~/types';
import {
isServiceImplementation,
isTypeRequest,
isTypeResponse,
mergeServiceErrors
} from '~/utils';
import { from } from 'rxjs';
import { allof } from '../intercepts';

export default function serviceIntercepts(
name: string,
service: Service | ServiceImplementation,
types: TreeTypes
): void {
if (!isServiceImplementation(service)) return;

const intercepts = service.intercepts;
delete service.intercepts;
if (!intercepts || !intercepts.length) return;

const request: Type | undefined =
typeof service.types.request === 'string'
? types[service.types.request]
: service.types.request;
const response: Type | undefined =
typeof service.types.response === 'string'
? types[service.types.response]
: service.types.response;
if (
!request ||
!response ||
!isTypeRequest(request) ||
!isTypeResponse(response)
) {
throw Error(`Invalid type kind for service: ${name}`);
}

const intercept = allof(intercepts);
const interceptFn = intercept.factory({
request: request.schema,
response: response.schema
});

switch (service.kind) {
case 'query':
case 'mutation': {
const resolve = service.resolve;
Object.assign(service, {
types: {
...service.types,
errors: mergeServiceErrors(service.types.errors, intercept.errors)
},
resolve(data: any, context: any): Promise<any> {
return interceptFn(data, context, (data: any) => {
return from(resolve.call(this, data, context));
}).toPromise();
}
});
return;
}
case 'subscription': {
const resolve = service.resolve;
Object.assign(service, {
types: {
...service.types,
errors: mergeServiceErrors(service.types.errors, intercept.errors)
},
resolve(data: any, context: any): Observable<any> {
return interceptFn(data, context, (data: any) => {
return resolve.call(this, data, context);
});
}
});
return;
}
default: {
throw Error(`Invalid kind for type`);
}
}
}
106 changes: 52 additions & 54 deletions packages/core/src/create/collection.ts
Original file line number Diff line number Diff line change
@@ -1,61 +1,59 @@
import { CollectionTreeImplementation } from '~/types';
import { CollectionTreeImplementation, InputCollection } from '~/types';
import { emptyCollection, mergeCollection } from '~/utils';

export default collection;

function collection<
C1 extends Partial<
CollectionTreeImplementation
> = CollectionTreeImplementation<{}, {}, {}>,
C2 extends Partial<CollectionTreeImplementation> = C1,
C3 extends Partial<CollectionTreeImplementation> = C1,
C4 extends Partial<CollectionTreeImplementation> = C1,
C5 extends Partial<CollectionTreeImplementation> = C1,
C6 extends Partial<CollectionTreeImplementation> = C1,
C7 extends Partial<CollectionTreeImplementation> = C1,
C8 extends Partial<CollectionTreeImplementation> = C1,
C9 extends Partial<CollectionTreeImplementation> = C1,
C10 extends Partial<CollectionTreeImplementation> = C1,
C11 extends Partial<CollectionTreeImplementation> = C1,
C12 extends Partial<CollectionTreeImplementation> = C1,
C13 extends Partial<CollectionTreeImplementation> = C1,
C14 extends Partial<CollectionTreeImplementation> = C1,
C15 extends Partial<CollectionTreeImplementation> = C1,
C16 extends Partial<CollectionTreeImplementation> = C1,
C17 extends Partial<CollectionTreeImplementation> = C1,
C18 extends Partial<CollectionTreeImplementation> = C1,
C19 extends Partial<CollectionTreeImplementation> = C1,
C20 extends Partial<CollectionTreeImplementation> = C1,
C21 extends Partial<CollectionTreeImplementation> = C1,
C22 extends Partial<CollectionTreeImplementation> = C1,
C23 extends Partial<CollectionTreeImplementation> = C1,
C24 extends Partial<CollectionTreeImplementation> = C1,
C25 extends Partial<CollectionTreeImplementation> = C1,
C26 extends Partial<CollectionTreeImplementation> = C1,
C27 extends Partial<CollectionTreeImplementation> = C1,
C28 extends Partial<CollectionTreeImplementation> = C1,
C29 extends Partial<CollectionTreeImplementation> = C1,
C30 extends Partial<CollectionTreeImplementation> = C1,
C31 extends Partial<CollectionTreeImplementation> = C1,
C32 extends Partial<CollectionTreeImplementation> = C1,
C33 extends Partial<CollectionTreeImplementation> = C1,
C34 extends Partial<CollectionTreeImplementation> = C1,
C35 extends Partial<CollectionTreeImplementation> = C1,
C36 extends Partial<CollectionTreeImplementation> = C1,
C37 extends Partial<CollectionTreeImplementation> = C1,
C38 extends Partial<CollectionTreeImplementation> = C1,
C39 extends Partial<CollectionTreeImplementation> = C1,
C40 extends Partial<CollectionTreeImplementation> = C1,
C41 extends Partial<CollectionTreeImplementation> = C1,
C42 extends Partial<CollectionTreeImplementation> = C1,
C43 extends Partial<CollectionTreeImplementation> = C1,
C44 extends Partial<CollectionTreeImplementation> = C1,
C45 extends Partial<CollectionTreeImplementation> = C1,
C46 extends Partial<CollectionTreeImplementation> = C1,
C47 extends Partial<CollectionTreeImplementation> = C1,
C48 extends Partial<CollectionTreeImplementation> = C1,
C49 extends Partial<CollectionTreeImplementation> = C1,
C50 extends Partial<CollectionTreeImplementation> = C1
C1 extends InputCollection = CollectionTreeImplementation<{}, {}, {}>,
C2 extends InputCollection = C1,
C3 extends InputCollection = C1,
C4 extends InputCollection = C1,
C5 extends InputCollection = C1,
C6 extends InputCollection = C1,
C7 extends InputCollection = C1,
C8 extends InputCollection = C1,
C9 extends InputCollection = C1,
C10 extends InputCollection = C1,
C11 extends InputCollection = C1,
C12 extends InputCollection = C1,
C13 extends InputCollection = C1,
C14 extends InputCollection = C1,
C15 extends InputCollection = C1,
C16 extends InputCollection = C1,
C17 extends InputCollection = C1,
C18 extends InputCollection = C1,
C19 extends InputCollection = C1,
C20 extends InputCollection = C1,
C21 extends InputCollection = C1,
C22 extends InputCollection = C1,
C23 extends InputCollection = C1,
C24 extends InputCollection = C1,
C25 extends InputCollection = C1,
C26 extends InputCollection = C1,
C27 extends InputCollection = C1,
C28 extends InputCollection = C1,
C29 extends InputCollection = C1,
C30 extends InputCollection = C1,
C31 extends InputCollection = C1,
C32 extends InputCollection = C1,
C33 extends InputCollection = C1,
C34 extends InputCollection = C1,
C35 extends InputCollection = C1,
C36 extends InputCollection = C1,
C37 extends InputCollection = C1,
C38 extends InputCollection = C1,
C39 extends InputCollection = C1,
C40 extends InputCollection = C1,
C41 extends InputCollection = C1,
C42 extends InputCollection = C1,
C43 extends InputCollection = C1,
C44 extends InputCollection = C1,
C45 extends InputCollection = C1,
C46 extends InputCollection = C1,
C47 extends InputCollection = C1,
C48 extends InputCollection = C1,
C49 extends InputCollection = C1,
C50 extends InputCollection = C1
>(
c1?: C1,
c2?: C2,
Expand Down Expand Up @@ -160,7 +158,7 @@ function collection<
C50;

function collection(
...collections: Array<Partial<CollectionTreeImplementation>>
...collections: InputCollection[]
): CollectionTreeImplementation {
let collection = emptyCollection() as CollectionTreeImplementation;

Expand Down
1 change: 1 addition & 0 deletions packages/core/src/create/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { default as application } from './application';
export { default as collection } from './collection';
export { default as scope } from './scope';
export { default as schema } from './schema';
Expand Down

0 comments on commit 5b13c7f

Please sign in to comment.