@crowdin/app-project-module 0.100.3 → 0.101.0

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/out/index.js CHANGED
@@ -87,6 +87,7 @@ const projectToolsApp = __importStar(require("./modules/project-tools"));
87
87
  const webhooks = __importStar(require("./modules/webhooks"));
88
88
  const workflowStepType = __importStar(require("./modules/workflow-step-type"));
89
89
  const aiRequestProcessors = __importStar(require("./modules/ai-request-processors"));
90
+ const automationAction = __importStar(require("./modules/automation-action"));
90
91
  const subscription_1 = require("./util/subscription");
91
92
  var types_2 = require("./types");
92
93
  Object.defineProperty(exports, "ProjectPermissions", { enumerable: true, get: function () { return types_2.ProjectPermissions; } });
@@ -220,6 +221,7 @@ function addCrowdinEndpoints(app, clientConfig) {
220
221
  webhooks.register({ config, app });
221
222
  workflowStepType.register({ config, app });
222
223
  aiRequestProcessors.register({ config, app });
224
+ automationAction.register({ config, app });
223
225
  addFormSchema({ config, app });
224
226
  return Object.assign(Object.assign({}, exports.metadataStore), { storage: storage.getStorage(), establishCrowdinConnection: (authRequest, moduleKey) => {
225
227
  let jwtToken = '';
@@ -26,6 +26,7 @@ function handle(moduleConfig) {
26
26
  : `/api/${moduleConfig.key}/form-data`,
27
27
  formSchema: JSON.stringify(moduleConfig.formSchema),
28
28
  formUiSchema: moduleConfig.formUiSchema ? JSON.stringify(moduleConfig.formUiSchema) : '{}',
29
+ formPatchDataUrl: moduleConfig.formPatchDataUrl,
29
30
  });
30
31
  }
31
32
  if (moduleConfig.uiPath) {
@@ -11,6 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  const crowdin_apps_functions_1 = require("@crowdin/crowdin-apps-functions");
13
13
  const storage_1 = require("../storage");
14
+ const types_1 = require("../types");
14
15
  const util_1 = require("../util");
15
16
  const connection_1 = require("../util/connection");
16
17
  const logger_1 = require("../util/logger");
@@ -48,16 +49,19 @@ function handle({ config, allowUnauthorized = false, moduleType, }) {
48
49
  if (!credentials) {
49
50
  throw new Error("Can't find organization by id");
50
51
  }
51
- logInfo('Building crowdin client instance');
52
- const { token } = yield (0, connection_1.prepareCrowdinClient)({ config, credentials, context });
53
- const { expired, subscribeLink } = yield (0, subscription_1.checkSubscription)({
54
- config,
55
- token,
56
- organization: credentials.id,
57
- accountType: credentials.type,
58
- });
59
- if (expired) {
60
- return res.render('subscription', { subscribeLink });
52
+ // TODO Skip subscription check here for APP_WITH_CODE
53
+ if (config.authenticationType !== types_1.AuthenticationType.APP_WITH_CODE) {
54
+ logInfo('Building crowdin client instance');
55
+ const { token } = yield (0, connection_1.prepareCrowdinClient)({ config, credentials, context });
56
+ const { expired, subscribeLink } = yield (0, subscription_1.checkSubscription)({
57
+ config,
58
+ token,
59
+ organization: credentials.id,
60
+ accountType: credentials.type,
61
+ });
62
+ if (expired) {
63
+ return res.render('subscription', { subscribeLink });
64
+ }
61
65
  }
62
66
  next();
63
67
  }));
@@ -0,0 +1,5 @@
1
+ /// <reference types="qs" />
2
+ import { Response } from 'express';
3
+ import { CrowdinClientRequest } from '../../../types';
4
+ import { AutomationActionModule } from '../types';
5
+ export default function getExecuteHandler(automationAction: AutomationActionModule): (req: CrowdinClientRequest | import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const util_1 = require("../../../util");
13
+ const logger_1 = require("../../../util/logger");
14
+ const validate_input_1 = require("../util/validate-input");
15
+ // Maximum response size: 250KB
16
+ const MAX_RESPONSE_SIZE = 250 * 1024;
17
+ function getExecuteHandler(automationAction) {
18
+ return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
19
+ req.logInfo('Received automation action execute request');
20
+ const isTaskTokenMode = automationAction.invocationWaitMode === 'taskToken';
21
+ // In taskToken mode, respond immediately to prevent Crowdin retries
22
+ // Execution continues asynchronously, communicating via API calls
23
+ if (isTaskTokenMode) {
24
+ res.status(200).send();
25
+ }
26
+ try {
27
+ let validationErrors;
28
+ let inputData = req.body.formData || req.body;
29
+ if (automationAction.inputSchema) {
30
+ req.logInfo('Validate automation input data');
31
+ const { errors, data } = (0, validate_input_1.validateAutomationData)({
32
+ data: req.body,
33
+ schema: automationAction.inputSchema,
34
+ });
35
+ validationErrors = errors;
36
+ inputData = data;
37
+ }
38
+ const result = yield automationAction.execute({
39
+ client: req.crowdinApiClient,
40
+ inputData,
41
+ validationErrors,
42
+ context: req.crowdinContext,
43
+ req: req.body,
44
+ });
45
+ if (result) {
46
+ const responseJson = JSON.stringify(result);
47
+ const responseSize = Buffer.byteLength(responseJson, 'utf8');
48
+ // Check response size before sending
49
+ if (responseSize > MAX_RESPONSE_SIZE) {
50
+ throw new Error(`Response size (${responseSize} bytes) exceeds the maximum allowed size of ${MAX_RESPONSE_SIZE} bytes (250KB)`);
51
+ }
52
+ }
53
+ if (automationAction.validateOutputData !== false && result && automationAction.outputSchema) {
54
+ req.logInfo('Validate automation output data');
55
+ const { errors } = (0, validate_input_1.validateAutomationData)({
56
+ data: result,
57
+ schema: automationAction.outputSchema,
58
+ preserveEmptyArrays: true,
59
+ });
60
+ if (errors) {
61
+ throw new Error(JSON.stringify(errors));
62
+ }
63
+ }
64
+ if (!isTaskTokenMode) {
65
+ res.json({
66
+ data: result,
67
+ });
68
+ }
69
+ }
70
+ catch (error) {
71
+ req.logError(error);
72
+ if (!isTaskTokenMode) {
73
+ res.status(500).send({ error: { message: (0, logger_1.getErrorMessage)(error) } });
74
+ }
75
+ }
76
+ }));
77
+ }
78
+ exports.default = getExecuteHandler;
@@ -0,0 +1,5 @@
1
+ /// <reference types="qs" />
2
+ import { Response } from 'express';
3
+ import { AutomationActionModule } from '../types';
4
+ import { CrowdinClientRequest } from '../../../types';
5
+ export default function getInputSchemaHandler(automationAction: AutomationActionModule): (req: CrowdinClientRequest | import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const logger_1 = require("../../../util/logger");
13
+ const util_1 = require("../../../util");
14
+ function getInputSchemaHandler(automationAction) {
15
+ return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
16
+ try {
17
+ let schema = {};
18
+ if (automationAction.inputSchema) {
19
+ schema = automationAction.inputSchema;
20
+ }
21
+ res.json(schema);
22
+ }
23
+ catch (error) {
24
+ req.logError(error);
25
+ res.status(500).send({ error: { message: (0, logger_1.getErrorMessage)(error) } });
26
+ }
27
+ }));
28
+ }
29
+ exports.default = getInputSchemaHandler;
@@ -0,0 +1,5 @@
1
+ /// <reference types="qs" />
2
+ import { Response } from 'express';
3
+ import { AutomationActionModule } from '../types';
4
+ import { CrowdinClientRequest } from '../../../types';
5
+ export default function getOutputSchemaHandler(automationAction: AutomationActionModule): (req: CrowdinClientRequest | import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const logger_1 = require("../../../util/logger");
13
+ const util_1 = require("../../../util");
14
+ function getOutputSchemaHandler(automationAction) {
15
+ return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
16
+ try {
17
+ let schema = {};
18
+ if (automationAction.outputSchema) {
19
+ schema = automationAction.outputSchema;
20
+ }
21
+ res.json(schema);
22
+ }
23
+ catch (error) {
24
+ req.logError(error);
25
+ res.status(500).send({ error: { message: (0, logger_1.getErrorMessage)(error) } });
26
+ }
27
+ }));
28
+ }
29
+ exports.default = getOutputSchemaHandler;
@@ -0,0 +1,5 @@
1
+ /// <reference types="qs" />
2
+ import { Response } from 'express';
3
+ import { AutomationActionModule } from '../types';
4
+ import { CrowdinClientRequest } from '../../../types';
5
+ export default function getInputSchemaHandler(automationAction: AutomationActionModule): (req: CrowdinClientRequest | import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const logger_1 = require("../../../util/logger");
13
+ const util_1 = require("../../../util");
14
+ function getInputSchemaHandler(automationAction) {
15
+ return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
16
+ var _a;
17
+ try {
18
+ let result = {};
19
+ if (automationAction.validateSettings) {
20
+ result = yield automationAction.validateSettings({
21
+ client: req.crowdinApiClient,
22
+ settings: ((_a = req === null || req === void 0 ? void 0 : req.body) === null || _a === void 0 ? void 0 : _a.settings) || [],
23
+ context: req.crowdinContext,
24
+ req: req.body,
25
+ });
26
+ }
27
+ res.json(result);
28
+ }
29
+ catch (error) {
30
+ req.logError(error);
31
+ res.status(500).send({ error: { message: (0, logger_1.getErrorMessage)(error) } });
32
+ }
33
+ }));
34
+ }
35
+ exports.default = getInputSchemaHandler;
@@ -0,0 +1,6 @@
1
+ import { Express } from 'express';
2
+ import { Config } from '../../types';
3
+ export declare function register({ config, app }: {
4
+ config: Config;
5
+ app: Express;
6
+ }): void;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.register = void 0;
7
+ const cors_1 = __importDefault(require("cors"));
8
+ const output_schema_1 = __importDefault(require("./handlers/output-schema"));
9
+ const input_schema_1 = __importDefault(require("./handlers/input-schema"));
10
+ const validate_settings_1 = __importDefault(require("./handlers/validate-settings"));
11
+ const execute_1 = __importDefault(require("./handlers/execute"));
12
+ const util_1 = require("./util");
13
+ const crowdin_client_1 = __importDefault(require("../../middlewares/crowdin-client"));
14
+ const ui_module_1 = __importDefault(require("../../middlewares/ui-module"));
15
+ const render_ui_module_1 = __importDefault(require("../../middlewares/render-ui-module"));
16
+ function register({ config, app }) {
17
+ if (!config.automationAction) {
18
+ return;
19
+ }
20
+ const automationActions = Array.isArray(config.automationAction)
21
+ ? config.automationAction
22
+ : [config.automationAction];
23
+ for (const automationAction of automationActions) {
24
+ app.options((0, util_1.getAutomationActionUrl)('/automation-action/output-schema', automationAction), (0, cors_1.default)());
25
+ app.get((0, util_1.getAutomationActionUrl)('/automation-action/output-schema', automationAction), (0, cors_1.default)(), (0, output_schema_1.default)(automationAction));
26
+ app.options((0, util_1.getAutomationActionUrl)('/automation-action/input-schema', automationAction), (0, cors_1.default)());
27
+ app.get((0, util_1.getAutomationActionUrl)('/automation-action/input-schema', automationAction), (0, cors_1.default)(), (0, input_schema_1.default)(automationAction));
28
+ app.post((0, util_1.getAutomationActionUrl)('/automation-action/validate-settings', automationAction), (0, crowdin_client_1.default)({
29
+ config,
30
+ optional: true,
31
+ checkSubscriptionExpiration: false,
32
+ moduleKey: automationAction.key,
33
+ }), (0, validate_settings_1.default)(automationAction));
34
+ app.post((0, util_1.getAutomationActionUrl)('/automation-action/execute', automationAction), (0, cors_1.default)(), (0, crowdin_client_1.default)({
35
+ config,
36
+ optional: true,
37
+ checkSubscriptionExpiration: false,
38
+ moduleKey: automationAction.key,
39
+ }), (0, execute_1.default)(automationAction));
40
+ if (automationAction.settingsUiModule) {
41
+ app.use((0, util_1.getAutomationActionUrl)('/automation-action', automationAction), (0, ui_module_1.default)({ config, moduleType: automationAction.key }), (0, render_ui_module_1.default)(automationAction.settingsUiModule));
42
+ }
43
+ }
44
+ }
45
+ exports.register = register;
@@ -0,0 +1,49 @@
1
+ import { CrowdinContextInfo, ImagePath, ModuleKey, UiModule } from '../../types';
2
+ import Crowdin from '@crowdin/crowdin-api-client';
3
+ export interface AutomationActionModule extends ModuleKey, ImagePath {
4
+ /**
5
+ * module name
6
+ */
7
+ name: string;
8
+ /**
9
+ * module description
10
+ */
11
+ description?: string;
12
+ /**
13
+ * invocation wait mode, default is sync
14
+ */
15
+ invocationWaitMode?: 'sync' | 'taskToken';
16
+ /**
17
+ * JSON schema defining the structure of data returned by the automation action
18
+ */
19
+ outputSchema?: any;
20
+ /**
21
+ * JSON schema defining the structure of input parameters expected by the automation action
22
+ */
23
+ inputSchema?: any;
24
+ validateSettings?: ({ client, settings, context, req, }: {
25
+ client: Crowdin;
26
+ settings: any;
27
+ context: CrowdinContextInfo;
28
+ req: any;
29
+ }) => Promise<any>;
30
+ /**
31
+ * function to execute the automation action
32
+ */
33
+ execute: ({ client, inputData, validationErrors, context, req, }: {
34
+ client: Crowdin;
35
+ inputData: any;
36
+ validationErrors?: Record<string, string> | undefined;
37
+ context: CrowdinContextInfo;
38
+ req: any;
39
+ }) => Promise<any>;
40
+ /**
41
+ * Settings UI module
42
+ */
43
+ settingsUiModule?: UiModule;
44
+ /**
45
+ * validate output data, default: true
46
+ * set to false to skip output validation
47
+ */
48
+ validateOutputData?: boolean;
49
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,3 @@
1
+ import { AutomationActionModule } from '../types';
2
+ export declare function getAutomationActionKey(automationAction: AutomationActionModule): string;
3
+ export declare function getAutomationActionUrl(path: string, automationAction: AutomationActionModule): string;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getAutomationActionUrl = exports.getAutomationActionKey = void 0;
7
+ const lodash_kebabcase_1 = __importDefault(require("lodash.kebabcase"));
8
+ function getAutomationActionKey(automationAction) {
9
+ return (0, lodash_kebabcase_1.default)(automationAction.name);
10
+ }
11
+ exports.getAutomationActionKey = getAutomationActionKey;
12
+ function getAutomationActionUrl(path, automationAction) {
13
+ return `${path}/${automationAction.key}`;
14
+ }
15
+ exports.getAutomationActionUrl = getAutomationActionUrl;
@@ -0,0 +1,9 @@
1
+ export declare function validateAutomationData({ data, schema, preserveEmptyArrays, }: {
2
+ data: any;
3
+ schema: any;
4
+ preserveEmptyArrays?: boolean;
5
+ }): {
6
+ valid: boolean;
7
+ errors: Record<string, string> | undefined;
8
+ data: any;
9
+ };
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.validateAutomationData = void 0;
7
+ const ajv_1 = __importDefault(require("ajv"));
8
+ const ajv = new ajv_1.default({ allErrors: true, strict: false, coerceTypes: true });
9
+ function formatAjvErrors(errors = []) {
10
+ if (!errors.length) {
11
+ return undefined;
12
+ }
13
+ return errors.reduce((acc, err) => {
14
+ const path = err.instancePath || '/';
15
+ acc[path] = err.message || '';
16
+ return acc;
17
+ }, {});
18
+ }
19
+ function removeEmptyValues(obj, options = { preserveEmptyArrays: true }) {
20
+ const { preserveEmptyArrays = true } = options;
21
+ if (Array.isArray(obj)) {
22
+ return obj
23
+ .map((item) => removeEmptyValues(item, options))
24
+ .filter((v) => v !== undefined &&
25
+ v !== null &&
26
+ !(Array.isArray(v) && v.length === 0 && !preserveEmptyArrays) &&
27
+ !(typeof v === 'object' && Object.keys(v).length === 0));
28
+ }
29
+ if (typeof obj === 'object' && obj !== null) {
30
+ const newObj = {};
31
+ for (const [key, value] of Object.entries(obj)) {
32
+ if (value === undefined) {
33
+ continue;
34
+ }
35
+ const cleanedValue = removeEmptyValues(value, options);
36
+ if (cleanedValue !== '' &&
37
+ cleanedValue !== null &&
38
+ cleanedValue !== undefined &&
39
+ !(Array.isArray(cleanedValue) && cleanedValue.length === 0 && !preserveEmptyArrays) &&
40
+ !(typeof cleanedValue === 'object' &&
41
+ !Array.isArray(cleanedValue) &&
42
+ Object.keys(cleanedValue).length === 0)) {
43
+ newObj[key] = cleanedValue;
44
+ }
45
+ }
46
+ return newObj;
47
+ }
48
+ return obj;
49
+ }
50
+ function validateAutomationData({ data, schema, preserveEmptyArrays = false, }) {
51
+ const cleaned = removeEmptyValues(data, { preserveEmptyArrays });
52
+ const validate = ajv.compile(schema);
53
+ const valid = validate(cleaned);
54
+ return {
55
+ valid: Boolean(valid),
56
+ errors: formatAjvErrors((validate === null || validate === void 0 ? void 0 : validate.errors) || []),
57
+ data: cleaned,
58
+ };
59
+ }
60
+ exports.validateAutomationData = validateAutomationData;
@@ -54,7 +54,7 @@ function handle(config) {
54
54
  }
55
55
  exports.default = handle;
56
56
  function fetchToken(config, event) {
57
- var _a, _b, _c;
57
+ var _a, _b, _c, _d;
58
58
  return __awaiter(this, void 0, void 0, function* () {
59
59
  if (config.authenticationType === types_1.AuthenticationType.CODE) {
60
60
  const token = yield (0, crowdin_apps_functions_1.generateOAuthToken)({
@@ -86,6 +86,23 @@ function fetchToken(config, event) {
86
86
  refreshToken: '',
87
87
  };
88
88
  }
89
+ if (config.authenticationType === types_1.AuthenticationType.APP_WITH_CODE) {
90
+ const token = yield (0, crowdin_apps_functions_1.fetchAppWithCodeToken)({
91
+ appId: config.identifier,
92
+ appSecret: event.appSecret,
93
+ clientId: config.clientId,
94
+ clientSecret: config.clientSecret,
95
+ domain: event.domain || '',
96
+ userId: event.userId,
97
+ code: event.code,
98
+ url: (_c = config.crowdinUrls) === null || _c === void 0 ? void 0 : _c.accountUrl,
99
+ });
100
+ return {
101
+ accessToken: (0, util_1.encryptData)(config, token.accessToken),
102
+ expiresIn: token.expiresIn,
103
+ refreshToken: '',
104
+ };
105
+ }
89
106
  const token = yield (0, crowdin_apps_functions_1.fetchAppToken)({
90
107
  appId: config.identifier,
91
108
  appSecret: event.appSecret,
@@ -93,7 +110,7 @@ function fetchToken(config, event) {
93
110
  clientSecret: config.clientSecret,
94
111
  domain: event.domain || '',
95
112
  userId: event.userId,
96
- url: (_c = config.crowdinUrls) === null || _c === void 0 ? void 0 : _c.accountUrl,
113
+ url: (_d = config.crowdinUrls) === null || _d === void 0 ? void 0 : _d.accountUrl,
97
114
  });
98
115
  return {
99
116
  accessToken: (0, util_1.encryptData)(config, token.accessToken),
@@ -6,6 +6,7 @@ const subscription_1 = require("../util/subscription");
6
6
  const api_1 = require("./api/api");
7
7
  const util_2 = require("./ai-tools/util");
8
8
  const util_3 = require("./workflow-step-type/util");
9
+ const index_1 = require("./automation-action/util/index");
9
10
  const ai_request_processors_1 = require("./ai-request-processors");
10
11
  function normalizeEnvironments(environments) {
11
12
  if (Array.isArray(environments)) {
@@ -382,6 +383,30 @@ function handle(config) {
382
383
  modules['workflow-step-type'].push(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ key: workflowStep.key, name: workflowStep.name || config.name }, ((workflowStep === null || workflowStep === void 0 ? void 0 : workflowStep.imagePath) ? { logo: (0, util_1.getLogoUrl)(workflowStep, `-${workflowStep.key}`) } : {})), { description: workflowStep.description || config.description, boundaries: workflowStep.boundaries }), (workflowStep.editorMode ? { editorMode: workflowStep.editorMode } : {})), { updateSettingsUrl: (0, util_3.getWorkflowStepUrl)('/workflow-step/settings', workflowStep), deleteSettingsUrl: (0, util_3.getWorkflowStepUrl)('/workflow-step/delete', workflowStep) }), (uiModule ? { url: (0, util_3.getWorkflowStepUrl)('/workflow-step', workflowStep) } : {})));
383
384
  }
384
385
  }
386
+ if (config.automationAction) {
387
+ // prevent possible overrides of the other modules
388
+ if (Array.isArray(config.automationAction)) {
389
+ config.automationAction = config.automationAction.map((automationAction) => (Object.assign({}, automationAction)));
390
+ }
391
+ else {
392
+ config.automationAction = Object.assign({}, config.automationAction);
393
+ }
394
+ const automationActions = Array.isArray(config.automationAction)
395
+ ? config.automationAction
396
+ : [config.automationAction];
397
+ modules['automation-action'] = [];
398
+ for (const automationAction of automationActions) {
399
+ if (!automationAction.key) {
400
+ automationAction.key = config.identifier + '-' + (0, index_1.getAutomationActionKey)(automationAction);
401
+ }
402
+ const uiModule = automationAction.settingsUiModule;
403
+ modules['automation-action'].push(Object.assign(Object.assign(Object.assign(Object.assign({ key: automationAction.key, name: automationAction.name || config.name, description: automationAction.description || config.description }, (automationAction.outputSchema
404
+ ? { outputSchemaUrl: (0, index_1.getAutomationActionUrl)('/automation-action/output-schema', automationAction) }
405
+ : {})), (automationAction.inputSchema
406
+ ? { inputSchemaUrl: (0, index_1.getAutomationActionUrl)('/automation-action/input-schema', automationAction) }
407
+ : {})), { validateSettingsUrl: (0, index_1.getAutomationActionUrl)('/automation-action/validate-settings', automationAction), executeUrl: (0, index_1.getAutomationActionUrl)('/automation-action/execute', automationAction), invocationWaitMode: automationAction.invocationWaitMode || 'sync' }), (uiModule ? { url: (0, index_1.getAutomationActionUrl)('/automation-action', automationAction) } : {})));
408
+ }
409
+ }
385
410
  const events = {
386
411
  installed: '/installed',
387
412
  uninstall: '/uninstall',