@strapi/plugin-graphql 4.0.0-next.9 → 4.0.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 (122) hide show
  1. package/README.md +1 -1
  2. package/admin/src/index.js +0 -8
  3. package/package.json +39 -34
  4. package/server/bootstrap.js +148 -0
  5. package/server/config/default-config.js +13 -0
  6. package/server/config/index.js +7 -0
  7. package/server/format-graphql-error.js +50 -0
  8. package/server/services/builders/dynamic-zones.js +97 -0
  9. package/server/services/builders/entity-meta.js +7 -0
  10. package/server/services/builders/entity.js +43 -0
  11. package/server/services/builders/enums.js +24 -0
  12. package/server/services/builders/filters/content-type.js +93 -0
  13. package/server/services/builders/filters/index.js +7 -0
  14. package/server/services/builders/filters/operators/and.js +15 -0
  15. package/server/services/builders/filters/operators/between.js +15 -0
  16. package/server/services/builders/filters/operators/contains.js +13 -0
  17. package/server/services/builders/filters/operators/containsi.js +13 -0
  18. package/server/services/builders/filters/operators/ends-with.js +13 -0
  19. package/server/services/builders/filters/operators/eq.js +23 -0
  20. package/server/services/builders/filters/operators/gt.js +13 -0
  21. package/server/services/builders/filters/operators/gte.js +13 -0
  22. package/server/services/builders/filters/operators/in.js +15 -0
  23. package/server/services/builders/filters/operators/index.js +38 -0
  24. package/server/services/builders/filters/operators/lt.js +13 -0
  25. package/server/services/builders/filters/operators/lte.js +13 -0
  26. package/server/services/builders/filters/operators/ne.js +13 -0
  27. package/server/services/builders/filters/operators/not-contains.js +13 -0
  28. package/server/services/builders/filters/operators/not-containsi.js +13 -0
  29. package/server/services/builders/filters/operators/not-in.js +15 -0
  30. package/server/services/builders/filters/operators/not-null.js +13 -0
  31. package/server/services/builders/filters/operators/not.js +19 -0
  32. package/server/services/builders/filters/operators/null.js +13 -0
  33. package/server/services/builders/filters/operators/or.js +15 -0
  34. package/server/services/builders/filters/operators/starts-with.js +13 -0
  35. package/server/services/builders/generic-morph.js +41 -0
  36. package/server/services/builders/index.js +92 -0
  37. package/server/services/builders/input.js +121 -0
  38. package/server/services/builders/mutations/collection-type.js +191 -0
  39. package/server/services/builders/mutations/index.js +9 -0
  40. package/server/services/builders/mutations/single-type.js +141 -0
  41. package/server/services/builders/queries/collection-type.js +120 -0
  42. package/server/services/builders/queries/index.js +9 -0
  43. package/server/services/builders/queries/single-type.js +70 -0
  44. package/server/services/builders/relation-response-collection.js +35 -0
  45. package/server/services/builders/resolvers/association.js +85 -0
  46. package/server/services/builders/resolvers/component.js +18 -0
  47. package/server/services/builders/resolvers/dynamic-zone.js +9 -0
  48. package/server/services/builders/resolvers/index.js +18 -0
  49. package/server/services/builders/resolvers/mutation.js +33 -0
  50. package/server/services/builders/resolvers/query.js +19 -0
  51. package/server/services/builders/response-collection.js +43 -0
  52. package/server/services/builders/response.js +32 -0
  53. package/server/services/builders/type.js +364 -0
  54. package/server/services/builders/utils.js +134 -0
  55. package/server/services/constants.js +147 -0
  56. package/server/services/content-api/index.js +179 -0
  57. package/server/services/content-api/policy.js +60 -0
  58. package/server/services/content-api/register-functions/collection-type.js +72 -0
  59. package/server/services/content-api/register-functions/component.js +15 -0
  60. package/server/services/content-api/register-functions/content-type/dynamic-zones.js +36 -0
  61. package/server/services/content-api/register-functions/content-type/enums.js +33 -0
  62. package/server/services/content-api/register-functions/content-type/filters.js +15 -0
  63. package/server/services/content-api/register-functions/content-type/index.js +13 -0
  64. package/server/services/content-api/register-functions/content-type/inputs.js +21 -0
  65. package/server/services/content-api/register-functions/index.js +22 -0
  66. package/server/services/content-api/register-functions/internals.js +13 -0
  67. package/server/services/content-api/register-functions/polymorphic.js +69 -0
  68. package/server/services/content-api/register-functions/scalars.js +14 -0
  69. package/server/services/content-api/register-functions/single-type.js +72 -0
  70. package/server/services/content-api/wrap-resolvers.js +144 -0
  71. package/server/services/extension/extension.js +95 -0
  72. package/server/services/extension/index.js +5 -0
  73. package/server/services/extension/shadow-crud-manager.js +159 -0
  74. package/server/services/format/index.js +7 -0
  75. package/server/services/format/return-types.js +27 -0
  76. package/server/services/index.js +21 -0
  77. package/server/services/internals/args/index.js +11 -0
  78. package/server/services/internals/args/pagination.js +19 -0
  79. package/server/services/internals/args/publication-state.js +12 -0
  80. package/server/services/internals/args/sort.js +10 -0
  81. package/server/services/internals/helpers/get-enabled-scalars.js +15 -0
  82. package/server/services/internals/helpers/index.js +7 -0
  83. package/server/services/internals/index.js +13 -0
  84. package/server/services/internals/scalars/index.js +18 -0
  85. package/server/services/internals/scalars/time.js +36 -0
  86. package/server/services/internals/types/error.js +34 -0
  87. package/server/services/internals/types/filters.js +39 -0
  88. package/server/services/internals/types/index.js +29 -0
  89. package/server/services/internals/types/pagination.js +24 -0
  90. package/server/services/internals/types/publication-state.js +24 -0
  91. package/server/services/internals/types/response-collection-meta.js +38 -0
  92. package/server/services/type-registry.js +104 -0
  93. package/server/services/utils/attributes.js +84 -0
  94. package/server/services/utils/index.js +11 -0
  95. package/server/services/utils/mappers/entity-to-response-entity.js +12 -0
  96. package/server/services/utils/mappers/graphql-filters-to-strapi-query.js +109 -0
  97. package/server/services/utils/mappers/graphql-scalar-to-operators.js +17 -0
  98. package/server/services/utils/mappers/index.js +13 -0
  99. package/server/services/utils/mappers/strapi-scalar-to-graphql-scalar.js +25 -0
  100. package/server/services/utils/naming.js +287 -0
  101. package/strapi-admin.js +3 -0
  102. package/strapi-server.js +7 -9
  103. package/admin/src/assets/images/logo.svg +0 -38
  104. package/config/routes.json +0 -3
  105. package/config/schema.graphql +0 -1
  106. package/config/settings.json +0 -12
  107. package/controllers/GraphQL.js +0 -9
  108. package/hooks/graphql/defaults.json +0 -5
  109. package/hooks/graphql/index.js +0 -174
  110. package/hooks/graphql/load-config.js +0 -42
  111. package/services/build-aggregation.js +0 -565
  112. package/services/data-loaders.js +0 -55
  113. package/services/naming.js +0 -15
  114. package/services/resolvers-builder.js +0 -204
  115. package/services/schema-definitions.js +0 -131
  116. package/services/schema-generator.js +0 -178
  117. package/services/shadow-crud.js +0 -612
  118. package/services/type-builder.js +0 -311
  119. package/services/utils.js +0 -200
  120. package/types/dynamiczoneScalar.js +0 -40
  121. package/types/publication-state.js +0 -16
  122. package/types/time.js +0 -26
@@ -1,612 +0,0 @@
1
- 'use strict';
2
-
3
- const _ = require('lodash');
4
- const { contentTypes } = require('@strapi/utils');
5
-
6
- const {
7
- hasDraftAndPublish,
8
- constants: { DP_PUB_STATE_LIVE },
9
- } = contentTypes;
10
-
11
- const DynamicZoneScalar = require('../types/dynamiczoneScalar');
12
-
13
- const { formatModelConnectionsGQL } = require('./build-aggregation');
14
- const types = require('./type-builder');
15
- const {
16
- actionExists,
17
- mergeSchemas,
18
- convertToParams,
19
- convertToQuery,
20
- amountLimiting,
21
- createDefaultSchema,
22
- } = require('./utils');
23
- const { toSDL, getTypeDescription } = require('./schema-definitions');
24
- const { toSingular, toPlural } = require('./naming');
25
- const { buildQuery, buildMutation } = require('./resolvers-builder');
26
-
27
- const OPTIONS = Symbol();
28
-
29
- const FIND_QUERY_ARGUMENTS = {
30
- sort: 'String',
31
- limit: 'Int',
32
- start: 'Int',
33
- where: 'JSON',
34
- publicationState: 'PublicationState',
35
- };
36
-
37
- const FIND_ONE_QUERY_ARGUMENTS = {
38
- id: 'ID!',
39
- publicationState: 'PublicationState',
40
- };
41
-
42
- /**
43
- * Builds a graphql schema from all the contentTypes & components loaded
44
- * @param {{ schema: object }} ctx
45
- * @returns {object}
46
- */
47
- const buildShadowCrud = ctx => {
48
- const models = Object.values(strapi.contentTypes).filter(model => model.plugin !== 'admin');
49
- const components = Object.values(strapi.components);
50
-
51
- const allSchemas = buildModels([...models, ...components], ctx);
52
-
53
- return mergeSchemas(createDefaultSchema(), ...allSchemas);
54
- };
55
-
56
- const assignOptions = (element, parent) => {
57
- if (Array.isArray(element)) {
58
- return element.map(el => assignOptions(el, parent));
59
- }
60
-
61
- return _.set(element, OPTIONS, _.get(parent, OPTIONS, {}));
62
- };
63
-
64
- const isQueryEnabled = (schema, name) => {
65
- return _.get(schema, `resolver.Query.${name}`) !== false;
66
- };
67
-
68
- const getQueryInfo = (schema, name) => {
69
- return _.get(schema, `resolver.Query.${name}`, {});
70
- };
71
-
72
- const isMutationEnabled = (schema, name) => {
73
- return _.get(schema, `resolver.Mutation.${name}`) !== false;
74
- };
75
-
76
- const getMutationInfo = (schema, name) => {
77
- return _.get(schema, `resolver.Mutation.${name}`, {});
78
- };
79
-
80
- const isTypeAttributeEnabled = (model, attr) =>
81
- _.get(strapi.plugins.graphql, `config._schema.graphql.type.${model.globalId}.${attr}`) !== false;
82
- const isNotPrivate = _.curry((model, attributeName) => {
83
- return !contentTypes.isPrivateAttribute(model, attributeName);
84
- });
85
-
86
- const wrapPublicationStateResolver = query => async (parent, args, ctx, ast) => {
87
- const results = await query(parent, args, ctx, ast);
88
-
89
- const queryOptions = _.pick(args, 'publicationState');
90
- return assignOptions(results, { [OPTIONS]: queryOptions });
91
- };
92
-
93
- const buildTypeDefObj = model => {
94
- const { associations = [], attributes, primaryKey, globalId } = model;
95
-
96
- const typeDef = {
97
- id: 'ID!',
98
- [primaryKey]: 'ID!',
99
- };
100
-
101
- // Add timestamps attributes.
102
- if (_.isArray(_.get(model, 'options.timestamps'))) {
103
- const [createdAtKey, updatedAtKey] = model.options.timestamps;
104
- typeDef[createdAtKey] = 'DateTime!';
105
- typeDef[updatedAtKey] = 'DateTime!';
106
- }
107
-
108
- Object.keys(attributes)
109
- .filter(isNotPrivate(model))
110
- .filter(attributeName => isTypeAttributeEnabled(model, attributeName))
111
- .forEach(attributeName => {
112
- const attribute = attributes[attributeName];
113
- // Convert our type to the GraphQL type.
114
- typeDef[attributeName] = types.convertType({
115
- attribute,
116
- modelName: globalId,
117
- attributeName,
118
- });
119
- });
120
-
121
- // Change field definition for collection relations
122
- associations
123
- .filter(association => association.type === 'collection')
124
- .filter(association => isNotPrivate(model, association.alias))
125
- .filter(attributeName => isTypeAttributeEnabled(model, attributeName))
126
- .forEach(association => {
127
- typeDef[`${association.alias}(sort: String, limit: Int, start: Int, where: JSON)`] =
128
- typeDef[association.alias];
129
-
130
- delete typeDef[association.alias];
131
- });
132
-
133
- return typeDef;
134
- };
135
-
136
- const generateEnumDefinitions = (model, globalId) => {
137
- const { attributes } = model;
138
- return Object.keys(attributes)
139
- .filter(attribute => attributes[attribute].type === 'enumeration')
140
- .filter(attribute => isTypeAttributeEnabled(model, attribute))
141
- .map(attribute => {
142
- const definition = attributes[attribute];
143
-
144
- const name = types.convertEnumType(definition, globalId, attribute);
145
- const values = definition.enum.map(v => `\t${v}`).join('\n');
146
- return `enum ${name} {\n${values}\n}\n`;
147
- })
148
- .join('');
149
- };
150
-
151
- const generateDynamicZoneDefinitions = (attributes, globalId, schema) => {
152
- Object.keys(attributes)
153
- .filter(attribute => attributes[attribute].type === 'dynamiczone')
154
- .forEach(attribute => {
155
- const { components } = attributes[attribute];
156
-
157
- const typeName = `${globalId}${_.upperFirst(_.camelCase(attribute))}DynamicZone`;
158
-
159
- if (components.length === 0) {
160
- // Create dummy type because graphql doesn't support empty ones
161
- schema.definition += `type ${typeName} { _:Boolean}`;
162
- } else {
163
- const componentsTypeNames = components.map(componentUID => {
164
- const compo = strapi.components[componentUID];
165
- if (!compo) {
166
- throw new Error(
167
- `Trying to creating dynamiczone type with unkown component ${componentUID}`
168
- );
169
- }
170
-
171
- return compo.globalId;
172
- });
173
-
174
- const unionType = `union ${typeName} = ${componentsTypeNames.join(' | ')}`;
175
-
176
- schema.definition += `\n${unionType}\n`;
177
- }
178
-
179
- const inputTypeName = `${typeName}Input`;
180
- schema.definition += `\nscalar ${inputTypeName}\n`;
181
-
182
- schema.resolvers[typeName] = {
183
- __resolveType(obj) {
184
- return strapi.components[obj.__component].globalId;
185
- },
186
- };
187
-
188
- schema.resolvers[inputTypeName] = new DynamicZoneScalar({
189
- name: inputTypeName,
190
- attribute,
191
- globalId,
192
- components,
193
- });
194
- });
195
- };
196
-
197
- const initQueryOptions = (targetModel, parent) => {
198
- if (hasDraftAndPublish(targetModel)) {
199
- return {
200
- _publicationState: _.get(parent, [OPTIONS, 'publicationState'], DP_PUB_STATE_LIVE),
201
- };
202
- }
203
-
204
- return {};
205
- };
206
-
207
- const buildAssocResolvers = model => {
208
- const { primaryKey, associations = [] } = model;
209
-
210
- return associations
211
- .filter(association => isNotPrivate(model, association.alias))
212
- .filter(association => isTypeAttributeEnabled(model, association.alias))
213
- .reduce((resolver, association) => {
214
- const target = association.model || association.collection;
215
- const targetModel = strapi.getModel(target, association.plugin);
216
-
217
- const { nature, alias } = association;
218
-
219
- switch (nature) {
220
- case 'oneToManyMorph':
221
- case 'manyMorphToOne':
222
- case 'manyMorphToMany':
223
- case 'manyToManyMorph': {
224
- resolver[alias] = async obj => {
225
- if (obj[alias]) {
226
- return assignOptions(obj[alias], obj);
227
- }
228
-
229
- const params = {
230
- ...initQueryOptions(targetModel, obj),
231
- id: obj[primaryKey],
232
- };
233
-
234
- const entry = await strapi.query(model.uid).findOne(params, [alias]);
235
-
236
- return assignOptions(entry[alias], obj);
237
- };
238
- break;
239
- }
240
- default: {
241
- resolver[alias] = async (obj, options) => {
242
- // force component relations to be refetched
243
- if (model.modelType === 'component') {
244
- obj[alias] = _.get(obj[alias], targetModel.primaryKey, obj[alias]);
245
- }
246
-
247
- const loader = strapi.plugins.graphql.services['data-loaders'].loaders[targetModel.uid];
248
-
249
- const localId = obj[model.primaryKey];
250
- const targetPK = targetModel.primaryKey;
251
- const foreignId = _.get(obj[alias], targetModel.primaryKey, obj[alias]);
252
-
253
- const params = {
254
- ...initQueryOptions(targetModel, obj),
255
- ...convertToParams(_.omit(amountLimiting(options), 'where')),
256
- ...convertToQuery(options.where),
257
- };
258
-
259
- if (['oneToOne', 'oneWay', 'manyToOne'].includes(nature)) {
260
- if (!_.has(obj, alias) || _.isNil(foreignId)) {
261
- return null;
262
- }
263
-
264
- // check this is an entity and not a mongo ID
265
- if (_.has(obj[alias], targetPK)) {
266
- return assignOptions(obj[alias], obj);
267
- }
268
-
269
- const query = {
270
- single: true,
271
- filters: {
272
- ...params,
273
- [targetPK]: foreignId,
274
- },
275
- };
276
-
277
- return loader.load(query).then(r => assignOptions(r, obj));
278
- }
279
-
280
- if (
281
- nature === 'oneToMany' ||
282
- (nature === 'manyToMany' && association.dominant !== true)
283
- ) {
284
- const { via } = association;
285
-
286
- const filters = {
287
- ...params,
288
- [via]: localId,
289
- };
290
-
291
- return loader.load({ filters }).then(r => assignOptions(r, obj));
292
- }
293
-
294
- if (
295
- nature === 'manyWay' ||
296
- (nature === 'manyToMany' && association.dominant === true)
297
- ) {
298
- let targetIds = [];
299
-
300
- // find the related ids to query them and apply the filters
301
- if (Array.isArray(obj[alias])) {
302
- targetIds = obj[alias].map(value => value[targetPK] || value);
303
- } else {
304
- const entry = await strapi
305
- .query(model.uid)
306
- .findOne({ [primaryKey]: obj[primaryKey] }, [alias]);
307
-
308
- if (_.isEmpty(entry[alias])) {
309
- return [];
310
- }
311
-
312
- targetIds = entry[alias].map(el => el[targetPK]);
313
- }
314
-
315
- const filters = {
316
- ...params,
317
- [`${targetPK}_in`]: targetIds.map(_.toString),
318
- };
319
-
320
- return loader.load({ filters }).then(r => assignOptions(r, obj));
321
- }
322
- };
323
- break;
324
- }
325
- }
326
-
327
- return resolver;
328
- }, {});
329
- };
330
-
331
- /**
332
- * Construct the GraphQL query & definition and apply the right resolvers.
333
- *
334
- * @return Object
335
- */
336
- const buildModels = (models, ctx) => {
337
- return models.map(model => {
338
- const { kind, modelType } = model;
339
-
340
- if (modelType === 'component') {
341
- return buildComponent(model);
342
- }
343
-
344
- switch (kind) {
345
- case 'singleType':
346
- return buildSingleType(model, ctx);
347
- default:
348
- return buildCollectionType(model, ctx);
349
- }
350
- });
351
- };
352
-
353
- const buildModelDefinition = (model, globalType = {}) => {
354
- const { globalId, primaryKey } = model;
355
-
356
- const typeDefObj = buildTypeDefObj(model);
357
-
358
- const schema = {
359
- definition: '',
360
- query: {},
361
- mutation: {},
362
- resolvers: {
363
- Query: {},
364
- Mutation: {},
365
- [globalId]: {
366
- id: parent => parent[primaryKey] || parent.id,
367
- ...buildAssocResolvers(model),
368
- },
369
- },
370
- typeDefObj,
371
- };
372
-
373
- schema.definition += generateEnumDefinitions(model, globalId);
374
- generateDynamicZoneDefinitions(model.attributes, globalId, schema);
375
-
376
- const description = getTypeDescription(globalType, model);
377
- const fields = toSDL(typeDefObj, globalType, model);
378
- const typeDef = `${description}type ${globalId} {${fields}}\n`;
379
-
380
- schema.definition += typeDef;
381
- return schema;
382
- };
383
-
384
- const buildComponent = component => {
385
- const { globalId } = component;
386
- const schema = buildModelDefinition(component);
387
-
388
- schema.definition += types.generateInputModel(component, globalId, {
389
- allowIds: true,
390
- });
391
-
392
- return schema;
393
- };
394
-
395
- const buildSingleType = (model, ctx) => {
396
- const { uid, modelName } = model;
397
-
398
- const singularName = toSingular(modelName);
399
-
400
- const globalType = _.get(ctx.schema, `type.${model.globalId}`, {});
401
-
402
- const localSchema = buildModelDefinition(model, globalType);
403
-
404
- // Add definition to the schema but this type won't be "queriable" or "mutable".
405
- if (globalType === false) {
406
- return localSchema;
407
- }
408
-
409
- if (isQueryEnabled(ctx.schema, singularName)) {
410
- const resolverOpts = {
411
- resolver: `${uid}.find`,
412
- ...getQueryInfo(ctx.schema, singularName),
413
- };
414
-
415
- const resolver = buildQuery(singularName, resolverOpts);
416
-
417
- const query = {
418
- query: {
419
- [singularName]: {
420
- args: {
421
- publicationState: 'PublicationState',
422
- ...(resolverOpts.args || {}),
423
- },
424
- type: model.globalId,
425
- },
426
- },
427
- resolvers: {
428
- Query: {
429
- [singularName]: wrapPublicationStateResolver(resolver),
430
- },
431
- },
432
- };
433
-
434
- _.merge(localSchema, query);
435
- }
436
-
437
- // Add model Input definition.
438
- localSchema.definition += types.generateInputModel(model, modelName);
439
-
440
- // build every mutation
441
- ['update', 'delete'].forEach(action => {
442
- const mutationSchema = buildMutationTypeDef({ model, action }, ctx);
443
-
444
- mergeSchemas(localSchema, mutationSchema);
445
- });
446
-
447
- return localSchema;
448
- };
449
-
450
- const buildCollectionType = (model, ctx) => {
451
- const { plugin, modelName, uid } = model;
452
-
453
- const singularName = toSingular(modelName);
454
- const pluralName = toPlural(modelName);
455
-
456
- const globalType = _.get(ctx.schema, `type.${model.globalId}`, {});
457
-
458
- const localSchema = buildModelDefinition(model, globalType);
459
- const { typeDefObj } = localSchema;
460
-
461
- // Add definition to the schema but this type won't be "queriable" or "mutable".
462
- if (globalType === false) {
463
- return localSchema;
464
- }
465
-
466
- if (isQueryEnabled(ctx.schema, singularName)) {
467
- const resolverOpts = {
468
- resolver: `${uid}.findOne`,
469
- ...getQueryInfo(ctx.schema, singularName),
470
- };
471
-
472
- if (actionExists(resolverOpts)) {
473
- const resolver = buildQuery(singularName, resolverOpts);
474
-
475
- const query = {
476
- query: {
477
- [singularName]: {
478
- args: {
479
- ...FIND_ONE_QUERY_ARGUMENTS,
480
- ...(resolverOpts.args || {}),
481
- },
482
- type: model.globalId,
483
- },
484
- },
485
- resolvers: {
486
- Query: {
487
- [singularName]: wrapPublicationStateResolver(resolver),
488
- },
489
- },
490
- };
491
-
492
- _.merge(localSchema, query);
493
- }
494
- }
495
-
496
- if (isQueryEnabled(ctx.schema, pluralName)) {
497
- const resolverOpts = {
498
- resolver: `${uid}.find`,
499
- ...getQueryInfo(ctx.schema, pluralName),
500
- };
501
-
502
- if (actionExists(resolverOpts)) {
503
- const resolver = buildQuery(pluralName, resolverOpts);
504
-
505
- const query = {
506
- query: {
507
- [pluralName]: {
508
- args: {
509
- ...FIND_QUERY_ARGUMENTS,
510
- ...(resolverOpts.args || {}),
511
- },
512
- type: `[${model.globalId}]`,
513
- },
514
- },
515
- resolvers: {
516
- Query: {
517
- [pluralName]: wrapPublicationStateResolver(resolver),
518
- },
519
- },
520
- };
521
-
522
- _.merge(localSchema, query);
523
-
524
- if (isQueryEnabled(ctx.schema, `${pluralName}Connection`)) {
525
- // Generate the aggregation for the given model
526
- const aggregationSchema = formatModelConnectionsGQL({
527
- fields: typeDefObj,
528
- model,
529
- name: modelName,
530
- resolver: resolverOpts,
531
- plugin,
532
- });
533
-
534
- mergeSchemas(localSchema, aggregationSchema);
535
- }
536
- }
537
- }
538
-
539
- // Add model Input definition.
540
- localSchema.definition += types.generateInputModel(model, modelName);
541
-
542
- // build every mutation
543
- ['create', 'update', 'delete'].forEach(action => {
544
- const mutationSchema = buildMutationTypeDef({ model, action }, ctx);
545
- mergeSchemas(localSchema, mutationSchema);
546
- });
547
-
548
- return localSchema;
549
- };
550
-
551
- // TODO:
552
- // - Implement batch methods (need to update the content-manager as well).
553
- // - Implement nested transactional methods (create/update).
554
- const buildMutationTypeDef = ({ model, action }, ctx) => {
555
- const capitalizedName = _.upperFirst(toSingular(model.modelName));
556
- const mutationName = `${action}${capitalizedName}`;
557
-
558
- const resolverOpts = {
559
- resolver: `${model.uid}.${action}`,
560
- transformOutput: result => ({ [toSingular(model.modelName)]: result }),
561
- ...getMutationInfo(ctx.schema, mutationName),
562
- isShadowCrud: true,
563
- };
564
-
565
- if (!actionExists(resolverOpts)) {
566
- return {};
567
- }
568
-
569
- const definition = types.generateInputPayloadArguments({
570
- model,
571
- name: model.modelName,
572
- mutationName,
573
- action,
574
- });
575
-
576
- // ignore if disabled
577
- if (!isMutationEnabled(ctx.schema, mutationName)) {
578
- return {
579
- definition,
580
- };
581
- }
582
-
583
- const { kind } = model;
584
-
585
- const args = {};
586
-
587
- if (kind !== 'singleType' || action !== 'delete') {
588
- Object.assign(args, {
589
- input: `${mutationName}Input`,
590
- });
591
- }
592
-
593
- return {
594
- definition,
595
- mutation: {
596
- [mutationName]: {
597
- args: {
598
- ...args,
599
- ...(resolverOpts.args || {}),
600
- },
601
- type: `${mutationName}Payload`,
602
- },
603
- },
604
- resolvers: {
605
- Mutation: {
606
- [mutationName]: buildMutation(mutationName, resolverOpts),
607
- },
608
- },
609
- };
610
- };
611
-
612
- module.exports = buildShadowCrud;