@restorecommerce/facade 1.0.0 → 1.1.0

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.
Files changed (43) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/codegen/index.js +1 -1
  3. package/dist/gql/protos/federation.d.ts +6 -0
  4. package/dist/gql/protos/federation.js +51 -0
  5. package/dist/gql/protos/graphql.d.ts +5 -27
  6. package/dist/gql/protos/graphql.js +3 -564
  7. package/dist/gql/protos/index.d.ts +3 -0
  8. package/dist/gql/protos/index.js +3 -0
  9. package/dist/gql/protos/registry.d.ts +2 -2
  10. package/dist/gql/protos/resolvers.d.ts +9 -0
  11. package/dist/gql/protos/resolvers.js +427 -0
  12. package/dist/gql/protos/schema.d.ts +14 -0
  13. package/dist/gql/protos/schema.js +234 -0
  14. package/dist/gql/protos/types.d.ts +20 -0
  15. package/dist/gql/protos/utils.d.ts +5 -0
  16. package/dist/gql/protos/utils.js +27 -1
  17. package/dist/index.d.ts +9 -9
  18. package/dist/index.js +78 -7
  19. package/dist/interfaces.d.ts +2 -0
  20. package/dist/modules/access-control/gql/schema.generated.d.ts +10 -1
  21. package/dist/modules/catalog/gql/schema.generated.d.ts +10 -1
  22. package/dist/modules/fulfillment/gql/schema.generated.d.ts +10 -1
  23. package/dist/modules/identity/gql/federation.d.ts +1 -1
  24. package/dist/modules/identity/gql/federation.js +1 -9
  25. package/dist/modules/identity/gql/schema.generated.d.ts +40 -1
  26. package/dist/modules/identity/gql/schema.generated.js +7 -1
  27. package/dist/modules/identity/gql/types.js +2 -0
  28. package/dist/modules/indexing/gql/federation.d.ts +2 -2
  29. package/dist/modules/indexing/gql/federation.js +2 -2
  30. package/dist/modules/indexing/gql/schema.d.ts +2 -2
  31. package/dist/modules/indexing/gql/schema.generated.d.ts +13 -2
  32. package/dist/modules/indexing/gql/schema.js +1 -2
  33. package/dist/modules/indexing/gql/types.d.ts +2 -0
  34. package/dist/modules/indexing/gql/types.js +5 -2
  35. package/dist/modules/indexing/interfaces.d.ts +2 -2
  36. package/dist/modules/invoicing/gql/schema.generated.d.ts +10 -1
  37. package/dist/modules/notification/gql/schema.generated.d.ts +10 -1
  38. package/dist/modules/ordering/gql/federation.js +4 -4
  39. package/dist/modules/ordering/gql/schema.generated.d.ts +10 -1
  40. package/dist/modules/resource/gql/schema.generated.d.ts +10 -1
  41. package/dist/modules/scheduling/gql/schema.generated.d.ts +5 -3
  42. package/generate.ts +6 -3
  43. 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;