@stackbit/cms-core 0.7.6-develop.2 → 0.8.0-develop.1
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/.tsbuildinfo +1 -1
- package/dist/content-store-utils.d.ts +44 -0
- package/dist/content-store-utils.d.ts.map +1 -1
- package/dist/content-store-utils.js +213 -1
- package/dist/content-store-utils.js.map +1 -1
- package/dist/content-store.d.ts +12 -1
- package/dist/content-store.d.ts.map +1 -1
- package/dist/content-store.js +45 -6
- package/dist/content-store.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/types/content-store-document-fields.d.ts +105 -0
- package/dist/types/content-store-document-fields.d.ts.map +1 -1
- package/dist/types/content-store-documents.d.ts +4 -1
- package/dist/types/content-store-documents.d.ts.map +1 -1
- package/dist/types/content-store-types.d.ts +10 -10
- package/dist/types/content-store-types.d.ts.map +1 -1
- package/dist/types/custom-actions.d.ts +108 -0
- package/dist/types/custom-actions.d.ts.map +1 -0
- package/dist/types/custom-actions.js +3 -0
- package/dist/types/custom-actions.js.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/dist/utils/csi-to-store-docs-converter.d.ts +2 -1
- package/dist/utils/csi-to-store-docs-converter.d.ts.map +1 -1
- package/dist/utils/csi-to-store-docs-converter.js +144 -11
- package/dist/utils/csi-to-store-docs-converter.js.map +1 -1
- package/dist/utils/custom-actions.d.ts +53 -0
- package/dist/utils/custom-actions.d.ts.map +1 -0
- package/dist/utils/custom-actions.js +586 -0
- package/dist/utils/custom-actions.js.map +1 -0
- package/dist/utils/document-hooks.d.ts +9 -0
- package/dist/utils/document-hooks.d.ts.map +1 -1
- package/dist/utils/document-hooks.js +3 -2
- package/dist/utils/document-hooks.js.map +1 -1
- package/dist/utils/search-utils.d.ts +1 -1
- package/dist/utils/search-utils.d.ts.map +1 -1
- package/dist/utils/store-to-api-docs-converter.d.ts.map +1 -1
- package/dist/utils/store-to-api-docs-converter.js +77 -29
- package/dist/utils/store-to-api-docs-converter.js.map +1 -1
- package/dist/utils/store-to-csi-docs-converter.d.ts +3 -0
- package/dist/utils/store-to-csi-docs-converter.d.ts.map +1 -1
- package/dist/utils/store-to-csi-docs-converter.js +2 -1
- package/dist/utils/store-to-csi-docs-converter.js.map +1 -1
- package/package.json +5 -5
- package/src/content-store-utils.ts +247 -0
- package/src/content-store.ts +62 -7
- package/src/index.ts +0 -1
- package/src/types/content-store-document-fields.ts +55 -2
- package/src/types/content-store-documents.ts +4 -1
- package/src/types/content-store-types.ts +10 -10
- package/src/types/custom-actions.ts +154 -0
- package/src/types/index.ts +1 -0
- package/src/utils/csi-to-store-docs-converter.ts +220 -12
- package/src/utils/custom-actions.ts +730 -0
- package/src/utils/document-hooks.ts +3 -3
- package/src/utils/search-utils.ts +1 -1
- package/src/utils/store-to-api-docs-converter.ts +88 -33
- package/src/utils/store-to-csi-docs-converter.ts +4 -4
|
@@ -0,0 +1,730 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import { Config, mapModelFieldsRecursively } from '@stackbit/sdk';
|
|
3
|
+
import * as StackbitTypes from '@stackbit/types';
|
|
4
|
+
import { mapPromise, omitByNil } from '@stackbit/utils';
|
|
5
|
+
import * as ContentStoreTypes from '../types';
|
|
6
|
+
import {
|
|
7
|
+
CustomActionStateChange,
|
|
8
|
+
APICustomAction,
|
|
9
|
+
APICustomActionDocumentSpecifier,
|
|
10
|
+
APICustomActionDocument,
|
|
11
|
+
APICustomActionObject,
|
|
12
|
+
APICustomActionField,
|
|
13
|
+
APIGetCustomActionRequest,
|
|
14
|
+
APIRunCustomActionRequest,
|
|
15
|
+
APIRunCustomActionRequestBulk,
|
|
16
|
+
ContentStoreCustomActionMap,
|
|
17
|
+
ContentStoreCustomActionGlobal,
|
|
18
|
+
ContentStoreCustomActionBulk,
|
|
19
|
+
ContentStoreCustomActionDocument,
|
|
20
|
+
ContentStoreCustomActionObjectModel,
|
|
21
|
+
ContentStoreCustomActionField,
|
|
22
|
+
APICustomActionGlobal,
|
|
23
|
+
APICustomActionBulk,
|
|
24
|
+
ContentStoreCustomAction,
|
|
25
|
+
ContentStoreCustomActionObjectField
|
|
26
|
+
} from '../types';
|
|
27
|
+
import { createConfigDelegate } from './config-delegate';
|
|
28
|
+
import { getContentSourceActionsForSourceThunk } from './document-hooks';
|
|
29
|
+
import { mapStoreDocumentToCSIDocumentWithSource, mapStoreFieldToCSIField } from './store-to-csi-docs-converter';
|
|
30
|
+
import {
|
|
31
|
+
getContentSourceDataByTypeAndProjectIdOrThrow,
|
|
32
|
+
getModelAndDocumentFieldForLocalizedFieldPath,
|
|
33
|
+
getUserContextForSrcTypeThunk
|
|
34
|
+
} from '../content-store-utils';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Removes "run" and "state" functions from actions of models, nested objects and fields.
|
|
38
|
+
* Sets "needsResolving" flag to let client know if the action needs additional resolving.
|
|
39
|
+
* @param {StackbitTypes.ModelMap} modelMap
|
|
40
|
+
*/
|
|
41
|
+
export function stripModelActions({ modelMap }: { modelMap: StackbitTypes.ModelMap }): StackbitTypes.ModelMap {
|
|
42
|
+
return _.mapValues(modelMap, (model) => {
|
|
43
|
+
if ('actions' in model) {
|
|
44
|
+
const { actions, ...restModel } = model;
|
|
45
|
+
model = restModel;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
model = mapModelFieldsRecursively(model, (field) => {
|
|
49
|
+
if ('actions' in field && Array.isArray(field.actions)) {
|
|
50
|
+
const { actions, ...restField } = field;
|
|
51
|
+
field = restField;
|
|
52
|
+
}
|
|
53
|
+
return field;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return model;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function getGlobalAndBulkAPIActions({
|
|
61
|
+
stackbitConfig,
|
|
62
|
+
customActionMap,
|
|
63
|
+
contentSourceDataById,
|
|
64
|
+
userLogger,
|
|
65
|
+
pageUrl,
|
|
66
|
+
user,
|
|
67
|
+
locale,
|
|
68
|
+
currentPageDocument
|
|
69
|
+
}: {
|
|
70
|
+
stackbitConfig: Config | null;
|
|
71
|
+
customActionMap: ContentStoreCustomActionMap;
|
|
72
|
+
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
73
|
+
userLogger: StackbitTypes.Logger;
|
|
74
|
+
pageUrl?: string;
|
|
75
|
+
user?: ContentStoreTypes.User;
|
|
76
|
+
locale?: string;
|
|
77
|
+
currentPageDocument?: APICustomActionDocumentSpecifier;
|
|
78
|
+
}): Promise<(APICustomActionGlobal | APICustomActionBulk)[]> {
|
|
79
|
+
if (!stackbitConfig || !Array.isArray(stackbitConfig.actions)) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const configDelegate = createConfigDelegate({
|
|
84
|
+
contentSourceDataById: contentSourceDataById,
|
|
85
|
+
logger: userLogger
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
return mapPromise(stackbitConfig.actions, async (action) => {
|
|
89
|
+
const actionId = `config.actions.${action.name}`;
|
|
90
|
+
const prevStoreAction = customActionMap[actionId] as ContentStoreCustomActionGlobal | ContentStoreCustomActionBulk | undefined;
|
|
91
|
+
const storeAction: ContentStoreCustomActionGlobal | ContentStoreCustomActionBulk = {
|
|
92
|
+
// if configuration is updated, the new action properties will override the stored action properties
|
|
93
|
+
...action,
|
|
94
|
+
actionId,
|
|
95
|
+
label: action.label ?? _.startCase(action.name),
|
|
96
|
+
runningHandler: prevStoreAction?.runningHandler,
|
|
97
|
+
lastResultState: prevStoreAction?.lastResultState
|
|
98
|
+
};
|
|
99
|
+
customActionMap[actionId] = storeAction;
|
|
100
|
+
|
|
101
|
+
let state: StackbitTypes.CustomActionState | 'unknown';
|
|
102
|
+
|
|
103
|
+
if (storeAction.runningHandler) {
|
|
104
|
+
state = 'running';
|
|
105
|
+
} else if (storeAction.state) {
|
|
106
|
+
const pageDocument = getCSIDocumentWithSourceFromDocumentSpec(currentPageDocument, configDelegate);
|
|
107
|
+
state = await storeAction.state({
|
|
108
|
+
actionId: actionId,
|
|
109
|
+
currentLocale: locale,
|
|
110
|
+
currentUser: user ? { name: user.name, email: user.email } : undefined,
|
|
111
|
+
currentPageUrl: pageUrl,
|
|
112
|
+
currentPageDocument: pageDocument,
|
|
113
|
+
...configDelegate
|
|
114
|
+
});
|
|
115
|
+
} else {
|
|
116
|
+
state = storeAction.lastResultState ?? 'enabled';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return omitByNil({
|
|
120
|
+
actionId: actionId,
|
|
121
|
+
type: storeAction.type,
|
|
122
|
+
name: storeAction.name,
|
|
123
|
+
label: storeAction.label,
|
|
124
|
+
icon: storeAction.icon,
|
|
125
|
+
state: state,
|
|
126
|
+
inputFields: storeAction.inputFields
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function convertDocumentActionToAPICustomDocumentAction({
|
|
132
|
+
action,
|
|
133
|
+
customActionMap,
|
|
134
|
+
csiDocument
|
|
135
|
+
}: {
|
|
136
|
+
action: StackbitTypes.CustomActionDocument;
|
|
137
|
+
customActionMap: ContentStoreCustomActionMap;
|
|
138
|
+
csiDocument: StackbitTypes.DocumentWithSource;
|
|
139
|
+
}): APICustomActionDocument {
|
|
140
|
+
const actionId = `${csiDocument.srcType}:${csiDocument.srcProjectId}:${csiDocument.id}:${action.name}`;
|
|
141
|
+
|
|
142
|
+
const prevStoreAction = customActionMap[actionId] as ContentStoreCustomActionDocument | undefined;
|
|
143
|
+
const storeAction: ContentStoreCustomActionDocument = {
|
|
144
|
+
// if configuration is updated, the new action properties will override the stored action properties
|
|
145
|
+
...action,
|
|
146
|
+
type: 'document',
|
|
147
|
+
actionId,
|
|
148
|
+
label: action.label ?? _.startCase(action.name),
|
|
149
|
+
runningHandler: prevStoreAction?.runningHandler,
|
|
150
|
+
lastResultState: prevStoreAction?.lastResultState,
|
|
151
|
+
documentSpec: {
|
|
152
|
+
srcType: csiDocument.srcType,
|
|
153
|
+
srcProjectId: csiDocument.srcProjectId,
|
|
154
|
+
srcDocumentId: csiDocument.id
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
customActionMap[actionId] = storeAction;
|
|
158
|
+
|
|
159
|
+
let state: StackbitTypes.CustomActionState | 'unknown';
|
|
160
|
+
let needsResolving = false;
|
|
161
|
+
if (storeAction?.runningHandler) {
|
|
162
|
+
state = 'running';
|
|
163
|
+
} else if (action.state) {
|
|
164
|
+
state = 'unknown';
|
|
165
|
+
needsResolving = true;
|
|
166
|
+
} else {
|
|
167
|
+
state = storeAction?.lastResultState ?? 'enabled';
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return omitByNil({
|
|
171
|
+
actionId: actionId,
|
|
172
|
+
type: 'document',
|
|
173
|
+
name: storeAction.name,
|
|
174
|
+
label: storeAction.label,
|
|
175
|
+
icon: storeAction.icon,
|
|
176
|
+
state: state,
|
|
177
|
+
inputFields: storeAction.inputFields,
|
|
178
|
+
needsResolving: needsResolving
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function convertObjectModelActionToAPICustomObjectAction({
|
|
183
|
+
action,
|
|
184
|
+
customActionMap,
|
|
185
|
+
csiParentDocument,
|
|
186
|
+
fieldPath
|
|
187
|
+
}: {
|
|
188
|
+
action: StackbitTypes.CustomActionObjectModel;
|
|
189
|
+
customActionMap: ContentStoreCustomActionMap;
|
|
190
|
+
csiParentDocument: StackbitTypes.DocumentWithSource;
|
|
191
|
+
fieldPath: (string | number)[];
|
|
192
|
+
}): APICustomActionObject {
|
|
193
|
+
const actionId = `${csiParentDocument.srcType}:${csiParentDocument.srcProjectId}:${csiParentDocument.id}:${fieldPath.join('.')}:${action.name}`;
|
|
194
|
+
|
|
195
|
+
const prevStoreAction = customActionMap[actionId] as ContentStoreCustomActionObjectModel | undefined;
|
|
196
|
+
const storeAction: ContentStoreCustomActionObjectModel = {
|
|
197
|
+
// if configuration is updated, the new action properties will override the stored action properties
|
|
198
|
+
...action,
|
|
199
|
+
type: 'objectModel',
|
|
200
|
+
actionId,
|
|
201
|
+
label: action.label ?? _.startCase(action.name),
|
|
202
|
+
runningHandler: prevStoreAction?.runningHandler,
|
|
203
|
+
lastResultState: prevStoreAction?.lastResultState,
|
|
204
|
+
documentSpec: {
|
|
205
|
+
srcType: csiParentDocument.srcType,
|
|
206
|
+
srcProjectId: csiParentDocument.srcProjectId,
|
|
207
|
+
srcDocumentId: csiParentDocument.id
|
|
208
|
+
},
|
|
209
|
+
fieldPath
|
|
210
|
+
};
|
|
211
|
+
customActionMap[actionId] = storeAction;
|
|
212
|
+
|
|
213
|
+
let state: StackbitTypes.CustomActionState | 'unknown';
|
|
214
|
+
let needsResolving = false;
|
|
215
|
+
if (storeAction?.runningHandler) {
|
|
216
|
+
state = 'running';
|
|
217
|
+
} else if (action.state) {
|
|
218
|
+
state = 'unknown';
|
|
219
|
+
needsResolving = true;
|
|
220
|
+
} else {
|
|
221
|
+
state = storeAction?.lastResultState ?? 'enabled';
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return omitByNil({
|
|
225
|
+
actionId: actionId,
|
|
226
|
+
type: 'object',
|
|
227
|
+
name: storeAction.name,
|
|
228
|
+
label: storeAction.label,
|
|
229
|
+
icon: storeAction.icon,
|
|
230
|
+
state: state,
|
|
231
|
+
inputFields: storeAction.inputFields,
|
|
232
|
+
needsResolving: needsResolving
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function convertFieldActionToAPICustomAction<T extends StackbitTypes.CustomActionField | StackbitTypes.CustomActionObjectField>({
|
|
237
|
+
action,
|
|
238
|
+
customActionMap,
|
|
239
|
+
csiParentDocument,
|
|
240
|
+
fieldPath
|
|
241
|
+
}: {
|
|
242
|
+
action: T;
|
|
243
|
+
customActionMap: ContentStoreCustomActionMap;
|
|
244
|
+
csiParentDocument: StackbitTypes.DocumentWithSource;
|
|
245
|
+
fieldPath: (string | number)[];
|
|
246
|
+
}): T extends StackbitTypes.CustomActionField ? APICustomActionField : APICustomActionObject {
|
|
247
|
+
const actionId = `${csiParentDocument.srcType}:${csiParentDocument.srcProjectId}:${csiParentDocument.id}:${fieldPath.join('.')}:${action.name}`;
|
|
248
|
+
|
|
249
|
+
const prevStoreAction = customActionMap[actionId] as ContentStoreCustomActionField | ContentStoreCustomActionObjectField | undefined;
|
|
250
|
+
const storeAction = {
|
|
251
|
+
// if configuration is updated, the new action properties will override the stored action properties
|
|
252
|
+
...action,
|
|
253
|
+
type: action.type === 'object' ? 'objectField' : 'field',
|
|
254
|
+
actionId,
|
|
255
|
+
label: action.label ?? _.startCase(action.name),
|
|
256
|
+
runningHandler: prevStoreAction?.runningHandler,
|
|
257
|
+
lastResultState: prevStoreAction?.lastResultState,
|
|
258
|
+
documentSpec: {
|
|
259
|
+
srcType: csiParentDocument.srcType,
|
|
260
|
+
srcProjectId: csiParentDocument.srcProjectId,
|
|
261
|
+
srcDocumentId: csiParentDocument.id
|
|
262
|
+
},
|
|
263
|
+
fieldPath
|
|
264
|
+
} as ContentStoreCustomActionField | ContentStoreCustomActionObjectField;
|
|
265
|
+
customActionMap[actionId] = storeAction;
|
|
266
|
+
|
|
267
|
+
let state: StackbitTypes.CustomActionState | 'unknown';
|
|
268
|
+
let needsResolving = false;
|
|
269
|
+
if (storeAction?.runningHandler) {
|
|
270
|
+
state = 'running';
|
|
271
|
+
} else if (action.state) {
|
|
272
|
+
state = 'unknown';
|
|
273
|
+
needsResolving = true;
|
|
274
|
+
} else {
|
|
275
|
+
state = storeAction?.lastResultState ?? 'enabled';
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return omitByNil({
|
|
279
|
+
actionId: actionId,
|
|
280
|
+
type: action.type ?? 'field',
|
|
281
|
+
name: storeAction.name,
|
|
282
|
+
label: storeAction.label,
|
|
283
|
+
icon: storeAction.icon,
|
|
284
|
+
state: state,
|
|
285
|
+
inputFields: storeAction.inputFields,
|
|
286
|
+
needsResolving: needsResolving
|
|
287
|
+
}) as T extends StackbitTypes.CustomActionField ? APICustomActionField : APICustomActionObject;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export async function resolveCustomActionsById({
|
|
291
|
+
getActionRequest,
|
|
292
|
+
customActionMap,
|
|
293
|
+
contentSourceDataById,
|
|
294
|
+
userLogger
|
|
295
|
+
}: {
|
|
296
|
+
getActionRequest: APIGetCustomActionRequest;
|
|
297
|
+
customActionMap: ContentStoreCustomActionMap;
|
|
298
|
+
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
299
|
+
userLogger: StackbitTypes.Logger;
|
|
300
|
+
}): Promise<APICustomAction[]> {
|
|
301
|
+
const result: APICustomAction[] = [];
|
|
302
|
+
const { customActionIds, locale, user, pageUrl, currentPageDocument } = getActionRequest;
|
|
303
|
+
|
|
304
|
+
const configDelegate = createConfigDelegate({
|
|
305
|
+
contentSourceDataById,
|
|
306
|
+
logger: userLogger
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
for (const actionId of customActionIds) {
|
|
310
|
+
const storeAction = customActionMap[actionId];
|
|
311
|
+
if (!storeAction) {
|
|
312
|
+
userLogger.debug(`getCustomActionsById: custom action not found, id: '${actionId}'`);
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
try {
|
|
317
|
+
let state: StackbitTypes.CustomActionState;
|
|
318
|
+
if (storeAction.runningHandler) {
|
|
319
|
+
state = 'running';
|
|
320
|
+
} else if (storeAction.state) {
|
|
321
|
+
const pageDocument = getCSIDocumentWithSourceFromDocumentSpec(currentPageDocument, configDelegate);
|
|
322
|
+
const commonStateOptions: StackbitTypes.CustomActionStateCommonOptions = {
|
|
323
|
+
actionId: actionId,
|
|
324
|
+
currentLocale: locale,
|
|
325
|
+
currentUser: user
|
|
326
|
+
? {
|
|
327
|
+
name: user.name,
|
|
328
|
+
email: user.email
|
|
329
|
+
}
|
|
330
|
+
: undefined,
|
|
331
|
+
currentPageUrl: pageUrl,
|
|
332
|
+
currentPageDocument: pageDocument,
|
|
333
|
+
...configDelegate
|
|
334
|
+
};
|
|
335
|
+
if (storeAction.type === 'global' || storeAction.type === 'bulk') {
|
|
336
|
+
state = await storeAction.state(commonStateOptions);
|
|
337
|
+
} else if (storeAction.type === 'document') {
|
|
338
|
+
const { document, model } = getCSIDocumentAndModelWithSourceFromDocumentSpec({
|
|
339
|
+
documentSpec: storeAction.documentSpec,
|
|
340
|
+
contentSourceDataById
|
|
341
|
+
});
|
|
342
|
+
state = await storeAction.state({
|
|
343
|
+
...commonStateOptions,
|
|
344
|
+
document: document,
|
|
345
|
+
model: model
|
|
346
|
+
});
|
|
347
|
+
} else if (storeAction.type === 'objectModel') {
|
|
348
|
+
const stateObjectParams = getHandlerParamsForObjectModelAction({
|
|
349
|
+
storeAction,
|
|
350
|
+
contentSourceDataById
|
|
351
|
+
});
|
|
352
|
+
state = await storeAction.state({
|
|
353
|
+
...commonStateOptions,
|
|
354
|
+
...stateObjectParams
|
|
355
|
+
});
|
|
356
|
+
} else if (storeAction.type === 'objectField') {
|
|
357
|
+
const stateObjectParams = getHandlerParamsForObjectFieldAction({
|
|
358
|
+
storeAction,
|
|
359
|
+
contentSourceDataById
|
|
360
|
+
});
|
|
361
|
+
state = await storeAction.state({
|
|
362
|
+
...commonStateOptions,
|
|
363
|
+
...stateObjectParams
|
|
364
|
+
});
|
|
365
|
+
} else if (storeAction.type === 'field') {
|
|
366
|
+
const stateFieldParams = getHandlerParamsForFieldAction({
|
|
367
|
+
storeAction,
|
|
368
|
+
contentSourceDataById
|
|
369
|
+
});
|
|
370
|
+
state = await storeAction.state({
|
|
371
|
+
...commonStateOptions,
|
|
372
|
+
...stateFieldParams
|
|
373
|
+
});
|
|
374
|
+
} else {
|
|
375
|
+
const _exhaustiveCheck: never = storeAction;
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
} else {
|
|
379
|
+
state = storeAction?.lastResultState ?? 'enabled';
|
|
380
|
+
}
|
|
381
|
+
result.push(
|
|
382
|
+
omitByNil({
|
|
383
|
+
actionId: actionId,
|
|
384
|
+
type: storeActionTypeToAPIActionType(storeAction.type),
|
|
385
|
+
name: storeAction.name,
|
|
386
|
+
label: storeAction.label,
|
|
387
|
+
icon: storeAction.icon,
|
|
388
|
+
state: state,
|
|
389
|
+
inputFields: storeAction.inputFields
|
|
390
|
+
})
|
|
391
|
+
);
|
|
392
|
+
} catch (error: any) {
|
|
393
|
+
userLogger.warn(`getCustomActionsById: error resolving custom action, id: '${actionId}', error: ${error.message}`);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return result;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
export function runCustomAction({
|
|
401
|
+
runActionRequest,
|
|
402
|
+
customActionMap,
|
|
403
|
+
contentSourceDataById,
|
|
404
|
+
userLogger,
|
|
405
|
+
stackbitConfig
|
|
406
|
+
}: {
|
|
407
|
+
runActionRequest: APIRunCustomActionRequest;
|
|
408
|
+
customActionMap: ContentStoreCustomActionMap;
|
|
409
|
+
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
410
|
+
userLogger: StackbitTypes.Logger;
|
|
411
|
+
stackbitConfig: Config | null;
|
|
412
|
+
}): Promise<CustomActionStateChange> {
|
|
413
|
+
const storeAction = customActionMap[runActionRequest.actionId];
|
|
414
|
+
if (!storeAction) {
|
|
415
|
+
throw new Error(`Error running action: action not found, action name: '${runActionRequest.actionName}' action ID: '${runActionRequest.actionId}'.`);
|
|
416
|
+
}
|
|
417
|
+
const prevResultState = storeAction.lastResultState;
|
|
418
|
+
if (storeAction.lastResultState && storeAction.lastResultState !== 'enabled') {
|
|
419
|
+
throw new Error(
|
|
420
|
+
`Error running action: action is not enabled, action name: '${runActionRequest.actionName}' action ID: '${runActionRequest.actionId}'.`
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
try {
|
|
425
|
+
const actionLogger = userLogger.createLogger({ label: `action:${storeAction.name}` });
|
|
426
|
+
const configDelegate = createConfigDelegate({ contentSourceDataById: contentSourceDataById, logger: actionLogger });
|
|
427
|
+
const currentPageDocument = getCSIDocumentWithSourceFromDocumentSpec(runActionRequest.currentPageDocument, configDelegate);
|
|
428
|
+
|
|
429
|
+
storeAction.runningHandler = true;
|
|
430
|
+
storeAction.lastResultState = 'running';
|
|
431
|
+
|
|
432
|
+
const commonRunOptions: StackbitTypes.CustomActionRunCommonOptions = {
|
|
433
|
+
actionId: storeAction.actionId,
|
|
434
|
+
inputData: runActionRequest.inputData,
|
|
435
|
+
currentLocale: runActionRequest.locale,
|
|
436
|
+
currentUser: runActionRequest.user,
|
|
437
|
+
currentPageUrl: runActionRequest.pageUrl,
|
|
438
|
+
currentPageDocument: currentPageDocument,
|
|
439
|
+
getContentSourceActionsForSource: getContentSourceActionsForSourceThunk({
|
|
440
|
+
getContentSourceDataById: () => contentSourceDataById,
|
|
441
|
+
logger: userLogger,
|
|
442
|
+
user: runActionRequest.user,
|
|
443
|
+
stackbitConfig: stackbitConfig
|
|
444
|
+
}),
|
|
445
|
+
getUserContextForContentSourceType: getUserContextForSrcTypeThunk(runActionRequest.user),
|
|
446
|
+
...configDelegate
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
let promise;
|
|
450
|
+
|
|
451
|
+
if (storeAction.type === 'global') {
|
|
452
|
+
promise = storeAction.run(commonRunOptions);
|
|
453
|
+
} else if (storeAction.type === 'bulk') {
|
|
454
|
+
const documents = (runActionRequest as APIRunCustomActionRequestBulk).documents.map((documentSpec) => {
|
|
455
|
+
const { document } = getCSIDocumentAndModelWithSourceFromDocumentSpec({
|
|
456
|
+
documentSpec,
|
|
457
|
+
contentSourceDataById
|
|
458
|
+
});
|
|
459
|
+
return document;
|
|
460
|
+
});
|
|
461
|
+
promise = storeAction.run({
|
|
462
|
+
...commonRunOptions,
|
|
463
|
+
documents
|
|
464
|
+
});
|
|
465
|
+
} else if (storeAction.type === 'document') {
|
|
466
|
+
const { document, model } = getCSIDocumentAndModelWithSourceFromDocumentSpec({
|
|
467
|
+
documentSpec: storeAction.documentSpec,
|
|
468
|
+
contentSourceDataById
|
|
469
|
+
});
|
|
470
|
+
promise = storeAction.run({
|
|
471
|
+
...commonRunOptions,
|
|
472
|
+
document,
|
|
473
|
+
model,
|
|
474
|
+
contentSourceActions: commonRunOptions.getContentSourceActionsForSource({
|
|
475
|
+
srcType: storeAction.documentSpec.srcType,
|
|
476
|
+
srcProjectId: storeAction.documentSpec.srcProjectId
|
|
477
|
+
})!
|
|
478
|
+
});
|
|
479
|
+
} else if (storeAction.type === 'objectModel') {
|
|
480
|
+
const handlerObjectParams = getHandlerParamsForObjectModelAction({
|
|
481
|
+
storeAction,
|
|
482
|
+
contentSourceDataById
|
|
483
|
+
});
|
|
484
|
+
promise = storeAction.run({
|
|
485
|
+
...commonRunOptions,
|
|
486
|
+
...handlerObjectParams,
|
|
487
|
+
contentSourceActions: commonRunOptions.getContentSourceActionsForSource({
|
|
488
|
+
srcType: storeAction.documentSpec.srcType,
|
|
489
|
+
srcProjectId: storeAction.documentSpec.srcProjectId
|
|
490
|
+
})!
|
|
491
|
+
});
|
|
492
|
+
} else if (storeAction.type === 'objectField') {
|
|
493
|
+
const handlerObjectParams = getHandlerParamsForObjectFieldAction({
|
|
494
|
+
storeAction,
|
|
495
|
+
contentSourceDataById
|
|
496
|
+
});
|
|
497
|
+
promise = storeAction.run({
|
|
498
|
+
...commonRunOptions,
|
|
499
|
+
...handlerObjectParams,
|
|
500
|
+
contentSourceActions: commonRunOptions.getContentSourceActionsForSource({
|
|
501
|
+
srcType: storeAction.documentSpec.srcType,
|
|
502
|
+
srcProjectId: storeAction.documentSpec.srcProjectId
|
|
503
|
+
})!
|
|
504
|
+
});
|
|
505
|
+
} else if (storeAction.type === 'field') {
|
|
506
|
+
const handlerFieldParams = getHandlerParamsForFieldAction({ storeAction, contentSourceDataById });
|
|
507
|
+
promise = storeAction.run({
|
|
508
|
+
...commonRunOptions,
|
|
509
|
+
...handlerFieldParams,
|
|
510
|
+
contentSourceActions: commonRunOptions.getContentSourceActionsForSource({
|
|
511
|
+
srcType: storeAction.documentSpec.srcType,
|
|
512
|
+
srcProjectId: storeAction.documentSpec.srcProjectId
|
|
513
|
+
})!
|
|
514
|
+
});
|
|
515
|
+
} else {
|
|
516
|
+
throw new Error(`action type ${(storeAction as any).type} not supported`);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
return promise
|
|
520
|
+
.then((actionResult) => {
|
|
521
|
+
storeAction.runningHandler = false;
|
|
522
|
+
storeAction.lastResultState = actionResult?.state;
|
|
523
|
+
userLogger.debug(`Action completed: ${storeAction.actionId}`);
|
|
524
|
+
return Promise.resolve(
|
|
525
|
+
omitByNil({
|
|
526
|
+
actionId: storeAction.actionId,
|
|
527
|
+
actionName: storeAction.name,
|
|
528
|
+
actionType: storeActionTypeToAPIActionType(storeAction.type),
|
|
529
|
+
// TODO: resolve the state if state function is defined
|
|
530
|
+
state: actionResult?.state ?? 'enabled',
|
|
531
|
+
success: actionResult?.success,
|
|
532
|
+
error: actionResult?.error
|
|
533
|
+
})
|
|
534
|
+
);
|
|
535
|
+
})
|
|
536
|
+
.catch((error: any) => {
|
|
537
|
+
storeAction.runningHandler = false;
|
|
538
|
+
storeAction.lastResultState = prevResultState;
|
|
539
|
+
userLogger.debug(`Error running action: ${error.message}`);
|
|
540
|
+
return Promise.resolve({
|
|
541
|
+
actionId: storeAction.actionId,
|
|
542
|
+
actionName: storeAction.name,
|
|
543
|
+
actionType: storeActionTypeToAPIActionType(storeAction.type),
|
|
544
|
+
// TODO: resolve the state if state function is defined
|
|
545
|
+
state: prevResultState ?? 'enabled',
|
|
546
|
+
error: `Error running action: ${error.message}`
|
|
547
|
+
});
|
|
548
|
+
});
|
|
549
|
+
} catch (error: any) {
|
|
550
|
+
if (storeAction) {
|
|
551
|
+
storeAction.runningHandler = false;
|
|
552
|
+
storeAction.lastResultState = prevResultState;
|
|
553
|
+
}
|
|
554
|
+
userLogger.debug(`Error running action: ${error.message}`);
|
|
555
|
+
return Promise.resolve({
|
|
556
|
+
actionId: runActionRequest.actionId,
|
|
557
|
+
actionName: storeAction?.name ?? runActionRequest.actionName,
|
|
558
|
+
actionType: storeActionTypeToAPIActionType(storeAction?.type) ?? runActionRequest.actionType,
|
|
559
|
+
// TODO: resolve the state if state function is defined
|
|
560
|
+
state: prevResultState ?? 'enabled',
|
|
561
|
+
error: `Error running action: ${error.message}`
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function getCSIDocumentWithSourceFromDocumentSpec(
|
|
567
|
+
documentSpec: APICustomActionDocumentSpecifier | undefined,
|
|
568
|
+
configDelegate: StackbitTypes.ConfigDelegate
|
|
569
|
+
): StackbitTypes.DocumentWithSource | undefined {
|
|
570
|
+
return documentSpec
|
|
571
|
+
? configDelegate.getDocumentById({
|
|
572
|
+
id: documentSpec.srcDocumentId,
|
|
573
|
+
srcType: documentSpec.srcType,
|
|
574
|
+
srcProjectId: documentSpec.srcProjectId
|
|
575
|
+
})
|
|
576
|
+
: undefined;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function getHandlerParamsForObjectModelAction({
|
|
580
|
+
storeAction,
|
|
581
|
+
contentSourceDataById
|
|
582
|
+
}: {
|
|
583
|
+
storeAction: Pick<ContentStoreCustomActionObjectModel, 'documentSpec' | 'fieldPath'>;
|
|
584
|
+
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
585
|
+
}): StackbitTypes.CustomActionObjectModelStateParams {
|
|
586
|
+
const fieldActionCommonParams = getHandlerParamsForFieldAction({
|
|
587
|
+
storeAction,
|
|
588
|
+
contentSourceDataById
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
if (!fieldActionCommonParams.documentField) {
|
|
592
|
+
throw new Error(`object document field not found at field path: ${storeAction.fieldPath.join('.')}`);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
const documentField = fieldActionCommonParams.documentField as StackbitTypes.DocumentModelFieldNonLocalized;
|
|
596
|
+
const documentSpec = storeAction.documentSpec;
|
|
597
|
+
const contentSourceData = getContentSourceDataByTypeAndProjectIdOrThrow(documentSpec.srcType, documentSpec.srcProjectId, contentSourceDataById);
|
|
598
|
+
const objectModel = contentSourceData.modelMap[documentField.modelName];
|
|
599
|
+
if (!objectModel || objectModel.type !== 'object') {
|
|
600
|
+
throw new Error(`object model '${documentField.modelName}' not found`);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
return {
|
|
604
|
+
...fieldActionCommonParams,
|
|
605
|
+
documentField,
|
|
606
|
+
modelField: fieldActionCommonParams.modelField as StackbitTypes.FieldModel,
|
|
607
|
+
objectModel: {
|
|
608
|
+
...objectModel,
|
|
609
|
+
srcType: documentSpec.srcType,
|
|
610
|
+
srcProjectId: documentSpec.srcProjectId
|
|
611
|
+
}
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function getHandlerParamsForObjectFieldAction({
|
|
616
|
+
storeAction,
|
|
617
|
+
contentSourceDataById
|
|
618
|
+
}: {
|
|
619
|
+
storeAction: Pick<ContentStoreCustomActionObjectField, 'documentSpec' | 'fieldPath'>;
|
|
620
|
+
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
621
|
+
}): StackbitTypes.CustomActionObjectFieldStateParams {
|
|
622
|
+
const fieldActionCommonParams = getHandlerParamsForFieldAction({
|
|
623
|
+
storeAction,
|
|
624
|
+
contentSourceDataById
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
if (!fieldActionCommonParams.documentField) {
|
|
628
|
+
throw new Error(`object document field not found at field path: ${storeAction.fieldPath.join('.')}`);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
return {
|
|
632
|
+
...fieldActionCommonParams,
|
|
633
|
+
documentField: fieldActionCommonParams.documentField as StackbitTypes.DocumentObjectFieldNonLocalized,
|
|
634
|
+
modelField: fieldActionCommonParams.modelField as StackbitTypes.FieldObject
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
function getHandlerParamsForFieldAction({
|
|
639
|
+
storeAction,
|
|
640
|
+
contentSourceDataById
|
|
641
|
+
}: {
|
|
642
|
+
storeAction: Pick<ContentStoreCustomActionField, 'documentSpec' | 'fieldPath'>;
|
|
643
|
+
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
644
|
+
}): StackbitTypes.CustomActionFieldStateParams {
|
|
645
|
+
const documentSpec = storeAction.documentSpec;
|
|
646
|
+
const contentSourceData = getContentSourceDataByTypeAndProjectIdOrThrow(documentSpec.srcType, documentSpec.srcProjectId, contentSourceDataById);
|
|
647
|
+
const document = contentSourceData.documentMap[documentSpec.srcDocumentId];
|
|
648
|
+
const csiDocument = contentSourceData.csiDocumentMap[documentSpec.srcDocumentId];
|
|
649
|
+
if (!document || !csiDocument) {
|
|
650
|
+
throw new Error(
|
|
651
|
+
`document not found for srcType: ${documentSpec.srcType}, srcProjectId: ${documentSpec.srcProjectId}, srcDocumentId: ${documentSpec.srcDocumentId}`
|
|
652
|
+
);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
const model = contentSourceData.modelMap[document.srcModelName];
|
|
656
|
+
if (!model) {
|
|
657
|
+
throw new Error(`model '${document.srcModelName}' not found`);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
const mappedCSIDocument = mapStoreDocumentToCSIDocumentWithSource({ document, csiDocument });
|
|
661
|
+
|
|
662
|
+
// the documentField should be localized because fieldPath includes locales
|
|
663
|
+
const { modelField, documentField } = getModelAndDocumentFieldForLocalizedFieldPath({
|
|
664
|
+
document,
|
|
665
|
+
model,
|
|
666
|
+
fieldPath: storeAction.fieldPath,
|
|
667
|
+
modelMap: contentSourceData.modelMap
|
|
668
|
+
}) as {
|
|
669
|
+
// list items cannot have actions, therefore we can safely reduce the type to non list item fields
|
|
670
|
+
modelField: StackbitTypes.Field;
|
|
671
|
+
documentField: ContentStoreTypes.DocumentField;
|
|
672
|
+
};
|
|
673
|
+
|
|
674
|
+
const csiDocumentField = mapStoreFieldToCSIField(documentField);
|
|
675
|
+
|
|
676
|
+
return {
|
|
677
|
+
parentDocument: mappedCSIDocument,
|
|
678
|
+
parentModel: {
|
|
679
|
+
...model,
|
|
680
|
+
srcType: documentSpec.srcType,
|
|
681
|
+
srcProjectId: documentSpec.srcProjectId
|
|
682
|
+
},
|
|
683
|
+
documentField: csiDocumentField,
|
|
684
|
+
modelField: modelField,
|
|
685
|
+
fieldPath: storeAction.fieldPath
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
function getCSIDocumentAndModelWithSourceFromDocumentSpec({
|
|
690
|
+
documentSpec,
|
|
691
|
+
contentSourceDataById
|
|
692
|
+
}: {
|
|
693
|
+
documentSpec: APICustomActionDocumentSpecifier;
|
|
694
|
+
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
695
|
+
}): {
|
|
696
|
+
document: StackbitTypes.DocumentWithSource;
|
|
697
|
+
model: StackbitTypes.ModelWithSource;
|
|
698
|
+
} {
|
|
699
|
+
const contentSourceData = getContentSourceDataByTypeAndProjectIdOrThrow(documentSpec.srcType, documentSpec.srcProjectId, contentSourceDataById);
|
|
700
|
+
const document = contentSourceData.documentMap[documentSpec.srcDocumentId];
|
|
701
|
+
const csiDocument = contentSourceData.csiDocumentMap[documentSpec.srcDocumentId];
|
|
702
|
+
if (!document || !csiDocument) {
|
|
703
|
+
throw new Error(
|
|
704
|
+
`document not found, srcType: ${documentSpec.srcType}, srcProjectId: ${documentSpec.srcProjectId}, srcDocumentId: ${documentSpec.srcDocumentId}`
|
|
705
|
+
);
|
|
706
|
+
}
|
|
707
|
+
const mappedDocument = mapStoreDocumentToCSIDocumentWithSource({
|
|
708
|
+
document,
|
|
709
|
+
csiDocument
|
|
710
|
+
});
|
|
711
|
+
const model = contentSourceData.modelMap[mappedDocument.modelName];
|
|
712
|
+
if (!model) {
|
|
713
|
+
throw new Error(`model '${mappedDocument.modelName}' not found`);
|
|
714
|
+
}
|
|
715
|
+
return {
|
|
716
|
+
document: mappedDocument,
|
|
717
|
+
model: {
|
|
718
|
+
...model,
|
|
719
|
+
srcType: documentSpec.srcType,
|
|
720
|
+
srcProjectId: documentSpec.srcProjectId
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
function storeActionTypeToAPIActionType(storeActionType: ContentStoreCustomAction['type']): APICustomAction['type'] {
|
|
726
|
+
if (storeActionType === 'objectModel' || storeActionType === 'objectField') {
|
|
727
|
+
return 'object';
|
|
728
|
+
}
|
|
729
|
+
return storeActionType;
|
|
730
|
+
}
|