@nascentdigital/funnel-cms 5.0.2 → 5.0.3

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.
@@ -34,7 +34,8 @@ export declare class ContentfulContentClient implements ContentClient, IContentf
34
34
  clearToken(): void;
35
35
  createEntry<T extends ContentfulData>(contentTypeId: string, entry: ContentfulData.Draft<T>): Promise<T>;
36
36
  getEntry<TEntry extends ContentfulData>({ entryId, includeLinks, version }: IContentfulContentClient.GetEntry.Args<TEntry>): Promise<IContentfulContentClient.GetEntry.Result<TEntry>>;
37
- getEntries<TEntry extends ContentfulData>({ limit, version, includeLinks, ...query }: IContentfulContentClient.GetEntries.Args<TEntry>): Promise<IContentfulContentClient.GetEntries.Result<TEntry>>;
37
+ getEntries<TEntry extends ContentfulData>({ pagination, version, includeLinks, ...query }: IContentfulContentClient.GetEntries.Args<TEntry>): Promise<IContentfulContentClient.GetEntries.Result<TEntry>>;
38
+ uploadAsset(args: IContentfulContentClient.UploadAsset.Args): Promise<IContentfulContentClient.UploadAsset.Result>;
38
39
  static forEditor(sdk: KnownAppSDK): Promise<ContentfulContentClient>;
39
40
  }
40
41
  export declare namespace ContentfulContentClient {
@@ -110,18 +110,22 @@ class ContentfulContentClient {
110
110
  }
111
111
  async getEntries(_a) {
112
112
  var _b, _c;
113
- var { limit = 1000, version = 'draft', includeLinks = false } = _a, query = __rest(_a, ["limit", "version", "includeLinks"]);
113
+ var { pagination = 'all', version = 'draft', includeLinks = false } = _a, query = __rest(_a, ["pagination", "version", "includeLinks"]);
114
+ // setup pagination
115
+ const itemsPerRequest = pagination === 'all' ? 1000 : Math.min(pagination.pageSize, 1000);
116
+ const page = pagination === 'all' ? 1 : pagination.page;
117
+ let skip = pagination === 'all' ? 0 : (page - 1) * pagination.pageSize;
118
+ let totalItemFetched = 0;
119
+ let hasMorePages = true;
120
+ let totalItems = undefined;
114
121
  // loop until no more pages
115
122
  const client = version === 'published' ? this.cda : this.cpa;
116
- let skip = 0;
117
- let total = 1;
118
123
  const entries = [];
119
124
  const includes = [];
120
- while (skip < total) {
125
+ do {
121
126
  // fetch next page
122
127
  const _d = await client.entry.getMany({
123
- query: Object.assign(Object.assign({}, query), { limit,
124
- skip, include: includeLinks === true
128
+ query: Object.assign(Object.assign({}, query), { limit: itemsPerRequest, skip, include: includeLinks === true
125
129
  ? 10
126
130
  : includeLinks || undefined })
127
131
  }), { items } = _d, response = __rest(_d, ["items"]);
@@ -129,19 +133,79 @@ class ContentfulContentClient {
129
133
  // add items
130
134
  items.forEach(item => entries.push(models_2.ContentfulData.fromCdaData(item)));
131
135
  includes.push(...((_c = (_b = response.includes) === null || _b === void 0 ? void 0 : _b.Entry) !== null && _c !== void 0 ? _c : []));
132
- // update counts
133
- total = response.total;
134
- skip += limit;
135
- }
136
+ // update paging info
137
+ totalItemFetched += items.length;
138
+ totalItems = response.total;
139
+ skip += items.length;
140
+ hasMorePages = pagination === 'all'
141
+ ? skip < totalItems
142
+ : totalItemFetched < pagination.pageSize && skip < totalItems;
143
+ } while (hasMorePages);
136
144
  console.debug('[ContentfulContentClient/getEntries] fetched entries', { entries, includes });
137
145
  // normalize references
138
146
  const references = includes.map(link => models_2.ContentfulData.fromCdaData(link));
139
147
  // return
140
148
  return {
141
149
  entries,
142
- references
150
+ references,
151
+ page,
152
+ pageCount: pagination === 'all'
153
+ ? 1
154
+ : Math.floor(totalItems / pagination.pageSize),
155
+ totalItemCount: totalItems
143
156
  };
144
157
  }
158
+ async uploadAsset(args) {
159
+ var _a;
160
+ // upload file
161
+ const { cma, locale } = this;
162
+ const uploadResult = await this.cma.upload.create({}, {
163
+ file: await args.file.arrayBuffer()
164
+ });
165
+ // create contentful asset
166
+ const contentfulAsset = await cma.asset.create({}, {
167
+ fields: models_2.ContentfulData.toEntryData({
168
+ title: args.name,
169
+ description: (_a = args.description) !== null && _a !== void 0 ? _a : '',
170
+ file: {
171
+ contentType: args.file.type,
172
+ fileName: args.name,
173
+ uploadFrom: {
174
+ sys: {
175
+ type: 'Link',
176
+ linkType: 'Upload',
177
+ id: uploadResult.sys.id
178
+ }
179
+ }
180
+ }
181
+ }, locale)
182
+ });
183
+ // process contentful asset (creates url etc.) and return
184
+ const processedAsset = await cma.asset.processForAllLocales({}, contentfulAsset);
185
+ // create funnel asset then return
186
+ const entry = await cma.entry.create({ contentTypeId: models_2.ContentfulAssetData.contentType }, {
187
+ fields: models_2.ContentfulData.toEntryData({
188
+ name: args.name,
189
+ description: args.description,
190
+ brand: args.brand,
191
+ type: args.type,
192
+ scope: args.scope,
193
+ tags: args.tags,
194
+ url: 'https:' + processedAsset.fields.file[locale].url,
195
+ size: args.size,
196
+ metadata: args.metadata,
197
+ media: {
198
+ sys: {
199
+ type: 'Link',
200
+ linkType: 'Asset',
201
+ id: processedAsset.sys.id
202
+ }
203
+ }
204
+ }, locale)
205
+ });
206
+ // return asset
207
+ return models_2.ContentfulData.fromCmaData(entry, locale);
208
+ }
145
209
  static async forEditor(sdk) {
146
210
  var _a;
147
211
  // get current site config (throw if we can't)
@@ -1,7 +1,19 @@
1
1
  "use strict";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
2
13
  Object.defineProperty(exports, "__esModule", { value: true });
3
14
  exports.ContentfulPageClient = void 0;
4
15
  const funnel_core_1 = require("@nascentdigital/funnel-core");
16
+ const errors_1 = require("../errors");
5
17
  const models_1 = require("./models");
6
18
  // class
7
19
  class ContentfulPageClient {
@@ -51,7 +63,7 @@ class ContentfulPageClient {
51
63
  };
52
64
  }
53
65
  async getSummaries(args) {
54
- // fetch items (bail if there are no instances
66
+ // fetch items (bail if there are no instances)
55
67
  const { cms } = this;
56
68
  const { entries } = await cms.getEntries(Object.assign({ content_type: models_1.ContentfulPageData.contentType, version: args.version, select: getPageSelect(funnel_core_1.Schema.Page.Data.AsLink.props) }, getPageQueryParams(args)));
57
69
  // return result
@@ -60,8 +72,15 @@ class ContentfulPageClient {
60
72
  getPreviews(args) {
61
73
  throw new funnel_core_1.NotImplementedError();
62
74
  }
63
- search(args) {
64
- throw new funnel_core_1.NotImplementedError();
75
+ async search(args) {
76
+ // fetch items (bail if there are no instances)
77
+ const { cms } = this;
78
+ const { entries, page, pageCount, totalItemCount } = await cms.getEntries(Object.assign({ content_type: models_1.ContentfulPageData.contentType, version: args.version, select: getPageSelect(funnel_core_1.Schema.Page.Data.AsLink.props) }, getPageQueryParams(args)));
79
+ // return entries
80
+ return {
81
+ items: entries.map(entry => models_1.ContentfulPageData.deserialize(entry, funnel_core_1.Schema.Page.Data.AsLink.props)),
82
+ page, pageCount, totalItemCount
83
+ };
65
84
  }
66
85
  query(args) {
67
86
  throw new funnel_core_1.NotImplementedError();
@@ -71,38 +90,31 @@ class ContentfulPageClient {
71
90
  }
72
91
  async getGroup(args) {
73
92
  // fetch items (bail if there are no instances
93
+ const { language, region, version } = args, queryArgs = __rest(args, ["language", "region", "version"]);
74
94
  const { cms } = this;
75
- const { entries } = await cms.getEntries(Object.assign({ content_type: models_1.ContentfulPageData.contentType, version: args.version, select: getPageSelect(funnel_core_1.Schema.Page.Data.AsGroup.Instance.props) }, getPageQueryParams(args)));
95
+ const { entries } = await cms.getEntries(Object.assign({ content_type: models_1.ContentfulPageData.contentType, version, select: getPageSelect(funnel_core_1.Schema.Page.Data.AsGroup.Instance.props) }, getPageQueryParams(queryArgs)));
76
96
  // bail if no items
77
97
  if (entries.length === 0) {
78
98
  console.debug('[ContentfulPageClient/getGroup] no items in group', args);
79
- const { brand, groupId: possibleGroupId, type, slug, region } = args;
99
+ const { brand, groupId: possibleGroupId, type, slug } = queryArgs;
80
100
  return {
81
- group: { groupId: possibleGroupId !== null && possibleGroupId !== void 0 ? possibleGroupId : funnel_core_1.Schema.Page.GroupId.empty, brand, type, slug, region, instances: [] }
101
+ group: { groupId: possibleGroupId !== null && possibleGroupId !== void 0 ? possibleGroupId : funnel_core_1.Schema.Page.GroupId.empty, brand, type, slug, instances: [] }
82
102
  };
83
103
  }
84
- console.debug('[ContentfulPageClient/getGroup] processing group items', { args, entries });
104
+ console.debug('[ContentfulPageClient/getGroup] processing group items', args);
85
105
  // collate the group ids into summaries (fetches full page if region/language match exists
86
106
  const instances = entries.map(entry => models_1.ContentfulPageData.deserialize(entry, funnel_core_1.Schema.Page.Data.AsGroup.Instance.props));
87
107
  // bind page (if it exists)
88
- const { language, version } = args;
89
108
  const pageInstance = instances
90
- .find(instance => instance.language === language);
109
+ .find(instance => instance.region === region && instance.language === language);
91
110
  console.debug('[ContentfulPageClient/getGroup] getting page using this.get...', { id: pageInstance === null || pageInstance === void 0 ? void 0 : pageInstance.id, version });
92
111
  const page = pageInstance
93
112
  ? await this.get({ id: pageInstance.id, version })
94
113
  : undefined;
95
114
  // return group (and optional page)
96
- const { type, brand, groupId, region, slug } = entries[0];
115
+ const { type, brand, groupId, slug } = entries[0];
97
116
  return {
98
- group: {
99
- groupId,
100
- type,
101
- brand,
102
- region,
103
- slug,
104
- instances
105
- },
117
+ group: { groupId, type, brand, slug, instances },
106
118
  page
107
119
  };
108
120
  }
@@ -113,7 +125,7 @@ class ContentfulPageClient {
113
125
  : args;
114
126
  // fetch entries (should be one entry)
115
127
  const { cms } = this;
116
- const { entries, references } = await cms.getEntries(Object.assign({ content_type: models_1.ContentfulPageData.contentType, includeLinks: true, version: args.version, limit: 1 }, getPageQueryParams(queryArgs)));
128
+ const { entries, references } = await cms.getEntries(Object.assign({ content_type: models_1.ContentfulPageData.contentType, includeLinks: true, version: args.version, pagination: { page: 1, pageSize: 1 } }, getPageQueryParams(queryArgs)));
117
129
  // skip if no entries
118
130
  if (entries.length === 0) {
119
131
  return undefined;
@@ -121,8 +133,204 @@ class ContentfulPageClient {
121
133
  // use first entry and deserialize
122
134
  return Object.assign({ data: models_1.ContentfulPageData.deserialize(entries[0]) }, models_1.ContentfulPageData.deserializeRefs(references));
123
135
  }
124
- create(args) {
125
- throw new funnel_core_1.NotImplementedError();
136
+ async create(args) {
137
+ console.debug('[ContentfulPageClient/create] processing create page request', args);
138
+ // create a new language of an existing page
139
+ if (funnel_core_1.Schema.Page.Data.AsNewLanguage.filter(args)) {
140
+ // fetch all pages in group (summaries only)
141
+ const existingPages = await this.getSummaries({
142
+ version: 'draft',
143
+ brand: args.brand,
144
+ groupId: args.groupId
145
+ });
146
+ // throw if there are no pages
147
+ if (existingPages.length === 0) {
148
+ throw new funnel_core_1.ArgumentError(`Page group "${args.groupId}" no longer exists in "${args.brand}"`);
149
+ }
150
+ // return existing page if it's there
151
+ let existingPage = existingPages
152
+ .find(existing => existing.region === args.region && existing.language === args.language);
153
+ if (existingPage) {
154
+ console.debug('[ContentfulPageClient/create/lang] returning existing page', existingPage);
155
+ return this.getGroup(Object.assign(Object.assign({}, existingPage), { version: 'draft' }));
156
+ }
157
+ // bind data + update url
158
+ existingPage = existingPages[0];
159
+ const draft = funnel_core_1.Schema.PageModel.fromDraft(Object.assign({ type: existingPage.type, slug: existingPage.slug, url: existingPage.url, name: existingPage.name, tags: existingPage.tags }, args));
160
+ draft.sync();
161
+ console.debug('[ContentfulPageClient/create/lang] creating from', { draft, existingPages });
162
+ // create page
163
+ try {
164
+ // create page
165
+ const draftData = draft.data;
166
+ const references = models_1.ContentfulData.findReferences([draftData.discover, draftData.content]);
167
+ const pageData = await this.cms.createEntry(models_1.ContentfulPageData.contentType, {
168
+ groupId: draftData.groupId,
169
+ brand: draftData.brand,
170
+ type: draftData.type,
171
+ region: draftData.region,
172
+ language: draftData.language,
173
+ name: draftData.name,
174
+ tags: draftData.tags,
175
+ slug: draftData.slug,
176
+ url: draftData.url,
177
+ preview: draftData.preview,
178
+ discover: draftData.discover,
179
+ content: draftData.content,
180
+ state: 'draft',
181
+ errors: true,
182
+ references
183
+ });
184
+ // transform entry to page data
185
+ const flattenedPage = {
186
+ data: pageData,
187
+ files: {},
188
+ collections: {},
189
+ pages: {}
190
+ };
191
+ const page = flattenedPage.data;
192
+ // return new page
193
+ return {
194
+ group: {
195
+ groupId: page.groupId,
196
+ brand: page.brand,
197
+ type: page.type,
198
+ slug: page.slug,
199
+ instances: [
200
+ // map exiting entries
201
+ ...existingPages.map(({ id, url, region, language, name, slug, tags, state, errors, createdOn, updatedOn, publishedOn }) => ({ id, url, region, language, name, slug, tags, state, errors, createdOn, updatedOn, publishedOn })),
202
+ // add new entry
203
+ {
204
+ id: page.id,
205
+ url: page.url,
206
+ region: page.region,
207
+ language: page.language,
208
+ name: page.name,
209
+ slug: page.slug,
210
+ tags: page.tags,
211
+ state: page.state,
212
+ errors: page.errors,
213
+ createdOn: page.createdOn,
214
+ updatedOn: page.updatedOn,
215
+ publishedOn: page.publishedOn
216
+ }
217
+ ]
218
+ },
219
+ page: flattenedPage
220
+ };
221
+ }
222
+ // or throw
223
+ catch (e) {
224
+ throw funnel_core_1.IsometricErrorUtils.coerceError(e);
225
+ }
226
+ }
227
+ // or create a net-new page
228
+ else {
229
+ console.debug('[ContentfulPageClient/create/new] creating fresh');
230
+ // get page's template
231
+ const site = funnel_core_1.Schema.Site.current();
232
+ const schema = site.regions[args.region][args.type];
233
+ // check slug if dynamic page
234
+ if (schema.variant === 'dynamic') {
235
+ // make sure there is a slug for a dynamic page
236
+ if (args.slug === undefined) {
237
+ throw new funnel_core_1.ArgumentError(`A "${args.type}" Page must provide a slug when created`);
238
+ }
239
+ // validate slug
240
+ const validateSlugResult = await this.validateSlug(Object.assign(Object.assign({}, args), { slug: args.slug }));
241
+ if (validateSlugResult.status !== 'available') {
242
+ throw new errors_1.SlugValidationError({ slug: args.slug, reason: validateSlugResult });
243
+ }
244
+ }
245
+ // fetch all pages matching brand/type/region
246
+ const { group: existingGroup, page: existingPage } = await this.getGroup({
247
+ version: 'draft',
248
+ brand: args.brand,
249
+ type: args.type,
250
+ region: args.region,
251
+ language: args.language,
252
+ slug: args.slug
253
+ });
254
+ // return existing page if it's there
255
+ if (existingPage) {
256
+ console.debug('[ContentfulPageClient/create/new] returning existing page', existingPage);
257
+ return { group: existingGroup, page: existingPage };
258
+ }
259
+ else if (existingGroup.instances.length > 0) {
260
+ console.debug('[ContentfulPageClient/create/new] using existing group', existingGroup);
261
+ }
262
+ else {
263
+ console.debug('[ContentfulPageClient/create/new] no existing group found');
264
+ }
265
+ // bind data + update url
266
+ const draft = funnel_core_1.Schema.PageModel.fromDraft(Object.assign(Object.assign({}, args), { groupId: existingGroup.groupId === funnel_core_1.Schema.Page.GroupId.empty
267
+ ? funnel_core_1.Value.uuid()
268
+ : existingGroup.groupId, url: '' }));
269
+ draft.sync();
270
+ // create page
271
+ try {
272
+ console.debug('[ContentfulPageClient/create/new] adding new page', draft.data);
273
+ // create page
274
+ const draftData = draft.data;
275
+ const references = models_1.ContentfulData.findReferences([draftData.discover, draftData.content]);
276
+ const pageData = await this.cms.createEntry(models_1.ContentfulPageData.contentType, {
277
+ groupId: draftData.groupId,
278
+ brand: draftData.brand,
279
+ type: draftData.type,
280
+ region: draftData.region,
281
+ language: draftData.language,
282
+ name: draftData.name,
283
+ tags: draftData.tags,
284
+ slug: draftData.slug,
285
+ url: draftData.url,
286
+ preview: draftData.preview,
287
+ discover: draftData.discover,
288
+ content: draftData.content,
289
+ state: 'draft',
290
+ errors: true,
291
+ references
292
+ });
293
+ // transform entry to page data
294
+ const flattenedPage = {
295
+ data: pageData,
296
+ files: {},
297
+ collections: {},
298
+ pages: {}
299
+ };
300
+ const page = flattenedPage.data;
301
+ // return page
302
+ return {
303
+ group: {
304
+ groupId: page.groupId,
305
+ brand: page.brand,
306
+ type: page.type,
307
+ slug: page.slug,
308
+ instances: [
309
+ ...existingGroup.instances,
310
+ {
311
+ id: page.id,
312
+ url: page.url,
313
+ region: page.region,
314
+ language: page.language,
315
+ name: page.name,
316
+ slug: page.slug,
317
+ tags: page.tags,
318
+ state: page.state,
319
+ errors: page.errors,
320
+ createdOn: page.createdOn,
321
+ updatedOn: page.updatedOn,
322
+ publishedOn: page.publishedOn
323
+ }
324
+ ]
325
+ },
326
+ page: flattenedPage
327
+ };
328
+ }
329
+ // or throw
330
+ catch (e) {
331
+ throw funnel_core_1.IsometricErrorUtils.coerceError(e);
332
+ }
333
+ }
126
334
  }
127
335
  duplicate(args) {
128
336
  throw new funnel_core_1.NotImplementedError();
@@ -12,7 +12,7 @@ class ContentfulSiteConfigClient {
12
12
  // fetch config model
13
13
  let { entries: [siteConfig] } = await this.cms.getEntries({
14
14
  content_type: models_1.ContentfulSiteConfigData.contentType,
15
- limit: 1,
15
+ pagination: { page: 1, pageSize: 1 },
16
16
  'fields.brand': brand
17
17
  });
18
18
  // or create it if it's not there
@@ -1,6 +1,7 @@
1
1
  import { CMAClient } from '@contentful/app-sdk';
2
2
  import { PlainClientAPI, QueryOptions } from 'contentful-management';
3
- import { Environment, User } from '../models';
3
+ import { AssetClient } from '../api';
4
+ import { Environment, Pagination, User } from '../models';
4
5
  import type { ContentfulAssetClient } from './ContentfulAssetClient';
5
6
  import type { ContentfulAuthClient } from './ContentfulAuthClient';
6
7
  import type { ContentfulChangeSetClient } from './ContentfulChangeSetClient';
@@ -8,7 +9,7 @@ import type { ContentfulCollectionClient } from './ContentfulCollectionClient';
8
9
  import type { ContentfulPageClient } from './ContentfulPageClient';
9
10
  import type { ContentfulSiteConfigClient } from './ContentfulSiteConfigClient';
10
11
  import type { ContentfulUserClient } from './ContentfulUserClient';
11
- import { ContentfulData } from './models';
12
+ import { ContentfulAssetData, ContentfulData } from './models';
12
13
  export interface IContentfulContentClient {
13
14
  readonly user: User;
14
15
  readonly locale: string;
@@ -25,6 +26,7 @@ export interface IContentfulContentClient {
25
26
  createEntry<TEntry extends ContentfulData>(contentType: string, entry: ContentfulData.Draft<TEntry>): Promise<TEntry>;
26
27
  getEntry<TEntry extends ContentfulData>(args: IContentfulContentClient.GetEntry.Args<TEntry>): Promise<IContentfulContentClient.GetEntry.Result<TEntry>>;
27
28
  getEntries<TEntry extends ContentfulData>(args: IContentfulContentClient.GetEntries.Args<TEntry>): Promise<IContentfulContentClient.GetEntries.Result<TEntry>>;
29
+ uploadAsset(args: IContentfulContentClient.UploadAsset.Args): Promise<IContentfulContentClient.UploadAsset.Result>;
28
30
  }
29
31
  export declare namespace IContentfulContentClient {
30
32
  type EntryQueryOptions = {
@@ -41,12 +43,19 @@ export declare namespace IContentfulContentClient {
41
43
  };
42
44
  }
43
45
  namespace GetEntries {
44
- type Args<TEntry extends ContentfulData> = Omit<QueryOptions, 'content_type' | 'include'> & EntryQueryOptions & {
46
+ type Args<TEntry extends ContentfulData> = Omit<QueryOptions, 'content_type' | 'include' | 'limit' | 'skip'> & EntryQueryOptions & {
45
47
  content_type: ContentfulData.ContentType<TEntry>;
48
+ pagination?: Pagination;
46
49
  };
47
- type Result<TEntry extends ContentfulData> = {
50
+ type Result<TEntry extends ContentfulData> = Pagination.Counts & {
48
51
  entries: TEntry[];
49
52
  references: ContentfulData[];
50
53
  };
51
54
  }
55
+ namespace UploadAsset {
56
+ type Args = AssetClient.CreateAsset.Args & {
57
+ file: Blob;
58
+ };
59
+ type Result = ContentfulAssetData;
60
+ }
52
61
  }
@@ -1,6 +1,8 @@
1
1
  import { Schema } from '@nascentdigital/funnel-core';
2
2
  import { ContentfulData } from './ContentfulData';
3
- export type ContentfulAssetData = ContentfulData<ContentfulAssetData.ContentType, Schema.Asset.Id> & Omit<Schema.AssetField.LinkedValue, 'id'>;
3
+ export type ContentfulAssetData = ContentfulData<ContentfulAssetData.ContentType, Schema.Asset.Id> & Omit<Schema.Asset.Linked['value'], 'id'> & {
4
+ media: ContentfulData.Link.Asset;
5
+ };
4
6
  export declare namespace ContentfulAssetData {
5
7
  const contentType: "asset";
6
8
  type ContentType = typeof contentType;
@@ -1,10 +1,12 @@
1
1
  import { RequiredBy, Schema } from '@nascentdigital/funnel-core';
2
2
  import { ContentfulData } from './ContentfulData';
3
- export type ContentfulCollectionData = ContentfulData<ContentfulCollectionData.ContentType, Schema.Collection.Id> & Omit<Schema.Collection.Data, 'id' | 'changeSetId' | 'snapshotId' | 'snapshot'>;
3
+ export type ContentfulCollectionData = ContentfulData<ContentfulCollectionData.ContentType, Schema.Collection.Id> & Omit<Schema.Collection.Data, 'id' | 'changeSetId' | 'snapshotId' | 'snapshot'> & {
4
+ references: ContentfulData.References;
5
+ };
4
6
  export declare namespace ContentfulCollectionData {
5
7
  const contentType: "collection";
6
8
  type ContentType = typeof contentType;
7
- type ForSchema = Schema.Collection.Data & Pick<ContentfulData, 'references'>;
9
+ type ForSchema = Schema.Collection.Data;
8
10
  type Potential = RequiredBy<Partial<ContentfulCollectionData>, keyof ContentfulData<ContentfulCollectionData.ContentType, Schema.Collection.Id>>;
9
11
  function deserialize(data: Potential, props?: ReadonlyArray<Schema.Collection.Data.Props.All>): ForSchema;
10
12
  function deserializeRefs(refs: ContentfulData[]): Schema.Collection.References;
@@ -7,13 +7,12 @@ export type ContentfulData<TType extends string = string, TId extends string = s
7
7
  id: TId;
8
8
  createdOn: number;
9
9
  updatedOn: number;
10
- references: ContentfulData.References;
11
10
  };
12
11
  export declare namespace ContentfulData {
13
12
  type ContentType<T extends ContentfulData> = T['__type'];
14
13
  type EntryId<T extends ContentfulData> = T['id'];
15
14
  type References = Array<Link.Entry | Link.Asset>;
16
- type Draft<T extends ContentfulData> = Omit<T, keyof ContentfulData>;
15
+ type Draft<T extends ContentfulData> = Omit<T, keyof ContentfulData | '__root'>;
17
16
  type Link<TType extends Link.Type = Link.Type> = {
18
17
  sys: {
19
18
  type: 'Link';
@@ -1,6 +1,8 @@
1
1
  import { RequiredBy, Schema } from '@nascentdigital/funnel-core';
2
2
  import { ContentfulData } from './ContentfulData';
3
- export type ContentfulPageData = ContentfulData<ContentfulPageData.ContentType, Schema.Page.Id> & Omit<Schema.Page.Data, 'id' | 'changeSetId' | 'snapshotId' | 'snapshot'>;
3
+ export type ContentfulPageData = ContentfulData<ContentfulPageData.ContentType, Schema.Page.Id> & Omit<Schema.Page.Data, 'id' | 'changeSetId' | 'snapshotId' | 'snapshot'> & {
4
+ references: ContentfulData.References;
5
+ };
4
6
  export declare namespace ContentfulPageData {
5
7
  const contentType: "page";
6
8
  type ContentType = typeof contentType;
@@ -10,7 +12,7 @@ export declare namespace ContentfulPageData {
10
12
  readonly asset: "files";
11
13
  };
12
14
  type ReferencesSpec = typeof referencesSpec;
13
- type ForSchema = Schema.Page.Data & Pick<ContentfulData, 'references'>;
15
+ type ForSchema = Schema.Page.Data;
14
16
  type Potential = RequiredBy<Partial<ContentfulPageData>, keyof ContentfulData<ContentfulPageData.ContentType, Schema.Page.Id>>;
15
17
  function deserialize(data: Potential, props?: ReadonlyArray<Schema.Page.Data.Props.All>): ForSchema;
16
18
  function deserializeRefs(refs: ContentfulData[]): Schema.Page.References;
@@ -17,4 +17,5 @@ export declare namespace Pagination {
17
17
  pageCount: number;
18
18
  totalItemCount: number;
19
19
  };
20
+ type Counts = Omit<Paginated<any>, 'items'>;
20
21
  }
@@ -150,12 +150,12 @@ class StrapiPageClient {
150
150
  async getGroup({ brand, groupId, type, slug, region, language, version, select }) {
151
151
  // create base result
152
152
  const result = {
153
- group: { groupId: groupId !== null && groupId !== void 0 ? groupId : funnel_core_1.Schema.Page.GroupId.empty, brand, type, slug, region, instances: [] }
153
+ group: { groupId: groupId !== null && groupId !== void 0 ? groupId : funnel_core_1.Schema.Page.GroupId.empty, brand, type, slug, instances: [] }
154
154
  };
155
155
  // fetch baseline page (fail if it doesn't exist)
156
156
  const client = this.provider.client;
157
157
  const collection = client.collection('pages');
158
- const { items: baselineItems = [] } = await Strapi_1.Strapi.paginate(Object.assign({ collection, pagination: 'all', fields: serialization_1.PageData.getStrapiFilter(funnel_core_1.Schema.Page.Data.Props.id) }, getPageQueryParams({ version, brand, groupId, type, slug, region })));
158
+ const { items: baselineItems = [] } = await Strapi_1.Strapi.paginate(Object.assign({ collection, pagination: 'all', fields: serialization_1.PageData.getStrapiFilter(funnel_core_1.Schema.Page.Data.Props.id) }, getPageQueryParams({ version, brand, groupId, type, slug })));
159
159
  const [baselinePage] = baselineItems;
160
160
  if (!baselinePage) {
161
161
  return result;
@@ -172,7 +172,7 @@ class StrapiPageClient {
172
172
  const instance = serialization_1.PageData.deserializeLegacy(item, client.baseURL, funnel_core_1.Schema.Page.Data.AsGroup.Instance.props);
173
173
  group.instances.push(instance);
174
174
  // set page if language matches
175
- if (instance.language === language) {
175
+ if (instance.region === region && instance.language === language) {
176
176
  result.page = await this.get({ id: instance.id, version, select });
177
177
  }
178
178
  }
@@ -239,13 +239,14 @@ class StrapiPageClient {
239
239
  throw new funnel_core_1.ArgumentError(`Page group "${args.groupId}" no longer exists in "${args.brand}"`);
240
240
  }
241
241
  // return existing page if it's there
242
- let existingPage = existingPages.find(existing => existing.language === args.language);
242
+ let existingPage = existingPages
243
+ .find(existing => existing.region === args.region && existing.language === args.language);
243
244
  if (existingPage) {
244
245
  return this.getGroup(Object.assign(Object.assign({}, existingPage), { version: 'draft' }));
245
246
  }
246
247
  // bind data + update url
247
248
  existingPage = existingPages[0];
248
- const draft = funnel_core_1.Schema.PageModel.fromDraft(Object.assign({ type: existingPage.type, slug: existingPage.slug, url: existingPage.url, region: existingPage.region, name: existingPage.name, tags: existingPage.tags }, args));
249
+ const draft = funnel_core_1.Schema.PageModel.fromDraft(Object.assign({ type: existingPage.type, slug: existingPage.slug, url: existingPage.url, name: existingPage.name, tags: existingPage.tags }, args));
249
250
  draft.sync();
250
251
  // resolve change set (rebind)
251
252
  const { provider } = this;
@@ -290,13 +291,13 @@ class StrapiPageClient {
290
291
  groupId: page.groupId,
291
292
  brand: page.brand,
292
293
  type: page.type,
293
- region: page.region,
294
294
  slug: page.slug,
295
295
  instances: [
296
- ...existingPages.map(({ id, url, language, name, slug, tags, state, errors, createdOn, updatedOn, publishedOn }) => ({ id, url, language, name, slug, tags, state, errors, createdOn, updatedOn, publishedOn })),
296
+ ...existingPages.map(({ id, url, region, language, name, slug, tags, state, errors, createdOn, updatedOn, publishedOn }) => ({ id, url, region, language, name, slug, tags, state, errors, createdOn, updatedOn, publishedOn })),
297
297
  {
298
298
  id: page.id,
299
299
  url: page.url,
300
+ region: page.region,
300
301
  language: page.language,
301
302
  name: page.name,
302
303
  slug: page.slug,
@@ -345,8 +346,8 @@ class StrapiPageClient {
345
346
  brand: args.brand,
346
347
  type: args.type,
347
348
  region: args.region,
348
- slug: args.slug,
349
- language: args.language
349
+ language: args.language,
350
+ slug: args.slug
350
351
  });
351
352
  // return existing page if it's there
352
353
  if (existingPage) {
@@ -400,13 +401,13 @@ class StrapiPageClient {
400
401
  groupId: page.groupId,
401
402
  brand: page.brand,
402
403
  type: page.type,
403
- region: args.region,
404
404
  slug: page.slug,
405
405
  instances: [
406
406
  ...existingGroup.instances,
407
407
  {
408
408
  id: page.id,
409
409
  url: page.url,
410
+ region: args.region,
410
411
  language: page.language,
411
412
  name: page.name,
412
413
  slug: page.slug,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nascentdigital/funnel-cms",
3
- "version": "5.0.2",
3
+ "version": "5.0.3",
4
4
  "repository": {
5
5
  "url": "git+https://github.com/nascentdigital/funnel.git",
6
6
  "directory": "libs/cms"
@@ -48,5 +48,5 @@
48
48
  "ts-jest": "^29",
49
49
  "type-fest": "^4"
50
50
  },
51
- "gitHead": "24d9690cf813052f6561c3ccef11c7cf69ad7f7c"
51
+ "gitHead": "0d1cc7d78504e8819998d928ffb342d622f75690"
52
52
  }