@stackbit/cms-core 3.0.7 → 3.0.8
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.d.ts +3 -2
- package/dist/content-store.d.ts.map +1 -1
- package/dist/content-store.js +16 -10
- package/dist/content-store.js.map +1 -1
- package/dist/types/content-store-types.d.ts +1 -0
- package/dist/types/content-store-types.d.ts.map +1 -1
- package/dist/types/custom-actions.d.ts +104 -20
- package/dist/types/custom-actions.d.ts.map +1 -1
- package/dist/utils/create-update-csi-docs.d.ts +6 -6
- package/dist/utils/create-update-csi-docs.d.ts.map +1 -1
- package/dist/utils/create-update-csi-docs.js +7 -14
- package/dist/utils/create-update-csi-docs.js.map +1 -1
- package/dist/utils/custom-actions.d.ts +13 -10
- package/dist/utils/custom-actions.d.ts.map +1 -1
- package/dist/utils/custom-actions.js +530 -339
- package/dist/utils/custom-actions.js.map +1 -1
- package/dist/utils/document-hooks.d.ts.map +1 -1
- package/dist/utils/document-hooks.js +18 -0
- package/dist/utils/document-hooks.js.map +1 -1
- package/dist/utils/field-path-utils.d.ts.map +1 -1
- package/dist/utils/field-path-utils.js +1 -1
- package/dist/utils/field-path-utils.js.map +1 -1
- package/dist/utils/model-utils.d.ts.map +1 -1
- package/dist/utils/model-utils.js +7 -3
- package/dist/utils/model-utils.js.map +1 -1
- package/package.json +5 -5
- package/src/content-store.ts +21 -12
- package/src/types/content-store-types.ts +1 -0
- package/src/types/custom-actions.ts +123 -20
- package/src/utils/create-update-csi-docs.ts +7 -15
- package/src/utils/custom-actions.ts +658 -403
- package/src/utils/document-hooks.ts +18 -0
- package/src/utils/field-path-utils.ts +3 -1
- package/src/utils/model-utils.ts +9 -5
|
@@ -1,30 +1,34 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
|
-
import { Config } from '@stackbit/sdk';
|
|
2
|
+
import { Config, PresetMap } from '@stackbit/sdk';
|
|
3
3
|
import * as StackbitTypes from '@stackbit/types';
|
|
4
|
-
import {
|
|
4
|
+
import { omitByNil } from '@stackbit/utils';
|
|
5
|
+
import { v4 as uuid } from 'uuid';
|
|
5
6
|
import * as ContentStoreTypes from '../types';
|
|
6
7
|
import {
|
|
7
8
|
CustomActionStateChange,
|
|
9
|
+
CustomActionRunStateMap,
|
|
10
|
+
CustomActionRunState,
|
|
8
11
|
APICustomAction,
|
|
12
|
+
APICustomActionGlobal,
|
|
13
|
+
APICustomActionBulk,
|
|
14
|
+
APICustomActionModel,
|
|
15
|
+
APICustomActionModelSpecifier,
|
|
9
16
|
APICustomActionDocumentSpecifier,
|
|
10
17
|
APIGetCustomActionRequest,
|
|
18
|
+
APIGetRunningCustomActionsRequest,
|
|
11
19
|
APIRunCustomActionRequest,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
ExtendedCustomActionObjectModel,
|
|
15
|
-
ExtendedCustomActionField,
|
|
16
|
-
APICustomActionGlobal,
|
|
17
|
-
APICustomActionBulk,
|
|
20
|
+
APIRunCustomActionRequestModel,
|
|
21
|
+
APIRunCustomActionResponse,
|
|
18
22
|
ExtendedCustomAction,
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
ExtendedCustomActionGlobal,
|
|
24
|
+
ExtendedCustomActionBulk,
|
|
25
|
+
ExtendedCustomActionModelGlobal
|
|
21
26
|
} from '../types';
|
|
22
27
|
import { createConfigDelegate } from './config-delegate';
|
|
23
28
|
import { getContentSourceActionsForSourceThunk } from './document-hooks';
|
|
24
29
|
import { mapStoreDocumentToCSIDocumentWithSource, mapStoreFieldToCSIField } from './store-to-csi-docs-converter';
|
|
25
30
|
import { getModelAndDocumentFieldForLocalizedFieldPath } from './field-path-utils';
|
|
26
31
|
import { getContentSourceDataByTypeAndProjectIdOrThrow, getUserContextForSrcTypeThunk } from '../content-store-utils';
|
|
27
|
-
import { CustomActionBulk, CustomActionGlobal, CustomActionPermissions } from '@stackbit/types';
|
|
28
32
|
|
|
29
33
|
export async function getGlobalAndBulkAPIActions({
|
|
30
34
|
stackbitConfig,
|
|
@@ -44,7 +48,7 @@ export async function getGlobalAndBulkAPIActions({
|
|
|
44
48
|
user?: ContentStoreTypes.User;
|
|
45
49
|
locale?: string;
|
|
46
50
|
currentPageDocument?: APICustomActionDocumentSpecifier;
|
|
47
|
-
}): Promise<(APICustomActionGlobal | APICustomActionBulk)[]> {
|
|
51
|
+
}): Promise<(APICustomActionGlobal | APICustomActionBulk | APICustomActionModel)[]> {
|
|
48
52
|
if (!stackbitConfig || !Array.isArray(stackbitConfig.actions)) {
|
|
49
53
|
return [];
|
|
50
54
|
}
|
|
@@ -54,10 +58,9 @@ export async function getGlobalAndBulkAPIActions({
|
|
|
54
58
|
logger: userLogger
|
|
55
59
|
});
|
|
56
60
|
|
|
57
|
-
|
|
61
|
+
const apiCustomActions: (APICustomActionGlobal | APICustomActionBulk | APICustomActionModel)[] = [];
|
|
62
|
+
for (const action of stackbitConfig.actions) {
|
|
58
63
|
const actionId = globalActionId(action);
|
|
59
|
-
const actionRunState = customActionRunStateMap[actionId];
|
|
60
|
-
let state: StackbitTypes.CustomActionState | 'unknown';
|
|
61
64
|
const pageDocument = getCSIDocumentWithSourceFromDocumentSpec(currentPageDocument, configDelegate);
|
|
62
65
|
const commonStateOptions: StackbitTypes.CustomActionStateCommonOptions = {
|
|
63
66
|
actionId: actionId,
|
|
@@ -68,36 +71,35 @@ export async function getGlobalAndBulkAPIActions({
|
|
|
68
71
|
...configDelegate
|
|
69
72
|
};
|
|
70
73
|
|
|
71
|
-
|
|
72
|
-
state = 'running';
|
|
73
|
-
} else if (action.state) {
|
|
74
|
-
state = await action.state(commonStateOptions);
|
|
75
|
-
} else {
|
|
76
|
-
state = actionRunState?.lastResultState ?? 'enabled';
|
|
77
|
-
}
|
|
74
|
+
const actionRunState = customActionRunStateMap[actionId];
|
|
78
75
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
76
|
+
const extendedAction = getExtendedActionFromGlobalAction({
|
|
77
|
+
action,
|
|
78
|
+
actionId,
|
|
79
|
+
actionRunState,
|
|
80
|
+
generateNewActionId: false
|
|
81
|
+
});
|
|
85
82
|
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
const state = await resolveCustomActionState({
|
|
84
|
+
extendedAction,
|
|
85
|
+
commonStateOptions
|
|
86
|
+
});
|
|
88
87
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
name: action.name,
|
|
93
|
-
label: action.label ?? _.startCase(action.name),
|
|
94
|
-
icon: action.icon,
|
|
95
|
-
state: state,
|
|
96
|
-
inputFields: action.inputFields,
|
|
97
|
-
hidden: action.hidden,
|
|
98
|
-
permissions: permissionsResult
|
|
88
|
+
const permissions = resolveCustomActionPermissions({
|
|
89
|
+
extendedAction,
|
|
90
|
+
commonStateOptions
|
|
99
91
|
});
|
|
100
|
-
|
|
92
|
+
|
|
93
|
+
apiCustomActions.push(
|
|
94
|
+
extendedActionToApiAction({
|
|
95
|
+
extendedAction,
|
|
96
|
+
state,
|
|
97
|
+
permissions
|
|
98
|
+
})
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return apiCustomActions;
|
|
101
103
|
}
|
|
102
104
|
|
|
103
105
|
export async function resolveCustomActionsById({
|
|
@@ -122,7 +124,7 @@ export async function resolveCustomActionsById({
|
|
|
122
124
|
});
|
|
123
125
|
|
|
124
126
|
for (const actionId of customActionIds) {
|
|
125
|
-
const extendedAction =
|
|
127
|
+
const extendedAction = getExtendedActionById({
|
|
126
128
|
actionId,
|
|
127
129
|
customActionRunStateMap,
|
|
128
130
|
contentSourceDataById,
|
|
@@ -144,78 +146,20 @@ export async function resolveCustomActionsById({
|
|
|
144
146
|
};
|
|
145
147
|
|
|
146
148
|
try {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
documentSpec: extendedAction.documentSpec,
|
|
156
|
-
contentSourceDataById
|
|
157
|
-
});
|
|
158
|
-
state = await extendedAction.state({
|
|
159
|
-
...commonStateOptions,
|
|
160
|
-
document: document,
|
|
161
|
-
model: model
|
|
162
|
-
});
|
|
163
|
-
} else if (extendedAction.type === 'objectModel') {
|
|
164
|
-
const stateObjectParams = getHandlerParamsForObjectModelAction({
|
|
165
|
-
extendedAction,
|
|
166
|
-
contentSourceDataById
|
|
167
|
-
});
|
|
168
|
-
state = await extendedAction.state({
|
|
169
|
-
...commonStateOptions,
|
|
170
|
-
...stateObjectParams
|
|
171
|
-
});
|
|
172
|
-
} else if (extendedAction.type === 'objectField') {
|
|
173
|
-
const stateObjectParams = getHandlerParamsForObjectFieldAction({
|
|
174
|
-
extendedAction,
|
|
175
|
-
contentSourceDataById
|
|
176
|
-
});
|
|
177
|
-
state = await extendedAction.state({
|
|
178
|
-
...commonStateOptions,
|
|
179
|
-
...stateObjectParams
|
|
180
|
-
});
|
|
181
|
-
} else if (extendedAction.type === 'field') {
|
|
182
|
-
const stateFieldParams = getHandlerParamsForFieldAction({
|
|
183
|
-
extendedAction,
|
|
184
|
-
contentSourceDataById
|
|
185
|
-
});
|
|
186
|
-
state = await extendedAction.state({
|
|
187
|
-
...commonStateOptions,
|
|
188
|
-
...stateFieldParams
|
|
189
|
-
});
|
|
190
|
-
} else {
|
|
191
|
-
const _exhaustiveCheck: never = extendedAction;
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
} else {
|
|
195
|
-
state = extendedAction.lastResultState ?? 'enabled';
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
let permissionsResult;
|
|
199
|
-
if (commonStateOptions.currentUser) {
|
|
200
|
-
const commonPermissionsOptions: StackbitTypes.CustomActionPermissionsCommonOptions = {
|
|
201
|
-
..._.omit(commonStateOptions, ['currentUser']),
|
|
202
|
-
userContext: commonStateOptions.currentUser
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
permissionsResult = resolveCustomActionPermissions({ action: extendedAction, commonPermissionsOptions, contentSourceDataById });
|
|
206
|
-
}
|
|
149
|
+
const state = await resolveCustomActionState({
|
|
150
|
+
extendedAction,
|
|
151
|
+
commonStateOptions
|
|
152
|
+
});
|
|
153
|
+
const permissions = resolveCustomActionPermissions({
|
|
154
|
+
extendedAction,
|
|
155
|
+
commonStateOptions
|
|
156
|
+
});
|
|
207
157
|
|
|
208
158
|
result.push(
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
label: extendedAction.label,
|
|
214
|
-
icon: extendedAction.icon,
|
|
215
|
-
state: state,
|
|
216
|
-
inputFields: extendedAction.inputFields,
|
|
217
|
-
hidden: extendedAction.hidden,
|
|
218
|
-
permissions: permissionsResult
|
|
159
|
+
extendedActionToApiAction({
|
|
160
|
+
extendedAction,
|
|
161
|
+
state,
|
|
162
|
+
permissions
|
|
219
163
|
})
|
|
220
164
|
);
|
|
221
165
|
} catch (error: any) {
|
|
@@ -226,103 +170,279 @@ export async function resolveCustomActionsById({
|
|
|
226
170
|
return result;
|
|
227
171
|
}
|
|
228
172
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
contentSourceDataById
|
|
173
|
+
async function resolveCustomActionState({
|
|
174
|
+
extendedAction,
|
|
175
|
+
commonStateOptions
|
|
233
176
|
}: {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
}): CustomActionPermissions | undefined {
|
|
244
|
-
if (typeof action?.permissions !== 'function' || !commonPermissionsOptions.userContext) {
|
|
245
|
-
return undefined;
|
|
177
|
+
extendedAction: ExtendedCustomAction;
|
|
178
|
+
commonStateOptions: StackbitTypes.CustomActionStateCommonOptions;
|
|
179
|
+
}): Promise<StackbitTypes.CustomActionState> {
|
|
180
|
+
if (extendedAction.isRunning) {
|
|
181
|
+
return 'running';
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (!('state' in extendedAction) || typeof extendedAction.state !== 'function') {
|
|
185
|
+
return 'enabled';
|
|
246
186
|
}
|
|
247
187
|
|
|
248
|
-
|
|
249
|
-
switch (action.type) {
|
|
188
|
+
switch (extendedAction.extendedType) {
|
|
250
189
|
case 'global':
|
|
251
190
|
case 'bulk':
|
|
252
|
-
return
|
|
191
|
+
return await extendedAction.state(commonStateOptions);
|
|
253
192
|
case 'document':
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
193
|
+
return await extendedAction.state({
|
|
194
|
+
...commonStateOptions,
|
|
195
|
+
document: extendedAction.documentWithSource,
|
|
196
|
+
model: extendedAction.modelWithSource
|
|
197
|
+
});
|
|
259
198
|
case 'objectModel':
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
199
|
+
return await extendedAction.state({
|
|
200
|
+
...commonStateOptions,
|
|
201
|
+
parentDocument: extendedAction.documentWithSource,
|
|
202
|
+
parentModel: extendedAction.modelWithSource,
|
|
203
|
+
documentField: extendedAction.documentField,
|
|
204
|
+
modelField: extendedAction.modelField,
|
|
205
|
+
objectModel: extendedAction.objectModel,
|
|
206
|
+
fieldPath: extendedAction.fieldPath
|
|
263
207
|
});
|
|
264
|
-
return action.permissions?.({ ...commonPermissionsOptions, ...stateParams });
|
|
265
208
|
case 'objectField':
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
209
|
+
return await extendedAction.state({
|
|
210
|
+
...commonStateOptions,
|
|
211
|
+
parentDocument: extendedAction.documentWithSource,
|
|
212
|
+
parentModel: extendedAction.modelWithSource,
|
|
213
|
+
documentField: extendedAction.documentField,
|
|
214
|
+
modelField: extendedAction.modelField,
|
|
215
|
+
fieldPath: extendedAction.fieldPath
|
|
269
216
|
});
|
|
270
|
-
return action.permissions?.({ ...commonPermissionsOptions, ...stateParams });
|
|
271
217
|
case 'field':
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
218
|
+
return await extendedAction.state({
|
|
219
|
+
...commonStateOptions,
|
|
220
|
+
parentDocument: extendedAction.documentWithSource,
|
|
221
|
+
parentModel: extendedAction.modelWithSource,
|
|
222
|
+
documentField: extendedAction.documentField,
|
|
223
|
+
modelField: extendedAction.modelField,
|
|
224
|
+
fieldPath: extendedAction.fieldPath
|
|
275
225
|
});
|
|
276
|
-
|
|
226
|
+
default: {
|
|
227
|
+
// actions of type 'model' do not support 'state' function
|
|
228
|
+
const _exhaustiveCheck: never = extendedAction;
|
|
229
|
+
throw new Error(`error getting action state, action of type '${_exhaustiveCheck['type']}' does not support states`);
|
|
230
|
+
}
|
|
277
231
|
}
|
|
278
232
|
}
|
|
279
233
|
|
|
280
|
-
|
|
281
|
-
|
|
234
|
+
function resolveCustomActionPermissions({
|
|
235
|
+
extendedAction,
|
|
236
|
+
commonStateOptions
|
|
237
|
+
}: {
|
|
238
|
+
extendedAction: ExtendedCustomAction;
|
|
239
|
+
commonStateOptions: StackbitTypes.CustomActionStateCommonOptions;
|
|
240
|
+
}): StackbitTypes.CustomActionPermissions | undefined {
|
|
241
|
+
if (typeof extendedAction?.permissions !== 'function' || !commonStateOptions.currentUser) {
|
|
242
|
+
return undefined;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const { currentUser, ...restOptions } = commonStateOptions;
|
|
246
|
+
const commonPermissionsOptions: StackbitTypes.CustomActionPermissionsCommonOptions = {
|
|
247
|
+
...restOptions,
|
|
248
|
+
userContext: currentUser
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
switch (extendedAction.extendedType) {
|
|
252
|
+
case 'global':
|
|
253
|
+
case 'bulk':
|
|
254
|
+
case 'modelGlobal':
|
|
255
|
+
case 'model':
|
|
256
|
+
case 'modelObject': {
|
|
257
|
+
return extendedAction.permissions?.(commonPermissionsOptions);
|
|
258
|
+
}
|
|
259
|
+
case 'document': {
|
|
260
|
+
return extendedAction.permissions?.({
|
|
261
|
+
...commonPermissionsOptions,
|
|
262
|
+
document: extendedAction.documentWithSource,
|
|
263
|
+
model: extendedAction.modelWithSource
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
case 'objectModel': {
|
|
267
|
+
return extendedAction.permissions?.({
|
|
268
|
+
...commonPermissionsOptions,
|
|
269
|
+
parentDocument: extendedAction.documentWithSource,
|
|
270
|
+
parentModel: extendedAction.modelWithSource,
|
|
271
|
+
documentField: extendedAction.documentField,
|
|
272
|
+
modelField: extendedAction.modelField,
|
|
273
|
+
objectModel: extendedAction.objectModel,
|
|
274
|
+
fieldPath: extendedAction.fieldPath
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
case 'objectField': {
|
|
278
|
+
return extendedAction.permissions?.({
|
|
279
|
+
...commonPermissionsOptions,
|
|
280
|
+
parentDocument: extendedAction.documentWithSource,
|
|
281
|
+
parentModel: extendedAction.modelWithSource,
|
|
282
|
+
documentField: extendedAction.documentField,
|
|
283
|
+
modelField: extendedAction.modelField,
|
|
284
|
+
fieldPath: extendedAction.fieldPath
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
case 'field': {
|
|
288
|
+
return extendedAction.permissions?.({
|
|
289
|
+
...commonPermissionsOptions,
|
|
290
|
+
parentDocument: extendedAction.documentWithSource,
|
|
291
|
+
parentModel: extendedAction.modelWithSource,
|
|
292
|
+
documentField: extendedAction.documentField,
|
|
293
|
+
modelField: extendedAction.modelField,
|
|
294
|
+
fieldPath: extendedAction.fieldPath
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
default: {
|
|
298
|
+
const _exhaustiveCheck: never = extendedAction;
|
|
299
|
+
return _exhaustiveCheck;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export function getRunningActions({
|
|
305
|
+
getRunningActionsRequest,
|
|
282
306
|
customActionRunStateMap,
|
|
283
307
|
contentSourceDataById,
|
|
284
308
|
stackbitConfig,
|
|
285
309
|
userLogger
|
|
310
|
+
}: {
|
|
311
|
+
getRunningActionsRequest: APIGetRunningCustomActionsRequest;
|
|
312
|
+
customActionRunStateMap: CustomActionRunStateMap;
|
|
313
|
+
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
314
|
+
stackbitConfig: Config | null;
|
|
315
|
+
userLogger: StackbitTypes.Logger;
|
|
316
|
+
}): APICustomAction[] {
|
|
317
|
+
if (!getRunningActionsRequest.user) {
|
|
318
|
+
return [];
|
|
319
|
+
}
|
|
320
|
+
const runningActions: APICustomAction[] = [];
|
|
321
|
+
for (const [actionId, actionRunState] of Object.entries(customActionRunStateMap)) {
|
|
322
|
+
if (actionRunState.isRunning && actionRunState.userId === getRunningActionsRequest.user.id) {
|
|
323
|
+
const extendedAction = getExtendedActionById({
|
|
324
|
+
actionId,
|
|
325
|
+
customActionRunStateMap,
|
|
326
|
+
contentSourceDataById,
|
|
327
|
+
stackbitConfig
|
|
328
|
+
});
|
|
329
|
+
if (!extendedAction) {
|
|
330
|
+
userLogger.debug(`custom action with id: '${actionId}' was not found`);
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
runningActions.push(
|
|
334
|
+
extendedActionToApiAction({
|
|
335
|
+
extendedAction,
|
|
336
|
+
state: 'running'
|
|
337
|
+
})
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return runningActions;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
type ActionType = ExtendedCustomAction['type'];
|
|
345
|
+
type ExtendedActionForActionType<Type extends ActionType, U extends ExtendedCustomAction = ExtendedCustomAction> = U extends { type: Type } ? U : never;
|
|
346
|
+
type APICustomActionForType<Type extends ActionType, U extends APICustomAction = APICustomAction> = U extends { type: Type } ? U : never;
|
|
347
|
+
|
|
348
|
+
function extendedActionToApiAction<Type extends ActionType>({
|
|
349
|
+
extendedAction,
|
|
350
|
+
state,
|
|
351
|
+
permissions
|
|
352
|
+
}: {
|
|
353
|
+
extendedAction: ExtendedActionForActionType<Type>;
|
|
354
|
+
state: StackbitTypes.CustomActionState;
|
|
355
|
+
permissions?: StackbitTypes.CustomActionPermissions;
|
|
356
|
+
}): APICustomActionForType<Type> {
|
|
357
|
+
const apiAction: APICustomAction = omitByNil({
|
|
358
|
+
type: extendedAction.type,
|
|
359
|
+
name: extendedAction.name,
|
|
360
|
+
label: extendedAction.label,
|
|
361
|
+
actionId: extendedAction.actionId,
|
|
362
|
+
userId: extendedAction.userId,
|
|
363
|
+
icon: extendedAction.icon,
|
|
364
|
+
state: state,
|
|
365
|
+
permissions: permissions,
|
|
366
|
+
models: extendedAction.extendedType === 'modelGlobal' && 'models' in extendedAction ? extendedAction.models : undefined,
|
|
367
|
+
inputFields: extendedAction.inputFields,
|
|
368
|
+
hidden: extendedAction.hidden
|
|
369
|
+
});
|
|
370
|
+
return apiAction as APICustomActionForType<Type>;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export async function runCustomAction({
|
|
374
|
+
runActionRequest,
|
|
375
|
+
customActionRunStateMap,
|
|
376
|
+
contentSourceDataById,
|
|
377
|
+
stackbitConfig,
|
|
378
|
+
userLogger,
|
|
379
|
+
presets,
|
|
380
|
+
onProgress
|
|
286
381
|
}: {
|
|
287
382
|
runActionRequest: APIRunCustomActionRequest;
|
|
288
383
|
customActionRunStateMap: CustomActionRunStateMap;
|
|
289
384
|
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
290
385
|
stackbitConfig: Config | null;
|
|
291
386
|
userLogger: StackbitTypes.Logger;
|
|
292
|
-
|
|
293
|
-
|
|
387
|
+
presets: PresetMap;
|
|
388
|
+
onProgress: (options: CustomActionStateChange) => void;
|
|
389
|
+
}): Promise<APIRunCustomActionResponse> {
|
|
390
|
+
const extendedAction = getExtendedActionById({
|
|
294
391
|
actionId: runActionRequest.actionId,
|
|
295
392
|
customActionRunStateMap,
|
|
296
393
|
contentSourceDataById,
|
|
297
|
-
stackbitConfig
|
|
394
|
+
stackbitConfig,
|
|
395
|
+
generateNewActionId: true
|
|
298
396
|
});
|
|
299
397
|
if (!extendedAction) {
|
|
300
398
|
throw new Error(`Error running action: action not found, action name: '${runActionRequest.actionName}' action ID: '${runActionRequest.actionId}'.`);
|
|
301
399
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
throw new Error(
|
|
305
|
-
`Error running action: action is not enabled, action name: '${runActionRequest.actionName}' action ID: '${runActionRequest.actionId}'.`
|
|
306
|
-
);
|
|
400
|
+
if (extendedAction.isRunning) {
|
|
401
|
+
throw new Error(`Error running action: action '${runActionRequest.actionName}' with ID '${runActionRequest.actionId}' is already running.`);
|
|
307
402
|
}
|
|
308
403
|
|
|
404
|
+
const actionId = extendedAction.actionId;
|
|
405
|
+
let isRunning = true;
|
|
406
|
+
|
|
309
407
|
try {
|
|
310
408
|
const actionLogger = userLogger.createLogger({ label: `action:${extendedAction.name}` });
|
|
311
409
|
const configDelegate = createConfigDelegate({ contentSourceDataById: contentSourceDataById, logger: actionLogger });
|
|
312
410
|
const currentPageDocument = getCSIDocumentWithSourceFromDocumentSpec(runActionRequest.currentPageDocument, configDelegate);
|
|
313
411
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
412
|
+
const actionRunState: CustomActionRunState = {
|
|
413
|
+
isRunning: true,
|
|
414
|
+
userId: runActionRequest.user?.id
|
|
317
415
|
};
|
|
416
|
+
customActionRunStateMap[actionId] = actionRunState;
|
|
417
|
+
|
|
418
|
+
const inputData = runActionRequest.inputData ?? {};
|
|
419
|
+
if (runActionRequest.actionType === 'model' && 'presetId' in runActionRequest) {
|
|
420
|
+
inputData.presetData = presets[runActionRequest.presetId!]?.data;
|
|
421
|
+
}
|
|
318
422
|
|
|
319
423
|
const commonRunOptions: StackbitTypes.CustomActionRunCommonOptions = {
|
|
320
|
-
actionId:
|
|
321
|
-
inputData:
|
|
424
|
+
actionId: actionId,
|
|
425
|
+
inputData: inputData,
|
|
322
426
|
currentLocale: runActionRequest.locale,
|
|
323
427
|
currentUser: runActionRequest.user,
|
|
324
428
|
currentPageUrl: runActionRequest.pageUrl,
|
|
325
429
|
currentPageDocument: currentPageDocument,
|
|
430
|
+
progress: ({ message, percent }) => {
|
|
431
|
+
if (!isRunning) {
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
onProgress(
|
|
435
|
+
omitByNil({
|
|
436
|
+
actionId: actionId,
|
|
437
|
+
actionName: extendedAction.name,
|
|
438
|
+
actionType: extendedAction.type,
|
|
439
|
+
userId: runActionRequest.user?.id,
|
|
440
|
+
state: 'running',
|
|
441
|
+
message,
|
|
442
|
+
percent
|
|
443
|
+
})
|
|
444
|
+
);
|
|
445
|
+
},
|
|
326
446
|
getContentSourceActionsForSource: getContentSourceActionsForSourceThunk({
|
|
327
447
|
getContentSourceDataById: () => contentSourceDataById,
|
|
328
448
|
logger: userLogger,
|
|
@@ -335,10 +455,15 @@ export function runCustomAction({
|
|
|
335
455
|
|
|
336
456
|
let promise: Promise<StackbitTypes.CustomActionResult | void>;
|
|
337
457
|
|
|
338
|
-
if (extendedAction.
|
|
458
|
+
if (extendedAction.extendedType === 'global') {
|
|
339
459
|
promise = extendedAction.run(commonRunOptions);
|
|
340
|
-
} else if (extendedAction.
|
|
341
|
-
|
|
460
|
+
} else if (extendedAction.extendedType === 'bulk') {
|
|
461
|
+
if (!('documents' in runActionRequest) || !Array.isArray(runActionRequest.documents)) {
|
|
462
|
+
throw new Error(
|
|
463
|
+
`Bulk action run request must contain array of documents, action name: '${runActionRequest.actionName}', action ID: '${runActionRequest.actionId}'.`
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
const documents = runActionRequest.documents.map((documentSpec) => {
|
|
342
467
|
const { document } = getCSIDocumentAndModelWithSourceFromDocumentSpec({
|
|
343
468
|
documentSpec,
|
|
344
469
|
contentSourceDataById
|
|
@@ -349,74 +474,140 @@ export function runCustomAction({
|
|
|
349
474
|
...commonRunOptions,
|
|
350
475
|
documents
|
|
351
476
|
});
|
|
352
|
-
} else if (extendedAction.
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
477
|
+
} else if (extendedAction.extendedType === 'modelGlobal') {
|
|
478
|
+
if (!('srcType' in runActionRequest) || !('srcProjectId' in runActionRequest) || !('modelName' in runActionRequest)) {
|
|
479
|
+
throw new Error(
|
|
480
|
+
`Global model action run request must contain srcType, srcProjectId and modelName, action name: '${runActionRequest.actionName}', action ID: '${runActionRequest.actionId}'.`
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
actionRunState.modelSpec = {
|
|
484
|
+
srcType: runActionRequest.srcType,
|
|
485
|
+
srcProjectId: runActionRequest.srcProjectId,
|
|
486
|
+
modelName: runActionRequest.modelName
|
|
487
|
+
};
|
|
488
|
+
const runOptions = getRunParamsForModelAction({
|
|
489
|
+
modelSpec: actionRunState.modelSpec,
|
|
490
|
+
location: runActionRequest.location,
|
|
491
|
+
contentSourceDataById,
|
|
492
|
+
getContentSourceActionsForSource: commonRunOptions.getContentSourceActionsForSource
|
|
493
|
+
});
|
|
494
|
+
promise = extendedAction.run({
|
|
495
|
+
...commonRunOptions,
|
|
496
|
+
...runOptions
|
|
497
|
+
});
|
|
498
|
+
} else if (extendedAction.extendedType === 'model') {
|
|
499
|
+
const runOptions = getRunParamsForModelAction({
|
|
500
|
+
modelSpec: extendedAction.modelSpec,
|
|
501
|
+
location: (runActionRequest as APIRunCustomActionRequestModel).location,
|
|
502
|
+
contentSourceDataById,
|
|
503
|
+
getContentSourceActionsForSource: commonRunOptions.getContentSourceActionsForSource
|
|
356
504
|
});
|
|
357
505
|
promise = extendedAction.run({
|
|
358
506
|
...commonRunOptions,
|
|
359
|
-
|
|
360
|
-
|
|
507
|
+
...runOptions
|
|
508
|
+
});
|
|
509
|
+
} else if (extendedAction.extendedType === 'document') {
|
|
510
|
+
promise = extendedAction.run({
|
|
511
|
+
...commonRunOptions,
|
|
512
|
+
document: extendedAction.documentWithSource,
|
|
513
|
+
model: extendedAction.modelWithSource,
|
|
361
514
|
contentSourceActions: commonRunOptions.getContentSourceActionsForSource({
|
|
362
|
-
srcType: extendedAction.
|
|
363
|
-
srcProjectId: extendedAction.
|
|
515
|
+
srcType: extendedAction.documentWithSource.srcType,
|
|
516
|
+
srcProjectId: extendedAction.documentWithSource.srcProjectId
|
|
364
517
|
})!
|
|
365
518
|
});
|
|
366
|
-
} else if (extendedAction.
|
|
367
|
-
const handlerObjectParams = getHandlerParamsForObjectModelAction({
|
|
368
|
-
extendedAction,
|
|
369
|
-
contentSourceDataById
|
|
370
|
-
});
|
|
519
|
+
} else if (extendedAction.extendedType === 'modelObject') {
|
|
371
520
|
promise = extendedAction.run({
|
|
372
521
|
...commonRunOptions,
|
|
373
|
-
|
|
522
|
+
parentDocument: extendedAction.documentWithSource,
|
|
523
|
+
parentModel: extendedAction.modelWithSource,
|
|
524
|
+
modelField: extendedAction.modelField,
|
|
525
|
+
objectModel: extendedAction.objectModel,
|
|
526
|
+
fieldPath: extendedAction.fieldPath,
|
|
527
|
+
location: (runActionRequest as APIRunCustomActionRequestModel).location,
|
|
374
528
|
contentSourceActions: commonRunOptions.getContentSourceActionsForSource({
|
|
375
|
-
srcType: extendedAction.
|
|
376
|
-
srcProjectId: extendedAction.
|
|
529
|
+
srcType: extendedAction.documentWithSource.srcType,
|
|
530
|
+
srcProjectId: extendedAction.documentWithSource.srcProjectId
|
|
377
531
|
})!
|
|
378
532
|
});
|
|
379
|
-
} else if (extendedAction.
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
533
|
+
} else if (extendedAction.extendedType === 'objectModel') {
|
|
534
|
+
promise = extendedAction.run({
|
|
535
|
+
...commonRunOptions,
|
|
536
|
+
parentDocument: extendedAction.documentWithSource,
|
|
537
|
+
parentModel: extendedAction.modelWithSource,
|
|
538
|
+
documentField: extendedAction.documentField,
|
|
539
|
+
modelField: extendedAction.modelField,
|
|
540
|
+
objectModel: extendedAction.objectModel,
|
|
541
|
+
fieldPath: extendedAction.fieldPath,
|
|
542
|
+
contentSourceActions: commonRunOptions.getContentSourceActionsForSource({
|
|
543
|
+
srcType: extendedAction.documentWithSource.srcType,
|
|
544
|
+
srcProjectId: extendedAction.documentWithSource.srcProjectId
|
|
545
|
+
})!
|
|
383
546
|
});
|
|
547
|
+
} else if (extendedAction.extendedType === 'objectField') {
|
|
384
548
|
promise = extendedAction.run({
|
|
385
549
|
...commonRunOptions,
|
|
386
|
-
|
|
550
|
+
parentDocument: extendedAction.documentWithSource,
|
|
551
|
+
parentModel: extendedAction.modelWithSource,
|
|
552
|
+
documentField: extendedAction.documentField,
|
|
553
|
+
modelField: extendedAction.modelField,
|
|
554
|
+
fieldPath: extendedAction.fieldPath,
|
|
387
555
|
contentSourceActions: commonRunOptions.getContentSourceActionsForSource({
|
|
388
|
-
srcType: extendedAction.
|
|
389
|
-
srcProjectId: extendedAction.
|
|
556
|
+
srcType: extendedAction.documentWithSource.srcType,
|
|
557
|
+
srcProjectId: extendedAction.documentWithSource.srcProjectId
|
|
390
558
|
})!
|
|
391
559
|
});
|
|
392
|
-
} else if (extendedAction.
|
|
393
|
-
const handlerFieldParams = getHandlerParamsForFieldAction({ extendedAction, contentSourceDataById });
|
|
560
|
+
} else if (extendedAction.extendedType === 'field') {
|
|
394
561
|
promise = extendedAction.run({
|
|
395
562
|
...commonRunOptions,
|
|
396
|
-
|
|
563
|
+
parentDocument: extendedAction.documentWithSource,
|
|
564
|
+
parentModel: extendedAction.modelWithSource,
|
|
565
|
+
documentField: extendedAction.documentField,
|
|
566
|
+
modelField: extendedAction.modelField,
|
|
567
|
+
fieldPath: extendedAction.fieldPath,
|
|
397
568
|
contentSourceActions: commonRunOptions.getContentSourceActionsForSource({
|
|
398
|
-
srcType: extendedAction.
|
|
399
|
-
srcProjectId: extendedAction.
|
|
569
|
+
srcType: extendedAction.documentWithSource.srcType,
|
|
570
|
+
srcProjectId: extendedAction.documentWithSource.srcProjectId
|
|
400
571
|
})!
|
|
401
572
|
});
|
|
402
573
|
} else {
|
|
403
|
-
|
|
574
|
+
const _exhaustiveCheck: never = extendedAction;
|
|
575
|
+
throw new Error(`action of type ${_exhaustiveCheck['type']} is not supported`);
|
|
404
576
|
}
|
|
405
577
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
578
|
+
onProgress(
|
|
579
|
+
omitByNil({
|
|
580
|
+
actionId: actionId,
|
|
581
|
+
actionName: extendedAction.name,
|
|
582
|
+
actionType: extendedAction.type,
|
|
583
|
+
userId: runActionRequest.user?.id,
|
|
584
|
+
state: 'running'
|
|
585
|
+
})
|
|
586
|
+
);
|
|
587
|
+
|
|
588
|
+
promise
|
|
589
|
+
.then(async (actionResult) => {
|
|
590
|
+
isRunning = false;
|
|
591
|
+
delete customActionRunStateMap[actionId];
|
|
592
|
+
userLogger.debug(`Action completed: ${actionId}`);
|
|
593
|
+
const state = await resolveCustomActionState({
|
|
594
|
+
extendedAction,
|
|
595
|
+
commonStateOptions: {
|
|
596
|
+
actionId: actionId,
|
|
597
|
+
currentLocale: runActionRequest.locale,
|
|
598
|
+
currentUser: runActionRequest.user,
|
|
599
|
+
currentPageUrl: runActionRequest.pageUrl,
|
|
600
|
+
currentPageDocument: currentPageDocument,
|
|
601
|
+
...configDelegate
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
onProgress(
|
|
414
605
|
omitByNil({
|
|
415
|
-
actionId:
|
|
606
|
+
actionId: actionId,
|
|
416
607
|
actionName: extendedAction.name,
|
|
417
|
-
actionType:
|
|
418
|
-
|
|
419
|
-
state:
|
|
608
|
+
actionType: extendedAction.type,
|
|
609
|
+
userId: runActionRequest.user?.id,
|
|
610
|
+
state: extendedAction.type === 'model' ? 'finished' : state,
|
|
420
611
|
success: actionResult?.success,
|
|
421
612
|
error: actionResult?.error,
|
|
422
613
|
result: actionResult?.result
|
|
@@ -424,37 +615,29 @@ export function runCustomAction({
|
|
|
424
615
|
);
|
|
425
616
|
})
|
|
426
617
|
.catch((error: any) => {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
618
|
+
isRunning = false;
|
|
619
|
+
delete customActionRunStateMap[actionId];
|
|
620
|
+
userLogger.warn(`Error running action: ${error.stack ?? error.message}`);
|
|
621
|
+
onProgress(
|
|
622
|
+
omitByNil({
|
|
623
|
+
actionId: extendedAction.actionId,
|
|
624
|
+
actionName: extendedAction.name,
|
|
625
|
+
actionType: extendedAction.type,
|
|
626
|
+
userId: runActionRequest.user?.id,
|
|
627
|
+
state: extendedAction.type === 'model' ? 'failed' : 'enabled',
|
|
628
|
+
error: `Error running action: ${error.message}`
|
|
629
|
+
})
|
|
630
|
+
);
|
|
440
631
|
});
|
|
441
632
|
} catch (error: any) {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
}
|
|
448
|
-
userLogger.debug(`Error running action: ${error.message}`);
|
|
449
|
-
return Promise.resolve({
|
|
450
|
-
actionId: runActionRequest.actionId,
|
|
451
|
-
actionName: extendedAction?.name ?? runActionRequest.actionName,
|
|
452
|
-
actionType: storeActionTypeToAPIActionType(extendedAction?.type) ?? runActionRequest.actionType,
|
|
453
|
-
// TODO: resolve the state if state function is defined
|
|
454
|
-
state: prevResultState ?? 'enabled',
|
|
455
|
-
error: `Error running action: ${error.message}`
|
|
456
|
-
});
|
|
633
|
+
isRunning = false;
|
|
634
|
+
delete customActionRunStateMap[actionId];
|
|
635
|
+
userLogger.warn(`Error running action: ${error.stack ?? error.message}`);
|
|
636
|
+
// rethrow the error to return erroneous response
|
|
637
|
+
throw error;
|
|
457
638
|
}
|
|
639
|
+
|
|
640
|
+
return { actionId };
|
|
458
641
|
}
|
|
459
642
|
|
|
460
643
|
function getCSIDocumentWithSourceFromDocumentSpec(
|
|
@@ -470,112 +653,34 @@ function getCSIDocumentWithSourceFromDocumentSpec(
|
|
|
470
653
|
: undefined;
|
|
471
654
|
}
|
|
472
655
|
|
|
473
|
-
function
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
479
|
-
}): StackbitTypes.CustomActionObjectModelStateParams {
|
|
480
|
-
const fieldActionCommonParams = getHandlerParamsForFieldAction({
|
|
481
|
-
extendedAction,
|
|
482
|
-
contentSourceDataById
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
if (!fieldActionCommonParams.documentField) {
|
|
486
|
-
throw new Error(`object document field not found at field path: ${extendedAction.fieldPath.join('.')}`);
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
const documentField = fieldActionCommonParams.documentField as StackbitTypes.DocumentModelFieldNonLocalized;
|
|
490
|
-
const documentSpec = extendedAction.documentSpec;
|
|
491
|
-
const contentSourceData = getContentSourceDataByTypeAndProjectIdOrThrow(documentSpec.srcType, documentSpec.srcProjectId, contentSourceDataById);
|
|
492
|
-
const objectModel = contentSourceData.modelMap[documentField.modelName];
|
|
493
|
-
if (!objectModel || objectModel.type !== 'object') {
|
|
494
|
-
throw new Error(`object model '${documentField.modelName}' not found`);
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
return {
|
|
498
|
-
...fieldActionCommonParams,
|
|
499
|
-
documentField,
|
|
500
|
-
modelField: fieldActionCommonParams.modelField as StackbitTypes.FieldModel,
|
|
501
|
-
objectModel: {
|
|
502
|
-
...objectModel,
|
|
503
|
-
srcType: documentSpec.srcType,
|
|
504
|
-
srcProjectId: documentSpec.srcProjectId
|
|
505
|
-
}
|
|
506
|
-
};
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
function getHandlerParamsForObjectFieldAction({
|
|
510
|
-
extendedAction,
|
|
511
|
-
contentSourceDataById
|
|
512
|
-
}: {
|
|
513
|
-
extendedAction: Pick<ExtendedCustomActionObjectField, 'documentSpec' | 'fieldPath'>;
|
|
514
|
-
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
515
|
-
}): StackbitTypes.CustomActionObjectFieldStateParams {
|
|
516
|
-
const fieldActionCommonParams = getHandlerParamsForFieldAction({
|
|
517
|
-
extendedAction,
|
|
518
|
-
contentSourceDataById
|
|
519
|
-
});
|
|
520
|
-
|
|
521
|
-
if (!fieldActionCommonParams.documentField) {
|
|
522
|
-
throw new Error(`object document field not found at field path: ${extendedAction.fieldPath.join('.')}`);
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
return {
|
|
526
|
-
...fieldActionCommonParams,
|
|
527
|
-
documentField: fieldActionCommonParams.documentField as StackbitTypes.DocumentObjectFieldNonLocalized,
|
|
528
|
-
modelField: fieldActionCommonParams.modelField as StackbitTypes.FieldObject
|
|
529
|
-
};
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
function getHandlerParamsForFieldAction({
|
|
533
|
-
extendedAction,
|
|
534
|
-
contentSourceDataById
|
|
656
|
+
function getRunParamsForModelAction({
|
|
657
|
+
modelSpec,
|
|
658
|
+
location,
|
|
659
|
+
contentSourceDataById,
|
|
660
|
+
getContentSourceActionsForSource
|
|
535
661
|
}: {
|
|
536
|
-
|
|
662
|
+
modelSpec: APICustomActionModelSpecifier;
|
|
663
|
+
location: 'new-card' | 'preset-card';
|
|
537
664
|
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
const
|
|
541
|
-
const
|
|
542
|
-
const
|
|
543
|
-
if (!
|
|
544
|
-
throw new Error(
|
|
545
|
-
`document not found for srcType: ${documentSpec.srcType}, srcProjectId: ${documentSpec.srcProjectId}, srcDocumentId: ${documentSpec.srcDocumentId}`
|
|
546
|
-
);
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
const model = contentSourceData.modelMap[document.srcModelName];
|
|
550
|
-
if (!model) {
|
|
551
|
-
throw new Error(`model '${document.srcModelName}' not found`);
|
|
665
|
+
getContentSourceActionsForSource: ReturnType<typeof getContentSourceActionsForSourceThunk>;
|
|
666
|
+
}): StackbitTypes.CustomActionModelRunOptions {
|
|
667
|
+
const { srcType, srcProjectId, modelName } = modelSpec;
|
|
668
|
+
const contentSourceData = getContentSourceDataByTypeAndProjectIdOrThrow(srcType, srcProjectId, contentSourceDataById);
|
|
669
|
+
const actionModel = contentSourceData.modelMap[modelName];
|
|
670
|
+
if (!actionModel) {
|
|
671
|
+
throw new Error(`model '${modelName}' not found`);
|
|
552
672
|
}
|
|
553
|
-
|
|
554
|
-
const mappedCSIDocument = mapStoreDocumentToCSIDocumentWithSource({ document, csiDocument });
|
|
555
|
-
|
|
556
|
-
// the documentField should be localized because fieldPath includes locales
|
|
557
|
-
const { modelField, documentField } = getModelAndDocumentFieldForLocalizedFieldPath({
|
|
558
|
-
document,
|
|
559
|
-
fieldPath: extendedAction.fieldPath,
|
|
560
|
-
modelMap: contentSourceData.modelMap
|
|
561
|
-
}) as {
|
|
562
|
-
// list items cannot have actions, therefore we can safely reduce the type to non list item fields
|
|
563
|
-
modelField: StackbitTypes.Field;
|
|
564
|
-
documentField?: ContentStoreTypes.DocumentField;
|
|
565
|
-
};
|
|
566
|
-
|
|
567
|
-
const csiDocumentField = documentField ? mapStoreFieldToCSIField(documentField) : undefined;
|
|
568
|
-
|
|
569
673
|
return {
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
srcProjectId: documentSpec.srcProjectId
|
|
674
|
+
actionModel: {
|
|
675
|
+
...actionModel,
|
|
676
|
+
srcType,
|
|
677
|
+
srcProjectId
|
|
575
678
|
},
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
679
|
+
location,
|
|
680
|
+
contentSourceActions: getContentSourceActionsForSource({
|
|
681
|
+
srcType,
|
|
682
|
+
srcProjectId
|
|
683
|
+
})!
|
|
579
684
|
};
|
|
580
685
|
}
|
|
581
686
|
|
|
@@ -597,16 +702,16 @@ function getCSIDocumentAndModelWithSourceFromDocumentSpec({
|
|
|
597
702
|
`document not found, srcType: ${documentSpec.srcType}, srcProjectId: ${documentSpec.srcProjectId}, srcDocumentId: ${documentSpec.srcDocumentId}`
|
|
598
703
|
);
|
|
599
704
|
}
|
|
600
|
-
const
|
|
705
|
+
const documentWithSource = mapStoreDocumentToCSIDocumentWithSource({
|
|
601
706
|
document,
|
|
602
707
|
csiDocument
|
|
603
708
|
});
|
|
604
|
-
const model = contentSourceData.modelMap[
|
|
709
|
+
const model = contentSourceData.modelMap[documentWithSource.modelName];
|
|
605
710
|
if (!model) {
|
|
606
|
-
throw new Error(`model '${
|
|
711
|
+
throw new Error(`model '${documentWithSource.modelName}' not found`);
|
|
607
712
|
}
|
|
608
713
|
return {
|
|
609
|
-
document:
|
|
714
|
+
document: documentWithSource,
|
|
610
715
|
model: {
|
|
611
716
|
...model,
|
|
612
717
|
srcType: documentSpec.srcType,
|
|
@@ -615,25 +720,23 @@ function getCSIDocumentAndModelWithSourceFromDocumentSpec({
|
|
|
615
720
|
};
|
|
616
721
|
}
|
|
617
722
|
|
|
618
|
-
function
|
|
619
|
-
if (storeActionType === 'objectModel' || storeActionType === 'objectField') {
|
|
620
|
-
return 'object';
|
|
621
|
-
}
|
|
622
|
-
return storeActionType;
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
function findCustomActionById({
|
|
723
|
+
function getExtendedActionById({
|
|
626
724
|
actionId,
|
|
627
725
|
customActionRunStateMap,
|
|
628
726
|
contentSourceDataById,
|
|
629
|
-
stackbitConfig
|
|
727
|
+
stackbitConfig,
|
|
728
|
+
generateNewActionId
|
|
630
729
|
}: {
|
|
631
730
|
actionId: string;
|
|
632
731
|
customActionRunStateMap: CustomActionRunStateMap;
|
|
633
732
|
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
634
733
|
stackbitConfig: Config | null;
|
|
734
|
+
generateNewActionId?: boolean;
|
|
635
735
|
}): ExtendedCustomAction | undefined {
|
|
636
736
|
const actionRunState = customActionRunStateMap[actionId];
|
|
737
|
+
|
|
738
|
+
// Check if this is a global action defined in the actions array of stackbit.config.
|
|
739
|
+
// Their types can be "global", "bulk" or "model"
|
|
637
740
|
if (isGlobalActionId(actionId)) {
|
|
638
741
|
if (!stackbitConfig || !Array.isArray(stackbitConfig.actions)) {
|
|
639
742
|
return undefined;
|
|
@@ -643,105 +746,241 @@ function findCustomActionById({
|
|
|
643
746
|
if (!action) {
|
|
644
747
|
return undefined;
|
|
645
748
|
}
|
|
749
|
+
return getExtendedActionFromGlobalAction({
|
|
750
|
+
action,
|
|
751
|
+
actionId,
|
|
752
|
+
actionRunState,
|
|
753
|
+
generateNewActionId
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
const { srcType, srcProjectId, modelName, srcDocumentId, fieldPath, actionName } = parseActionId(actionId) ?? {};
|
|
758
|
+
if (!srcType || !srcProjectId || !actionName) {
|
|
759
|
+
return undefined;
|
|
760
|
+
}
|
|
761
|
+
const contentSourceData = getContentSourceDataByTypeAndProjectIdOrThrow(srcType, srcProjectId, contentSourceDataById);
|
|
762
|
+
|
|
763
|
+
// If "modelName" was specified without "srcDocumentId" then it has to be a
|
|
764
|
+
// "model" action defined in a model of type 'page' or 'data'.
|
|
765
|
+
if (!srcDocumentId && modelName) {
|
|
766
|
+
const model = contentSourceData.modelMap[modelName];
|
|
767
|
+
if (!model || (model.type !== 'page' && model.type !== 'data')) {
|
|
768
|
+
return undefined;
|
|
769
|
+
}
|
|
770
|
+
const action = model.actions?.find((action) => action.name === actionName);
|
|
771
|
+
if (!action || action.type !== 'model') {
|
|
772
|
+
return undefined;
|
|
773
|
+
}
|
|
646
774
|
return {
|
|
647
775
|
...action,
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
776
|
+
extendedType: 'model',
|
|
777
|
+
actionId:
|
|
778
|
+
generateNewActionId && !actionRunState
|
|
779
|
+
? JSON.stringify({
|
|
780
|
+
srcType,
|
|
781
|
+
srcProjectId,
|
|
782
|
+
modelName,
|
|
783
|
+
actionName,
|
|
784
|
+
actionRunId: uuid().substring(0, 8)
|
|
785
|
+
})
|
|
786
|
+
: actionId,
|
|
787
|
+
label: getActionLabel(action),
|
|
788
|
+
modelSpec: { srcType, srcProjectId, modelName },
|
|
789
|
+
userId: actionRunState?.userId,
|
|
790
|
+
isRunning: actionRunState?.isRunning
|
|
652
791
|
};
|
|
653
792
|
}
|
|
654
|
-
|
|
655
|
-
if (!
|
|
793
|
+
|
|
794
|
+
if (!srcDocumentId) {
|
|
656
795
|
return undefined;
|
|
657
796
|
}
|
|
658
|
-
const documentSpec: APICustomActionDocumentSpecifier = { srcType, srcProjectId, srcDocumentId };
|
|
659
|
-
const contentSourceData = getContentSourceDataByTypeAndProjectIdOrThrow(srcType, srcProjectId, contentSourceDataById);
|
|
660
797
|
const document = contentSourceData.documentMap[srcDocumentId];
|
|
661
|
-
|
|
798
|
+
const csiDocument = contentSourceData.csiDocumentMap[srcDocumentId];
|
|
799
|
+
if (!document || !csiDocument) {
|
|
662
800
|
return undefined;
|
|
663
801
|
}
|
|
664
|
-
|
|
665
|
-
const model = contentSourceData.modelMap[
|
|
802
|
+
|
|
803
|
+
const model = contentSourceData.modelMap[document.srcModelName];
|
|
666
804
|
// The model of a document is always 'page' or 'data',
|
|
667
|
-
// this condition helps
|
|
805
|
+
// this condition helps Typescript infer the right type of model.actions
|
|
668
806
|
if (!model || (model.type !== 'page' && model.type !== 'data')) {
|
|
669
807
|
return undefined;
|
|
670
808
|
}
|
|
809
|
+
const documentWithSource = mapStoreDocumentToCSIDocumentWithSource({
|
|
810
|
+
document,
|
|
811
|
+
csiDocument
|
|
812
|
+
});
|
|
813
|
+
const modelWithSource = { ...model, srcType, srcProjectId };
|
|
671
814
|
if (typeof fieldPath === 'undefined') {
|
|
672
|
-
// fieldPath was not provided,
|
|
673
|
-
// and the action type must be 'document'
|
|
815
|
+
// If fieldPath was not provided, then the model must be of type "page" or "data",
|
|
816
|
+
// and the action type must be of type 'document'
|
|
674
817
|
const action = model.actions?.find((action) => action.name === actionName);
|
|
675
|
-
if (!action) {
|
|
818
|
+
if (!action || action.type !== 'document') {
|
|
676
819
|
return undefined;
|
|
677
820
|
}
|
|
678
821
|
return {
|
|
679
822
|
// if configuration is updated, the new action properties will override the stored action properties
|
|
680
823
|
...action,
|
|
681
824
|
type: 'document',
|
|
825
|
+
extendedType: 'document',
|
|
682
826
|
actionId,
|
|
683
|
-
label:
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
827
|
+
label: getActionLabel(action),
|
|
828
|
+
documentWithSource,
|
|
829
|
+
modelWithSource,
|
|
830
|
+
userId: actionRunState?.userId,
|
|
831
|
+
isRunning: actionRunState?.isRunning
|
|
687
832
|
};
|
|
688
833
|
} else {
|
|
689
834
|
const { modelField, documentField } = getModelAndDocumentFieldForLocalizedFieldPath({
|
|
690
835
|
document,
|
|
691
836
|
fieldPath,
|
|
692
837
|
modelMap: contentSourceData.modelMap
|
|
693
|
-
})
|
|
838
|
+
}) as {
|
|
839
|
+
// list items cannot have actions, therefore we can safely omit the list item types
|
|
840
|
+
modelField: StackbitTypes.Field;
|
|
841
|
+
documentField?: ContentStoreTypes.DocumentField;
|
|
842
|
+
};
|
|
843
|
+
const csiDocumentField = documentField ? mapStoreFieldToCSIField(documentField) : undefined;
|
|
694
844
|
|
|
845
|
+
// Find an action with the provided 'actionName' in model.field.actions
|
|
695
846
|
if ('actions' in modelField && Array.isArray(modelField.actions)) {
|
|
696
847
|
const action = modelField.actions.find((action) => action.name === actionName);
|
|
697
848
|
if (action) {
|
|
849
|
+
if (action.type === 'object') {
|
|
850
|
+
if (!csiDocumentField) {
|
|
851
|
+
throw new Error(`object document field not found at field path: ${fieldPath.join('.')}`);
|
|
852
|
+
}
|
|
853
|
+
if (modelField.type !== 'object' || csiDocumentField.type !== 'object') {
|
|
854
|
+
// action of type 'object' cannot be defined on field other than 'object'
|
|
855
|
+
return undefined;
|
|
856
|
+
}
|
|
857
|
+
return {
|
|
858
|
+
// if configuration is updated, the new action properties will override the stored action properties
|
|
859
|
+
...action,
|
|
860
|
+
extendedType: 'objectField',
|
|
861
|
+
actionId,
|
|
862
|
+
label: getActionLabel(action),
|
|
863
|
+
documentWithSource,
|
|
864
|
+
modelWithSource,
|
|
865
|
+
documentField: csiDocumentField as StackbitTypes.DocumentObjectFieldNonLocalized,
|
|
866
|
+
modelField: modelField as StackbitTypes.FieldObject,
|
|
867
|
+
fieldPath,
|
|
868
|
+
userId: actionRunState?.userId,
|
|
869
|
+
isRunning: actionRunState?.isRunning
|
|
870
|
+
};
|
|
871
|
+
}
|
|
698
872
|
return {
|
|
699
873
|
// if configuration is updated, the new action properties will override the stored action properties
|
|
700
874
|
...action,
|
|
701
|
-
type:
|
|
875
|
+
type: 'field',
|
|
876
|
+
extendedType: 'field',
|
|
702
877
|
actionId,
|
|
703
|
-
label:
|
|
704
|
-
|
|
878
|
+
label: getActionLabel(action),
|
|
879
|
+
documentWithSource,
|
|
880
|
+
modelWithSource,
|
|
881
|
+
documentField: csiDocumentField,
|
|
882
|
+
modelField,
|
|
705
883
|
fieldPath,
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
}
|
|
884
|
+
userId: actionRunState?.userId,
|
|
885
|
+
isRunning: actionRunState?.isRunning
|
|
886
|
+
};
|
|
709
887
|
}
|
|
710
888
|
}
|
|
711
889
|
|
|
890
|
+
// If the field is of type "model", find an action with the provided 'actionName' in model.actions
|
|
712
891
|
if (modelField.type === 'model') {
|
|
713
|
-
if (
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
892
|
+
if (modelName) {
|
|
893
|
+
const objectModel = contentSourceData.modelMap[modelName];
|
|
894
|
+
if (!objectModel || objectModel.type !== 'object') {
|
|
895
|
+
return undefined;
|
|
896
|
+
}
|
|
897
|
+
// This is a nested model of type "object", so the action must be CustomActionModelObject
|
|
898
|
+
const action = objectModel.actions?.find((action) => action.name === actionName);
|
|
899
|
+
if (!action || action.type !== 'model') {
|
|
900
|
+
return undefined;
|
|
901
|
+
}
|
|
902
|
+
return {
|
|
903
|
+
// if configuration is updated, the new action properties will override the stored action properties
|
|
904
|
+
...action,
|
|
905
|
+
extendedType: 'modelObject',
|
|
906
|
+
actionId,
|
|
907
|
+
label: getActionLabel(action),
|
|
908
|
+
documentWithSource,
|
|
909
|
+
modelWithSource,
|
|
910
|
+
modelField,
|
|
911
|
+
objectModel: { ...objectModel, srcType, srcProjectId },
|
|
912
|
+
fieldPath,
|
|
913
|
+
userId: actionRunState?.userId,
|
|
914
|
+
isRunning: actionRunState?.isRunning
|
|
915
|
+
};
|
|
916
|
+
} else {
|
|
917
|
+
if (!csiDocumentField || csiDocumentField.type !== 'model' || csiDocumentField.localized) {
|
|
918
|
+
return undefined;
|
|
919
|
+
}
|
|
920
|
+
const modelName = csiDocumentField.modelName;
|
|
921
|
+
const objectModel = contentSourceData.modelMap[modelName];
|
|
922
|
+
if (!objectModel || objectModel.type !== 'object') {
|
|
923
|
+
return undefined;
|
|
924
|
+
}
|
|
925
|
+
// This is a nested model of type "object", so the action must be CustomActionObjectModel
|
|
926
|
+
const action = objectModel.actions?.find((action) => action.name === actionName);
|
|
927
|
+
if (!action || action.type !== 'object') {
|
|
928
|
+
return undefined;
|
|
929
|
+
}
|
|
930
|
+
return {
|
|
931
|
+
// if configuration is updated, the new action properties will override the stored action properties
|
|
932
|
+
...action,
|
|
933
|
+
type: 'object',
|
|
934
|
+
extendedType: 'objectModel',
|
|
935
|
+
actionId,
|
|
936
|
+
label: getActionLabel(action),
|
|
937
|
+
documentWithSource,
|
|
938
|
+
modelWithSource,
|
|
939
|
+
documentField: csiDocumentField,
|
|
940
|
+
modelField,
|
|
941
|
+
objectModel: { ...objectModel, srcType, srcProjectId },
|
|
942
|
+
fieldPath,
|
|
943
|
+
userId: actionRunState?.userId,
|
|
944
|
+
isRunning: actionRunState?.isRunning
|
|
945
|
+
};
|
|
728
946
|
}
|
|
729
|
-
return {
|
|
730
|
-
// if configuration is updated, the new action properties will override the stored action properties
|
|
731
|
-
...action,
|
|
732
|
-
type: 'objectModel',
|
|
733
|
-
actionId,
|
|
734
|
-
label: action.label ?? _.startCase(action.name),
|
|
735
|
-
documentSpec,
|
|
736
|
-
fieldPath,
|
|
737
|
-
runningHandler: actionRunState?.runningHandler,
|
|
738
|
-
lastResultState: actionRunState?.lastResultState
|
|
739
|
-
};
|
|
740
947
|
}
|
|
741
948
|
}
|
|
742
949
|
}
|
|
743
950
|
|
|
744
|
-
function
|
|
951
|
+
function getExtendedActionFromGlobalAction({
|
|
952
|
+
action,
|
|
953
|
+
actionId,
|
|
954
|
+
actionRunState,
|
|
955
|
+
generateNewActionId
|
|
956
|
+
}: {
|
|
957
|
+
action: StackbitTypes.CustomActionGlobal | StackbitTypes.CustomActionBulk | StackbitTypes.CustomActionModel;
|
|
958
|
+
actionId: string;
|
|
959
|
+
actionRunState?: CustomActionRunState;
|
|
960
|
+
generateNewActionId?: boolean;
|
|
961
|
+
}): ExtendedCustomActionGlobal | ExtendedCustomActionBulk | ExtendedCustomActionModelGlobal {
|
|
962
|
+
if (action.type === 'model') {
|
|
963
|
+
return {
|
|
964
|
+
...action,
|
|
965
|
+
extendedType: 'modelGlobal',
|
|
966
|
+
actionId: generateNewActionId && !actionRunState ? `${actionId}.${uuid().substring(0, 8)}` : actionId,
|
|
967
|
+
label: getActionLabel(action),
|
|
968
|
+
modelSpec: actionRunState?.modelSpec,
|
|
969
|
+
userId: actionRunState?.userId,
|
|
970
|
+
isRunning: actionRunState?.isRunning
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
return {
|
|
974
|
+
...action,
|
|
975
|
+
extendedType: action.type,
|
|
976
|
+
actionId,
|
|
977
|
+
label: getActionLabel(action),
|
|
978
|
+
userId: actionRunState?.userId,
|
|
979
|
+
isRunning: actionRunState?.isRunning
|
|
980
|
+
} as ExtendedCustomActionGlobal | ExtendedCustomActionBulk;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
function globalActionId(action: StackbitTypes.CustomActionGlobal | StackbitTypes.CustomActionBulk | StackbitTypes.CustomActionModel): string {
|
|
745
984
|
return `config.actions.${action.name}`;
|
|
746
985
|
}
|
|
747
986
|
|
|
@@ -750,7 +989,8 @@ function isGlobalActionId(actionId: string) {
|
|
|
750
989
|
}
|
|
751
990
|
|
|
752
991
|
function getGlobalActionNameFromId(actionId: string) {
|
|
753
|
-
|
|
992
|
+
const result = actionId.match(/^config\.actions\.(.+?)(?:\.([\w-]+))?$/);
|
|
993
|
+
return result ? result[1] : undefined;
|
|
754
994
|
}
|
|
755
995
|
|
|
756
996
|
function parseActionId(actionId: string):
|
|
@@ -758,6 +998,7 @@ function parseActionId(actionId: string):
|
|
|
758
998
|
srcType?: string;
|
|
759
999
|
srcProjectId?: string;
|
|
760
1000
|
srcDocumentId?: string;
|
|
1001
|
+
modelName?: string;
|
|
761
1002
|
actionName?: string;
|
|
762
1003
|
fieldPath?: string[];
|
|
763
1004
|
}
|
|
@@ -768,3 +1009,17 @@ function parseActionId(actionId: string):
|
|
|
768
1009
|
return undefined;
|
|
769
1010
|
}
|
|
770
1011
|
}
|
|
1012
|
+
|
|
1013
|
+
function getActionLabel(
|
|
1014
|
+
action:
|
|
1015
|
+
| StackbitTypes.CustomActionGlobal
|
|
1016
|
+
| StackbitTypes.CustomActionBulk
|
|
1017
|
+
| StackbitTypes.CustomActionModel
|
|
1018
|
+
| StackbitTypes.CustomActionDocument
|
|
1019
|
+
| StackbitTypes.CustomActionModelObject
|
|
1020
|
+
| StackbitTypes.CustomActionObjectModel
|
|
1021
|
+
| StackbitTypes.CustomActionObjectField
|
|
1022
|
+
| StackbitTypes.CustomActionField
|
|
1023
|
+
): string {
|
|
1024
|
+
return action.label ?? _.startCase(action.name);
|
|
1025
|
+
}
|