@webiny/api-headless-cms 5.19.1-beta.0 → 5.20.0-beta.2

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.
@@ -276,6 +276,12 @@ const createContentEntryCrud = params => {
276
276
  id: entryId
277
277
  });
278
278
  },
279
+
280
+ /**
281
+ * TODO determine if this method is required at all.
282
+ *
283
+ * @internal
284
+ */
279
285
  getEntry: async (model, params) => {
280
286
  await checkEntryPermissions({
281
287
  rwd: "r"
@@ -300,17 +306,40 @@ const createContentEntryCrud = params => {
300
306
 
301
307
  return items[0];
302
308
  },
309
+
310
+ /**
311
+ * @description Should not be used directly. Internal use only!
312
+ *
313
+ * @internal
314
+ */
303
315
  listEntries: async (model, params) => {
304
316
  const permission = await checkEntryPermissions({
305
317
  rwd: "r"
306
318
  });
307
319
  await utils.checkModelAccess(context, model);
308
- const where = params.where || {};
320
+ const {
321
+ where
322
+ } = params;
323
+ /**
324
+ * Where must contain either latest or published keys.
325
+ * We cannot list entries without one of those
326
+ */
327
+
328
+ if (where.latest && where.published) {
329
+ throw new _error.default("Cannot list entries that are both published and latest.", "LIST_ENTRIES_ERROR", {
330
+ where
331
+ });
332
+ } else if (!where.latest && !where.published) {
333
+ throw new _error.default("Cannot list entries if we do not have latest or published defined.", "LIST_ENTRIES_ERROR", {
334
+ where
335
+ });
336
+ }
309
337
  /**
310
338
  * Possibly only get records which are owned by current user.
311
339
  * Or if searching for the owner set that value - in the case that user can see other entries than their own.
312
340
  */
313
341
 
342
+
314
343
  const ownedBy = permission.own ? getIdentity().id : where.ownedBy;
315
344
 
316
345
  const listWhere = _objectSpread(_objectSpread({}, where), {}, {
@@ -3,7 +3,12 @@ import { Topic } from "@webiny/pubsub/types";
3
3
  import { PluginsContainer } from "@webiny/plugins";
4
4
  export interface Params {
5
5
  onBeforeCreate: Topic<BeforeModelCreateTopicParams>;
6
+ onBeforeCreateFrom: Topic<BeforeModelCreateTopicParams>;
6
7
  storageOperations: HeadlessCmsStorageOperations;
7
8
  plugins: PluginsContainer;
8
9
  }
10
+ /**
11
+ * We attach both on before create and createFrom events here.
12
+ * Callables are identical.
13
+ */
9
14
  export declare const assignBeforeModelCreate: (params: Params) => void;
@@ -103,13 +103,11 @@ const getModelId = model => {
103
103
  });
104
104
  };
105
105
 
106
- const assignBeforeModelCreate = params => {
107
- const {
108
- onBeforeCreate,
109
- storageOperations,
110
- plugins
111
- } = params;
112
- onBeforeCreate.subscribe(async params => {
106
+ const createOnBeforeCb = ({
107
+ plugins,
108
+ storageOperations
109
+ }) => {
110
+ return async params => {
113
111
  const {
114
112
  model
115
113
  } = params;
@@ -140,7 +138,28 @@ const assignBeforeModelCreate = params => {
140
138
  checkModelIdEndingAllowed(modelId);
141
139
  checkModelIdUniqueness(modelIdList, modelId);
142
140
  model.modelId = modelId;
143
- });
141
+ };
142
+ };
143
+
144
+ /**
145
+ * We attach both on before create and createFrom events here.
146
+ * Callables are identical.
147
+ */
148
+ const assignBeforeModelCreate = params => {
149
+ const {
150
+ onBeforeCreate,
151
+ onBeforeCreateFrom,
152
+ storageOperations,
153
+ plugins
154
+ } = params;
155
+ onBeforeCreate.subscribe(createOnBeforeCb({
156
+ storageOperations,
157
+ plugins
158
+ }));
159
+ onBeforeCreateFrom.subscribe(createOnBeforeCb({
160
+ storageOperations,
161
+ plugins
162
+ }));
144
163
  };
145
164
 
146
165
  exports.assignBeforeModelCreate = assignBeforeModelCreate;
@@ -1,3 +1,4 @@
1
1
  export declare const CreateContentModelModel: any;
2
+ export declare const CreateContentModelModelFrom: any;
2
3
  export declare const ContentModelFieldModel: any;
3
4
  export declare const UpdateContentModelModel: any;
@@ -5,7 +5,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
5
5
  Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
- exports.UpdateContentModelModel = exports.CreateContentModelModel = exports.ContentModelFieldModel = void 0;
8
+ exports.UpdateContentModelModel = exports.CreateContentModelModelFrom = exports.CreateContentModelModel = exports.ContentModelFieldModel = void 0;
9
9
 
10
10
  var _validation = require("@webiny/validation");
11
11
 
@@ -36,6 +36,24 @@ const CreateContentModelModel = (0, _fields.withFields)({
36
36
  })
37
37
  })();
38
38
  exports.CreateContentModelModel = CreateContentModelModel;
39
+ const CreateContentModelModelFrom = (0, _fields.withFields)({
40
+ name: (0, _fields.string)({
41
+ validation: requiredShortString
42
+ }),
43
+ modelId: (0, _fields.string)({
44
+ validation: shortString
45
+ }),
46
+ description: (0, _fields.string)({
47
+ validation: shortString
48
+ }),
49
+ group: (0, _fields.string)({
50
+ validation: requiredShortString
51
+ }),
52
+ locale: (0, _fields.string)({
53
+ validation: shortString
54
+ })
55
+ })();
56
+ exports.CreateContentModelModelFrom = CreateContentModelModelFrom;
39
57
  const RendererModel = (0, _fields.withFields)({
40
58
  name: (0, _fields.string)({
41
59
  validation: requiredShortString
@@ -185,6 +185,8 @@ const createModelsCrud = params => {
185
185
 
186
186
  const onBeforeCreate = (0, _pubsub.createTopic)();
187
187
  const onAfterCreate = (0, _pubsub.createTopic)();
188
+ const onBeforeCreateFrom = (0, _pubsub.createTopic)();
189
+ const onAfterCreateFrom = (0, _pubsub.createTopic)();
188
190
  const onBeforeUpdate = (0, _pubsub.createTopic)();
189
191
  const onAfterUpdate = (0, _pubsub.createTopic)();
190
192
  const onBeforeDelete = (0, _pubsub.createTopic)();
@@ -195,6 +197,7 @@ const createModelsCrud = params => {
195
197
 
196
198
  (0, _beforeCreate.assignBeforeModelCreate)({
197
199
  onBeforeCreate,
200
+ onBeforeCreateFrom,
198
201
  plugins: context.plugins,
199
202
  storageOperations
200
203
  });
@@ -223,6 +226,8 @@ const createModelsCrud = params => {
223
226
  return {
224
227
  onBeforeModelCreate: onBeforeCreate,
225
228
  onAfterModelCreate: onAfterCreate,
229
+ onBeforeModelCreateFrom: onBeforeCreateFrom,
230
+ onAfterModelCreateFrom: onAfterCreateFrom,
226
231
  onBeforeModelUpdate: onBeforeUpdate,
227
232
  onAfterModelUpdate: onAfterUpdate,
228
233
  onBeforeModelDelete: onBeforeDelete,
@@ -259,8 +264,10 @@ const createModelsCrud = params => {
259
264
  }
260
265
 
261
266
  const identity = getIdentity();
262
-
263
- const model = _objectSpread(_objectSpread({}, input), {}, {
267
+ const model = {
268
+ name: input.name,
269
+ description: input.description,
270
+ modelId: input.modelId,
264
271
  titleFieldId: "id",
265
272
  locale: getLocale().code,
266
273
  tenant: getTenant().id,
@@ -279,8 +286,7 @@ const createModelsCrud = params => {
279
286
  lockedFields: [],
280
287
  layout: [],
281
288
  webinyVersion: context.WEBINY_VERSION
282
- });
283
-
289
+ };
284
290
  await onBeforeCreate.publish({
285
291
  model,
286
292
  input
@@ -334,6 +340,83 @@ const createModelsCrud = params => {
334
340
  return resultModel;
335
341
  },
336
342
 
343
+ async createModelFrom(modelId, data) {
344
+ await checkModelPermissions("w");
345
+ /**
346
+ * Get a model record; this will also perform ownership validation.
347
+ */
348
+
349
+ const original = await get(modelId);
350
+ const createdData = new _models.CreateContentModelModelFrom().populate({
351
+ name: data.name,
352
+ modelId: data.modelId,
353
+ description: data.description || original.description,
354
+ group: data.group,
355
+ locale: data.locale
356
+ });
357
+ await createdData.validate();
358
+ const input = await createdData.toJSON();
359
+ const locale = await context.i18n.getLocale(input.locale || original.locale);
360
+
361
+ if (!locale) {
362
+ throw new _handlerGraphql.NotFoundError(`There is no locale "${input.locale}".`);
363
+ }
364
+ /**
365
+ * Use storage operations directly because we cannot get group from different locale via context methods.
366
+ */
367
+
368
+
369
+ const group = await context.cms.storageOperations.groups.get({
370
+ id: input.group,
371
+ tenant: original.tenant,
372
+ locale: locale.code
373
+ });
374
+
375
+ if (!group) {
376
+ throw new _handlerGraphql.NotFoundError(`There is no group "${input.group}".`);
377
+ }
378
+
379
+ const identity = getIdentity();
380
+
381
+ const model = _objectSpread(_objectSpread({}, original), {}, {
382
+ locale: locale.code,
383
+ group: {
384
+ id: group.id,
385
+ name: group.name
386
+ },
387
+ name: input.name,
388
+ modelId: input.modelId,
389
+ description: input.description,
390
+ createdBy: {
391
+ id: identity.id,
392
+ displayName: identity.displayName,
393
+ type: identity.type
394
+ },
395
+ createdOn: new Date().toISOString(),
396
+ savedOn: new Date().toISOString(),
397
+ lockedFields: [],
398
+ webinyVersion: context.WEBINY_VERSION
399
+ });
400
+
401
+ await onBeforeCreateFrom.publish({
402
+ model,
403
+ original,
404
+ input
405
+ });
406
+ const createdModel = await storageOperations.models.create({
407
+ input,
408
+ model
409
+ });
410
+ loaders.listModels.clearAll();
411
+ await updateManager(context, model);
412
+ await onAfterCreateFrom.publish({
413
+ input,
414
+ original,
415
+ model: createdModel
416
+ });
417
+ return createdModel;
418
+ },
419
+
337
420
  async updateModel(modelId, inputData) {
338
421
  await checkModelPermissions("w"); // Get a model record; this will also perform ownership validation.
339
422
 
@@ -348,25 +431,30 @@ const createModelsCrud = params => {
348
431
  return {};
349
432
  }
350
433
 
434
+ let group = {
435
+ id: original.group.id,
436
+ name: original.group.name
437
+ };
438
+
351
439
  if (input.group) {
352
440
  context.security.disableAuthorization();
353
- const group = await context.cms.getGroup(input.group);
441
+ const groupData = await context.cms.getGroup(input.group);
354
442
  context.security.enableAuthorization();
355
443
 
356
- if (!group) {
444
+ if (!groupData) {
357
445
  throw new _handlerGraphql.NotFoundError(`There is no group "${input.group}".`);
358
446
  }
359
447
 
360
- input.group = {
361
- id: group.id,
362
- name: group.name
448
+ group = {
449
+ id: groupData.id,
450
+ name: groupData.name
363
451
  };
364
452
  }
365
453
 
366
454
  const modelFields = await (0, _createFieldModels.createFieldModels)(original, inputData);
367
- (0, _validateLayout.validateLayout)(input, modelFields);
368
455
 
369
456
  const model = _objectSpread(_objectSpread(_objectSpread({}, original), input), {}, {
457
+ group,
370
458
  tenant: original.tenant || getTenant().id,
371
459
  locale: original.locale || getLocale().code,
372
460
  webinyVersion: context.WEBINY_VERSION,
@@ -374,6 +462,7 @@ const createModelsCrud = params => {
374
462
  savedOn: new Date().toISOString()
375
463
  });
376
464
 
465
+ (0, _validateLayout.validateLayout)(model, modelFields);
377
466
  await onBeforeUpdate.publish({
378
467
  input,
379
468
  original,
@@ -27,6 +27,11 @@ const typeFromField = ({
27
27
  const mTypeName = (0, _createTypeName.createManageTypeName)(typeName); // `field` is an "object" field
28
28
 
29
29
  const fields = field.settings.fields;
30
+
31
+ if (!fields || fields.length === 0) {
32
+ return null;
33
+ }
34
+
30
35
  const fieldTypeName = `${mTypeName}_${(0, _upperFirst.default)(field.fieldId)}`;
31
36
  const typeFields = [];
32
37
  const nestedTypes = []; // Once the loop below starts, we'll be executing a recursive "object" type generation.
@@ -84,16 +89,22 @@ const plugin = {
84
89
  model,
85
90
  fieldTypePlugins
86
91
  }) {
87
- const {
88
- fieldType,
89
- typeDefs
90
- } = typeFromField({
92
+ const result = typeFromField({
91
93
  typeOfType: "type",
92
94
  model,
93
95
  type: "read",
94
96
  field,
95
97
  fieldTypePlugins
96
98
  });
99
+
100
+ if (!result) {
101
+ return null;
102
+ }
103
+
104
+ const {
105
+ fieldType,
106
+ typeDefs
107
+ } = result;
97
108
  return {
98
109
  fields: `${field.fieldId}: ${field.multipleValues ? `[${fieldType}!]` : fieldType}`,
99
110
  typeDefs
@@ -105,13 +116,18 @@ const plugin = {
105
116
  createFieldResolvers,
106
117
  graphQLType
107
118
  }) {
119
+ if (!field.settings.fields || field.settings.fields.length === 0) {
120
+ return false;
121
+ }
122
+
108
123
  const fieldType = `${graphQLType}_${(0, _upperFirst.default)(field.fieldId)}`;
124
+ const typeResolvers = createFieldResolvers({
125
+ graphQLType: fieldType,
126
+ fields: field.settings.fields
127
+ });
109
128
  return {
110
129
  resolver: null,
111
- typeResolvers: createFieldResolvers({
112
- graphQLType: fieldType,
113
- fields: field.settings.fields
114
- })
130
+ typeResolvers: typeResolvers || {}
115
131
  };
116
132
  }
117
133
 
@@ -122,16 +138,22 @@ const plugin = {
122
138
  field,
123
139
  fieldTypePlugins
124
140
  }) {
125
- const {
126
- fieldType,
127
- typeDefs
128
- } = typeFromField({
141
+ const result = typeFromField({
129
142
  typeOfType: "type",
130
143
  model,
131
144
  type: "manage",
132
145
  field,
133
146
  fieldTypePlugins
134
147
  });
148
+
149
+ if (!result) {
150
+ return null;
151
+ }
152
+
153
+ const {
154
+ fieldType,
155
+ typeDefs
156
+ } = result;
135
157
  return {
136
158
  fields: `${field.fieldId}: ${field.multipleValues ? `[${fieldType}!]` : fieldType}`,
137
159
  typeDefs
@@ -143,16 +165,22 @@ const plugin = {
143
165
  field,
144
166
  fieldTypePlugins
145
167
  }) {
146
- const {
147
- fieldType,
148
- typeDefs
149
- } = typeFromField({
168
+ const result = typeFromField({
150
169
  typeOfType: "input",
151
170
  model,
152
171
  type: "manage",
153
172
  field,
154
173
  fieldTypePlugins
155
174
  });
175
+
176
+ if (!result) {
177
+ return null;
178
+ }
179
+
180
+ const {
181
+ fieldType,
182
+ typeDefs
183
+ } = result;
156
184
  return {
157
185
  fields: `${field.fieldId}: ${field.multipleValues ? `[${fieldType}!]` : fieldType}`,
158
186
  typeDefs
@@ -164,13 +192,18 @@ const plugin = {
164
192
  field,
165
193
  createFieldResolvers
166
194
  }) {
195
+ if (!field.settings.fields || field.settings.fields.length === 0) {
196
+ return false;
197
+ }
198
+
167
199
  const fieldType = `${graphQLType}_${(0, _upperFirst.default)(field.fieldId)}`;
200
+ const typeResolvers = createFieldResolvers({
201
+ graphQLType: fieldType,
202
+ fields: field.settings.fields
203
+ });
168
204
  return {
169
205
  resolver: null,
170
- typeResolvers: createFieldResolvers({
171
- graphQLType: fieldType,
172
- fields: field.settings.fields
173
- })
206
+ typeResolvers: typeResolvers || {}
174
207
  };
175
208
  }
176
209
 
@@ -1,4 +1,4 @@
1
- import { CmsModelManager, CmsModel, CmsContext } from "../../../types";
1
+ import { CmsModelManager, CmsModel, CmsContext, CmsEntryListParams } from "../../../types";
2
2
  export declare class DefaultCmsModelManager implements CmsModelManager {
3
3
  private readonly _context;
4
4
  private readonly _model;
@@ -6,9 +6,8 @@ export declare class DefaultCmsModelManager implements CmsModelManager {
6
6
  create(data: any): Promise<import("../../../types").CmsEntry>;
7
7
  delete(id: string): Promise<void>;
8
8
  get(id: string): Promise<import("../../../types").CmsEntry>;
9
- list(args: any): Promise<[import("../../../types").CmsEntry[], import("../../../types").CmsEntryMeta]>;
10
- listPublished(args: any): Promise<[import("../../../types").CmsEntry[], import("../../../types").CmsEntryMeta]>;
11
- listLatest(args: any): Promise<[import("../../../types").CmsEntry[], import("../../../types").CmsEntryMeta]>;
9
+ listPublished(params: CmsEntryListParams): Promise<[import("../../../types").CmsEntry[], import("../../../types").CmsEntryMeta]>;
10
+ listLatest(params: CmsEntryListParams): Promise<[import("../../../types").CmsEntry[], import("../../../types").CmsEntryMeta]>;
12
11
  getPublishedByIds(ids: string[]): Promise<import("../../../types").CmsEntry[]>;
13
12
  getLatestByIds(ids: string[]): Promise<import("../../../types").CmsEntry[]>;
14
13
  update(id: any, data: any): Promise<import("../../../types").CmsEntry>;
@@ -39,16 +39,12 @@ class DefaultCmsModelManager {
39
39
  return this._context.cms.getEntryById(this._model, id);
40
40
  }
41
41
 
42
- async list(args) {
43
- return this._context.cms.listEntries(this._model, args);
42
+ async listPublished(params) {
43
+ return this._context.cms.listPublishedEntries(this._model, params);
44
44
  }
45
45
 
46
- async listPublished(args) {
47
- return this._context.cms.listPublishedEntries(this._model, args);
48
- }
49
-
50
- async listLatest(args) {
51
- return this._context.cms.listLatestEntries(this._model, args);
46
+ async listLatest(params) {
47
+ return this._context.cms.listLatestEntries(this._model, params);
52
48
  }
53
49
 
54
50
  async getPublishedByIds(ids) {
@@ -76,11 +76,15 @@ const plugin = context => {
76
76
  const getters = models.filter(model => modelIds.includes(model.modelId)).map(async model => {
77
77
  const latest = query === "__latest__";
78
78
  const modelManager = await context.cms.getModelManager(model.modelId);
79
+ const where = {};
80
+
81
+ if (!latest) {
82
+ where[`${model.titleFieldId}_contains`] = query;
83
+ }
84
+
79
85
  const [items] = await modelManager.listLatest({
80
86
  limit,
81
- where: latest ? undefined : {
82
- [`${model.titleFieldId}_contains`]: query
83
- }
87
+ where
84
88
  });
85
89
  return items.map(entry => ({
86
90
  id: entry.id,
@@ -50,6 +50,14 @@ const plugin = context => {
50
50
  return new _handlerGraphql.ErrorResponse(e);
51
51
  }
52
52
  },
53
+ createContentModelFrom: async (_, args, context) => {
54
+ try {
55
+ const model = await context.cms.createModelFrom(args.modelId, args.data);
56
+ return new _handlerGraphql.Response(model);
57
+ } catch (e) {
58
+ return new _handlerGraphql.ErrorResponse(e);
59
+ }
60
+ },
53
61
  updateContentModel: async (_, args, context) => {
54
62
  const {
55
63
  modelId,
@@ -120,6 +128,14 @@ const plugin = context => {
120
128
  description: String
121
129
  }
122
130
 
131
+ input CmsContentModelCreateFromInput {
132
+ name: String!
133
+ modelId: String
134
+ group: RefInput!
135
+ description: String
136
+ locale: String
137
+ }
138
+
123
139
  input CmsContentModelUpdateInput {
124
140
  name: String
125
141
  group: RefInput
@@ -132,6 +148,11 @@ const plugin = context => {
132
148
  extend type Mutation {
133
149
  createContentModel(data: CmsContentModelCreateInput!): CmsContentModelResponse
134
150
 
151
+ createContentModelFrom(
152
+ modelId: ID!
153
+ data: CmsContentModelCreateFromInput!
154
+ ): CmsContentModelResponse
155
+
135
156
  updateContentModel(
136
157
  modelId: ID!
137
158
  data: CmsContentModelUpdateInput!
@@ -55,10 +55,16 @@ function createFieldResolversFactory({
55
55
  field,
56
56
  createFieldResolvers
57
57
  }) : null;
58
+ /**
59
+ * When fieldResolver is false it will completely skip adding fieldId into the resolvers.
60
+ * This is to fix the breaking of GraphQL schema.
61
+ */
58
62
 
59
- if (typeof fieldResolver === "function") {
63
+ if (fieldResolver === false) {
64
+ continue;
65
+ } else if (typeof fieldResolver === "function") {
60
66
  resolver = fieldResolver;
61
- } else if (fieldResolver !== null) {
67
+ } else if (fieldResolver) {
62
68
  resolver = fieldResolver.resolver;
63
69
  Object.assign(typeResolvers, fieldResolver.typeResolvers);
64
70
  }
@@ -54,6 +54,13 @@ const createManageResolvers = ({
54
54
  model,
55
55
  fieldTypePlugins
56
56
  }) => {
57
+ if (model.fields.length === 0) {
58
+ return {
59
+ Query: {},
60
+ Mutation: {}
61
+ };
62
+ }
63
+
57
64
  const typeName = (0, _createTypeName.createTypeName)(model.modelId);
58
65
  const mTypeName = (0, _createTypeName.createManageTypeName)(typeName);
59
66
  const createFieldResolvers = (0, _createFieldResolvers.createFieldResolversFactory)({
@@ -62,6 +69,18 @@ const createManageResolvers = ({
62
69
  model,
63
70
  fieldTypePlugins
64
71
  });
72
+ const fieldResolvers = createFieldResolvers({
73
+ graphQLType: mTypeName,
74
+ fields: model.fields,
75
+ isRoot: true,
76
+ // These are extra fields we want to apply to field resolvers of "gqlType"
77
+ extraResolvers: _objectSpread(_objectSpread({}, (0, _commonFieldResolvers.commonFieldResolvers)()), {}, {
78
+ meta(entry) {
79
+ return entry;
80
+ }
81
+
82
+ })
83
+ });
65
84
  return _objectSpread(_objectSpread({
66
85
  Query: {
67
86
  [`get${typeName}`]: (0, _resolveGet.resolveGet)({
@@ -106,18 +125,7 @@ const createManageResolvers = ({
106
125
  model
107
126
  })
108
127
  }
109
- }, createFieldResolvers({
110
- graphQLType: mTypeName,
111
- fields: model.fields,
112
- isRoot: true,
113
- // These are extra fields we want to apply to field resolvers of "gqlType"
114
- extraResolvers: _objectSpread(_objectSpread({}, (0, _commonFieldResolvers.commonFieldResolvers)()), {}, {
115
- meta(entry) {
116
- return entry;
117
- }
118
-
119
- })
120
- })), {}, {
128
+ }, fieldResolvers), {}, {
121
129
  [`${mTypeName}Meta`]: {
122
130
  title(entry) {
123
131
  return (0, _getEntryTitle.getEntryTitle)(model, entry);
@@ -47,10 +47,15 @@ const createManageSDL = ({
47
47
  type: "manage",
48
48
  fieldTypePlugins
49
49
  });
50
+
51
+ if (inputFields.length === 0) {
52
+ return "";
53
+ }
54
+
50
55
  return (
51
56
  /* GraphQL */
52
57
  `
53
- """${model.description}"""
58
+ """${model.description || model.modelId}"""
54
59
  ${fields.map(f => f.typeDefs).filter(Boolean).join("\n")}
55
60
 
56
61
  type ${mTypeName} {
@@ -28,6 +28,12 @@ const createPreviewResolvers = ({
28
28
  model,
29
29
  fieldTypePlugins
30
30
  }) => {
31
+ if (model.fields.length === 0) {
32
+ return {
33
+ Query: {}
34
+ };
35
+ }
36
+
31
37
  const typeName = (0, _createTypeName.createTypeName)(model.modelId);
32
38
  const rTypeName = (0, _createTypeName.createReadTypeName)(typeName);
33
39
  const createFieldResolvers = (0, _createFieldResolvers.createFieldResolversFactory)({
@@ -36,6 +42,11 @@ const createPreviewResolvers = ({
36
42
  model,
37
43
  fieldTypePlugins
38
44
  });
45
+ const fieldResolvers = createFieldResolvers({
46
+ graphQLType: rTypeName,
47
+ fields: model.fields,
48
+ isRoot: true
49
+ });
39
50
  return _objectSpread({
40
51
  Query: {
41
52
  [`get${typeName}`]: (0, _resolveGet.resolveGet)({
@@ -45,11 +56,7 @@ const createPreviewResolvers = ({
45
56
  model
46
57
  })
47
58
  }
48
- }, createFieldResolvers({
49
- graphQLType: rTypeName,
50
- fields: model.fields,
51
- isRoot: true
52
- }));
59
+ }, fieldResolvers);
53
60
  };
54
61
 
55
62
  exports.createPreviewResolvers = createPreviewResolvers;
@@ -28,6 +28,12 @@ const createReadResolvers = ({
28
28
  model,
29
29
  fieldTypePlugins
30
30
  }) => {
31
+ if (model.fields.length === 0) {
32
+ return {
33
+ Query: {}
34
+ };
35
+ }
36
+
31
37
  const typeName = (0, _createTypeName.createTypeName)(model.modelId);
32
38
  const rTypeName = (0, _createTypeName.createReadTypeName)(typeName);
33
39
  const createFieldResolvers = (0, _createFieldResolvers.createFieldResolversFactory)({
@@ -36,6 +42,11 @@ const createReadResolvers = ({
36
42
  model,
37
43
  fieldTypePlugins
38
44
  });
45
+ const fieldResolvers = createFieldResolvers({
46
+ graphQLType: rTypeName,
47
+ fields: model.fields,
48
+ isRoot: true
49
+ });
39
50
  return _objectSpread({
40
51
  Query: {
41
52
  [`get${typeName}`]: (0, _resolveGet.resolveGet)({
@@ -45,11 +56,7 @@ const createReadResolvers = ({
45
56
  model
46
57
  })
47
58
  }
48
- }, createFieldResolvers({
49
- graphQLType: rTypeName,
50
- fields: model.fields,
51
- isRoot: true
52
- }));
59
+ }, fieldResolvers);
53
60
  };
54
61
 
55
62
  exports.createReadResolvers = createReadResolvers;
@@ -41,6 +41,11 @@ const createReadSDL = ({
41
41
  type: "read",
42
42
  fieldTypePlugins
43
43
  });
44
+
45
+ if (fieldsRender.length === 0) {
46
+ return "";
47
+ }
48
+
44
49
  return `
45
50
  """${model.description || ""}"""
46
51
  ${fieldsRender.map(f => f.typeDefs).filter(Boolean).join("\n")}
@@ -86,7 +86,7 @@ const generateSchemaPlugins = async context => {
86
86
  return;
87
87
  }
88
88
  });
89
- return newPlugins;
89
+ return newPlugins.filter(pl => !!pl.schema.typeDefs);
90
90
  };
91
91
 
92
92
  exports.generateSchemaPlugins = generateSchemaPlugins;
@@ -39,7 +39,9 @@ const renderField = ({
39
39
  fieldTypePlugins
40
40
  });
41
41
 
42
- if (typeof defs === "string") {
42
+ if (!defs) {
43
+ return null;
44
+ } else if (typeof defs === "string") {
43
45
  return {
44
46
  fields: defs
45
47
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webiny/api-headless-cms",
3
- "version": "5.19.1-beta.0",
3
+ "version": "5.20.0-beta.2",
4
4
  "main": "index.js",
5
5
  "keywords": [
6
6
  "cms:base"
@@ -21,23 +21,23 @@
21
21
  "@babel/runtime": "7.16.3",
22
22
  "@commodo/fields": "1.1.2-beta.20",
23
23
  "@graphql-tools/schema": "7.1.5",
24
- "@webiny/api-file-manager": "5.19.1-beta.0",
25
- "@webiny/api-i18n": "5.19.1-beta.0",
26
- "@webiny/api-i18n-content": "5.19.1-beta.0",
27
- "@webiny/api-i18n-ddb": "5.19.1-beta.0",
28
- "@webiny/api-security": "5.19.1-beta.0",
29
- "@webiny/api-tenancy": "5.19.1-beta.0",
30
- "@webiny/api-upgrade": "5.19.1-beta.0",
31
- "@webiny/error": "5.19.1-beta.0",
32
- "@webiny/handler": "5.19.1-beta.0",
33
- "@webiny/handler-aws": "5.19.1-beta.0",
34
- "@webiny/handler-db": "5.19.1-beta.0",
35
- "@webiny/handler-graphql": "5.19.1-beta.0",
36
- "@webiny/handler-http": "5.19.1-beta.0",
37
- "@webiny/plugins": "5.19.1-beta.0",
38
- "@webiny/pubsub": "5.19.1-beta.0",
39
- "@webiny/utils": "5.19.1-beta.0",
40
- "@webiny/validation": "5.19.1-beta.0",
24
+ "@webiny/api-file-manager": "5.20.0-beta.2",
25
+ "@webiny/api-i18n": "5.20.0-beta.2",
26
+ "@webiny/api-i18n-content": "5.20.0-beta.2",
27
+ "@webiny/api-i18n-ddb": "5.20.0-beta.2",
28
+ "@webiny/api-security": "5.20.0-beta.2",
29
+ "@webiny/api-tenancy": "5.20.0-beta.2",
30
+ "@webiny/api-upgrade": "5.20.0-beta.2",
31
+ "@webiny/error": "5.20.0-beta.2",
32
+ "@webiny/handler": "5.20.0-beta.2",
33
+ "@webiny/handler-aws": "5.20.0-beta.2",
34
+ "@webiny/handler-db": "5.20.0-beta.2",
35
+ "@webiny/handler-graphql": "5.20.0-beta.2",
36
+ "@webiny/handler-http": "5.20.0-beta.2",
37
+ "@webiny/plugins": "5.20.0-beta.2",
38
+ "@webiny/pubsub": "5.20.0-beta.2",
39
+ "@webiny/utils": "5.20.0-beta.2",
40
+ "@webiny/validation": "5.20.0-beta.2",
41
41
  "boolean": "3.1.4",
42
42
  "commodo-fields-object": "1.0.6",
43
43
  "dataloader": "2.0.0",
@@ -54,10 +54,10 @@
54
54
  "@babel/core": "^7.5.5",
55
55
  "@babel/preset-env": "^7.5.5",
56
56
  "@babel/preset-flow": "^7.0.0",
57
- "@webiny/api-security-so-ddb": "^5.19.1-beta.0",
58
- "@webiny/api-tenancy-so-ddb": "^5.19.1-beta.0",
59
- "@webiny/cli": "^5.19.1-beta.0",
60
- "@webiny/project-utils": "^5.19.1-beta.0",
57
+ "@webiny/api-security-so-ddb": "^5.20.0-beta.2",
58
+ "@webiny/api-tenancy-so-ddb": "^5.20.0-beta.2",
59
+ "@webiny/cli": "^5.20.0-beta.2",
60
+ "@webiny/project-utils": "^5.20.0-beta.2",
61
61
  "apollo-graphql": "^0.4.1",
62
62
  "get-yarn-workspaces": "^1.0.2",
63
63
  "graphql": "^14.6.0",
@@ -77,5 +77,5 @@
77
77
  "build": "yarn webiny run build",
78
78
  "watch": "yarn webiny run watch"
79
79
  },
80
- "gitHead": "c217ea1361f1d1536c6c4ee577579e1faca240eb"
80
+ "gitHead": "86c73ea3b5bf71372f255aa24ee8c6ed98466b7f"
81
81
  }
@@ -17,7 +17,7 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
17
17
 
18
18
  var _default = () => {
19
19
  return new _ContextPlugin.ContextPlugin(context => {
20
- const locale = context.i18nContent.getLocale();
20
+ const locale = context.i18nContent.getCurrentLocale();
21
21
  context.cms = _objectSpread(_objectSpread({}, context.cms || {}), {}, {
22
22
  locale: locale ? locale.code : "en-US",
23
23
 
package/types.d.ts CHANGED
@@ -364,7 +364,7 @@ export interface CmsModelFieldToGraphQLCreateResolver {
364
364
  }): GraphQLFieldResolver | {
365
365
  resolver: GraphQLFieldResolver;
366
366
  typeResolvers: Resolvers<CmsContext>;
367
- };
367
+ } | false;
368
368
  }
369
369
  /**
370
370
  * @category Plugin
@@ -455,7 +455,7 @@ export interface CmsModelFieldToGraphQLPlugin extends Plugin {
455
455
  model: CmsModel;
456
456
  field: CmsModelField;
457
457
  fieldTypePlugins: CmsFieldTypePlugins;
458
- }): CmsModelFieldDefinition | string;
458
+ }): CmsModelFieldDefinition | string | null;
459
459
  /**
460
460
  * Definition for field resolver.
461
461
  * By default it is simple return of the `instance.values[fieldId]` but if required, users can define their own.
@@ -556,7 +556,7 @@ export interface CmsModelFieldToGraphQLPlugin extends Plugin {
556
556
  model: CmsModel;
557
557
  field: CmsModelField;
558
558
  fieldTypePlugins: CmsFieldTypePlugins;
559
- }) => CmsModelFieldDefinition | string;
559
+ }) => CmsModelFieldDefinition | string | null;
560
560
  /**
561
561
  * Definition for input GraphQL field type.
562
562
  *
@@ -905,6 +905,22 @@ export interface CmsModelCreateInput {
905
905
  * Description of the content model.
906
906
  */
907
907
  description?: string;
908
+ /**
909
+ * Group where to put the content model in.
910
+ */
911
+ group: string;
912
+ }
913
+ /**
914
+ * A GraphQL params.data parameter received when creating content model from existing model.
915
+ *
916
+ * @category GraphQL params
917
+ * @category CmsModel
918
+ */
919
+ export interface CmsModelCreateFromInput extends CmsModelCreateInput {
920
+ /**
921
+ * Locale into which we want to clone the model into.
922
+ */
923
+ locale?: string;
908
924
  }
909
925
  /**
910
926
  * A definition for content model field received from the user.
@@ -977,6 +993,10 @@ export interface CmsModelUpdateInput {
977
993
  * A new content model name.
978
994
  */
979
995
  name?: string;
996
+ /**
997
+ * A group we want to move the model to.
998
+ */
999
+ group?: string;
980
1000
  /**
981
1001
  * A new description of the content model.
982
1002
  */
@@ -1118,18 +1138,14 @@ export interface CmsStorageEntry extends CmsEntry {
1118
1138
  * @category CmsModel
1119
1139
  */
1120
1140
  export interface CmsModelManager {
1121
- /**
1122
- * List entries in this content model.
1123
- */
1124
- list: (params?: CmsEntryListParams) => Promise<[CmsEntry[], CmsEntryMeta]>;
1125
1141
  /**
1126
1142
  * List only published entries in the content model.
1127
1143
  */
1128
- listPublished: (params?: CmsEntryListParams) => Promise<[CmsEntry[], CmsEntryMeta]>;
1144
+ listPublished: (params: CmsEntryListParams) => Promise<[CmsEntry[], CmsEntryMeta]>;
1129
1145
  /**
1130
1146
  * List latest entries in the content model. Used for administration.
1131
1147
  */
1132
- listLatest: (params?: CmsEntryListParams) => Promise<[CmsEntry[], CmsEntryMeta]>;
1148
+ listLatest: (params: CmsEntryListParams) => Promise<[CmsEntry[], CmsEntryMeta]>;
1133
1149
  /**
1134
1150
  * Get a list of published entries by the ID list.
1135
1151
  */
@@ -1156,20 +1172,30 @@ export interface CmsModelManager {
1156
1172
  delete: (id: string) => Promise<void>;
1157
1173
  }
1158
1174
  export interface BeforeModelCreateTopicParams {
1159
- input: Partial<CmsModel>;
1175
+ input: CmsModelCreateInput;
1160
1176
  model: CmsModel;
1161
1177
  }
1162
1178
  export interface AfterModelCreateTopicParams {
1163
- input: Partial<CmsModel>;
1179
+ input: CmsModelCreateInput;
1180
+ model: CmsModel;
1181
+ }
1182
+ export interface BeforeModelCreateFromTopicParams {
1183
+ input: CmsModelCreateFromInput;
1184
+ original: CmsModel;
1185
+ model: CmsModel;
1186
+ }
1187
+ export interface AfterModelCreateFromTopicParams {
1188
+ input: CmsModelCreateFromInput;
1189
+ original: CmsModel;
1164
1190
  model: CmsModel;
1165
1191
  }
1166
1192
  export interface BeforeModelUpdateTopicParams {
1167
- input: Partial<CmsModel>;
1193
+ input: CmsModelUpdateInput;
1168
1194
  original: CmsModel;
1169
1195
  model: CmsModel;
1170
1196
  }
1171
1197
  export interface AfterModelUpdateTopicParams {
1172
- input: Partial<CmsModel>;
1198
+ input: CmsModelUpdateInput;
1173
1199
  original: CmsModel;
1174
1200
  model: CmsModel;
1175
1201
  }
@@ -1211,12 +1237,12 @@ export interface CmsModelContext {
1211
1237
  * Create a content model.
1212
1238
  */
1213
1239
  createModel: (data: CmsModelCreateInput) => Promise<CmsModel>;
1240
+ /**
1241
+ * Create a content model from the given model - clone.
1242
+ */
1243
+ createModelFrom: (modelId: string, data: CmsModelCreateFromInput) => Promise<CmsModel>;
1214
1244
  /**
1215
1245
  * Update content model without data validation. Used internally.
1216
- *
1217
- * @param model - existing content model
1218
- * @param data - data to be updated
1219
- *
1220
1246
  * @hidden
1221
1247
  */
1222
1248
  updateModelDirect: (params: CmsModelUpdateDirectParams) => Promise<CmsModel>;
@@ -1244,6 +1270,8 @@ export interface CmsModelContext {
1244
1270
  */
1245
1271
  onBeforeModelCreate: Topic<BeforeModelCreateTopicParams>;
1246
1272
  onAfterModelCreate: Topic<AfterModelCreateTopicParams>;
1273
+ onBeforeModelCreateFrom: Topic<BeforeModelCreateFromTopicParams>;
1274
+ onAfterModelCreateFrom: Topic<AfterModelCreateFromTopicParams>;
1247
1275
  onBeforeModelUpdate: Topic<BeforeModelUpdateTopicParams>;
1248
1276
  onAfterModelUpdate: Topic<AfterModelUpdateTopicParams>;
1249
1277
  onBeforeModelDelete: Topic<BeforeModelDeleteTopicParams>;
@@ -1496,7 +1524,7 @@ export interface CmsEntryContext {
1496
1524
  /**
1497
1525
  * List entries for a model. Internal method used by get, listLatest and listPublished.
1498
1526
  */
1499
- listEntries: (model: CmsModel, params?: CmsEntryListParams) => Promise<[CmsEntry[], CmsEntryMeta]>;
1527
+ listEntries: (model: CmsModel, params: CmsEntryListParams) => Promise<[CmsEntry[], CmsEntryMeta]>;
1500
1528
  /**
1501
1529
  * Lists latest entries. Used for manage API.
1502
1530
  */
package/types.js CHANGED
@@ -191,6 +191,13 @@ exports.CONTENT_ENTRY_STATUS = void 0;
191
191
  * @category CmsModel
192
192
  */
193
193
 
194
+ /**
195
+ * A GraphQL params.data parameter received when creating content model from existing model.
196
+ *
197
+ * @category GraphQL params
198
+ * @category CmsModel
199
+ */
200
+
194
201
  /**
195
202
  * A definition for content model field received from the user.
196
203
  *