@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.
- package/dist/contentful/ContentfulContentClient.d.ts +2 -1
- package/dist/contentful/ContentfulContentClient.js +75 -11
- package/dist/contentful/ContentfulPageClient.js +229 -21
- package/dist/contentful/ContentfulSiteConfigClient.js +1 -1
- package/dist/contentful/IContentfulContentClient.d.ts +13 -4
- package/dist/contentful/models/ContentfulAssetData.d.ts +3 -1
- package/dist/contentful/models/ContentfulCollectionData.d.ts +4 -2
- package/dist/contentful/models/ContentfulData.d.ts +1 -2
- package/dist/contentful/models/ContentfulPageData.d.ts +4 -2
- package/dist/models/Pagination.d.ts +1 -0
- package/dist/strapi/StrapiPageClient.js +11 -10
- package/package.json +2 -2
|
@@ -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>({
|
|
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 {
|
|
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
|
-
|
|
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
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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,
|
|
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',
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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.
|
|
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
|
|
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
|
|
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;
|
|
@@ -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,
|
|
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
|
|
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
|
|
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,
|
|
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
|
-
|
|
349
|
-
|
|
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.
|
|
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": "
|
|
51
|
+
"gitHead": "0d1cc7d78504e8819998d928ffb342d622f75690"
|
|
52
52
|
}
|