-
Notifications
You must be signed in to change notification settings - Fork 33
/
resolvers-composition.ts
129 lines (108 loc) · 3.96 KB
/
resolvers-composition.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import { IResolvers, IFieldResolver } from '@graphql-toolkit/types';
import { chainFunctions, asArray } from './helpers';
import { flattenArray } from './flatten-array';
import get from 'lodash.get';
import set from 'lodash.set';
import { isScalarType } from 'graphql';
export type ResolversComposition<Resolver extends IFieldResolver<any, any> = IFieldResolver<any, any>> = (
next: Resolver
) => Resolver;
export type ResolversComposerMapping<Resolvers extends IResolvers = IResolvers> =
| {
[TypeName in keyof Resolvers]?: {
[FieldName in keyof Resolvers[TypeName]]: Resolvers[TypeName][FieldName] extends IFieldResolver<any, any>
?
| ResolversComposition<Resolvers[TypeName][FieldName]>
| Array<ResolversComposition<Resolvers[TypeName][FieldName]>>
: ResolversComposition | ResolversComposition[];
};
}
| {
[path: string]: ResolversComposition | ResolversComposition[];
};
function resolveRelevantMappings<Resolvers extends IResolvers>(
resolvers: Resolvers,
path: string,
allMappings: ResolversComposerMapping<Resolvers>
): string[] {
const splitted = path.split('.');
if (splitted.length === 2) {
const typeName = splitted[0];
if (isScalarType(resolvers[typeName])) {
return [];
}
const fieldName = splitted[1];
if (typeName === '*') {
return flattenArray(
Object.keys(resolvers).map((typeName) =>
resolveRelevantMappings(resolvers, `${typeName}.${fieldName}`, allMappings)
)
);
}
if (fieldName === '*') {
return flattenArray(
Object.keys(resolvers[typeName]).map((field) =>
resolveRelevantMappings(resolvers, `${typeName}.${field}`, allMappings)
)
).filter((mapItem) => !allMappings[mapItem]);
} else {
const paths = [];
if (resolvers[typeName] && resolvers[typeName][fieldName]) {
if (resolvers[typeName][fieldName]['subscribe']) {
paths.push(path + '.subscribe');
}
if (resolvers[typeName][fieldName]['resolve']) {
paths.push(path + '.resolve');
}
if (typeof resolvers[typeName][fieldName] === 'function') {
paths.push(path);
}
}
return paths;
}
} else if (splitted.length === 1) {
const typeName = splitted[0];
return flattenArray(
Object.keys(resolvers[typeName]).map((fieldName) =>
resolveRelevantMappings(resolvers, `${typeName}.${fieldName}`, allMappings)
)
);
}
return [];
}
/**
* Wraps the resolvers object with the resolvers composition objects.
* Implemented as a simple and basic middleware mechanism.
*
* @param resolvers - resolvers object
* @param mapping - resolvers composition mapping
* @hidden
*/
export function composeResolvers<Resolvers extends IResolvers>(
resolvers: Resolvers,
mapping: ResolversComposerMapping<Resolvers> = {}
): Resolvers {
const mappingResult: { [path: string]: Function[] } = {};
Object.keys(mapping).map((resolverPath: string) => {
if (mapping[resolverPath] instanceof Array || typeof mapping[resolverPath] === 'function') {
const composeFns = mapping[resolverPath] as ResolversComposition | ResolversComposition[];
const relevantFields = resolveRelevantMappings(resolvers, resolverPath, mapping);
relevantFields.forEach((path: string) => {
mappingResult[path] = asArray(composeFns);
});
} else {
Object.keys(mapping[resolverPath]).forEach((fieldName) => {
const composeFns = mapping[resolverPath][fieldName];
const relevantFields = resolveRelevantMappings(resolvers, resolverPath + '.' + fieldName, mapping);
relevantFields.forEach((path: string) => {
mappingResult[path] = asArray(composeFns);
});
});
}
});
Object.keys(mappingResult).forEach((path) => {
const fns = chainFunctions([...asArray(mappingResult[path]), () => get(resolvers, path)]);
set(resolvers, path, fns());
});
return resolvers;
}