@haiiro/docuflow 0.0.12 → 0.0.13
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/README.md +38 -8
- package/manifest.json +99 -13
- package/package.json +6 -3
- package/public/app/docflow-app.css +2 -0
- package/public/app/docflow-app.js +94 -0
- package/src/front-components/docflow-documents.front-component.mjs +3 -3
- package/src/front-components/docflow-processes.front-component.mjs +19 -19
- package/src/front-components/docflow-processes.front-component.tsx +18 -2
- package/src/front-components/docflow-tasks.front-component.mjs +3 -3
- package/src/front-components/launch-process.front-component.mjs +75 -0
- package/src/front-components/launch-process.front-component.tsx +270 -0
- package/src/front-components/process-steps.front-component.mjs +24 -24
- package/src/front-components/process-steps.front-component.tsx +54 -9
- package/src/front-components/task-form.front-component.mjs +21 -21
- package/src/logic-functions/bitrix-app.logic-function.mjs +160 -87
- package/src/logic-functions/bitrix-app.logic-function.ts +22 -59
- package/src/logic-functions/bitrix-install.logic-function.mjs +0 -11
- package/src/logic-functions/bitrix-install.logic-function.ts +0 -3
- package/src/logic-functions/docflow-app-session.logic-function.mjs +60043 -0
- package/src/logic-functions/docflow-app-session.logic-function.ts +54 -0
- package/src/logic-functions/docflow-app.logic-function.mjs +60122 -0
- package/src/logic-functions/docflow-app.logic-function.ts +37 -0
- package/src/logic-functions/launch-process.logic-function.mjs +113 -29
- package/src/logic-functions/launch-process.logic-function.ts +83 -22
- package/src/logic-functions/save-launch-fields.logic-function.mjs +176 -9
- package/src/logic-functions/save-launch-fields.logic-function.ts +24 -6
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { defineLogicFunction } from 'twenty-sdk/define';
|
|
2
|
+
import {
|
|
3
|
+
type LogicFunctionExecutionContext,
|
|
4
|
+
type RoutePayload,
|
|
5
|
+
} from 'twenty-sdk/logic-function';
|
|
6
|
+
|
|
7
|
+
import { readDocflowAppSection } from 'src/shared/app/docflow-app.util';
|
|
8
|
+
import { getCrmUrl } from 'src/server/crm-url';
|
|
9
|
+
import { renderDocflowAppPage } from 'src/server/docflow-app-page';
|
|
10
|
+
import { DOCFLOW_APP_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER } from 'src/universal-identifiers';
|
|
11
|
+
|
|
12
|
+
// The standalone docflow page: tasks, processes and documents without the
|
|
13
|
+
// rest of the CRM. It needs no sign-in to load: the page itself signs in with
|
|
14
|
+
// the CRM session (see /docflow/app/session). `?connect=1` is the popup that
|
|
15
|
+
// signs in the page framed in Bitrix24.
|
|
16
|
+
const handler = async (event: RoutePayload, context: LogicFunctionExecutionContext) =>
|
|
17
|
+
renderDocflowAppPage({
|
|
18
|
+
workspaceId: context.workspaceId,
|
|
19
|
+
config: {
|
|
20
|
+
mode: event.queryStringParameters.connect === '1' ? 'connect' : 'crm',
|
|
21
|
+
crmUrl: getCrmUrl() ?? '',
|
|
22
|
+
section: readDocflowAppSection(event.queryStringParameters.section),
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export default defineLogicFunction({
|
|
27
|
+
universalIdentifier: DOCFLOW_APP_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER,
|
|
28
|
+
name: 'docflow-app',
|
|
29
|
+
description: 'Отдельная страница документооборота без остальной CRM',
|
|
30
|
+
timeoutSeconds: 10,
|
|
31
|
+
handler,
|
|
32
|
+
httpRouteTriggerSettings: {
|
|
33
|
+
path: '/docflow/app',
|
|
34
|
+
httpMethod: 'GET',
|
|
35
|
+
isAuthRequired: false,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
@@ -1725,10 +1725,67 @@ var RUN_WORKFLOW_VERSION_MUTATION = `mutation RunWorkflowVersion($input: RunWork
|
|
|
1725
1725
|
runWorkflowVersion(input: $input) { workflowRunId }
|
|
1726
1726
|
}`;
|
|
1727
1727
|
|
|
1728
|
+
// src/shared/api/object-summaries.ts
|
|
1729
|
+
import { MetadataApiClient } from "twenty-client-sdk/metadata";
|
|
1730
|
+
var OBJECTS_PAGE_SIZE = 100;
|
|
1731
|
+
var getObjectSummaries = async () => {
|
|
1732
|
+
const client = new MetadataApiClient();
|
|
1733
|
+
const summaries = /* @__PURE__ */ new Map();
|
|
1734
|
+
let after;
|
|
1735
|
+
do {
|
|
1736
|
+
const { objects } = await client.query({
|
|
1737
|
+
objects: {
|
|
1738
|
+
__args: {
|
|
1739
|
+
paging: { first: OBJECTS_PAGE_SIZE, ...after !== void 0 ? { after } : {} },
|
|
1740
|
+
filter: {}
|
|
1741
|
+
},
|
|
1742
|
+
pageInfo: { hasNextPage: true, endCursor: true },
|
|
1743
|
+
edges: {
|
|
1744
|
+
node: {
|
|
1745
|
+
nameSingular: true,
|
|
1746
|
+
namePlural: true,
|
|
1747
|
+
labelSingular: true,
|
|
1748
|
+
labelIdentifierFieldMetadataId: true,
|
|
1749
|
+
fieldsList: { id: true, name: true }
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
});
|
|
1754
|
+
for (const { node } of objects.edges) {
|
|
1755
|
+
summaries.set(node.nameSingular, {
|
|
1756
|
+
nameSingular: node.nameSingular,
|
|
1757
|
+
namePlural: node.namePlural,
|
|
1758
|
+
labelSingular: node.labelSingular,
|
|
1759
|
+
labelFieldName: node.fieldsList?.find(
|
|
1760
|
+
(field) => field.id === node.labelIdentifierFieldMetadataId
|
|
1761
|
+
)?.name
|
|
1762
|
+
});
|
|
1763
|
+
}
|
|
1764
|
+
after = objects.pageInfo.hasNextPage === true ? objects.pageInfo.endCursor ?? void 0 : void 0;
|
|
1765
|
+
} while (after !== void 0);
|
|
1766
|
+
return summaries;
|
|
1767
|
+
};
|
|
1768
|
+
|
|
1769
|
+
// src/shared/api/rest-response.util.ts
|
|
1770
|
+
var readDataValue = (response) => {
|
|
1771
|
+
const data = response?.data;
|
|
1772
|
+
if (typeof data !== "object" || data === null) {
|
|
1773
|
+
return void 0;
|
|
1774
|
+
}
|
|
1775
|
+
return Object.values(data)[0];
|
|
1776
|
+
};
|
|
1777
|
+
var readRestRecord = (response) => {
|
|
1778
|
+
const value = readDataValue(response);
|
|
1779
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
1780
|
+
};
|
|
1781
|
+
|
|
1728
1782
|
// src/shared/docflow.constants.ts
|
|
1729
1783
|
var DOCFLOW_SECTION = "DOCFLOW";
|
|
1730
1784
|
var DOCFLOW_LAUNCH_PARAMETERS_KEY = "smbDocflowParameters";
|
|
1731
1785
|
var LAUNCH_PARAMETERS_STEP_INPUT = `{{trigger.${DOCFLOW_LAUNCH_PARAMETERS_KEY}}}`;
|
|
1786
|
+
var LAUNCH_TRIGGER_NAME = "\u0417\u0430\u043F\u0443\u0441\u043A \u0441 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u0430\u043C\u0438";
|
|
1787
|
+
var LAUNCH_RECORD_KEY = "record";
|
|
1788
|
+
var LAUNCH_RECORDS_LIMIT = 50;
|
|
1732
1789
|
var FORM_FIELD_REQUIRED_SETTING = "smbIsRequired";
|
|
1733
1790
|
var FORM_FIELD_ROLE_SETTING = "smbRole";
|
|
1734
1791
|
|
|
@@ -1766,7 +1823,9 @@ var parseFormFields = (value) => Array.isArray(value) ? value.filter(
|
|
|
1766
1823
|
) : [];
|
|
1767
1824
|
|
|
1768
1825
|
// src/shared/workflow/launch-step.util.ts
|
|
1826
|
+
var isLaunchTrigger = (trigger) => trigger?.type === "WEBHOOK" && trigger.name === LAUNCH_TRIGGER_NAME;
|
|
1769
1827
|
var parseLaunchFields = (value) => getFillableFormFields(parseFormFields(value));
|
|
1828
|
+
var parseLaunchObject = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
1770
1829
|
var buildLaunchParameters = ({
|
|
1771
1830
|
fields,
|
|
1772
1831
|
values
|
|
@@ -1776,9 +1835,10 @@ var buildLaunchParameters = ({
|
|
|
1776
1835
|
return [field.name, value === void 0 || value === "" ? null : value];
|
|
1777
1836
|
})
|
|
1778
1837
|
);
|
|
1779
|
-
var buildLaunchPayload = (parameters) => ({
|
|
1838
|
+
var buildLaunchPayload = (parameters, record) => ({
|
|
1780
1839
|
...parameters,
|
|
1781
|
-
[DOCFLOW_LAUNCH_PARAMETERS_KEY]: parameters
|
|
1840
|
+
[DOCFLOW_LAUNCH_PARAMETERS_KEY]: parameters,
|
|
1841
|
+
...record !== void 0 ? { [LAUNCH_RECORD_KEY]: record } : {}
|
|
1782
1842
|
});
|
|
1783
1843
|
|
|
1784
1844
|
// node_modules/twenty-sdk/dist/logic-function/isDefined-Dtu5EYqP-CMNeFg4z.mjs
|
|
@@ -119427,19 +119487,6 @@ var jsonResponse = (status, body) => new AL(JSON.stringify(body), {
|
|
|
119427
119487
|
});
|
|
119428
119488
|
var readJsonBody = (body) => typeof body === "object" && body !== null && !Array.isArray(body) ? body : {};
|
|
119429
119489
|
|
|
119430
|
-
// src/shared/api/rest-response.util.ts
|
|
119431
|
-
var readDataValue = (response) => {
|
|
119432
|
-
const data = response?.data;
|
|
119433
|
-
if (typeof data !== "object" || data === null) {
|
|
119434
|
-
return void 0;
|
|
119435
|
-
}
|
|
119436
|
-
return Object.values(data)[0];
|
|
119437
|
-
};
|
|
119438
|
-
var readRestRecord = (response) => {
|
|
119439
|
-
const value = readDataValue(response);
|
|
119440
|
-
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
119441
|
-
};
|
|
119442
|
-
|
|
119443
119490
|
// src/server/records.ts
|
|
119444
119491
|
var clients = {
|
|
119445
119492
|
person: new u2(),
|
|
@@ -119485,6 +119532,7 @@ var logDocflowError = (event, error, details) => console.error(PREFIX, event, de
|
|
|
119485
119532
|
var LAUNCH_PROCESS_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER = "de04e9c8-7ebe-4ac2-b4b3-4ca256eb94db";
|
|
119486
119533
|
|
|
119487
119534
|
// src/logic-functions/launch-process.logic-function.ts
|
|
119535
|
+
var readRecordIds = (value) => Array.isArray(value) ? [...new Set(value.filter((id3) => typeof id3 === "string" && id3.length > 0))] : [];
|
|
119488
119536
|
var handler = async (event, context) => {
|
|
119489
119537
|
if (context.workspaceMemberId === null) {
|
|
119490
119538
|
return jsonResponse(401, { message: "\u0417\u0430\u043F\u0443\u0441\u043A\u0430\u0442\u044C \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u044B \u043C\u043E\u0436\u0435\u0442 \u0442\u043E\u043B\u044C\u043A\u043E \u0441\u043E\u0442\u0440\u0443\u0434\u043D\u0438\u043A" });
|
|
@@ -119492,6 +119540,7 @@ var handler = async (event, context) => {
|
|
|
119492
119540
|
const body = readJsonBody(event.body);
|
|
119493
119541
|
const workflowVersionId = body.workflowVersionId;
|
|
119494
119542
|
const values = readJsonBody(body.values);
|
|
119543
|
+
const recordIds = readRecordIds(body.recordIds);
|
|
119495
119544
|
if (typeof workflowVersionId !== "string" || workflowVersionId.length === 0) {
|
|
119496
119545
|
return jsonResponse(400, { message: "\u041D\u0435 \u0443\u043A\u0430\u0437\u0430\u043D \u043F\u0440\u043E\u0446\u0435\u0441\u0441" });
|
|
119497
119546
|
}
|
|
@@ -119512,21 +119561,56 @@ var handler = async (event, context) => {
|
|
|
119512
119561
|
missingFieldNames: missingFields.map(({ name }) => name)
|
|
119513
119562
|
});
|
|
119514
119563
|
}
|
|
119515
|
-
const
|
|
119516
|
-
|
|
119517
|
-
|
|
119518
|
-
|
|
119519
|
-
|
|
119520
|
-
|
|
119521
|
-
}
|
|
119522
|
-
|
|
119523
|
-
|
|
119524
|
-
|
|
119525
|
-
|
|
119526
|
-
|
|
119564
|
+
const launchObjectName = parseLaunchObject(workflow.smbLaunchObject);
|
|
119565
|
+
const isLaunchedOnRecords = launchObjectName !== void 0 && isLaunchTrigger(version.trigger);
|
|
119566
|
+
let records = [void 0];
|
|
119567
|
+
if (isLaunchedOnRecords) {
|
|
119568
|
+
if (recordIds.length === 0) {
|
|
119569
|
+
return jsonResponse(422, { message: "\u041F\u0440\u043E\u0446\u0435\u0441\u0441 \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u0435\u0442\u0441\u044F \u043D\u0430 \u0437\u0430\u043F\u0438\u0441\u0438: \u0432\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0437\u0430\u043F\u0438\u0441\u044C" });
|
|
119570
|
+
}
|
|
119571
|
+
if (recordIds.length > LAUNCH_RECORDS_LIMIT) {
|
|
119572
|
+
return jsonResponse(422, { message: `\u0417\u0430 \u043E\u0434\u0438\u043D \u0440\u0430\u0437 \u2014 \u043D\u0435 \u0431\u043E\u043B\u044C\u0448\u0435 ${LAUNCH_RECORDS_LIMIT} \u0437\u0430\u043F\u0438\u0441\u0435\u0439` });
|
|
119573
|
+
}
|
|
119574
|
+
const namePlural = (await getObjectSummaries()).get(launchObjectName)?.namePlural;
|
|
119575
|
+
if (namePlural === void 0) {
|
|
119576
|
+
return jsonResponse(404, { message: "\u041E\u0431\u044A\u0435\u043A\u0442, \u043D\u0430 \u043A\u043E\u0442\u043E\u0440\u043E\u043C \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u0435\u0442\u0441\u044F \u043F\u0440\u043E\u0446\u0435\u0441\u0441, \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D" });
|
|
119577
|
+
}
|
|
119578
|
+
records = await Promise.all(
|
|
119579
|
+
recordIds.map(async (recordId) => {
|
|
119580
|
+
try {
|
|
119581
|
+
return readRestRecord(
|
|
119582
|
+
await restClient("person").get(`/rest/${namePlural}/${recordId}`, { query: { depth: 0 } })
|
|
119583
|
+
);
|
|
119584
|
+
} catch (error) {
|
|
119585
|
+
logDocflowError("launch: record not read", error, { namePlural, recordId });
|
|
119586
|
+
return void 0;
|
|
119587
|
+
}
|
|
119588
|
+
})
|
|
119589
|
+
);
|
|
119590
|
+
if (records.some((record) => record === void 0)) {
|
|
119591
|
+
return jsonResponse(404, { message: "\u0417\u0430\u043F\u0438\u0441\u044C \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u0430" });
|
|
119592
|
+
}
|
|
119593
|
+
}
|
|
119594
|
+
const workflowRunIds = [];
|
|
119595
|
+
for (const record of records) {
|
|
119596
|
+
const workflowRunId = crypto.randomUUID();
|
|
119597
|
+
try {
|
|
119598
|
+
await runWorkflowVersion({
|
|
119599
|
+
workflowVersionId,
|
|
119600
|
+
workflowRunId,
|
|
119601
|
+
payload: fields.length > 0 || record !== void 0 ? buildLaunchPayload(parameters, record) : {}
|
|
119602
|
+
});
|
|
119603
|
+
} catch (error) {
|
|
119604
|
+
logDocflowError("launch: run failed", error, { workflowVersionId, recordId: record?.id });
|
|
119605
|
+
return jsonResponse(403, {
|
|
119606
|
+
message: error instanceof CoreGraphqlError && error.userMessage !== void 0 ? error.userMessage : "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u044C \u043F\u0440\u043E\u0446\u0435\u0441\u0441",
|
|
119607
|
+
workflowRunIds
|
|
119608
|
+
});
|
|
119609
|
+
}
|
|
119610
|
+
workflowRunIds.push(workflowRunId);
|
|
119527
119611
|
}
|
|
119528
|
-
logDocflow("launch: started", { workflowVersionId,
|
|
119529
|
-
return { workflowRunId };
|
|
119612
|
+
logDocflow("launch: started", { workflowVersionId, workflowRunIds, parameters, recordIds });
|
|
119613
|
+
return { workflowRunId: workflowRunIds[0], workflowRunIds };
|
|
119530
119614
|
};
|
|
119531
119615
|
var launch_process_logic_function_default = defineLogicFunction({
|
|
119532
119616
|
universalIdentifier: LAUNCH_PROCESS_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER,
|
|
@@ -5,22 +5,33 @@ import {
|
|
|
5
5
|
} from 'twenty-sdk/logic-function';
|
|
6
6
|
|
|
7
7
|
import { CoreGraphqlError } from 'src/shared/api/core-graphql.util';
|
|
8
|
-
import {
|
|
8
|
+
import { getObjectSummaries } from 'src/shared/api/object-summaries';
|
|
9
|
+
import { readRestRecord } from 'src/shared/api/rest-response.util';
|
|
10
|
+
import { DOCFLOW_SECTION, LAUNCH_RECORDS_LIMIT } from 'src/shared/docflow.constants';
|
|
9
11
|
import { findMissingRequiredFields } from 'src/shared/form/form-fields.util';
|
|
10
12
|
import {
|
|
11
13
|
buildLaunchParameters,
|
|
12
14
|
buildLaunchPayload,
|
|
15
|
+
isLaunchTrigger,
|
|
13
16
|
parseLaunchFields,
|
|
17
|
+
parseLaunchObject,
|
|
14
18
|
} from 'src/shared/workflow/launch-step.util';
|
|
15
19
|
import { readJsonBody, jsonResponse } from 'src/server/http-response';
|
|
16
|
-
import { getWorkflow, getWorkflowVersion } from 'src/server/records';
|
|
20
|
+
import { getWorkflow, getWorkflowVersion, restClient } from 'src/server/records';
|
|
17
21
|
import { runWorkflowVersion } from 'src/server/workflow-engine';
|
|
18
22
|
import { logDocflow, logDocflowError } from 'src/shared/log.util';
|
|
19
23
|
import { LAUNCH_PROCESS_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER } from 'src/universal-identifiers';
|
|
20
24
|
|
|
25
|
+
const readRecordIds = (value: unknown): string[] =>
|
|
26
|
+
Array.isArray(value)
|
|
27
|
+
? [...new Set(value.filter((id): id is string => typeof id === 'string' && id.length > 0))]
|
|
28
|
+
: [];
|
|
29
|
+
|
|
21
30
|
// Launch with parameters: they are checked here and ride in the run payload,
|
|
22
|
-
// so they stay in the run as its trigger output
|
|
23
|
-
// the
|
|
31
|
+
// so they stay in the run as its trigger output ({{trigger.<field>}}). A
|
|
32
|
+
// process launched on records runs once per record, the record read as the
|
|
33
|
+
// person and put into its run as {{trigger.record}}, like the standard manual
|
|
34
|
+
// launch on a selection does.
|
|
24
35
|
const handler = async (
|
|
25
36
|
event: RoutePayload,
|
|
26
37
|
context: LogicFunctionExecutionContext,
|
|
@@ -32,6 +43,7 @@ const handler = async (
|
|
|
32
43
|
const body = readJsonBody(event.body);
|
|
33
44
|
const workflowVersionId = body.workflowVersionId;
|
|
34
45
|
const values = readJsonBody(body.values);
|
|
46
|
+
const recordIds = readRecordIds(body.recordIds);
|
|
35
47
|
|
|
36
48
|
if (typeof workflowVersionId !== 'string' || workflowVersionId.length === 0) {
|
|
37
49
|
return jsonResponse(400, { message: 'Не указан процесс' });
|
|
@@ -63,28 +75,77 @@ const handler = async (
|
|
|
63
75
|
});
|
|
64
76
|
}
|
|
65
77
|
|
|
66
|
-
const
|
|
78
|
+
const launchObjectName = parseLaunchObject(workflow.smbLaunchObject);
|
|
79
|
+
// The record's object comes from the published trigger: the field may
|
|
80
|
+
// already hold the object of a draft not published yet.
|
|
81
|
+
const isLaunchedOnRecords = launchObjectName !== undefined && isLaunchTrigger(version.trigger);
|
|
82
|
+
let records: (Record<string, unknown> | undefined)[] = [undefined];
|
|
83
|
+
|
|
84
|
+
if (isLaunchedOnRecords) {
|
|
85
|
+
if (recordIds.length === 0) {
|
|
86
|
+
return jsonResponse(422, { message: 'Процесс запускается на записи: выберите запись' });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (recordIds.length > LAUNCH_RECORDS_LIMIT) {
|
|
90
|
+
return jsonResponse(422, { message: `За один раз — не больше ${LAUNCH_RECORDS_LIMIT} записей` });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const namePlural = (await getObjectSummaries()).get(launchObjectName)?.namePlural;
|
|
94
|
+
|
|
95
|
+
if (namePlural === undefined) {
|
|
96
|
+
return jsonResponse(404, { message: 'Объект, на котором запускается процесс, не найден' });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Read as the person: a record they cannot see does not start the process
|
|
100
|
+
records = await Promise.all(
|
|
101
|
+
recordIds.map(async (recordId) => {
|
|
102
|
+
try {
|
|
103
|
+
return readRestRecord<Record<string, unknown>>(
|
|
104
|
+
await restClient('person').get(`/rest/${namePlural}/${recordId}`, { query: { depth: 0 } }),
|
|
105
|
+
);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
logDocflowError('launch: record not read', error, { namePlural, recordId });
|
|
108
|
+
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
}),
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
if (records.some((record) => record === undefined)) {
|
|
115
|
+
return jsonResponse(404, { message: 'Запись не найдена' });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
67
118
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
119
|
+
const workflowRunIds: string[] = [];
|
|
120
|
+
|
|
121
|
+
for (const record of records) {
|
|
122
|
+
const workflowRunId = crypto.randomUUID();
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
await runWorkflowVersion({
|
|
126
|
+
workflowVersionId,
|
|
127
|
+
workflowRunId,
|
|
128
|
+
payload:
|
|
129
|
+
fields.length > 0 || record !== undefined ? buildLaunchPayload(parameters, record) : {},
|
|
130
|
+
});
|
|
131
|
+
} catch (error) {
|
|
132
|
+
logDocflowError('launch: run failed', error, { workflowVersionId, recordId: record?.id });
|
|
133
|
+
|
|
134
|
+
return jsonResponse(403, {
|
|
135
|
+
message:
|
|
136
|
+
error instanceof CoreGraphqlError && error.userMessage !== undefined
|
|
137
|
+
? error.userMessage
|
|
138
|
+
: 'Не удалось запустить процесс',
|
|
139
|
+
workflowRunIds,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
workflowRunIds.push(workflowRunId);
|
|
83
144
|
}
|
|
84
145
|
|
|
85
|
-
logDocflow('launch: started', { workflowVersionId,
|
|
146
|
+
logDocflow('launch: started', { workflowVersionId, workflowRunIds, parameters, recordIds });
|
|
86
147
|
|
|
87
|
-
return { workflowRunId };
|
|
148
|
+
return { workflowRunId: workflowRunIds[0], workflowRunIds };
|
|
88
149
|
};
|
|
89
150
|
|
|
90
151
|
export default defineLogicFunction({
|
|
@@ -1734,6 +1734,66 @@ var CREATE_DRAFT_FROM_WORKFLOW_VERSION_MUTATION = `mutation CreateDraftFromWorkf
|
|
|
1734
1734
|
createDraftFromWorkflowVersion(input: $input) { id }
|
|
1735
1735
|
}`;
|
|
1736
1736
|
|
|
1737
|
+
// src/shared/api/object-summaries.ts
|
|
1738
|
+
import { MetadataApiClient } from "twenty-client-sdk/metadata";
|
|
1739
|
+
var OBJECTS_PAGE_SIZE = 100;
|
|
1740
|
+
var listLaunchObjects = async () => {
|
|
1741
|
+
const client = new MetadataApiClient();
|
|
1742
|
+
const launchObjects = [];
|
|
1743
|
+
let after;
|
|
1744
|
+
do {
|
|
1745
|
+
const { objects } = await client.query({
|
|
1746
|
+
objects: {
|
|
1747
|
+
__args: {
|
|
1748
|
+
paging: { first: OBJECTS_PAGE_SIZE, ...after !== void 0 ? { after } : {} },
|
|
1749
|
+
filter: { isActive: { is: true }, isSystem: { is: false } }
|
|
1750
|
+
},
|
|
1751
|
+
pageInfo: { hasNextPage: true, endCursor: true },
|
|
1752
|
+
edges: {
|
|
1753
|
+
node: {
|
|
1754
|
+
id: true,
|
|
1755
|
+
nameSingular: true,
|
|
1756
|
+
namePlural: true,
|
|
1757
|
+
labelSingular: true,
|
|
1758
|
+
icon: true,
|
|
1759
|
+
fieldsList: {
|
|
1760
|
+
id: true,
|
|
1761
|
+
name: true,
|
|
1762
|
+
label: true,
|
|
1763
|
+
type: true,
|
|
1764
|
+
icon: true,
|
|
1765
|
+
isActive: true,
|
|
1766
|
+
isSystem: true,
|
|
1767
|
+
relation: { type: true }
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
});
|
|
1773
|
+
for (const { node } of objects.edges) {
|
|
1774
|
+
launchObjects.push({
|
|
1775
|
+
id: node.id,
|
|
1776
|
+
nameSingular: node.nameSingular,
|
|
1777
|
+
namePlural: node.namePlural,
|
|
1778
|
+
labelSingular: node.labelSingular,
|
|
1779
|
+
icon: node.icon,
|
|
1780
|
+
fields: (node.fieldsList ?? []).map((field) => ({
|
|
1781
|
+
id: field.id,
|
|
1782
|
+
name: field.name,
|
|
1783
|
+
label: field.label,
|
|
1784
|
+
type: String(field.type),
|
|
1785
|
+
icon: field.icon,
|
|
1786
|
+
isActive: field.isActive,
|
|
1787
|
+
isSystem: field.isSystem,
|
|
1788
|
+
relationType: field.relation?.type ?? null
|
|
1789
|
+
}))
|
|
1790
|
+
});
|
|
1791
|
+
}
|
|
1792
|
+
after = objects.pageInfo.hasNextPage === true ? objects.pageInfo.endCursor ?? void 0 : void 0;
|
|
1793
|
+
} while (after !== void 0);
|
|
1794
|
+
return launchObjects;
|
|
1795
|
+
};
|
|
1796
|
+
|
|
1737
1797
|
// src/shared/api/rest-response.util.ts
|
|
1738
1798
|
var readDataValue = (response) => {
|
|
1739
1799
|
const data = response?.data;
|
|
@@ -1759,6 +1819,7 @@ var readRestRecord = (response) => {
|
|
|
1759
1819
|
var DOCFLOW_LAUNCH_PARAMETERS_KEY = "smbDocflowParameters";
|
|
1760
1820
|
var LAUNCH_PARAMETERS_STEP_INPUT = `{{trigger.${DOCFLOW_LAUNCH_PARAMETERS_KEY}}}`;
|
|
1761
1821
|
var LAUNCH_TRIGGER_NAME = "\u0417\u0430\u043F\u0443\u0441\u043A \u0441 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u0430\u043C\u0438";
|
|
1822
|
+
var LAUNCH_RECORD_KEY = "record";
|
|
1762
1823
|
var FORM_FIELD_ROLE_SETTING = "smbRole";
|
|
1763
1824
|
var TRIGGER_STEP_ID = "trigger";
|
|
1764
1825
|
|
|
@@ -1801,6 +1862,8 @@ var isLaunchParametersStep = (step) => {
|
|
|
1801
1862
|
var isLaunchTrigger = (trigger) => trigger?.type === "WEBHOOK" && trigger.name === LAUNCH_TRIGGER_NAME;
|
|
1802
1863
|
var findLaunchParametersStep = (steps) => (steps ?? []).find(isLaunchParametersStep);
|
|
1803
1864
|
var parseLaunchFields = (value) => getFillableFormFields(parseFormFields(value));
|
|
1865
|
+
var parseLaunchObject = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
1866
|
+
var getLaunchFieldsError = (fields) => fields.some(({ name }) => name === LAUNCH_RECORD_KEY) ? `\u0418\u043C\u044F \u043F\u043E\u043B\u044F \xAB${LAUNCH_RECORD_KEY}\xBB \u0437\u0430\u043D\u044F\u0442\u043E: \u0442\u0430\u043A \u043F\u0440\u043E\u0446\u0435\u0441\u0441 \u043F\u043E\u043B\u0443\u0447\u0430\u0435\u0442 \u0437\u0430\u043F\u0438\u0441\u044C, \u043D\u0430 \u043A\u043E\u0442\u043E\u0440\u043E\u0439 \u0435\u0433\u043E \u0437\u0430\u043F\u0443\u0441\u0442\u0438\u043B\u0438` : void 0;
|
|
1804
1867
|
|
|
1805
1868
|
// src/shared/form/form-field-builder.util.ts
|
|
1806
1869
|
var OUTPUT_LEAF_TYPES = {
|
|
@@ -1831,6 +1894,96 @@ var buildFieldsExpectedBody = (fields) => Object.fromEntries(
|
|
|
1831
1894
|
])
|
|
1832
1895
|
);
|
|
1833
1896
|
|
|
1897
|
+
// src/shared/workflow/launch-record.util.ts
|
|
1898
|
+
var COMPOSITE_PARTS = {
|
|
1899
|
+
ACTOR: [["source", "SELECT"], ["workspaceMemberId", "UUID"], ["name", "TEXT"], ["context", "RAW_JSON"]],
|
|
1900
|
+
ADDRESS: [
|
|
1901
|
+
["addressStreet1", "TEXT"],
|
|
1902
|
+
["addressStreet2", "TEXT"],
|
|
1903
|
+
["addressCity", "TEXT"],
|
|
1904
|
+
["addressPostcode", "TEXT"],
|
|
1905
|
+
["addressState", "TEXT"],
|
|
1906
|
+
["addressCountry", "TEXT"],
|
|
1907
|
+
["addressLat", "NUMERIC"],
|
|
1908
|
+
["addressLng", "NUMERIC"]
|
|
1909
|
+
],
|
|
1910
|
+
CURRENCY: [["amountMicros", "NUMERIC"], ["currencyCode", "TEXT"]],
|
|
1911
|
+
EMAILS: [["primaryEmail", "TEXT"], ["additionalEmails", "RAW_JSON"]],
|
|
1912
|
+
FULL_NAME: [["firstName", "TEXT"], ["lastName", "TEXT"]],
|
|
1913
|
+
LINKS: [["primaryLinkLabel", "TEXT"], ["primaryLinkUrl", "TEXT"], ["secondaryLinks", "RAW_JSON"]],
|
|
1914
|
+
PHONES: [
|
|
1915
|
+
["primaryPhoneNumber", "TEXT"],
|
|
1916
|
+
["primaryPhoneCountryCode", "TEXT"],
|
|
1917
|
+
["primaryPhoneCallingCode", "TEXT"],
|
|
1918
|
+
["additionalPhones", "RAW_JSON"]
|
|
1919
|
+
],
|
|
1920
|
+
RICH_TEXT: [["blocknote", "TEXT"], ["markdown", "TEXT"]]
|
|
1921
|
+
};
|
|
1922
|
+
var EXCLUDED_SYSTEM_FIELDS = ["searchVector", "position"];
|
|
1923
|
+
var toTitleCase = (camelCase) => camelCase.replace(/([A-Z])/g, " $1").replace(/^./, (first) => first.toUpperCase());
|
|
1924
|
+
var buildFieldSchema = (field) => {
|
|
1925
|
+
const icon = field.icon ?? void 0;
|
|
1926
|
+
if (field.type === "RELATION") {
|
|
1927
|
+
return [
|
|
1928
|
+
[
|
|
1929
|
+
`${field.name}Id`,
|
|
1930
|
+
{
|
|
1931
|
+
isLeaf: true,
|
|
1932
|
+
icon,
|
|
1933
|
+
type: "UUID",
|
|
1934
|
+
label: toTitleCase(`${field.name}Id`),
|
|
1935
|
+
value: null,
|
|
1936
|
+
fieldMetadataId: field.id,
|
|
1937
|
+
isCompositeSubField: false
|
|
1938
|
+
}
|
|
1939
|
+
]
|
|
1940
|
+
];
|
|
1941
|
+
}
|
|
1942
|
+
const parts = COMPOSITE_PARTS[field.type];
|
|
1943
|
+
if (parts !== void 0) {
|
|
1944
|
+
return [
|
|
1945
|
+
[
|
|
1946
|
+
field.name,
|
|
1947
|
+
{
|
|
1948
|
+
isLeaf: false,
|
|
1949
|
+
icon,
|
|
1950
|
+
type: field.type,
|
|
1951
|
+
label: field.label,
|
|
1952
|
+
fieldMetadataId: field.id,
|
|
1953
|
+
value: Object.fromEntries(
|
|
1954
|
+
parts.map(([name, type]) => [
|
|
1955
|
+
name,
|
|
1956
|
+
{ isLeaf: true, type, label: toTitleCase(name), value: null, fieldMetadataId: field.id, isCompositeSubField: true }
|
|
1957
|
+
])
|
|
1958
|
+
)
|
|
1959
|
+
}
|
|
1960
|
+
]
|
|
1961
|
+
];
|
|
1962
|
+
}
|
|
1963
|
+
return [
|
|
1964
|
+
[
|
|
1965
|
+
field.name,
|
|
1966
|
+
{ isLeaf: true, icon, type: field.type, label: field.label, value: null, fieldMetadataId: field.id, isCompositeSubField: false }
|
|
1967
|
+
]
|
|
1968
|
+
];
|
|
1969
|
+
};
|
|
1970
|
+
var isRecordVariable = (field) => field.isActive !== false && !(field.isSystem === true && EXCLUDED_SYSTEM_FIELDS.includes(field.name)) && field.type !== "MORPH_RELATION" && (field.type !== "RELATION" || field.relationType === "MANY_TO_ONE");
|
|
1971
|
+
var buildLaunchRecordOutputSchema = (object) => ({
|
|
1972
|
+
isLeaf: false,
|
|
1973
|
+
icon: object.icon ?? void 0,
|
|
1974
|
+
label: object.labelSingular,
|
|
1975
|
+
value: {
|
|
1976
|
+
object: {
|
|
1977
|
+
icon: object.icon ?? void 0,
|
|
1978
|
+
label: object.labelSingular,
|
|
1979
|
+
objectMetadataId: object.id,
|
|
1980
|
+
fieldIdName: "id"
|
|
1981
|
+
},
|
|
1982
|
+
fields: Object.fromEntries(object.fields.filter(isRecordVariable).flatMap(buildFieldSchema)),
|
|
1983
|
+
_outputSchemaType: "RECORD"
|
|
1984
|
+
}
|
|
1985
|
+
});
|
|
1986
|
+
|
|
1834
1987
|
// node_modules/twenty-client-sdk/dist/chunk-Dqa2HsxW.mjs
|
|
1835
1988
|
var e = Object.create;
|
|
1836
1989
|
var t = Object.defineProperty;
|
|
@@ -61230,7 +61383,7 @@ var MANUAL_TRIGGER = {
|
|
|
61230
61383
|
},
|
|
61231
61384
|
nextStepIds: []
|
|
61232
61385
|
};
|
|
61233
|
-
var buildLaunchTrigger = (base, fields) => ({
|
|
61386
|
+
var buildLaunchTrigger = (base, fields, launchObject) => ({
|
|
61234
61387
|
name: LAUNCH_TRIGGER_NAME,
|
|
61235
61388
|
type: "WEBHOOK",
|
|
61236
61389
|
position: base.position ?? { x: 0, y: 0 },
|
|
@@ -61238,12 +61391,19 @@ var buildLaunchTrigger = (base, fields) => ({
|
|
|
61238
61391
|
settings: {
|
|
61239
61392
|
httpMethod: "POST",
|
|
61240
61393
|
authentication: "API_KEY",
|
|
61241
|
-
expectedBody:
|
|
61242
|
-
|
|
61394
|
+
expectedBody: {
|
|
61395
|
+
...buildFieldsExpectedBody(fields),
|
|
61396
|
+
...launchObject !== void 0 ? { [LAUNCH_RECORD_KEY]: { id: "" } } : {}
|
|
61397
|
+
},
|
|
61398
|
+
outputSchema: {
|
|
61399
|
+
...buildFieldsOutputSchema(fields),
|
|
61400
|
+
...launchObject !== void 0 ? { [LAUNCH_RECORD_KEY]: buildLaunchRecordOutputSchema(launchObject) } : {}
|
|
61401
|
+
}
|
|
61243
61402
|
}
|
|
61244
61403
|
});
|
|
61245
61404
|
var buildManualTrigger = (base) => ({
|
|
61246
61405
|
...MANUAL_TRIGGER,
|
|
61406
|
+
...base.type === "MANUAL" ? { name: base.name ?? MANUAL_TRIGGER.name, settings: base.settings } : {},
|
|
61247
61407
|
position: base.position ?? MANUAL_TRIGGER.position,
|
|
61248
61408
|
nextStepIds: base.nextStepIds ?? []
|
|
61249
61409
|
});
|
|
@@ -61298,7 +61458,8 @@ var removeLegacyLaunchStep = async (draft) => {
|
|
|
61298
61458
|
};
|
|
61299
61459
|
var syncLaunchTrigger = async ({
|
|
61300
61460
|
workflowId,
|
|
61301
|
-
fields
|
|
61461
|
+
fields,
|
|
61462
|
+
launchObject
|
|
61302
61463
|
}) => {
|
|
61303
61464
|
const draft = await findOrCreateDraft(workflowId);
|
|
61304
61465
|
if (draft === void 0) {
|
|
@@ -61315,7 +61476,7 @@ var syncLaunchTrigger = async ({
|
|
|
61315
61476
|
variables: {
|
|
61316
61477
|
input: {
|
|
61317
61478
|
workflowVersionId: draft.id,
|
|
61318
|
-
trigger: fields.length > 0 ? buildLaunchTrigger(trigger, fields) : buildManualTrigger(trigger)
|
|
61479
|
+
trigger: fields.length > 0 || launchObject !== void 0 ? buildLaunchTrigger(trigger, fields, launchObject) : buildManualTrigger(trigger)
|
|
61319
61480
|
}
|
|
61320
61481
|
}
|
|
61321
61482
|
});
|
|
@@ -119622,23 +119783,29 @@ var handler = async (event) => {
|
|
|
119622
119783
|
const body = readJsonBody(event.body);
|
|
119623
119784
|
const workflowId = body.workflowId;
|
|
119624
119785
|
const fields = parseLaunchFields(body.fields);
|
|
119786
|
+
const launchObjectName = parseLaunchObject(body.launchObject);
|
|
119625
119787
|
if (typeof workflowId !== "string" || workflowId.length === 0 || !Array.isArray(body.fields)) {
|
|
119626
119788
|
return jsonResponse(400, { message: "\u041D\u0435 \u0443\u043A\u0430\u0437\u0430\u043D \u043F\u0440\u043E\u0446\u0435\u0441\u0441 \u0438\u043B\u0438 \u043F\u043E\u043B\u044F" });
|
|
119627
119789
|
}
|
|
119628
|
-
const fieldsError = getFormFieldsError(fields);
|
|
119790
|
+
const fieldsError = getFormFieldsError(fields) ?? (launchObjectName !== void 0 ? getLaunchFieldsError(fields) : void 0);
|
|
119629
119791
|
if (fieldsError !== void 0) {
|
|
119630
119792
|
return jsonResponse(422, { message: fieldsError });
|
|
119631
119793
|
}
|
|
119794
|
+
const launchObject = launchObjectName !== void 0 ? (await listLaunchObjects()).find(({ nameSingular }) => nameSingular === launchObjectName) : void 0;
|
|
119795
|
+
if (launchObjectName !== void 0 && launchObject === void 0) {
|
|
119796
|
+
return jsonResponse(422, { message: `\u041E\u0431\u044A\u0435\u043A\u0442 \xAB${launchObjectName}\xBB \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D` });
|
|
119797
|
+
}
|
|
119632
119798
|
const workflow = readRestRecord(
|
|
119633
119799
|
await restClient("person").patch(`/rest/workflows/${workflowId}`, {
|
|
119634
|
-
smbLaunchFields: fields.length > 0 ? fields : null
|
|
119800
|
+
smbLaunchFields: fields.length > 0 ? fields : null,
|
|
119801
|
+
smbLaunchObject: launchObject?.nameSingular ?? null
|
|
119635
119802
|
})
|
|
119636
119803
|
);
|
|
119637
119804
|
if (workflow === void 0) {
|
|
119638
119805
|
return jsonResponse(404, { message: "\u041F\u0440\u043E\u0446\u0435\u0441\u0441 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D" });
|
|
119639
119806
|
}
|
|
119640
119807
|
try {
|
|
119641
|
-
const result = await syncLaunchTrigger({ workflowId, fields });
|
|
119808
|
+
const result = await syncLaunchTrigger({ workflowId, fields, launchObject });
|
|
119642
119809
|
logDocflow("launch fields: draft synced", { workflowId, result });
|
|
119643
119810
|
if (result?.status === "NOT_MANUAL") {
|
|
119644
119811
|
return jsonResponse(409, {
|
|
@@ -119656,7 +119823,7 @@ var handler = async (event) => {
|
|
|
119656
119823
|
var save_launch_fields_logic_function_default = defineLogicFunction({
|
|
119657
119824
|
universalIdentifier: SAVE_LAUNCH_FIELDS_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER,
|
|
119658
119825
|
name: "docflow-save-launch-fields",
|
|
119659
|
-
description: "\u0421\u043E\u0445\u0440\u0430\u043D\u044F\u0435\u0442 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u044B \u0437\u0430\u043F\u0443\u0441\u043A\u0430 \u0438 \u0434\u0435\u043B\u0430\u0435\u0442 \u043F\u0435\u0440\u0432\u044B\u043C \u0431\u043B\u043E\u043A\u043E\u043C \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u0430 \xAB\u0417\u0430\u043F\u0443\u0441\u043A \u0441 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u0430\u043C\u0438\xBB",
|
|
119826
|
+
description: "\u0421\u043E\u0445\u0440\u0430\u043D\u044F\u0435\u0442 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u044B \u0438 \u043E\u0431\u044A\u0435\u043A\u0442 \u0437\u0430\u043F\u0443\u0441\u043A\u0430 \u0438 \u0434\u0435\u043B\u0430\u0435\u0442 \u043F\u0435\u0440\u0432\u044B\u043C \u0431\u043B\u043E\u043A\u043E\u043C \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u0430 \xAB\u0417\u0430\u043F\u0443\u0441\u043A \u0441 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u0430\u043C\u0438\xBB",
|
|
119660
119827
|
timeoutSeconds: 30,
|
|
119661
119828
|
handler,
|
|
119662
119829
|
httpRouteTriggerSettings: {
|