@restorecommerce/facade 1.0.1 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/codegen/index.js +1 -1
- package/dist/gql/protos/federation.d.ts +6 -0
- package/dist/gql/protos/federation.js +51 -0
- package/dist/gql/protos/graphql.d.ts +5 -27
- package/dist/gql/protos/graphql.js +3 -564
- package/dist/gql/protos/index.d.ts +3 -0
- package/dist/gql/protos/index.js +3 -0
- package/dist/gql/protos/registry.d.ts +2 -2
- package/dist/gql/protos/resolvers.d.ts +9 -0
- package/dist/gql/protos/resolvers.js +427 -0
- package/dist/gql/protos/schema.d.ts +14 -0
- package/dist/gql/protos/schema.js +234 -0
- package/dist/gql/protos/types.d.ts +20 -0
- package/dist/gql/protos/utils.d.ts +5 -0
- package/dist/gql/protos/utils.js +27 -1
- package/dist/index.d.ts +9 -9
- package/dist/index.js +78 -7
- package/dist/interfaces.d.ts +2 -0
- package/dist/modules/access-control/gql/schema.generated.d.ts +30 -0
- package/dist/modules/access-control/gql/schema.generated.js +7 -1
- package/dist/modules/catalog/gql/schema.generated.d.ts +30 -0
- package/dist/modules/catalog/gql/schema.generated.js +7 -1
- package/dist/modules/fulfillment/gql/schema.generated.d.ts +183 -113
- package/dist/modules/fulfillment/gql/schema.generated.js +13 -6
- package/dist/modules/identity/gql/federation.d.ts +1 -1
- package/dist/modules/identity/gql/federation.js +1 -9
- package/dist/modules/identity/gql/schema.generated.d.ts +35 -0
- package/dist/modules/identity/gql/schema.generated.js +7 -1
- package/dist/modules/identity/gql/types.js +2 -0
- package/dist/modules/indexing/gql/federation.d.ts +2 -2
- package/dist/modules/indexing/gql/federation.js +2 -2
- package/dist/modules/indexing/gql/schema.d.ts +2 -2
- package/dist/modules/indexing/gql/schema.generated.d.ts +43 -2
- package/dist/modules/indexing/gql/schema.generated.js +7 -0
- package/dist/modules/indexing/gql/schema.js +1 -2
- package/dist/modules/indexing/gql/types.d.ts +2 -0
- package/dist/modules/indexing/gql/types.js +5 -2
- package/dist/modules/indexing/interfaces.d.ts +2 -2
- package/dist/modules/invoicing/gql/schema.generated.d.ts +87 -8
- package/dist/modules/invoicing/gql/schema.generated.js +7 -1
- package/dist/modules/notification/gql/schema.generated.d.ts +30 -0
- package/dist/modules/notification/gql/schema.generated.js +7 -1
- package/dist/modules/ordering/gql/federation.js +4 -4
- package/dist/modules/ordering/gql/schema.generated.d.ts +257 -195
- package/dist/modules/ordering/gql/schema.generated.js +18 -1
- package/dist/modules/ostorage/gql/schema.generated.d.ts +30 -0
- package/dist/modules/ostorage/gql/schema.generated.js +7 -1
- package/dist/modules/payment/gql/schema.generated.d.ts +30 -0
- package/dist/modules/payment/gql/schema.generated.js +7 -1
- package/dist/modules/resource/gql/schema.generated.d.ts +114 -12
- package/dist/modules/resource/gql/schema.generated.js +7 -1
- package/dist/modules/scheduling/gql/schema.generated.d.ts +30 -0
- package/dist/modules/scheduling/gql/schema.generated.js +7 -1
- package/generate.ts +6 -3
- package/package.json +18 -9
@@ -0,0 +1,427 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.generateSubServiceResolvers = exports.generateResolver = exports.registerResolverFunction = exports.getGQLResolverFunctions = void 0;
|
4
|
+
const types_1 = require("./types");
|
5
|
+
const array_prototype_flat_1 = require("array.prototype.flat");
|
6
|
+
const registry_1 = require("./registry");
|
7
|
+
const graphql_1 = require("./graphql");
|
8
|
+
const utils_1 = require("./utils");
|
9
|
+
const rc_grpc_clients_1 = require("@restorecommerce/rc-grpc-clients");
|
10
|
+
const resource_base_1 = require("@restorecommerce/rc-grpc-clients/dist/generated/io/restorecommerce/resource_base");
|
11
|
+
const stream = require("stream");
|
12
|
+
const _ = require("lodash");
|
13
|
+
const kafka_client_1 = require("@restorecommerce/kafka-client");
|
14
|
+
const inputMethodType = new Map();
|
15
|
+
const getGQLResolverFunctions = (service, key, serviceKey, cfg) => {
|
16
|
+
if (!service.method) {
|
17
|
+
return {};
|
18
|
+
}
|
19
|
+
return service.method.reduce((obj, method) => {
|
20
|
+
var _a, _b, _c, _d;
|
21
|
+
if ((_b = (_a = cfg[serviceKey]) === null || _a === void 0 ? void 0 : _a.methods) === null || _b === void 0 ? void 0 : _b.blacklist) {
|
22
|
+
const blacklistMethods = (_d = (_c = cfg[serviceKey]) === null || _c === void 0 ? void 0 : _c.methods) === null || _d === void 0 ? void 0 : _d.blacklist;
|
23
|
+
if (blacklistMethods.includes(method.name)) {
|
24
|
+
return {};
|
25
|
+
}
|
26
|
+
}
|
27
|
+
const typing = (0, registry_1.getTyping)(method.inputType);
|
28
|
+
const outputTyping = (0, registry_1.getTyping)(method.outputType);
|
29
|
+
if (!typing) {
|
30
|
+
throw Error(`Method '${method.name}' could not find input type: ${method.inputType}`);
|
31
|
+
}
|
32
|
+
if (!outputTyping) {
|
33
|
+
throw Error(`Method '${method.name}' could not find output type: ${method.outputType}`);
|
34
|
+
}
|
35
|
+
if (!('fromPartial' in typing.processor)) {
|
36
|
+
throw Error(`Method ${method.name} input type '${method.inputType}' does not contain 'fromPartial' function`);
|
37
|
+
}
|
38
|
+
let subjectField = null;
|
39
|
+
if (typing) {
|
40
|
+
for (let field of typing.meta.field) {
|
41
|
+
if (field.typeName === types_1.authSubjectType) {
|
42
|
+
subjectField = field.name;
|
43
|
+
break;
|
44
|
+
}
|
45
|
+
}
|
46
|
+
}
|
47
|
+
let methodName = method.name;
|
48
|
+
if (graphql_1.Mutate.indexOf(method.name) > -1) {
|
49
|
+
methodName = 'Mutate';
|
50
|
+
}
|
51
|
+
if (methodName in obj) {
|
52
|
+
return obj;
|
53
|
+
}
|
54
|
+
obj[methodName] = async (args, context) => {
|
55
|
+
var _a, _b;
|
56
|
+
const client = context[key].client;
|
57
|
+
const service = client[serviceKey];
|
58
|
+
try {
|
59
|
+
const converted = await (0, graphql_1.preprocessGQLInput)(args.input, typing.input);
|
60
|
+
const scope = (_a = args === null || args === void 0 ? void 0 : args.input) === null || _a === void 0 ? void 0 : _a.scope;
|
61
|
+
let req = typing.processor.fromPartial(converted);
|
62
|
+
// convert enum strings to integers
|
63
|
+
req = (0, utils_1.convertEnumToInt)(typing, req);
|
64
|
+
if (subjectField !== null) {
|
65
|
+
req.subject = (0, registry_1.getTyping)(types_1.authSubjectType).processor.fromPartial({});
|
66
|
+
const authToken = context.request.req.headers['authorization'];
|
67
|
+
if (authToken && authToken.startsWith('Bearer ')) {
|
68
|
+
req.subject.token = authToken.split(' ')[1];
|
69
|
+
}
|
70
|
+
if (scope) {
|
71
|
+
req.subject.scope = scope;
|
72
|
+
}
|
73
|
+
}
|
74
|
+
let realMethod = method.name;
|
75
|
+
if (graphql_1.Mutate.indexOf(method.name) > -1) {
|
76
|
+
const mode = (_b = args === null || args === void 0 ? void 0 : args.input) === null || _b === void 0 ? void 0 : _b.mode;
|
77
|
+
if (!mode) {
|
78
|
+
throw new Error('Please specify mode');
|
79
|
+
}
|
80
|
+
if (mode === 'CREATE') {
|
81
|
+
realMethod = 'Create';
|
82
|
+
}
|
83
|
+
else if (mode === 'UPDATE') {
|
84
|
+
realMethod = 'Update';
|
85
|
+
}
|
86
|
+
else if (mode === 'UPSERT') {
|
87
|
+
realMethod = 'Upsert';
|
88
|
+
}
|
89
|
+
}
|
90
|
+
const methodFunc = service[(0, utils_1.camelCase)(realMethod)] || service[realMethod];
|
91
|
+
const rawResult = await methodFunc(req);
|
92
|
+
const result = (0, graphql_1.postProcessGQLValue)(rawResult, outputTyping.output);
|
93
|
+
const grpcClientConfig = cfg.client;
|
94
|
+
const bufferFields = (0, utils_1.getKeys)(grpcClientConfig === null || grpcClientConfig === void 0 ? void 0 : grpcClientConfig.bufferFields);
|
95
|
+
if (result instanceof stream.Readable) {
|
96
|
+
let operationStatus = { code: 0, message: '' };
|
97
|
+
let aggregatedResponse = await new Promise((resolve, reject) => {
|
98
|
+
let response = {};
|
99
|
+
let combinedChunks = {};
|
100
|
+
result.on('data', (chunk) => {
|
101
|
+
const chunkObj = _.cloneDeep(chunk);
|
102
|
+
if (!combinedChunks) {
|
103
|
+
combinedChunks = chunk;
|
104
|
+
}
|
105
|
+
else {
|
106
|
+
Object.assign(combinedChunks, chunk);
|
107
|
+
}
|
108
|
+
const existingBufferFields = _.intersection(Object.keys(chunk), bufferFields);
|
109
|
+
for (let bufferField of existingBufferFields) {
|
110
|
+
if (chunkObj[bufferField] && chunkObj[bufferField].value) {
|
111
|
+
if (!response[bufferField]) {
|
112
|
+
response[bufferField] = { value: [] };
|
113
|
+
}
|
114
|
+
if (response[bufferField] && response[bufferField].value && !_.isArray(response[bufferField].value)) {
|
115
|
+
response[bufferField].value = [];
|
116
|
+
}
|
117
|
+
let data = JSON.parse(chunkObj[bufferField].value.toString());
|
118
|
+
if (_.isArray(data)) {
|
119
|
+
for (let dataObj of data) {
|
120
|
+
response[bufferField].value.push(dataObj);
|
121
|
+
}
|
122
|
+
}
|
123
|
+
else {
|
124
|
+
response[bufferField].value.push(data);
|
125
|
+
}
|
126
|
+
}
|
127
|
+
}
|
128
|
+
});
|
129
|
+
result.on('error', (err) => {
|
130
|
+
console.error(err);
|
131
|
+
operationStatus.code = err.code ? err.code : 500;
|
132
|
+
operationStatus.message = err.message;
|
133
|
+
});
|
134
|
+
result.on('end', () => {
|
135
|
+
if (_.isEmpty(operationStatus.message)) {
|
136
|
+
operationStatus.code = 200;
|
137
|
+
operationStatus.message = 'success';
|
138
|
+
}
|
139
|
+
if (!_.isEmpty(response)) {
|
140
|
+
resolve(response);
|
141
|
+
}
|
142
|
+
else if (!_.isEmpty(combinedChunks)) {
|
143
|
+
resolve(combinedChunks);
|
144
|
+
}
|
145
|
+
});
|
146
|
+
});
|
147
|
+
return { details: aggregatedResponse, operationStatus };
|
148
|
+
}
|
149
|
+
if ('items' in result) {
|
150
|
+
let items = (0, utils_1.decodeBufferFields)(result.items, bufferFields);
|
151
|
+
return {
|
152
|
+
details: {
|
153
|
+
items: items,
|
154
|
+
operationStatus: result.operationStatus // overall status
|
155
|
+
},
|
156
|
+
};
|
157
|
+
}
|
158
|
+
else {
|
159
|
+
return {
|
160
|
+
details: (0, utils_1.decodeBufferFields)([result], bufferFields)[0]
|
161
|
+
};
|
162
|
+
}
|
163
|
+
}
|
164
|
+
catch (error) {
|
165
|
+
console.error(error);
|
166
|
+
return {
|
167
|
+
details: {
|
168
|
+
items: [],
|
169
|
+
operationStatus: {
|
170
|
+
code: error.code,
|
171
|
+
message: error.message
|
172
|
+
}
|
173
|
+
}
|
174
|
+
};
|
175
|
+
}
|
176
|
+
};
|
177
|
+
return obj;
|
178
|
+
}, {});
|
179
|
+
};
|
180
|
+
exports.getGQLResolverFunctions = getGQLResolverFunctions;
|
181
|
+
const namespaceResolverRegistry = new Map();
|
182
|
+
const subscriptionResolvers = {};
|
183
|
+
const registerResolverFunction = (namespace, name, func, mutation = false, subspace = undefined, service) => {
|
184
|
+
if (!namespaceResolverRegistry.has(namespace)) {
|
185
|
+
namespaceResolverRegistry.set(namespace, new Map());
|
186
|
+
}
|
187
|
+
if (!namespaceResolverRegistry.get(namespace).has(mutation)) {
|
188
|
+
namespaceResolverRegistry.get(namespace).set(mutation, new Map());
|
189
|
+
}
|
190
|
+
let space = namespaceResolverRegistry.get(namespace).get(mutation);
|
191
|
+
if (subspace) {
|
192
|
+
if (!space.has(subspace)) {
|
193
|
+
space.set(subspace, new Map());
|
194
|
+
}
|
195
|
+
space = space.get(subspace);
|
196
|
+
}
|
197
|
+
if (space.has(name)) {
|
198
|
+
if (subspace) {
|
199
|
+
throw new Error(`Namespace "${namespace}"."${subspace}" already contains a function: ${name} (mutation: ${mutation})`);
|
200
|
+
}
|
201
|
+
else {
|
202
|
+
throw new Error(`Namespace "${namespace}" already contains a function: ${name} (mutation: ${mutation})`);
|
203
|
+
}
|
204
|
+
}
|
205
|
+
if (service) {
|
206
|
+
const key = (namespace === null || namespace === void 0 ? void 0 : namespace.toLocaleLowerCase()) + '.' + (subspace === null || subspace === void 0 ? void 0 : subspace.toLocaleLowerCase()) + '.' + (name === null || name === void 0 ? void 0 : name.toLocaleLowerCase());
|
207
|
+
const value = service.method.find((m) => m.name === name);
|
208
|
+
if (key && (value === null || value === void 0 ? void 0 : value.inputType)) {
|
209
|
+
inputMethodType.set(key, value.inputType);
|
210
|
+
}
|
211
|
+
}
|
212
|
+
space.set(name, func);
|
213
|
+
};
|
214
|
+
exports.registerResolverFunction = registerResolverFunction;
|
215
|
+
const generateResolver = (...namespaces) => {
|
216
|
+
const queryResolvers = {};
|
217
|
+
const mutationResolvers = {};
|
218
|
+
namespaces.forEach(ns => {
|
219
|
+
if (!namespaceResolverRegistry.has(ns)) {
|
220
|
+
throw new Error(`Namespace "${ns}" has no registered functions`);
|
221
|
+
}
|
222
|
+
if (namespaceResolverRegistry.get(ns).has(false)) {
|
223
|
+
const res = {};
|
224
|
+
namespaceResolverRegistry.get(ns).get(false).forEach((value, key) => {
|
225
|
+
if (value instanceof Map) {
|
226
|
+
res[key] = Object.fromEntries(value);
|
227
|
+
}
|
228
|
+
else {
|
229
|
+
res[key] = value;
|
230
|
+
}
|
231
|
+
});
|
232
|
+
queryResolvers[ns] = () => res;
|
233
|
+
}
|
234
|
+
if (namespaceResolverRegistry.get(ns).has(true)) {
|
235
|
+
const res = {};
|
236
|
+
namespaceResolverRegistry.get(ns).get(true).forEach((value, key) => {
|
237
|
+
if (value instanceof Map) {
|
238
|
+
res[key] = Object.fromEntries(value);
|
239
|
+
}
|
240
|
+
else {
|
241
|
+
res[key] = value;
|
242
|
+
}
|
243
|
+
});
|
244
|
+
mutationResolvers[ns] = () => res;
|
245
|
+
}
|
246
|
+
});
|
247
|
+
const resolvers = {};
|
248
|
+
if (Object.keys(queryResolvers).length > 0) {
|
249
|
+
resolvers.Query = queryResolvers;
|
250
|
+
}
|
251
|
+
if (Object.keys(mutationResolvers).length > 0) {
|
252
|
+
resolvers.Mutation = mutationResolvers;
|
253
|
+
}
|
254
|
+
if (Object.keys(subscriptionResolvers).length > 0) {
|
255
|
+
resolvers.Subscription = subscriptionResolvers;
|
256
|
+
}
|
257
|
+
return resolvers;
|
258
|
+
};
|
259
|
+
exports.generateResolver = generateResolver;
|
260
|
+
const generateSubServiceResolvers = (subServices, config, namespace) => {
|
261
|
+
subServices.forEach((meta) => {
|
262
|
+
var _a;
|
263
|
+
meta.fileDescriptor.service.forEach(service => {
|
264
|
+
if (meta.options && meta.options.services && meta.options.services[service.name]) {
|
265
|
+
const subName = meta.options.services[service.name].options['service_name'];
|
266
|
+
const { mutations, queries } = (0, graphql_1.getWhitelistBlacklistConfig)(service, config, meta, subName);
|
267
|
+
const func = (0, exports.getGQLResolverFunctions)(service, namespace, subName || namespace, config);
|
268
|
+
Object.keys(func).forEach(k => {
|
269
|
+
const regNamespace = config.root ? subName : namespace;
|
270
|
+
const regSubspace = config.root ? undefined : subName;
|
271
|
+
(0, exports.registerResolverFunction)(regNamespace, k, func[k], !queries.has(k) && mutations.has(k), regSubspace, service);
|
272
|
+
});
|
273
|
+
}
|
274
|
+
});
|
275
|
+
if (utils_1.useSubscriptions) {
|
276
|
+
Object.entries(((_a = meta.options) === null || _a === void 0 ? void 0 : _a.messages) || {}).forEach(([messageName, option]) => {
|
277
|
+
if (option.options && 'kafka_subscriber' in option.options) {
|
278
|
+
const kafkaSubscriber = option.options.kafka_subscriber;
|
279
|
+
const fieldName = namespace + (0, utils_1.capitalize)(kafkaSubscriber.plural);
|
280
|
+
const baseMessageName = meta.fileDescriptor.package + '.' + messageName;
|
281
|
+
const typing = (0, registry_1.getTyping)('.' + baseMessageName);
|
282
|
+
if (typing) {
|
283
|
+
subscriptionResolvers[fieldName] = {
|
284
|
+
// resolve: (parent, args, context, info) => {
|
285
|
+
// const obj = parent[fieldName];
|
286
|
+
// let missing = false;
|
287
|
+
// for (const selection of info.operation.selectionSet.selections) {
|
288
|
+
// if ('name' in selection && !(selection.name.value in obj)) {
|
289
|
+
// missing = true;
|
290
|
+
// break;
|
291
|
+
// }
|
292
|
+
// }
|
293
|
+
// if (!missing) {
|
294
|
+
// return obj;
|
295
|
+
// }
|
296
|
+
// // TODO Implement user lookup
|
297
|
+
// return obj;
|
298
|
+
// },
|
299
|
+
subscribe: async (parent, args, context, info) => {
|
300
|
+
const action = args.action || 'CREATED';
|
301
|
+
let event = kafkaSubscriber.created;
|
302
|
+
switch (action) {
|
303
|
+
case 'UPDATED':
|
304
|
+
event = kafkaSubscriber.updated;
|
305
|
+
break;
|
306
|
+
case 'DELETED':
|
307
|
+
event = kafkaSubscriber.deleted;
|
308
|
+
break;
|
309
|
+
}
|
310
|
+
const events = new kafka_client_1.Events({
|
311
|
+
provider: 'kafka',
|
312
|
+
kafka: {
|
313
|
+
...context.kafkaConfig
|
314
|
+
},
|
315
|
+
[event]: {
|
316
|
+
messageObject: baseMessageName
|
317
|
+
}
|
318
|
+
}, context.logger);
|
319
|
+
await events.start();
|
320
|
+
const commandTopic = await events.topic(kafkaSubscriber.topic);
|
321
|
+
let deferred = null;
|
322
|
+
const pending = [];
|
323
|
+
commandTopic.on(event, (message) => {
|
324
|
+
pending.push({ id: message.id });
|
325
|
+
deferred === null || deferred === void 0 ? void 0 : deferred.resolve(false);
|
326
|
+
});
|
327
|
+
return {
|
328
|
+
[Symbol.asyncIterator]() {
|
329
|
+
return this;
|
330
|
+
},
|
331
|
+
async next() {
|
332
|
+
if (pending.length) {
|
333
|
+
return { value: { [fieldName]: pending.shift() } };
|
334
|
+
}
|
335
|
+
return (await new Promise((resolve, reject) => (deferred = { resolve, reject })))
|
336
|
+
? { done: true, value: undefined }
|
337
|
+
: { value: { [fieldName]: pending.shift() } };
|
338
|
+
},
|
339
|
+
async throw(err) {
|
340
|
+
throw err;
|
341
|
+
},
|
342
|
+
async return() {
|
343
|
+
await events.stop();
|
344
|
+
return { done: true, value: undefined };
|
345
|
+
},
|
346
|
+
};
|
347
|
+
}
|
348
|
+
};
|
349
|
+
}
|
350
|
+
}
|
351
|
+
});
|
352
|
+
}
|
353
|
+
});
|
354
|
+
if (config.root) {
|
355
|
+
return (0, exports.generateResolver)(...(0, array_prototype_flat_1.default)(subServices.map(meta => {
|
356
|
+
return meta.fileDescriptor.service.map(service => {
|
357
|
+
if (meta.options && meta.options.services && meta.options.services[service.name]) {
|
358
|
+
return meta.options.services[service.name].options['service_name'];
|
359
|
+
}
|
360
|
+
return null;
|
361
|
+
}).filter(s => s !== null);
|
362
|
+
})));
|
363
|
+
}
|
364
|
+
const finalResolver = (0, exports.generateResolver)(namespace);
|
365
|
+
subServices.forEach((meta) => {
|
366
|
+
meta.fileDescriptor.service.forEach(service => {
|
367
|
+
if (meta.options && meta.options.messages) {
|
368
|
+
for (const key of Object.keys(meta.options.messages)) {
|
369
|
+
const message = meta.options.messages[key];
|
370
|
+
if (message.fields) {
|
371
|
+
const typing = (0, registry_1.getTyping)('.' + meta.fileDescriptor.package + '.' + key);
|
372
|
+
if (typing) {
|
373
|
+
const result = {};
|
374
|
+
for (const fieldName of Object.keys(message.fields)) {
|
375
|
+
const field = message.fields[fieldName];
|
376
|
+
if ('resolver' in field) {
|
377
|
+
const fieldJsonName = (0, utils_1.snakeToCamel)(fieldName);
|
378
|
+
const resolver = field['resolver'];
|
379
|
+
// TODO This creates an N+1 problem!
|
380
|
+
result[resolver.fieldName] = async (parent, _, ctx) => {
|
381
|
+
if (!parent || !(fieldJsonName in parent) || parent[fieldJsonName] === undefined) {
|
382
|
+
return undefined;
|
383
|
+
}
|
384
|
+
const client = ctx[resolver.targetService].client;
|
385
|
+
const service = client[resolver.targetSubService];
|
386
|
+
const idList = Array.isArray(parent[fieldJsonName]) ? parent[fieldJsonName] : [parent[fieldJsonName]];
|
387
|
+
// TODO Support custom input messages
|
388
|
+
const req = rc_grpc_clients_1.ReadRequest.fromPartial({
|
389
|
+
filters: [{
|
390
|
+
filter: idList.map(id => ({
|
391
|
+
field: 'id',
|
392
|
+
operation: resource_base_1.Filter_Operation.eq,
|
393
|
+
value: id,
|
394
|
+
type: resource_base_1.Filter_ValueType.STRING
|
395
|
+
})),
|
396
|
+
operator: resource_base_1.FilterOp_Operator.or
|
397
|
+
}]
|
398
|
+
});
|
399
|
+
req.subject = (0, registry_1.getTyping)(types_1.authSubjectType).processor.fromPartial({});
|
400
|
+
const authToken = ctx.request.req.headers['authorization'];
|
401
|
+
if (authToken && authToken.startsWith('Bearer ')) {
|
402
|
+
req.subject.token = authToken.split(' ')[1];
|
403
|
+
}
|
404
|
+
const methodFunc = service[(0, utils_1.camelCase)(resolver.targetMethod)] || service[resolver.targetMethod];
|
405
|
+
const result = await methodFunc(req);
|
406
|
+
if (result && result.items && result.items.length) {
|
407
|
+
if (Array.isArray(parent[fieldJsonName])) {
|
408
|
+
return result.items.map((item) => item.payload);
|
409
|
+
}
|
410
|
+
else {
|
411
|
+
return result.items[0].payload;
|
412
|
+
}
|
413
|
+
}
|
414
|
+
return undefined;
|
415
|
+
};
|
416
|
+
}
|
417
|
+
}
|
418
|
+
finalResolver[typing.output.name] = result;
|
419
|
+
}
|
420
|
+
}
|
421
|
+
}
|
422
|
+
}
|
423
|
+
});
|
424
|
+
});
|
425
|
+
return finalResolver;
|
426
|
+
};
|
427
|
+
exports.generateSubServiceResolvers = generateSubServiceResolvers;
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import { MethodDescriptorProto, ServiceDescriptorProto } from 'ts-proto-descriptors';
|
2
|
+
import { ProtoMetadata, ServiceConfig, SubSpaceServiceConfig } from './types';
|
3
|
+
import { GraphQLSchema, ThunkObjMap } from 'graphql';
|
4
|
+
import { GraphQLFieldConfig, GraphQLFieldConfigMap } from 'graphql/type/definition';
|
5
|
+
export declare const getGQLSchema: <TSource, TContext>(method: MethodDescriptorProto) => GraphQLFieldConfig<TSource, TContext, any>;
|
6
|
+
export declare const getGQLSchemas: <TSource, TContext>(service: ServiceDescriptorProto) => GraphQLFieldConfigMap<TSource, TContext>;
|
7
|
+
declare type SchemaBaseOrSub = ThunkObjMap<GraphQLFieldConfig<any, any>> | Map<string, ThunkObjMap<GraphQLFieldConfig<any, any>>>;
|
8
|
+
export declare const registerResolverSchema: (namespace: string, name: string, schema: SchemaBaseOrSub, mutation: boolean | undefined, subspace: string | undefined, config: ServiceConfig) => void;
|
9
|
+
export declare const generateSchema: (setup: {
|
10
|
+
prefix: string;
|
11
|
+
namespace: string;
|
12
|
+
}[]) => GraphQLSchema;
|
13
|
+
export declare const generateSubServiceSchemas: (subServices: ProtoMetadata[], config: SubSpaceServiceConfig, namespace: string, prefix: string) => GraphQLSchema;
|
14
|
+
export {};
|
@@ -0,0 +1,234 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.generateSubServiceSchemas = exports.generateSchema = exports.registerResolverSchema = exports.getGQLSchemas = exports.getGQLSchema = void 0;
|
4
|
+
const graphql_1 = require("graphql");
|
5
|
+
const array_prototype_flat_1 = require("array.prototype.flat");
|
6
|
+
const graphql_2 = require("./graphql");
|
7
|
+
const utils_1 = require("./utils");
|
8
|
+
const registry_1 = require("./registry");
|
9
|
+
const typeCache = new Map();
|
10
|
+
const subscriptionAction = new graphql_1.GraphQLEnumType({
|
11
|
+
name: 'SubscriptionAction',
|
12
|
+
values: {
|
13
|
+
CREATED: { value: 'CREATED' },
|
14
|
+
UPDATED: { value: 'UPDATED' },
|
15
|
+
DELETED: { value: 'DELETED' }
|
16
|
+
}
|
17
|
+
});
|
18
|
+
const subscriptionInput = {
|
19
|
+
action: {
|
20
|
+
type: subscriptionAction
|
21
|
+
}
|
22
|
+
};
|
23
|
+
const subscriptionOutput = new graphql_1.GraphQLObjectType({
|
24
|
+
name: 'SubscriptionOutput',
|
25
|
+
fields: {
|
26
|
+
id: {
|
27
|
+
type: graphql_1.GraphQLString
|
28
|
+
}
|
29
|
+
}
|
30
|
+
});
|
31
|
+
const getGQLSchema = (method) => {
|
32
|
+
const fields = {};
|
33
|
+
const responseTyping = (0, registry_1.getTyping)(method.outputType);
|
34
|
+
if (!responseTyping) {
|
35
|
+
throw Error('Method doesn\'t have registered typings: ' + method.outputType);
|
36
|
+
}
|
37
|
+
if (method.outputType !== '.google.protobuf.Empty') {
|
38
|
+
fields['details'] = {
|
39
|
+
type: responseTyping.output,
|
40
|
+
};
|
41
|
+
}
|
42
|
+
const outName = 'Proto' + (0, utils_1.capitalizeProtoName)(method.outputType);
|
43
|
+
let out = typeCache.get(outName);
|
44
|
+
if (!out) {
|
45
|
+
out = new graphql_1.GraphQLObjectType({
|
46
|
+
name: outName,
|
47
|
+
fields,
|
48
|
+
});
|
49
|
+
typeCache.set(outName, out);
|
50
|
+
}
|
51
|
+
const typing = (0, registry_1.getTyping)(method.inputType);
|
52
|
+
if (!typing) {
|
53
|
+
throw Error('Method doesn\'t have registered typings: ' + method.inputType);
|
54
|
+
}
|
55
|
+
return {
|
56
|
+
type: out,
|
57
|
+
args: method.inputType === '.google.protobuf.Empty' ? undefined : {
|
58
|
+
input: {
|
59
|
+
type: new graphql_1.GraphQLNonNull(typing.input)
|
60
|
+
}
|
61
|
+
}
|
62
|
+
};
|
63
|
+
};
|
64
|
+
exports.getGQLSchema = getGQLSchema;
|
65
|
+
const getGQLSchemas = (service) => {
|
66
|
+
var _a;
|
67
|
+
return (_a = service.method) === null || _a === void 0 ? void 0 : _a.reduce((obj, method) => {
|
68
|
+
obj[method.name] = (0, exports.getGQLSchema)(method);
|
69
|
+
return obj;
|
70
|
+
}, {});
|
71
|
+
};
|
72
|
+
exports.getGQLSchemas = getGQLSchemas;
|
73
|
+
const namespaceResolverSchemaRegistry = new Map();
|
74
|
+
const subscriptionFields = {};
|
75
|
+
const registerResolverSchema = (namespace, name, schema, mutation = false, subspace = undefined, config) => {
|
76
|
+
if (!namespaceResolverSchemaRegistry.has(namespace)) {
|
77
|
+
namespaceResolverSchemaRegistry.set(namespace, new Map());
|
78
|
+
}
|
79
|
+
if (subspace && config[subspace]) {
|
80
|
+
const blacklistMethods = config[subspace].methods.blacklist;
|
81
|
+
if (blacklistMethods.includes(name)) {
|
82
|
+
return;
|
83
|
+
}
|
84
|
+
}
|
85
|
+
if (!namespaceResolverSchemaRegistry.get(namespace).has(mutation)) {
|
86
|
+
namespaceResolverSchemaRegistry.get(namespace).set(mutation, new Map());
|
87
|
+
}
|
88
|
+
let space = namespaceResolverSchemaRegistry.get(namespace).get(mutation);
|
89
|
+
if (subspace) {
|
90
|
+
if (!space.has(subspace)) {
|
91
|
+
space.set(subspace, new Map());
|
92
|
+
}
|
93
|
+
space = space.get(subspace);
|
94
|
+
}
|
95
|
+
if (space.has(name)) {
|
96
|
+
if (subspace) {
|
97
|
+
throw new Error(`Namespace "${namespace}"."${subspace}" already contains a schema: ${name} (mutation: ${mutation})`);
|
98
|
+
}
|
99
|
+
else {
|
100
|
+
throw new Error(`Namespace "${namespace}" already contains a schema: ${name} (mutation: ${mutation})`);
|
101
|
+
}
|
102
|
+
}
|
103
|
+
// register create, update and upsert with Mutate
|
104
|
+
if (graphql_2.Mutate.indexOf(name) > -1) {
|
105
|
+
name = 'Mutate';
|
106
|
+
}
|
107
|
+
space.set(name, schema);
|
108
|
+
};
|
109
|
+
exports.registerResolverSchema = registerResolverSchema;
|
110
|
+
const generateSchema = (setup) => {
|
111
|
+
const queryFields = {};
|
112
|
+
const mutationFields = {};
|
113
|
+
setup.forEach(s => {
|
114
|
+
if (!namespaceResolverSchemaRegistry.has(s.namespace)) {
|
115
|
+
throw new Error(`Namespace "${s.namespace}" has no registered schemas`);
|
116
|
+
}
|
117
|
+
if (namespaceResolverSchemaRegistry.get(s.namespace).has(false)) {
|
118
|
+
const fields = {};
|
119
|
+
namespaceResolverSchemaRegistry.get(s.namespace).get(false).forEach((value, key) => {
|
120
|
+
if (value instanceof Map) {
|
121
|
+
const capitalName = (0, utils_1.capitalizeProtoName)(key);
|
122
|
+
fields[key] = {
|
123
|
+
type: new graphql_1.GraphQLNonNull(new graphql_1.GraphQLObjectType({
|
124
|
+
name: s.prefix + capitalName + 'Query',
|
125
|
+
fields: Object.fromEntries(value),
|
126
|
+
}))
|
127
|
+
};
|
128
|
+
}
|
129
|
+
else {
|
130
|
+
fields[key] = value;
|
131
|
+
}
|
132
|
+
});
|
133
|
+
queryFields[s.namespace] = {
|
134
|
+
type: new graphql_1.GraphQLNonNull(new graphql_1.GraphQLObjectType({
|
135
|
+
name: s.prefix + 'Query',
|
136
|
+
fields
|
137
|
+
}))
|
138
|
+
};
|
139
|
+
}
|
140
|
+
if (namespaceResolverSchemaRegistry.get(s.namespace).has(true)) {
|
141
|
+
const fields = {};
|
142
|
+
namespaceResolverSchemaRegistry.get(s.namespace).get(true).forEach((value, key) => {
|
143
|
+
if (value instanceof Map) {
|
144
|
+
const capitalName = (0, utils_1.capitalizeProtoName)(key);
|
145
|
+
fields[key] = {
|
146
|
+
type: new graphql_1.GraphQLNonNull(new graphql_1.GraphQLObjectType({
|
147
|
+
name: s.prefix + capitalName + 'Mutation',
|
148
|
+
fields: Object.fromEntries(value),
|
149
|
+
}))
|
150
|
+
};
|
151
|
+
}
|
152
|
+
else {
|
153
|
+
fields[key] = value;
|
154
|
+
}
|
155
|
+
});
|
156
|
+
mutationFields[s.namespace] = {
|
157
|
+
type: new graphql_1.GraphQLNonNull(new graphql_1.GraphQLObjectType({
|
158
|
+
name: s.prefix + 'Mutation',
|
159
|
+
fields
|
160
|
+
}))
|
161
|
+
};
|
162
|
+
}
|
163
|
+
});
|
164
|
+
const config = {};
|
165
|
+
if (Object.keys(queryFields).length > 0) {
|
166
|
+
config.query = new graphql_1.GraphQLObjectType({
|
167
|
+
name: 'Query',
|
168
|
+
fields: queryFields
|
169
|
+
});
|
170
|
+
}
|
171
|
+
if (Object.keys(mutationFields).length > 0) {
|
172
|
+
config.mutation = new graphql_1.GraphQLObjectType({
|
173
|
+
name: 'Mutation',
|
174
|
+
fields: mutationFields
|
175
|
+
});
|
176
|
+
}
|
177
|
+
if (Object.keys(subscriptionFields).length > 0) {
|
178
|
+
config.subscription = new graphql_1.GraphQLObjectType({
|
179
|
+
name: 'Subscription',
|
180
|
+
fields: subscriptionFields
|
181
|
+
});
|
182
|
+
}
|
183
|
+
return new graphql_1.GraphQLSchema({
|
184
|
+
...config,
|
185
|
+
});
|
186
|
+
};
|
187
|
+
exports.generateSchema = generateSchema;
|
188
|
+
const generateSubServiceSchemas = (subServices, config, namespace, prefix) => {
|
189
|
+
subServices.forEach((meta) => {
|
190
|
+
var _a;
|
191
|
+
meta.fileDescriptor.service.forEach(service => {
|
192
|
+
if (meta.options && meta.options.services && meta.options.services[service.name]) {
|
193
|
+
const subName = meta.options.services[service.name].options['service_name'];
|
194
|
+
const { mutations, queries } = (0, graphql_2.getWhitelistBlacklistConfig)(service, config, meta, subName);
|
195
|
+
const schemas = (0, exports.getGQLSchemas)(service);
|
196
|
+
Object.keys(schemas).forEach(key => {
|
197
|
+
(0, exports.registerResolverSchema)(config.root ? subName : namespace, key, schemas[key], !queries.has(key) && mutations.has(key), config.root ? undefined : subName, config);
|
198
|
+
});
|
199
|
+
}
|
200
|
+
});
|
201
|
+
if (utils_1.useSubscriptions) {
|
202
|
+
Object.entries(((_a = meta.options) === null || _a === void 0 ? void 0 : _a.messages) || {}).forEach(([messageName, option]) => {
|
203
|
+
if (option.options && 'kafka_subscriber' in option.options) {
|
204
|
+
const fieldName = namespace + (0, utils_1.capitalize)(option.options.kafka_subscriber.plural);
|
205
|
+
const typing = (0, registry_1.getTyping)('.' + meta.fileDescriptor.package + '.' + messageName);
|
206
|
+
if (typing) {
|
207
|
+
subscriptionFields[fieldName] = {
|
208
|
+
// TODO Implement user lookup
|
209
|
+
// type: typing?.output!,
|
210
|
+
type: subscriptionOutput,
|
211
|
+
args: subscriptionInput
|
212
|
+
};
|
213
|
+
}
|
214
|
+
}
|
215
|
+
});
|
216
|
+
}
|
217
|
+
});
|
218
|
+
if (config.root) {
|
219
|
+
return (0, exports.generateSchema)((0, array_prototype_flat_1.default)(subServices.map(meta => {
|
220
|
+
return meta.fileDescriptor.service.map(service => {
|
221
|
+
if (meta.options && meta.options.services && meta.options.services[service.name]) {
|
222
|
+
const subName = meta.options.services[service.name].options['service_name'];
|
223
|
+
return {
|
224
|
+
prefix: prefix + subName.substring(0, 1).toUpperCase() + subName.substring(1).toLowerCase(),
|
225
|
+
namespace: subName
|
226
|
+
};
|
227
|
+
}
|
228
|
+
return null;
|
229
|
+
}).filter(s => s !== null);
|
230
|
+
})));
|
231
|
+
}
|
232
|
+
return (0, exports.generateSchema)([{ prefix, namespace }]);
|
233
|
+
};
|
234
|
+
exports.generateSubServiceSchemas = generateSubServiceSchemas;
|