Skip to content

Commit

Permalink
feat(create): adds collection, scope, and services
Browse files Browse the repository at this point in the history
  • Loading branch information
rafamel committed Sep 14, 2019
1 parent bab385d commit 502c98b
Show file tree
Hide file tree
Showing 10 changed files with 281 additions and 7 deletions.
42 changes: 36 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@
"typescript": "^3.6.3"
},
"dependencies": {
"@types/json-schema": "^7.0.3"
"@types/json-schema": "^7.0.3",
"camelcase": "^5.3.1"
},
"peerDependencies": {
"rxjs": "6.x"
Expand Down
9 changes: 9 additions & 0 deletions src/create/collection/empty.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { CollectionTree } from '~/types';

export function emptyCollection(): CollectionTree {
return {
types: { error: {}, request: {}, response: {} },
services: { query: {}, mutation: {}, subscription: {} },
scopes: {}
};
}
58 changes: 58 additions & 0 deletions src/create/collection/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { CollectionTree, Service, Tree, ServiceItem, InputHook } from '~/types';
import { query, mutation, subscription } from '../service';
import { services as createServices } from '../services';
import { mergeCollectionArray } from './merge';
import { emptyCollection } from './empty';

export function setCollectionHooks(
collection: CollectionTree,
hooks: InputHook | InputHook[]
): CollectionTree {
const empty = emptyCollection();

return mergeCollectionArray([
{ ...empty, ...collection, services: empty.services },
setServicesHooks('query', collection.services.query, hooks),
setServicesHooks('mutation', collection.services.mutation, hooks),
setServicesHooks('subscription', collection.services.subscription, hooks),
...Object.entries(collection.scopes).map(([name, scope]) => {
const { types, ...other } = setCollectionHooks(
{ ...empty, ...scope },
hooks
);
return { ...empty, types, scopes: { [name]: other } };
})
]);
}

export function setServicesHooks<T extends Service>(
kind: keyof Tree['services'],
services: { [key: string]: T },
hooks: InputHook | InputHook[]
): CollectionTree {
const entries = Object.entries(services);
return createServices(
entries.reduce(
(acc: { [key: string]: ServiceItem }, [name, service]) =>
Object.assign(acc, { [name]: setServiceHooks(kind, service, hooks) }),
{}
)
);
}

export function setServiceHooks<T extends Service>(
kind: keyof Tree['services'],
service: T,
hooks: InputHook | InputHook[]
): ServiceItem<T> {
switch (kind) {
case 'query':
return query(service, hooks) as ServiceItem<T>;
case 'mutation':
return mutation(service, hooks) as ServiceItem<T>;
case 'subscription':
return subscription(service, hooks) as ServiceItem<T>;
default:
throw Error(`Invalid service kind`);
}
}
15 changes: 15 additions & 0 deletions src/create/collection/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { CollectionTree, InputHook } from '~/types';
import { mergeCollectionArray } from './merge';
import { emptyCollection } from './empty';
import { setCollectionHooks } from './hooks';

export function collection(
collections?: CollectionTree | CollectionTree[],
hooks?: InputHook | InputHook[]
): CollectionTree {
const collection = Array.isArray(collections)
? mergeCollectionArray(collections)
: collections || emptyCollection();

return hooks ? setCollectionHooks(collection, hooks) : collection;
}
54 changes: 54 additions & 0 deletions src/create/collection/merge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { CollectionTree, ScopeTree } from '~/types';
import { emptyCollection } from './empty';

export function mergeCollectionArray(
collections: CollectionTree[]
): CollectionTree {
return collections.reduce(
(acc, collection) => mergeCollections(acc, collection),
emptyCollection()
);
}

export function mergeCollections(
a: CollectionTree,
b: CollectionTree
): CollectionTree {
const { types: aTypes, ...aScope } = a;
const { types: bTypes, ...bScope } = b;
return {
types: {
error: { ...aTypes.error, ...bTypes.error },
request: { ...aTypes.request, ...bTypes.request },
response: { ...aTypes.response, ...bTypes.response }
},
...mergeScopes(aScope, bScope)
};
}

export function mergeScopes(a: ScopeTree, b: ScopeTree): ScopeTree {
const scopeKeys = {
a: Object.keys(a.scopes),
b: Object.keys(b.scopes)
};
const nonUniqueScopeKeys = scopeKeys.a.filter(
(x) => scopeKeys.b.indexOf(x) !== -1
);

return {
services: {
query: { ...a.services.query, ...b.services.query },
mutation: { ...a.services.mutation, ...b.services.mutation },
subscription: { ...a.services.subscription, ...b.services.subscription }
},
scopes: {
...a.scopes,
...b.scopes,
...nonUniqueScopeKeys.reduce(
(acc: { [key: string]: ScopeTree }, x) =>
Object.assign(acc, mergeScopes(a.scopes[x], b.scopes[x])),
{}
)
}
};
}
4 changes: 4 additions & 0 deletions src/create/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
export * from './collection';
export * from './service';
export * from './services';
export * from './schema';
export * from './scope';
15 changes: 15 additions & 0 deletions src/create/scope.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { CollectionTree, InputHook } from '~/types';
import { collection as createCollection } from './collection';

export function scope(
name: string,
collection: CollectionTree,
hooks?: InputHook | InputHook[]
): CollectionTree {
const { types, ...other } = collection;
const tree = createCollection();
tree.types = types;
tree.scopes[name] = other;

return hooks ? createCollection(tree, hooks) : tree;
}
46 changes: 46 additions & 0 deletions src/create/services/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { CollectionTree, InputHook, InputServices } from '~/types';
import { itemToCollection } from './item-to-collection';
import { collection } from '../collection';
import { scope as createScope } from '../scope';

export function services(
services: InputServices,
hooks?: InputHook | InputHook[]
): CollectionTree;
export function services(
name: string,
services: InputServices,
hooks?: InputHook | InputHook[]
): CollectionTree;
export function services(
name: string,
scope: boolean,
services: InputServices,
hooks?: InputHook | InputHook[]
): CollectionTree;
export function services(...args: any[]): CollectionTree {
const hasName = typeof args[0] === 'string';
const hasScope = typeof args[1] === 'boolean';
const name: string = hasName ? args[0] : '';
const scope: boolean = hasScope ? args[1] : false;
const services: InputServices = hasScope
? args[2]
: hasName
? args[1]
: args[0];
const hooks: InputHook | InputHook[] | undefined = hasScope
? args[3]
: hasName
? args[2]
: args[1];

const collections: CollectionTree[] = [];
const entries = Object.entries(services);
for (const [key, item] of entries) {
collections.push(itemToCollection(name, key, item));
}

return scope
? createScope(name, collection(collections, hooks))
: collection(collections, hooks);
}
42 changes: 42 additions & 0 deletions src/create/services/item-to-collection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { ServiceItem, CollectionTree } from '~/types';
import { collection } from '../collection';
import camelcase from 'camelcase';
import { request, response, error } from '../type';

export function itemToCollection(
prefix: string,
name: string,
item: ServiceItem
): CollectionTree {
const collections: CollectionTree[] = [];
const service = {
...item.service,
types: { ...item.service.types }
};

const pre =
camelcase(prefix, { pascalCase: true }) +
camelcase(name, { pascalCase: true });
if (item.types.request) {
collections.push(request(pre + 'Request', { schema: item.types.request }));
service.types.request = pre + 'Request';
}
if (item.types.response) {
collections.push(
response(pre + 'Response', { schema: item.types.response })
);
service.types.response = pre + 'Response';
}
if (item.types.errors) {
for (const { name: id, ...other } of item.types.errors) {
collections.push(error(pre + id, other));
service.types.errors.push(pre + id);
}
}

const tree = collection();
tree.services[item.kind][name] = service;
collections.push(tree);

return collection(collections);
}

0 comments on commit 502c98b

Please sign in to comment.