@flusys/nestjs-entity-builder 9.1.1 → 9.2.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/README.md +24 -12
- package/config/message-keys.d.ts +29 -0
- package/controllers/entity-definition.controller.d.ts +1 -0
- package/controllers/field-definition.controller.d.ts +1 -0
- package/controllers/flow-definition.controller.d.ts +1 -0
- package/dtos/definition-bundle.dto.d.ts +1 -0
- package/dtos/entity-definition.dto.d.ts +1 -2
- package/fesm/21.js +31 -2
- package/fesm/458.js +8 -3
- package/fesm/677.js +890 -465
- package/fesm/{582.js → 857.js} +201 -265
- package/fesm/870.js +580 -247
- package/fesm/897.js +164 -22
- package/fesm/946.js +5 -1
- package/fesm/controllers/index.js +1 -1
- package/fesm/dtos/index.js +11 -0
- package/fesm/guards/index.js +2 -2
- package/fesm/index.js +39 -6
- package/fesm/interfaces/index.js +74 -3
- package/fesm/modules/index.js +1 -1
- package/fesm/rule-engine/index.js +2 -2
- package/fesm/services/index.js +13 -3
- package/flow-engine/flow-input.validator.d.ts +2 -1
- package/flow-engine/flow-run.types.d.ts +1 -0
- package/flow-engine/flow.types.d.ts +25 -4
- package/interfaces/definition-bundle.interface.d.ts +2 -1
- package/interfaces/generic-record.interface.d.ts +11 -1
- package/package.json +3 -4
- package/rule-engine/rule-engine.service.d.ts +2 -1
- package/rule-engine/rule-regex.d.ts +0 -2
- package/rule-engine/rule-values.d.ts +4 -1
- package/rule-engine/rule.interface.d.ts +2 -3
- package/services/definition-bundle.diff.d.ts +6 -0
- package/services/definition-bundle.service.d.ts +10 -2
- package/services/entity-flow-scaffold.d.ts +2 -1
- package/services/entity-permission.service.d.ts +1 -0
- package/services/flow-definition.service.d.ts +1 -1
- package/services/flow-runtime.service.d.ts +1 -0
- package/services/generic-entity.service.d.ts +5 -0
- package/services/schema-change-executor.service.d.ts +1 -0
- package/services/schema-dialect-adapter.service.d.ts +2 -0
package/fesm/870.js
CHANGED
|
@@ -383,6 +383,7 @@ _ts_decorate([
|
|
|
383
383
|
/** Form values arrive as text: `true` / `false` become booleans, anything else is left for the validator to refuse. */ const FormBoolean = ()=>(0,class_transformer__rspack_import_1.Transform)(({ value })=>value === 'true' ? true : value === 'false' ? false : value);
|
|
384
384
|
/** The form fields sent with the bundle file to plan-import / apply-import. */ class ImportBundleOptionsDto {
|
|
385
385
|
deprecateMissingFields;
|
|
386
|
+
deactivateMissingFlows;
|
|
386
387
|
confirm;
|
|
387
388
|
expectedChecksum;
|
|
388
389
|
expectedPlanChecksum;
|
|
@@ -397,6 +398,16 @@ _ts_decorate([
|
|
|
397
398
|
(0,class_validator__rspack_import_2.IsBoolean)(),
|
|
398
399
|
_ts_metadata("design:type", Boolean)
|
|
399
400
|
], ImportBundleOptionsDto.prototype, "deprecateMissingFields", void 0);
|
|
401
|
+
_ts_decorate([
|
|
402
|
+
(0,_nestjs_swagger__rspack_import_0.ApiPropertyOptional)({
|
|
403
|
+
description: 'Switch off (and publish) the live flows this environment has and the bundle does not (they are kept, not deleted)',
|
|
404
|
+
default: false
|
|
405
|
+
}),
|
|
406
|
+
(0,class_validator__rspack_import_2.IsOptional)(),
|
|
407
|
+
FormBoolean(),
|
|
408
|
+
(0,class_validator__rspack_import_2.IsBoolean)(),
|
|
409
|
+
_ts_metadata("design:type", Boolean)
|
|
410
|
+
], ImportBundleOptionsDto.prototype, "deactivateMissingFlows", void 0);
|
|
400
411
|
_ts_decorate([
|
|
401
412
|
(0,_nestjs_swagger__rspack_import_0.ApiPropertyOptional)({
|
|
402
413
|
description: 'apply-import: acknowledge steps that lose (or may lose) data',
|
|
@@ -461,9 +472,17 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
461
472
|
3801(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
462
473
|
/* import */ var _flusys_nestjs_shared_utils__rspack_import_0 = __webpack_require__(9216);
|
|
463
474
|
/* import */ var node_crypto__rspack_import_1 = __webpack_require__(2561);
|
|
464
|
-
/* import */ var
|
|
465
|
-
/* import */ var
|
|
466
|
-
/* import */ var
|
|
475
|
+
/* import */ var _config_message_keys_js__rspack_import_6 = __webpack_require__(5732);
|
|
476
|
+
/* import */ var _enums_entity_status_enum_js__rspack_import_7 = __webpack_require__(1313);
|
|
477
|
+
/* import */ var _enums_field_type_enum_js__rspack_import_8 = __webpack_require__(9614);
|
|
478
|
+
/* import */ var _flow_engine_flow_types_js__rspack_import_2 = __webpack_require__(6256);
|
|
479
|
+
/* import */ var _rule_engine_rule_permission_js__rspack_import_3 = __webpack_require__(5191);
|
|
480
|
+
/* import */ var _rule_engine_rule_interface_js__rspack_import_4 = __webpack_require__(2354);
|
|
481
|
+
/* import */ var _flow_definition_service_js__rspack_import_5 = __webpack_require__(5229);
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
|
|
467
486
|
|
|
468
487
|
|
|
469
488
|
|
|
@@ -543,6 +562,84 @@ const escapeRegExp = (text)=>text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
|
543
562
|
].toSorted(byCode)
|
|
544
563
|
};
|
|
545
564
|
}
|
|
565
|
+
const UUID_PATTERN = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi;
|
|
566
|
+
/**
|
|
567
|
+
* The record, user, company and email configuration ids written into a flow's step settings (step ids
|
|
568
|
+
* are never UUIDs). Such an id belongs to the environment it was typed in: each needs a `${var:NAME}`.
|
|
569
|
+
*/ function fixedIds(nodes) {
|
|
570
|
+
const ids = new Set();
|
|
571
|
+
mapConfigStrings(nodes, (text)=>{
|
|
572
|
+
for (const [id] of text.matchAll(UUID_PATTERN))ids.add(id.toLowerCase());
|
|
573
|
+
return text;
|
|
574
|
+
});
|
|
575
|
+
return [
|
|
576
|
+
...ids
|
|
577
|
+
].toSorted(byCode);
|
|
578
|
+
}
|
|
579
|
+
/** The fixed codes of a HAS_PERMISSION / HAS_ANY_PERMISSION / HAS_ALL_PERMISSIONS call; more than a check may name is refused when the flow is validated. */ function literalPermissionCodes(value) {
|
|
580
|
+
try {
|
|
581
|
+
return (0,_rule_engine_rule_permission_js__rspack_import_3/* .parsePermissionCodes */.PS)(value);
|
|
582
|
+
} catch {
|
|
583
|
+
return [];
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Every permission code a flow checks: the rule of each `permission` URL (the execute permission when
|
|
588
|
+
* it has none), its Permission check steps, and the codes its permission functions name as literals.
|
|
589
|
+
*/ function permissionCodes(flow) {
|
|
590
|
+
const codes = new Set();
|
|
591
|
+
const add = (list)=>list.forEach((code)=>codes.add(code));
|
|
592
|
+
for (const endpoint of (0,_flow_engine_flow_types_js__rspack_import_2/* .flowEndpoints */.Xr)(flow))if (endpoint.authMode === 'permission') add((0,_flusys_nestjs_shared_utils__rspack_import_0.permissionLogicCodes)((0,_flow_definition_service_js__rspack_import_5/* .flowPermissions */.j4)(endpoint, flow.slug)));
|
|
593
|
+
const walk = (value)=>{
|
|
594
|
+
if (Array.isArray(value)) return value.forEach(walk);
|
|
595
|
+
if (!value || typeof value !== 'object') return;
|
|
596
|
+
const call = value;
|
|
597
|
+
if (call.type === 'function' && (0,_rule_engine_rule_interface_js__rspack_import_4/* .isPermissionFunction */.A8)(call.name) && call.args?.[0]?.type === 'literal') add(literalPermissionCodes(call.args[0].value));
|
|
598
|
+
Object.values(value).forEach(walk);
|
|
599
|
+
};
|
|
600
|
+
for (const node of flow.nodes){
|
|
601
|
+
if (node?.type === 'permission_check' && node.config?.['permissions']) add((0,_flusys_nestjs_shared_utils__rspack_import_0.permissionLogicCodes)(node.config['permissions']));
|
|
602
|
+
walk(node?.config);
|
|
603
|
+
}
|
|
604
|
+
return [
|
|
605
|
+
...codes
|
|
606
|
+
].toSorted(byCode);
|
|
607
|
+
}
|
|
608
|
+
const SECRET_NAME = /authorization|token|secret|api[-_]?key|password|passwd|cookie|signature|credential/i;
|
|
609
|
+
const AUTH_SCHEME = /^\s*(bearer|basic|token|digest)\s+/i;
|
|
610
|
+
/** Shorter written text is a scheme, a separator or a placeholder's leftovers, not a credential. */ const MIN_SECRET_LENGTH = 8;
|
|
611
|
+
/** The text an expression writes itself: a literal, a template or concat parts without their `{{ ... }}` and `${var:NAME}`. */ function writtenText(expression) {
|
|
612
|
+
if (typeof expression === 'string') return expression;
|
|
613
|
+
if (!expression || typeof expression !== 'object') return '';
|
|
614
|
+
const e = expression;
|
|
615
|
+
if (e.type === 'literal') return typeof e.value === 'string' ? e.value : '';
|
|
616
|
+
if (e.type === 'template') return typeof e.template === 'string' ? e.template.replace(/\{\{[^}]*\}\}/g, '') : '';
|
|
617
|
+
if (e.type === 'concat' && Array.isArray(e.parts)) return e.parts.map(writtenText).join('');
|
|
618
|
+
return '';
|
|
619
|
+
}
|
|
620
|
+
const isWrittenSecret = (text)=>text.replace(VARIABLE_PATTERN, '').replace(AUTH_SCHEME, '').trim().length >= MIN_SECRET_LENGTH;
|
|
621
|
+
/**
|
|
622
|
+
* HTTP steps that send a credential written into the step (a header such as `Authorization` or a query
|
|
623
|
+
* parameter such as `api_key`): an export puts it in git as plain text unless it is a `${var:NAME}`.
|
|
624
|
+
*/ function writtenSecrets(nodes) {
|
|
625
|
+
const found = [];
|
|
626
|
+
for (const node of nodes.filter((n)=>n?.type === 'http_request')){
|
|
627
|
+
const config = node.config ?? {};
|
|
628
|
+
for (const [name, value] of Object.entries(config.headers ?? {})){
|
|
629
|
+
if (SECRET_NAME.test(name) && isWrittenSecret(writtenText(value))) found.push({
|
|
630
|
+
step: node.name || node.id,
|
|
631
|
+
name
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
for (const [, name, value] of writtenText(config.url).matchAll(/[?&]([^=&#]+)=([^&#]*)/g)){
|
|
635
|
+
if (SECRET_NAME.test(name) && isWrittenSecret(value)) found.push({
|
|
636
|
+
step: node.name || node.id,
|
|
637
|
+
name
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
return found;
|
|
642
|
+
}
|
|
546
643
|
/** Slugs the Call flow steps of a flow run. */ function calledFlowSlugs(nodes) {
|
|
547
644
|
return nodes.filter((node)=>node?.type === 'call_flow' && typeof node.config?.['flowSlug'] === 'string').map((node)=>node.config['flowSlug']);
|
|
548
645
|
}
|
|
@@ -613,17 +710,17 @@ function changedValues(from, to, keys) {
|
|
|
613
710
|
const problems = [];
|
|
614
711
|
const codes = new Set();
|
|
615
712
|
for (const entity of entities){
|
|
616
|
-
if (codes.has(entity.code)) problems.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(
|
|
713
|
+
if (codes.has(entity.code)) problems.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_6/* .BUNDLE_MESSAGES.DUPLICATE_ENTITY */.iX.DUPLICATE_ENTITY, {
|
|
617
714
|
entity: entity.code
|
|
618
715
|
}));
|
|
619
|
-
if (entity.status ===
|
|
716
|
+
if (entity.status === _enums_entity_status_enum_js__rspack_import_7/* .EntityStatus.DRAFT */.c.DRAFT) problems.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_6/* .BUNDLE_MESSAGES.ENTITY_DRAFT_IN_BUNDLE */.iX.ENTITY_DRAFT_IN_BUNDLE, {
|
|
620
717
|
entity: entity.code
|
|
621
718
|
}));
|
|
622
719
|
codes.add(entity.code);
|
|
623
720
|
const fieldCodes = new Set();
|
|
624
721
|
const fieldIds = new Set();
|
|
625
722
|
for (const field of entity.fields){
|
|
626
|
-
if (fieldCodes.has(field.code) || fieldIds.has(field.id)) problems.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(
|
|
723
|
+
if (fieldCodes.has(field.code) || fieldIds.has(field.id)) problems.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_6/* .BUNDLE_MESSAGES.DUPLICATE_FIELD */.iX.DUPLICATE_FIELD, {
|
|
627
724
|
entity: entity.code,
|
|
628
725
|
field: field.code
|
|
629
726
|
}));
|
|
@@ -636,9 +733,9 @@ function changedValues(from, to, keys) {
|
|
|
636
733
|
...current.map((e)=>e.code)
|
|
637
734
|
]);
|
|
638
735
|
for (const entity of entities){
|
|
639
|
-
for (const field of entity.fields.filter((f)=>f.fieldType ===
|
|
736
|
+
for (const field of entity.fields.filter((f)=>f.fieldType === _enums_field_type_enum_js__rspack_import_8/* .FieldType.RELATION */.P.RELATION && !f.deprecated)){
|
|
640
737
|
if (!field.relationTarget || !known.has(field.relationTarget)) {
|
|
641
|
-
problems.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(
|
|
738
|
+
problems.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_6/* .BUNDLE_MESSAGES.RELATION_TARGET_UNKNOWN */.iX.RELATION_TARGET_UNKNOWN, {
|
|
642
739
|
entity: entity.code,
|
|
643
740
|
field: field.code,
|
|
644
741
|
target: field.relationTarget ?? ''
|
|
@@ -696,7 +793,7 @@ function changedValues(from, to, keys) {
|
|
|
696
793
|
const first = pending[0];
|
|
697
794
|
const temporary = temporaryCode(first.targetFieldId, taken);
|
|
698
795
|
ordered.push(step(first, first.from, temporary, first.restored || first.waited, [
|
|
699
|
-
(0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(
|
|
796
|
+
(0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_6/* .BUNDLE_MESSAGES.RENAME_VIA_TEMPORARY */.iX.RENAME_VIA_TEMPORARY, {
|
|
700
797
|
field: first.from,
|
|
701
798
|
temporary,
|
|
702
799
|
to: first.to
|
|
@@ -740,7 +837,7 @@ function changedValues(from, to, keys) {
|
|
|
740
837
|
for (const entity of sorted){
|
|
741
838
|
const target = pairs.get(entity);
|
|
742
839
|
if (target && target.code !== entity.code) {
|
|
743
|
-
result.blockers.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(
|
|
840
|
+
result.blockers.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_6/* .BUNDLE_MESSAGES.ENTITY_CODE_CHANGED */.iX.ENTITY_CODE_CHANGED, {
|
|
744
841
|
entity: entity.code,
|
|
745
842
|
current: target.code
|
|
746
843
|
}));
|
|
@@ -752,15 +849,15 @@ function changedValues(from, to, keys) {
|
|
|
752
849
|
kind: 'create_entity',
|
|
753
850
|
entityCode: entity.code,
|
|
754
851
|
details: {
|
|
755
|
-
fields: live.filter((f)=>f.fieldType !==
|
|
852
|
+
fields: live.filter((f)=>f.fieldType !== _enums_field_type_enum_js__rspack_import_8/* .FieldType.RELATION */.P.RELATION).map((f)=>f.code)
|
|
756
853
|
},
|
|
757
854
|
dependsOnEarlierStep: false,
|
|
758
855
|
entity: {
|
|
759
856
|
...entity,
|
|
760
|
-
fields: live.filter((f)=>f.fieldType !==
|
|
857
|
+
fields: live.filter((f)=>f.fieldType !== _enums_field_type_enum_js__rspack_import_8/* .FieldType.RELATION */.P.RELATION)
|
|
761
858
|
}
|
|
762
859
|
});
|
|
763
|
-
for (const field of live.filter((f)=>f.fieldType ===
|
|
860
|
+
for (const field of live.filter((f)=>f.fieldType === _enums_field_type_enum_js__rspack_import_8/* .FieldType.RELATION */.P.RELATION)){
|
|
764
861
|
relationAdds.push({
|
|
765
862
|
kind: 'add_field',
|
|
766
863
|
entityCode: entity.code,
|
|
@@ -773,7 +870,7 @@ function changedValues(from, to, keys) {
|
|
|
773
870
|
field
|
|
774
871
|
});
|
|
775
872
|
}
|
|
776
|
-
if (entity.status ===
|
|
873
|
+
if (entity.status === _enums_entity_status_enum_js__rspack_import_7/* .EntityStatus.DEPRECATED */.c.DEPRECATED) {
|
|
777
874
|
entityUpdatesLast.push({
|
|
778
875
|
kind: 'update_entity',
|
|
779
876
|
entityCode: entity.code,
|
|
@@ -784,20 +881,20 @@ function changedValues(from, to, keys) {
|
|
|
784
881
|
},
|
|
785
882
|
dependsOnEarlierStep: true,
|
|
786
883
|
changes: {
|
|
787
|
-
status:
|
|
884
|
+
status: _enums_entity_status_enum_js__rspack_import_7/* .EntityStatus.DEPRECATED */.c.DEPRECATED
|
|
788
885
|
}
|
|
789
886
|
});
|
|
790
887
|
}
|
|
791
888
|
continue;
|
|
792
889
|
}
|
|
793
|
-
if (target.status ===
|
|
794
|
-
result.blockers.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(
|
|
890
|
+
if (target.status === _enums_entity_status_enum_js__rspack_import_7/* .EntityStatus.DRAFT */.c.DRAFT) {
|
|
891
|
+
result.blockers.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_6/* .BUNDLE_MESSAGES.ENTITY_DRAFT_HERE */.iX.ENTITY_DRAFT_HERE, {
|
|
795
892
|
entity: entity.code
|
|
796
893
|
}));
|
|
797
894
|
continue;
|
|
798
895
|
}
|
|
799
896
|
const entityChanges = changedValues(target, entity, ENTITY_COMPARED_KEYS);
|
|
800
|
-
const deprecating = entityChanges['status'] ===
|
|
897
|
+
const deprecating = entityChanges['status'] === _enums_entity_status_enum_js__rspack_import_7/* .EntityStatus.DEPRECATED */.c.DEPRECATED;
|
|
801
898
|
if (Object.keys(entityChanges).length) {
|
|
802
899
|
(deprecating ? entityUpdatesLast : entityUpdatesFirst).push({
|
|
803
900
|
kind: 'update_entity',
|
|
@@ -934,9 +1031,12 @@ function changedValues(from, to, keys) {
|
|
|
934
1031
|
__webpack_require__.d(__webpack_exports__, {
|
|
935
1032
|
Og: () => (matchDefinitions),
|
|
936
1033
|
Q5: () => (calledFlowSlugs),
|
|
1034
|
+
QJ: () => (permissionCodes),
|
|
937
1035
|
dX: () => (stripApiKeys),
|
|
938
1036
|
dj: () => (canonicalJson),
|
|
1037
|
+
f7: () => (fixedIds),
|
|
939
1038
|
gT: () => (applyVariables),
|
|
1039
|
+
l6: () => (writtenSecrets),
|
|
940
1040
|
lj: () => (extractVariables),
|
|
941
1041
|
qG: () => (orderFlows),
|
|
942
1042
|
zb: () => (diffEntities)
|
|
@@ -1018,26 +1118,30 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
1018
1118
|
|
|
1019
1119
|
},
|
|
1020
1120
|
7463(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
1021
|
-
/* import */ var
|
|
1022
|
-
/* import */ var
|
|
1023
|
-
/* import */ var
|
|
1024
|
-
/* import */ var
|
|
1025
|
-
/* import */ var
|
|
1026
|
-
/* import */ var
|
|
1027
|
-
/* import */ var
|
|
1028
|
-
/* import */ var
|
|
1029
|
-
/* import */ var
|
|
1030
|
-
/* import */ var
|
|
1031
|
-
/* import */ var
|
|
1032
|
-
/* import */ var
|
|
1033
|
-
/* import */ var
|
|
1034
|
-
/* import */ var
|
|
1035
|
-
/* import */ var
|
|
1036
|
-
/* import */ var
|
|
1037
|
-
/* import */ var
|
|
1038
|
-
/* import */ var
|
|
1039
|
-
/* import */ var
|
|
1040
|
-
/* import */ var
|
|
1121
|
+
/* import */ var _flusys_nestjs_shared_interfaces__rspack_import_0 = __webpack_require__(5103);
|
|
1122
|
+
/* import */ var _flusys_nestjs_shared_utils__rspack_import_1 = __webpack_require__(9216);
|
|
1123
|
+
/* import */ var _nestjs_common__rspack_import_2 = __webpack_require__(6157);
|
|
1124
|
+
/* import */ var node_crypto__rspack_import_3 = __webpack_require__(2561);
|
|
1125
|
+
/* import */ var typeorm__rspack_import_4 = __webpack_require__(7158);
|
|
1126
|
+
/* import */ var _config_message_keys_js__rspack_import_20 = __webpack_require__(5732);
|
|
1127
|
+
/* import */ var _dtos_schema_change_dto_js__rspack_import_5 = __webpack_require__(4945);
|
|
1128
|
+
/* import */ var _entities_entity_definition_entity_js__rspack_import_6 = __webpack_require__(3189);
|
|
1129
|
+
/* import */ var _enums_entity_status_enum_js__rspack_import_19 = __webpack_require__(1313);
|
|
1130
|
+
/* import */ var _enums_field_type_enum_js__rspack_import_22 = __webpack_require__(9614);
|
|
1131
|
+
/* import */ var _entities_field_definition_entity_js__rspack_import_7 = __webpack_require__(2206);
|
|
1132
|
+
/* import */ var _entities_flow_definition_entity_js__rspack_import_8 = __webpack_require__(3770);
|
|
1133
|
+
/* import */ var _flow_engine_flow_errors_js__rspack_import_9 = __webpack_require__(8542);
|
|
1134
|
+
/* import */ var _flow_engine_flow_types_js__rspack_import_10 = __webpack_require__(6256);
|
|
1135
|
+
/* import */ var _interfaces_definition_bundle_interface_js__rspack_import_21 = __webpack_require__(8835);
|
|
1136
|
+
/* import */ var _interfaces_flow_definition_interface_js__rspack_import_23 = __webpack_require__(9609);
|
|
1137
|
+
/* import */ var _definition_bundle_diff_js__rspack_import_11 = __webpack_require__(3801);
|
|
1138
|
+
/* import */ var _entity_builder_config_service_js__rspack_import_12 = __webpack_require__(4281);
|
|
1139
|
+
/* import */ var _entity_builder_datasource_provider_js__rspack_import_13 = __webpack_require__(424);
|
|
1140
|
+
/* import */ var _entity_permission_service_js__rspack_import_14 = __webpack_require__(1462);
|
|
1141
|
+
/* import */ var _flow_definition_service_js__rspack_import_15 = __webpack_require__(5229);
|
|
1142
|
+
/* import */ var _schema_dialect_adapter_service_js__rspack_import_16 = __webpack_require__(7259);
|
|
1143
|
+
/* import */ var _schema_evolution_service_js__rspack_import_17 = __webpack_require__(3606);
|
|
1144
|
+
/* import */ var _schema_sync_service_js__rspack_import_18 = __webpack_require__(3474);
|
|
1041
1145
|
function _ts_decorate(decorators, target, key, desc) {
|
|
1042
1146
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1043
1147
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
@@ -1077,16 +1181,20 @@ function _ts_param(paramIndex, decorator) {
|
|
|
1077
1181
|
|
|
1078
1182
|
|
|
1079
1183
|
|
|
1184
|
+
|
|
1185
|
+
|
|
1186
|
+
|
|
1187
|
+
|
|
1080
1188
|
|
|
1081
1189
|
|
|
1082
1190
|
|
|
1083
1191
|
|
|
1084
1192
|
/** The reasons a failed step gives besides its message: a change's blockers, a flow's validation errors. */ function failureDetails(error) {
|
|
1085
|
-
if (!(error instanceof
|
|
1193
|
+
if (!(error instanceof _nestjs_common__rspack_import_2.HttpException)) return [];
|
|
1086
1194
|
const response = error.getResponse();
|
|
1087
1195
|
if (typeof response !== 'object') return [];
|
|
1088
1196
|
const details = response.blockers ?? response.errors ?? [];
|
|
1089
|
-
return details.filter((d)=>typeof d?.messageKey === 'string').map((d)=>(0,
|
|
1197
|
+
return details.filter((d)=>typeof d?.messageKey === 'string').map((d)=>(0,_flusys_nestjs_shared_utils__rspack_import_1.messageRef)(d.messageKey, d.messageVariables));
|
|
1090
1198
|
}
|
|
1091
1199
|
/** Structural changes that, unpreviewed, may turn out to lose data (a type conversion, a column made required or unique, audit or soft delete switched off). */ const LOSSY_FIELD_CHANGES = [
|
|
1092
1200
|
'fieldType',
|
|
@@ -1105,21 +1213,21 @@ function mayLoseData(step) {
|
|
|
1105
1213
|
/**
|
|
1106
1214
|
* What was reviewed: the bundle, the options and every step (kind, subject, changes, and whether its
|
|
1107
1215
|
* preview loses data). Not the SQL or row counts, which change with every write and would refuse each apply.
|
|
1108
|
-
*/ function planChecksum(bundle,
|
|
1216
|
+
*/ function planChecksum(bundle, options, steps) {
|
|
1109
1217
|
const reviewed = steps.map((step)=>({
|
|
1110
1218
|
kind: step.kind,
|
|
1111
|
-
subject: (0,
|
|
1219
|
+
subject: (0,_definition_bundle_diff_js__rspack_import_11/* .bundleSubject */.bs)(step),
|
|
1112
1220
|
details: step.details,
|
|
1113
1221
|
destructive: step.preview?.destructive ?? null
|
|
1114
1222
|
}));
|
|
1115
|
-
return (0,
|
|
1223
|
+
return (0,node_crypto__rspack_import_3.createHash)('sha256').update((0,_definition_bundle_diff_js__rspack_import_11/* .canonicalJson */.dj)({
|
|
1116
1224
|
bundle,
|
|
1117
|
-
|
|
1225
|
+
...options,
|
|
1118
1226
|
steps: reviewed
|
|
1119
1227
|
})).digest('hex');
|
|
1120
1228
|
}
|
|
1121
|
-
const
|
|
1122
|
-
const
|
|
1229
|
+
/** One import at a time per database: two would plan against the same state and race each other's steps. */ const IMPORT_LOCK = 'eb_bundle_import';
|
|
1230
|
+
const newApiKey = ()=>`fk_${(0,node_crypto__rspack_import_3.randomBytes)(4).toString('hex')}_${(0,node_crypto__rspack_import_3.randomBytes)(16).toString('hex')}`;
|
|
1123
1231
|
const needsApiKey = (node)=>node?.type === 'trigger' && node.config?.['authMode'] === 'api_key';
|
|
1124
1232
|
/** The stored API keys of a flow's Request steps (published and draft), by step id. */ function storedApiKeys(flow) {
|
|
1125
1233
|
const keys = new Map();
|
|
@@ -1127,7 +1235,7 @@ const needsApiKey = (node)=>node?.type === 'trigger' && node.config?.['authMode'
|
|
|
1127
1235
|
...flow?.nodes ?? [],
|
|
1128
1236
|
...flow?.draft?.nodes ?? []
|
|
1129
1237
|
]){
|
|
1130
|
-
if (node?.type === 'trigger' && (0,
|
|
1238
|
+
if (node?.type === 'trigger' && (0,_flow_engine_flow_types_js__rspack_import_10/* .isStoredApiKey */.xA)(node.config?.['apiKey']) && !keys.has(node.id)) keys.set(node.id, node.config['apiKey']);
|
|
1131
1239
|
}
|
|
1132
1240
|
return keys;
|
|
1133
1241
|
}
|
|
@@ -1137,12 +1245,16 @@ class DefinitionBundleService {
|
|
|
1137
1245
|
schemaSync;
|
|
1138
1246
|
evolution;
|
|
1139
1247
|
flows;
|
|
1140
|
-
|
|
1248
|
+
dialect;
|
|
1249
|
+
actionRegistry;
|
|
1250
|
+
constructor(provider, config, schemaSync, evolution, flows, dialect, actionRegistry){
|
|
1141
1251
|
this.provider = provider;
|
|
1142
1252
|
this.config = config;
|
|
1143
1253
|
this.schemaSync = schemaSync;
|
|
1144
1254
|
this.evolution = evolution;
|
|
1145
1255
|
this.flows = flows;
|
|
1256
|
+
this.dialect = dialect;
|
|
1257
|
+
this.actionRegistry = actionRegistry;
|
|
1146
1258
|
}
|
|
1147
1259
|
variables() {
|
|
1148
1260
|
return this.config.getBundleVariables();
|
|
@@ -1152,51 +1264,78 @@ class DefinitionBundleService {
|
|
|
1152
1264
|
const current = await this.loadEntities();
|
|
1153
1265
|
const selected = dto.entityCodes ? current.filter((e)=>dto.entityCodes.includes(e.code)) : current;
|
|
1154
1266
|
// A DRAFT entity has no table where it is designed, so there is nothing to promote yet.
|
|
1155
|
-
const drafts = selected.filter((e)=>e.status ===
|
|
1156
|
-
const entities = selected.filter((e)=>e.status !==
|
|
1267
|
+
const drafts = selected.filter((e)=>e.status === _enums_entity_status_enum_js__rspack_import_19/* .EntityStatus.DRAFT */.c.DRAFT).toSorted((a, b)=>(0,_definition_bundle_diff_js__rspack_import_11/* .byCode */.VU)(a.code, b.code));
|
|
1268
|
+
const entities = selected.filter((e)=>e.status !== _enums_entity_status_enum_js__rspack_import_19/* .EntityStatus.DRAFT */.c.DRAFT).toSorted((a, b)=>(0,_definition_bundle_diff_js__rspack_import_11/* .byCode */.VU)(a.code, b.code)).map((entity)=>({
|
|
1157
1269
|
...entity,
|
|
1158
|
-
fields: entity.fields.toSorted((a, b)=>a.order - b.order || (0,
|
|
1270
|
+
fields: entity.fields.toSorted((a, b)=>a.order - b.order || (0,_definition_bundle_diff_js__rspack_import_11/* .byCode */.VU)(a.code, b.code))
|
|
1159
1271
|
}));
|
|
1160
|
-
const flowRepo = await this.provider.getRepository(
|
|
1272
|
+
const flowRepo = await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_8/* .FlowDefinition */.G);
|
|
1161
1273
|
const rows = (await flowRepo.find({
|
|
1162
1274
|
where: dto.flowSlugs ? {
|
|
1163
|
-
slug: (0,
|
|
1275
|
+
slug: (0,typeorm__rspack_import_4.In)(dto.flowSlugs)
|
|
1164
1276
|
} : {}
|
|
1165
|
-
})).toSorted((a, b)=>(0,
|
|
1277
|
+
})).toSorted((a, b)=>(0,_definition_bundle_diff_js__rspack_import_11/* .byCode */.VU)(a.slug, b.slug));
|
|
1166
1278
|
const warnings = [
|
|
1167
|
-
...drafts.map((entity)=>(0,
|
|
1279
|
+
...drafts.map((entity)=>(0,_flusys_nestjs_shared_utils__rspack_import_1.messageRef)(_config_message_keys_js__rspack_import_20/* .BUNDLE_MESSAGES.ENTITY_DRAFT_SKIPPED */.iX.ENTITY_DRAFT_SKIPPED, {
|
|
1168
1280
|
entity: entity.code
|
|
1169
1281
|
})),
|
|
1170
|
-
...rows.filter((flow)=>!(0,
|
|
1282
|
+
...rows.filter((flow)=>!(0,_flow_definition_service_js__rspack_import_15/* .isPublished */.HF)(flow)).map((flow)=>(0,_flusys_nestjs_shared_utils__rspack_import_1.messageRef)(_config_message_keys_js__rspack_import_20/* .BUNDLE_MESSAGES.FLOW_UNPUBLISHED_SKIPPED */.iX.FLOW_UNPUBLISHED_SKIPPED, {
|
|
1171
1283
|
flow: flow.slug
|
|
1172
1284
|
}))
|
|
1173
1285
|
];
|
|
1174
1286
|
const variables = this.variables();
|
|
1175
|
-
const flows = rows.filter(
|
|
1176
|
-
const snapshot = (0,
|
|
1287
|
+
const flows = rows.filter(_flow_definition_service_js__rspack_import_15/* .isPublished */.HF).map((flow)=>{
|
|
1288
|
+
const snapshot = (0,_flow_definition_service_js__rspack_import_15/* .snapshotOf */.eY)(flow);
|
|
1177
1289
|
return {
|
|
1178
1290
|
...snapshot,
|
|
1179
1291
|
description: snapshot.description ?? null,
|
|
1180
|
-
nodes: (0,
|
|
1292
|
+
nodes: (0,_definition_bundle_diff_js__rspack_import_11/* .extractVariables */.lj)((0,_definition_bundle_diff_js__rspack_import_11/* .stripApiKeys */.dX)(snapshot.nodes ?? []), variables),
|
|
1181
1293
|
edges: snapshot.edges ?? []
|
|
1182
1294
|
};
|
|
1183
1295
|
});
|
|
1296
|
+
warnings.push(...this.leftBehind(entities, flows));
|
|
1297
|
+
for (const flow of flows){
|
|
1298
|
+
for (const { step, name } of (0,_definition_bundle_diff_js__rspack_import_11/* .writtenSecrets */.l6)(flow.nodes))warnings.push((0,_flusys_nestjs_shared_utils__rspack_import_1.messageRef)(_config_message_keys_js__rspack_import_20/* .BUNDLE_MESSAGES.FLOW_SECRET_WRITTEN */.iX.FLOW_SECRET_WRITTEN, {
|
|
1299
|
+
flow: flow.slug,
|
|
1300
|
+
step,
|
|
1301
|
+
name
|
|
1302
|
+
}));
|
|
1303
|
+
}
|
|
1184
1304
|
// Keys sorted at every level: PostgreSQL and MySQL hand JSON columns back in different key orders.
|
|
1185
|
-
const bundle = JSON.parse((0,
|
|
1186
|
-
format:
|
|
1305
|
+
const bundle = JSON.parse((0,_definition_bundle_diff_js__rspack_import_11/* .canonicalJson */.dj)({
|
|
1306
|
+
format: _interfaces_definition_bundle_interface_js__rspack_import_21/* .DEFINITION_BUNDLE_FORMAT */.D,
|
|
1187
1307
|
version: (/* inlined export .DEFINITION_BUNDLE_VERSION */1),
|
|
1188
1308
|
entities,
|
|
1189
1309
|
flows
|
|
1190
1310
|
}));
|
|
1191
1311
|
return {
|
|
1192
1312
|
bundle,
|
|
1193
|
-
checksum: (0,
|
|
1313
|
+
checksum: (0,_definition_bundle_diff_js__rspack_import_11/* .bundleChecksum */.i9)(bundle),
|
|
1194
1314
|
warnings
|
|
1195
1315
|
};
|
|
1196
1316
|
}
|
|
1317
|
+
/** Relations and Call flow steps that reach outside the bundle: the environment importing it must already have their targets. */ leftBehind(entities, flows) {
|
|
1318
|
+
const entityCodes = new Set(entities.map((e)=>e.code));
|
|
1319
|
+
const slugs = new Set(flows.map((f)=>f.slug));
|
|
1320
|
+
const relations = entities.flatMap((entity)=>entity.fields.filter((f)=>f.fieldType === _enums_field_type_enum_js__rspack_import_22/* .FieldType.RELATION */.P.RELATION && !f.deprecated && f.relationTarget && !entityCodes.has(f.relationTarget)).map((f)=>(0,_flusys_nestjs_shared_utils__rspack_import_1.messageRef)(_config_message_keys_js__rspack_import_20/* .BUNDLE_MESSAGES.EXPORT_RELATION_LEFT_OUT */.iX.EXPORT_RELATION_LEFT_OUT, {
|
|
1321
|
+
entity: entity.code,
|
|
1322
|
+
field: f.code,
|
|
1323
|
+
target: f.relationTarget
|
|
1324
|
+
})));
|
|
1325
|
+
const calls = flows.flatMap((flow)=>[
|
|
1326
|
+
...new Set((0,_definition_bundle_diff_js__rspack_import_11/* .calledFlowSlugs */.Q5)(flow.nodes))
|
|
1327
|
+
].filter((slug)=>!slugs.has(slug)).toSorted(_definition_bundle_diff_js__rspack_import_11/* .byCode */.VU).map((called)=>(0,_flusys_nestjs_shared_utils__rspack_import_1.messageRef)(_config_message_keys_js__rspack_import_20/* .BUNDLE_MESSAGES.EXPORT_CALLED_FLOW_LEFT_OUT */.iX.EXPORT_CALLED_FLOW_LEFT_OUT, {
|
|
1328
|
+
flow: flow.slug,
|
|
1329
|
+
called
|
|
1330
|
+
})));
|
|
1331
|
+
return [
|
|
1332
|
+
...relations,
|
|
1333
|
+
...calls
|
|
1334
|
+
];
|
|
1335
|
+
}
|
|
1197
1336
|
/** Every entity of this environment with its fields, relations named by target code. */ async loadEntities() {
|
|
1198
|
-
const definitions = await (await this.provider.getRepository(
|
|
1199
|
-
const fields = await (await this.provider.getRepository(
|
|
1337
|
+
const definitions = await (await this.provider.getRepository(_entities_entity_definition_entity_js__rspack_import_6/* .EntityDefinition */.f)).find();
|
|
1338
|
+
const fields = await (await this.provider.getRepository(_entities_field_definition_entity_js__rspack_import_7/* .FieldDefinition */.c)).find();
|
|
1200
1339
|
const codeOf = new Map(definitions.map((d)=>[
|
|
1201
1340
|
d.id,
|
|
1202
1341
|
d.code
|
|
@@ -1237,10 +1376,12 @@ class DefinitionBundleService {
|
|
|
1237
1376
|
/** The plan, with what apply-import runs: the entity operations and the flows, in order. */ async buildPlan(dto, user) {
|
|
1238
1377
|
const bundle = dto.bundle;
|
|
1239
1378
|
const current = await this.loadEntities();
|
|
1240
|
-
const entityDiff = (0,
|
|
1379
|
+
const entityDiff = (0,_definition_bundle_diff_js__rspack_import_11/* .diffEntities */.zb)(bundle.entities, current, {
|
|
1241
1380
|
deprecateMissingFields: dto.deprecateMissingFields === true
|
|
1242
1381
|
});
|
|
1243
|
-
const flowDiff = await this.diffFlows(bundle.flows
|
|
1382
|
+
const flowDiff = await this.diffFlows(bundle.flows, {
|
|
1383
|
+
deactivateMissingFlows: dto.deactivateMissingFlows === true
|
|
1384
|
+
});
|
|
1244
1385
|
const existingCodes = new Set(current.map((e)=>e.code));
|
|
1245
1386
|
const steps = [];
|
|
1246
1387
|
for (const operation of entityDiff.operations){
|
|
@@ -1256,7 +1397,7 @@ class DefinitionBundleService {
|
|
|
1256
1397
|
warnings: [
|
|
1257
1398
|
...operation.warnings ?? [],
|
|
1258
1399
|
...dependent ? [
|
|
1259
|
-
(0,
|
|
1400
|
+
(0,_flusys_nestjs_shared_utils__rspack_import_1.messageRef)(_config_message_keys_js__rspack_import_20/* .BUNDLE_MESSAGES.STEP_AFTER_EARLIER */.iX.STEP_AFTER_EARLIER)
|
|
1260
1401
|
] : []
|
|
1261
1402
|
],
|
|
1262
1403
|
blockers: []
|
|
@@ -1266,13 +1407,15 @@ class DefinitionBundleService {
|
|
|
1266
1407
|
step.preview = await this.previewEntityStep(operation, current, user);
|
|
1267
1408
|
step.blockers.push(...step.preview.blockers);
|
|
1268
1409
|
} catch (error) {
|
|
1269
|
-
step.blockers.push((0,
|
|
1410
|
+
step.blockers.push((0,_flow_engine_flow_errors_js__rspack_import_9/* .errorRef */.O$)(error));
|
|
1270
1411
|
}
|
|
1271
1412
|
}
|
|
1272
1413
|
steps.push(step);
|
|
1273
1414
|
}
|
|
1274
1415
|
const schemaChanges = entityDiff.operations.length > 0;
|
|
1275
|
-
|
|
1416
|
+
// Flows switched off run last, callers first: each is still running when the flows that call it are checked.
|
|
1417
|
+
const importedSlugs = new Set(flowDiff.planned.filter((p)=>p.kind !== 'deactivate_flow').map((p)=>p.flow.slug));
|
|
1418
|
+
const savedFlows = [];
|
|
1276
1419
|
for (const planned of flowDiff.planned){
|
|
1277
1420
|
const step = {
|
|
1278
1421
|
kind: planned.kind,
|
|
@@ -1286,22 +1429,30 @@ class DefinitionBundleService {
|
|
|
1286
1429
|
...planned.blockers
|
|
1287
1430
|
]
|
|
1288
1431
|
};
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1432
|
+
if (planned.kind !== 'deactivate_flow') savedFlows.push([
|
|
1433
|
+
step,
|
|
1434
|
+
planned.flow
|
|
1435
|
+
]);
|
|
1436
|
+
// Switching a flow off saves it too: a flow with a code step needs that permission from whoever imports it, as from whoever saves it.
|
|
1437
|
+
await this.flows.assertMayAuthor((0,_flow_definition_service_js__rspack_import_15/* .toFlowSettings */.RK)(planned.flow), user).catch((error)=>step.blockers.push((0,_flow_engine_flow_errors_js__rspack_import_9/* .errorRef */.O$)(error)));
|
|
1438
|
+
const dependsOnImport = schemaChanges || (0,_definition_bundle_diff_js__rspack_import_11/* .calledFlowSlugs */.Q5)(planned.flow.nodes).some((slug)=>importedSlugs.has(slug));
|
|
1292
1439
|
if (dependsOnImport) {
|
|
1293
|
-
step.warnings.push((0,
|
|
1440
|
+
step.warnings.push((0,_flusys_nestjs_shared_utils__rspack_import_1.messageRef)(_config_message_keys_js__rspack_import_20/* .BUNDLE_MESSAGES.FLOWS_CHECKED_ON_APPLY */.iX.FLOWS_CHECKED_ON_APPLY));
|
|
1294
1441
|
} else if (!step.blockers.length) {
|
|
1295
|
-
const { errors, warnings } = await this.flows.validate((0,
|
|
1442
|
+
const { errors, warnings } = await this.flows.validate((0,_flow_definition_service_js__rspack_import_15/* .toFlowSettings */.RK)(planned.flow));
|
|
1296
1443
|
step.blockers.push(...errors);
|
|
1297
1444
|
step.warnings.push(...warnings);
|
|
1298
1445
|
}
|
|
1299
1446
|
steps.push(step);
|
|
1300
1447
|
}
|
|
1301
|
-
|
|
1448
|
+
await this.addPermissionWarnings(steps, savedFlows);
|
|
1449
|
+
const checksum = (0,_definition_bundle_diff_js__rspack_import_11/* .bundleChecksum */.i9)(bundle);
|
|
1302
1450
|
const plan = {
|
|
1303
1451
|
checksum,
|
|
1304
|
-
planChecksum: planChecksum(checksum,
|
|
1452
|
+
planChecksum: planChecksum(checksum, {
|
|
1453
|
+
deprecateMissingFields: dto.deprecateMissingFields === true,
|
|
1454
|
+
deactivateMissingFlows: dto.deactivateMissingFlows === true
|
|
1455
|
+
}, steps),
|
|
1305
1456
|
steps,
|
|
1306
1457
|
unchanged: {
|
|
1307
1458
|
entities: entityDiff.unchangedEntities,
|
|
@@ -1325,6 +1476,44 @@ class DefinitionBundleService {
|
|
|
1325
1476
|
flows: flowDiff.planned
|
|
1326
1477
|
};
|
|
1327
1478
|
}
|
|
1479
|
+
/**
|
|
1480
|
+
* Permissions are IAM data, never promoted: an import only creates the codes of new entities and of
|
|
1481
|
+
* flows that ask for their execute permission, and no role has those here yet. A flow that checks a
|
|
1482
|
+
* code this environment does not have (and the import does not create) refuses every caller there.
|
|
1483
|
+
*/ async addPermissionWarnings(steps, savedFlows) {
|
|
1484
|
+
if (!this.actionRegistry?.findMissingCodes) return;
|
|
1485
|
+
const createdBy = new Map();
|
|
1486
|
+
for (const step of steps.filter((s)=>s.kind === 'create_entity'))for (const code of (0,_entity_permission_service_js__rspack_import_14/* .entityPermissionCodes */.Z)(step.entityCode))createdBy.set(code, step);
|
|
1487
|
+
for (const [step, flow] of savedFlows)if ((0,_flow_engine_flow_types_js__rspack_import_10/* .flowEndpoints */.Xr)(flow).some(_flow_definition_service_js__rspack_import_15/* .asksExecutePermission */.b$)) createdBy.set((0,_flow_definition_service_js__rspack_import_15/* .defaultFlowPermissionCode */.Er)(flow.slug), step);
|
|
1488
|
+
const checked = savedFlows.map(([step, flow])=>[
|
|
1489
|
+
step,
|
|
1490
|
+
(0,_definition_bundle_diff_js__rspack_import_11/* .permissionCodes */.QJ)(flow).filter((code)=>!code.includes('*'))
|
|
1491
|
+
]);
|
|
1492
|
+
const asked = [
|
|
1493
|
+
...new Set([
|
|
1494
|
+
...createdBy.keys(),
|
|
1495
|
+
...checked.flatMap(([, codes])=>codes)
|
|
1496
|
+
])
|
|
1497
|
+
];
|
|
1498
|
+
if (!asked.length) return;
|
|
1499
|
+
const missing = new Set(await this.actionRegistry.findMissingCodes(asked));
|
|
1500
|
+
const ungranted = new Map();
|
|
1501
|
+
for (const [code, step] of createdBy)if (missing.has(code)) ungranted.set(step, [
|
|
1502
|
+
...ungranted.get(step) ?? [],
|
|
1503
|
+
code
|
|
1504
|
+
]);
|
|
1505
|
+
for (const [step, codes] of ungranted)step.warnings.push((0,_flusys_nestjs_shared_utils__rspack_import_1.messageRef)(_config_message_keys_js__rspack_import_20/* .BUNDLE_MESSAGES.PERMISSIONS_TO_GRANT */.iX.PERMISSIONS_TO_GRANT, {
|
|
1506
|
+
subject: (0,_definition_bundle_diff_js__rspack_import_11/* .bundleSubject */.bs)(step),
|
|
1507
|
+
codes: codes.join(', ')
|
|
1508
|
+
}));
|
|
1509
|
+
for (const [step, codes] of checked){
|
|
1510
|
+
const unknown = codes.filter((code)=>missing.has(code) && !createdBy.has(code));
|
|
1511
|
+
if (unknown.length) step.warnings.push((0,_flusys_nestjs_shared_utils__rspack_import_1.messageRef)(_config_message_keys_js__rspack_import_20/* .BUNDLE_MESSAGES.FLOW_PERMISSION_UNKNOWN */.iX.FLOW_PERMISSION_UNKNOWN, {
|
|
1512
|
+
flow: step.flowSlug,
|
|
1513
|
+
codes: unknown.join(', ')
|
|
1514
|
+
}));
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1328
1517
|
/** A relation to an entity this import creates: its id is only known once the entity exists. */ relationNotYetCreated(operation, existingCodes) {
|
|
1329
1518
|
const target = operation.field?.relationTarget ?? operation.changes?.['relationTarget'];
|
|
1330
1519
|
return !!target && !existingCodes.has(target);
|
|
@@ -1333,8 +1522,8 @@ class DefinitionBundleService {
|
|
|
1333
1522
|
if (operation.kind === 'create_entity') return this.schemaSync.planCreateEntity(this.createRequest(operation.entity), user);
|
|
1334
1523
|
return this.evolution.plan(this.changeRequest(operation, current), user);
|
|
1335
1524
|
}
|
|
1336
|
-
async diffFlows(bundleFlows) {
|
|
1337
|
-
const repo = await this.provider.getRepository(
|
|
1525
|
+
async diffFlows(bundleFlows, options) {
|
|
1526
|
+
const repo = await this.provider.getRepository(_entities_flow_definition_entity_js__rspack_import_8/* .FlowDefinition */.G);
|
|
1338
1527
|
const existing = await repo.find();
|
|
1339
1528
|
const bySlug = new Map(existing.map((flow)=>[
|
|
1340
1529
|
flow.slug,
|
|
@@ -1347,7 +1536,7 @@ class DefinitionBundleService {
|
|
|
1347
1536
|
slug: true
|
|
1348
1537
|
},
|
|
1349
1538
|
where: {
|
|
1350
|
-
slug: (0,
|
|
1539
|
+
slug: (0,typeorm__rspack_import_4.In)(slugs)
|
|
1351
1540
|
},
|
|
1352
1541
|
withDeleted: true
|
|
1353
1542
|
})).filter((f)=>!bySlug.has(f.slug)).map((f)=>f.slug)) : new Set();
|
|
@@ -1360,31 +1549,37 @@ class DefinitionBundleService {
|
|
|
1360
1549
|
};
|
|
1361
1550
|
const seen = new Set();
|
|
1362
1551
|
for (const slug of slugs){
|
|
1363
|
-
if (seen.has(slug)) result.blockers.push((0,
|
|
1552
|
+
if (seen.has(slug)) result.blockers.push((0,_flusys_nestjs_shared_utils__rspack_import_1.messageRef)(_config_message_keys_js__rspack_import_20/* .BUNDLE_MESSAGES.DUPLICATE_FLOW */.iX.DUPLICATE_FLOW, {
|
|
1364
1553
|
flow: slug
|
|
1365
1554
|
}));
|
|
1366
1555
|
seen.add(slug);
|
|
1367
1556
|
}
|
|
1368
|
-
for (const bundleFlow of (0,
|
|
1369
|
-
const { nodes, missing } = (0,
|
|
1557
|
+
for (const bundleFlow of (0,_definition_bundle_diff_js__rspack_import_11/* .orderFlows */.qG)(bundleFlows)){
|
|
1558
|
+
const { nodes, missing } = (0,_definition_bundle_diff_js__rspack_import_11/* .applyVariables */.gT)(bundleFlow.nodes ?? [], variables);
|
|
1370
1559
|
const flow = {
|
|
1371
1560
|
...bundleFlow,
|
|
1372
1561
|
nodes,
|
|
1373
1562
|
edges: bundleFlow.edges ?? []
|
|
1374
1563
|
};
|
|
1375
|
-
const blockers = missing.map((name)=>(0,
|
|
1564
|
+
const blockers = missing.map((name)=>(0,_flusys_nestjs_shared_utils__rspack_import_1.messageRef)(_config_message_keys_js__rspack_import_20/* .BUNDLE_MESSAGES.VARIABLE_MISSING */.iX.VARIABLE_MISSING, {
|
|
1376
1565
|
name,
|
|
1377
1566
|
flow: flow.slug
|
|
1378
1567
|
}));
|
|
1379
1568
|
const target = bySlug.get(flow.slug);
|
|
1380
|
-
if (!target && deleted.has(flow.slug)) blockers.push((0,
|
|
1569
|
+
if (!target && deleted.has(flow.slug)) blockers.push((0,_flusys_nestjs_shared_utils__rspack_import_1.messageRef)(_config_message_keys_js__rspack_import_20/* .BUNDLE_MESSAGES.FLOW_SLUG_DELETED */.iX.FLOW_SLUG_DELETED, {
|
|
1381
1570
|
flow: flow.slug
|
|
1382
1571
|
}));
|
|
1383
1572
|
const keys = storedApiKeys(target);
|
|
1384
|
-
const warnings = flow.nodes.filter((node)=>needsApiKey(node) && !keys.has(node.id)).map((node)=>(0,
|
|
1573
|
+
const warnings = flow.nodes.filter((node)=>needsApiKey(node) && !keys.has(node.id)).map((node)=>(0,_flusys_nestjs_shared_utils__rspack_import_1.messageRef)(_config_message_keys_js__rspack_import_20/* .BUNDLE_MESSAGES.FLOW_API_KEY_GENERATED */.iX.FLOW_API_KEY_GENERATED, {
|
|
1385
1574
|
flow: flow.slug,
|
|
1386
|
-
path: triggerPath(node) ?? ''
|
|
1575
|
+
path: (0,_flow_engine_flow_types_js__rspack_import_10/* .triggerPath */.um)(node) ?? ''
|
|
1387
1576
|
}));
|
|
1577
|
+
// Read before the variables are filled in: an id this environment provides through `${var:NAME}` is not fixed.
|
|
1578
|
+
const ids = (0,_definition_bundle_diff_js__rspack_import_11/* .fixedIds */.f7)(bundleFlow.nodes ?? []);
|
|
1579
|
+
if (ids.length) warnings.push((0,_flusys_nestjs_shared_utils__rspack_import_1.messageRef)(_config_message_keys_js__rspack_import_20/* .BUNDLE_MESSAGES.FLOW_FIXED_IDS */.iX.FLOW_FIXED_IDS, {
|
|
1580
|
+
flow: flow.slug,
|
|
1581
|
+
ids: ids.join(', ')
|
|
1582
|
+
}));
|
|
1388
1583
|
if (!target) {
|
|
1389
1584
|
result.planned.push({
|
|
1390
1585
|
kind: 'create_flow',
|
|
@@ -1396,13 +1591,13 @@ class DefinitionBundleService {
|
|
|
1396
1591
|
});
|
|
1397
1592
|
continue;
|
|
1398
1593
|
}
|
|
1399
|
-
const live = (0,
|
|
1400
|
-
const changed =
|
|
1401
|
-
if (!changed.length && (0,
|
|
1594
|
+
const live = (0,_flow_definition_service_js__rspack_import_15/* .snapshotOf */.eY)(target);
|
|
1595
|
+
const changed = _interfaces_flow_definition_interface_js__rspack_import_23/* .FLOW_VERSIONED_FIELDS.filter */.v.filter((key)=>key === 'nodes' ? (0,_definition_bundle_diff_js__rspack_import_11/* .canonicalJson */.dj)((0,_definition_bundle_diff_js__rspack_import_11/* .stripApiKeys */.dX)(live.nodes ?? [])) !== (0,_definition_bundle_diff_js__rspack_import_11/* .canonicalJson */.dj)((0,_definition_bundle_diff_js__rspack_import_11/* .stripApiKeys */.dX)(flow.nodes)) : (0,_definition_bundle_diff_js__rspack_import_11/* .canonicalJson */.dj)(live[key] ?? null) !== (0,_definition_bundle_diff_js__rspack_import_11/* .canonicalJson */.dj)(flow[key] ?? null));
|
|
1596
|
+
if (!changed.length && (0,_flow_definition_service_js__rspack_import_15/* .isPublished */.HF)(target) && !blockers.length) {
|
|
1402
1597
|
result.unchanged++;
|
|
1403
1598
|
continue;
|
|
1404
1599
|
}
|
|
1405
|
-
if (target.draft) warnings.push((0,
|
|
1600
|
+
if (target.draft) warnings.push((0,_flusys_nestjs_shared_utils__rspack_import_1.messageRef)(_config_message_keys_js__rspack_import_20/* .BUNDLE_MESSAGES.FLOW_DRAFT_REPLACED */.iX.FLOW_DRAFT_REPLACED, {
|
|
1406
1601
|
flow: flow.slug
|
|
1407
1602
|
}));
|
|
1408
1603
|
result.planned.push({
|
|
@@ -1416,10 +1611,38 @@ class DefinitionBundleService {
|
|
|
1416
1611
|
blockers
|
|
1417
1612
|
});
|
|
1418
1613
|
}
|
|
1419
|
-
|
|
1614
|
+
const missing = existing.filter((flow)=>!seen.has(flow.slug));
|
|
1615
|
+
const deactivated = options.deactivateMissingFlows ? missing.filter((flow)=>flow.isActive && (0,_flow_definition_service_js__rspack_import_15/* .isPublished */.HF)(flow)) : [];
|
|
1616
|
+
for (const extra of missing.filter((flow)=>!deactivated.includes(flow)))result.extras.push({
|
|
1420
1617
|
kind: 'flow',
|
|
1421
1618
|
flowSlug: extra.slug
|
|
1422
1619
|
});
|
|
1620
|
+
// Callers first: a flow is switched off only once the live flows that call it are.
|
|
1621
|
+
for (const live of (0,_definition_bundle_diff_js__rspack_import_11/* .orderFlows */.qG)(deactivated.map((flow)=>(0,_flow_definition_service_js__rspack_import_15/* .snapshotOf */.eY)(flow))).toReversed()){
|
|
1622
|
+
const target = bySlug.get(live.slug);
|
|
1623
|
+
const callers = bundleFlows.filter((flow)=>flow.isActive && (0,_definition_bundle_diff_js__rspack_import_11/* .calledFlowSlugs */.Q5)(flow.nodes ?? []).includes(live.slug)).map((flow)=>flow.slug);
|
|
1624
|
+
result.planned.push({
|
|
1625
|
+
kind: 'deactivate_flow',
|
|
1626
|
+
flow: live,
|
|
1627
|
+
existingId: target.id,
|
|
1628
|
+
details: {
|
|
1629
|
+
changes: [
|
|
1630
|
+
'isActive'
|
|
1631
|
+
]
|
|
1632
|
+
},
|
|
1633
|
+
warnings: target.draft ? [
|
|
1634
|
+
(0,_flusys_nestjs_shared_utils__rspack_import_1.messageRef)(_config_message_keys_js__rspack_import_20/* .BUNDLE_MESSAGES.FLOW_DRAFT_REPLACED */.iX.FLOW_DRAFT_REPLACED, {
|
|
1635
|
+
flow: live.slug
|
|
1636
|
+
})
|
|
1637
|
+
] : [],
|
|
1638
|
+
blockers: callers.length ? [
|
|
1639
|
+
(0,_flusys_nestjs_shared_utils__rspack_import_1.messageRef)(_config_message_keys_js__rspack_import_20/* .BUNDLE_MESSAGES.FLOW_DEACTIVATE_CALLED */.iX.FLOW_DEACTIVATE_CALLED, {
|
|
1640
|
+
flow: live.slug,
|
|
1641
|
+
callers: callers.toSorted(_definition_bundle_diff_js__rspack_import_11/* .byCode */.VU).join(', ')
|
|
1642
|
+
})
|
|
1643
|
+
] : []
|
|
1644
|
+
});
|
|
1645
|
+
}
|
|
1423
1646
|
return result;
|
|
1424
1647
|
}
|
|
1425
1648
|
// ── requests for the designer's services ────────────────────────────────────────────
|
|
@@ -1459,7 +1682,7 @@ class DefinitionBundleService {
|
|
|
1459
1682
|
switch(operation.kind){
|
|
1460
1683
|
case 'add_field':
|
|
1461
1684
|
return {
|
|
1462
|
-
action:
|
|
1685
|
+
action: _dtos_schema_change_dto_js__rspack_import_5/* .SchemaChangeAction.ADD_FIELD */.M3.ADD_FIELD,
|
|
1463
1686
|
entityDefinitionId: entity?.id,
|
|
1464
1687
|
field: this.fieldSpec(operation.field, idOf(operation.field.relationTarget))
|
|
1465
1688
|
};
|
|
@@ -1471,24 +1694,24 @@ class DefinitionBundleService {
|
|
|
1471
1694
|
relationTargetEntityId: idOf(relationTarget) ?? null
|
|
1472
1695
|
});
|
|
1473
1696
|
return {
|
|
1474
|
-
action:
|
|
1697
|
+
action: _dtos_schema_change_dto_js__rspack_import_5/* .SchemaChangeAction.UPDATE_FIELD */.M3.UPDATE_FIELD,
|
|
1475
1698
|
fieldDefinitionId: operation.targetFieldId,
|
|
1476
1699
|
changes
|
|
1477
1700
|
};
|
|
1478
1701
|
}
|
|
1479
1702
|
case 'deprecate_field':
|
|
1480
1703
|
return {
|
|
1481
|
-
action:
|
|
1704
|
+
action: _dtos_schema_change_dto_js__rspack_import_5/* .SchemaChangeAction.DEPRECATE_FIELD */.M3.DEPRECATE_FIELD,
|
|
1482
1705
|
fieldDefinitionId: operation.targetFieldId
|
|
1483
1706
|
};
|
|
1484
1707
|
case 'restore_field':
|
|
1485
1708
|
return {
|
|
1486
|
-
action:
|
|
1709
|
+
action: _dtos_schema_change_dto_js__rspack_import_5/* .SchemaChangeAction.RESTORE_FIELD */.M3.RESTORE_FIELD,
|
|
1487
1710
|
fieldDefinitionId: operation.targetFieldId
|
|
1488
1711
|
};
|
|
1489
1712
|
default:
|
|
1490
1713
|
return {
|
|
1491
|
-
action:
|
|
1714
|
+
action: _dtos_schema_change_dto_js__rspack_import_5/* .SchemaChangeAction.UPDATE_ENTITY */.M3.UPDATE_ENTITY,
|
|
1492
1715
|
entityDefinitionId: entity?.id,
|
|
1493
1716
|
changes: operation.changes
|
|
1494
1717
|
};
|
|
@@ -1496,21 +1719,44 @@ class DefinitionBundleService {
|
|
|
1496
1719
|
}
|
|
1497
1720
|
// ── apply ───────────────────────────────────────────────────────────────────────────
|
|
1498
1721
|
async applyImport(dto, user) {
|
|
1499
|
-
const checksum = (0,
|
|
1722
|
+
const checksum = (0,_definition_bundle_diff_js__rspack_import_11/* .bundleChecksum */.i9)(dto.bundle);
|
|
1500
1723
|
if (dto.expectedChecksum && dto.expectedChecksum !== checksum) {
|
|
1501
|
-
throw new
|
|
1724
|
+
throw new _nestjs_common__rspack_import_2.ConflictException({
|
|
1502
1725
|
message: 'The bundle is not the one that was reviewed. Plan the import again.',
|
|
1503
|
-
messageKey:
|
|
1726
|
+
messageKey: _config_message_keys_js__rspack_import_20/* .BUNDLE_MESSAGES.CHECKSUM_MISMATCH */.iX.CHECKSUM_MISMATCH,
|
|
1504
1727
|
messageVariables: {
|
|
1505
1728
|
checksum
|
|
1506
1729
|
}
|
|
1507
1730
|
});
|
|
1508
1731
|
}
|
|
1732
|
+
return this.withImportLock(()=>this.runImport(dto, user));
|
|
1733
|
+
}
|
|
1734
|
+
/** Held on a connection of its own for the whole import (plan and every step), released before that connection goes back to the pool. */ async withImportLock(fn) {
|
|
1735
|
+
const driver = await this.provider.getDriver();
|
|
1736
|
+
const qr = await this.provider.getQueryRunner();
|
|
1737
|
+
try {
|
|
1738
|
+
await qr.connect();
|
|
1739
|
+
if (!await this.dialect.tryAcquireSessionLock(qr, IMPORT_LOCK, driver)) {
|
|
1740
|
+
throw new _nestjs_common__rspack_import_2.ConflictException({
|
|
1741
|
+
message: 'Another import is running in this environment. Try again when it has finished.',
|
|
1742
|
+
messageKey: _config_message_keys_js__rspack_import_20/* .BUNDLE_MESSAGES.IMPORT_IN_PROGRESS */.iX.IMPORT_IN_PROGRESS
|
|
1743
|
+
});
|
|
1744
|
+
}
|
|
1745
|
+
try {
|
|
1746
|
+
return await fn();
|
|
1747
|
+
} finally{
|
|
1748
|
+
await this.dialect.releaseSessionLock(qr, IMPORT_LOCK, driver).catch(()=>undefined);
|
|
1749
|
+
}
|
|
1750
|
+
} finally{
|
|
1751
|
+
await qr.release().catch(()=>undefined);
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
async runImport(dto, user) {
|
|
1509
1755
|
const { plan, operations, flows } = await this.buildPlan(dto, user);
|
|
1510
1756
|
if (dto.expectedPlanChecksum && dto.expectedPlanChecksum !== plan.planChecksum) {
|
|
1511
|
-
throw new
|
|
1757
|
+
throw new _nestjs_common__rspack_import_2.ConflictException({
|
|
1512
1758
|
message: 'The plan changed since it was reviewed. Plan the import again.',
|
|
1513
|
-
messageKey:
|
|
1759
|
+
messageKey: _config_message_keys_js__rspack_import_20/* .BUNDLE_MESSAGES.PLAN_CHANGED */.iX.PLAN_CHANGED,
|
|
1514
1760
|
messageVariables: {
|
|
1515
1761
|
planChecksum: plan.planChecksum
|
|
1516
1762
|
}
|
|
@@ -1523,33 +1769,33 @@ class DefinitionBundleService {
|
|
|
1523
1769
|
...b
|
|
1524
1770
|
})),
|
|
1525
1771
|
...plan.steps.flatMap((s)=>s.blockers.map((b)=>({
|
|
1526
|
-
field: (0,
|
|
1772
|
+
field: (0,_definition_bundle_diff_js__rspack_import_11/* .bundleSubject */.bs)(s),
|
|
1527
1773
|
...b
|
|
1528
1774
|
})))
|
|
1529
1775
|
];
|
|
1530
1776
|
if (blockers.length) {
|
|
1531
|
-
throw new
|
|
1777
|
+
throw new _nestjs_common__rspack_import_2.ConflictException({
|
|
1532
1778
|
message: 'The import is blocked. Plan it again to see why.',
|
|
1533
|
-
messageKey:
|
|
1779
|
+
messageKey: _config_message_keys_js__rspack_import_20/* .BUNDLE_MESSAGES.APPLY_BLOCKED */.iX.APPLY_BLOCKED,
|
|
1534
1780
|
errors: blockers
|
|
1535
1781
|
});
|
|
1536
1782
|
}
|
|
1537
1783
|
if (plan.mayLoseData && dto.confirm !== true) {
|
|
1538
1784
|
const lossy = plan.steps.filter((s)=>s.preview?.destructive || !s.preview && mayLoseData(s)).map((s)=>({
|
|
1539
|
-
field: (0,
|
|
1540
|
-
messageKey:
|
|
1785
|
+
field: (0,_definition_bundle_diff_js__rspack_import_11/* .bundleSubject */.bs)(s),
|
|
1786
|
+
messageKey: _config_message_keys_js__rspack_import_20/* .BUNDLE_MESSAGES.CONFIRM_REQUIRED */.iX.CONFIRM_REQUIRED
|
|
1541
1787
|
}));
|
|
1542
|
-
throw new
|
|
1788
|
+
throw new _nestjs_common__rspack_import_2.BadRequestException({
|
|
1543
1789
|
message: 'The import loses data or information. Review the plan and resend with confirm: true.',
|
|
1544
|
-
messageKey:
|
|
1790
|
+
messageKey: _config_message_keys_js__rspack_import_20/* .BUNDLE_MESSAGES.CONFIRM_REQUIRED */.iX.CONFIRM_REQUIRED,
|
|
1545
1791
|
errors: lossy
|
|
1546
1792
|
});
|
|
1547
1793
|
}
|
|
1548
1794
|
// A confirmation covers the steps that were reviewed, never ones the environment has grown since.
|
|
1549
1795
|
if (plan.mayLoseData && !dto.expectedPlanChecksum) {
|
|
1550
|
-
throw new
|
|
1796
|
+
throw new _nestjs_common__rspack_import_2.BadRequestException({
|
|
1551
1797
|
message: 'Send the planChecksum of the reviewed plan as expectedPlanChecksum to confirm it.',
|
|
1552
|
-
messageKey:
|
|
1798
|
+
messageKey: _config_message_keys_js__rspack_import_20/* .BUNDLE_MESSAGES.PLAN_CHECKSUM_REQUIRED */.iX.PLAN_CHECKSUM_REQUIRED
|
|
1553
1799
|
});
|
|
1554
1800
|
}
|
|
1555
1801
|
const result = {
|
|
@@ -1577,7 +1823,7 @@ class DefinitionBundleService {
|
|
|
1577
1823
|
result.steps.push({
|
|
1578
1824
|
...step,
|
|
1579
1825
|
status: 'failed',
|
|
1580
|
-
error: (0,
|
|
1826
|
+
error: (0,_flow_engine_flow_errors_js__rspack_import_9/* .errorRef */.O$)(error),
|
|
1581
1827
|
...reasons.length ? {
|
|
1582
1828
|
blockers: reasons
|
|
1583
1829
|
} : {}
|
|
@@ -1608,7 +1854,7 @@ class DefinitionBundleService {
|
|
|
1608
1854
|
if (operation.kind === 'create_entity') {
|
|
1609
1855
|
const entity = operation.entity;
|
|
1610
1856
|
await this.schemaSync.createEntity(this.createRequest(entity), user, {
|
|
1611
|
-
entityId: await this.idIsFree(
|
|
1857
|
+
entityId: await this.idIsFree(_entities_entity_definition_entity_js__rspack_import_6/* .EntityDefinition */.f, entity.id) ? entity.id : undefined,
|
|
1612
1858
|
fieldIds: Object.fromEntries(await this.freeFieldIds(entity.fields))
|
|
1613
1859
|
});
|
|
1614
1860
|
return;
|
|
@@ -1617,7 +1863,7 @@ class DefinitionBundleService {
|
|
|
1617
1863
|
...this.changeRequest(operation, current),
|
|
1618
1864
|
confirm
|
|
1619
1865
|
};
|
|
1620
|
-
const fieldId = operation.kind === 'add_field' && await this.idIsFree(
|
|
1866
|
+
const fieldId = operation.kind === 'add_field' && await this.idIsFree(_entities_field_definition_entity_js__rspack_import_7/* .FieldDefinition */.c, operation.field.id) ? operation.field.id : undefined;
|
|
1621
1867
|
await this.evolution.apply(request, user, {
|
|
1622
1868
|
fieldId
|
|
1623
1869
|
});
|
|
@@ -1632,12 +1878,12 @@ class DefinitionBundleService {
|
|
|
1632
1878
|
}
|
|
1633
1879
|
async freeFieldIds(fields) {
|
|
1634
1880
|
if (!fields.length) return [];
|
|
1635
|
-
const taken = new Set((await (await this.provider.getRepository(
|
|
1881
|
+
const taken = new Set((await (await this.provider.getRepository(_entities_field_definition_entity_js__rspack_import_7/* .FieldDefinition */.c)).find({
|
|
1636
1882
|
select: {
|
|
1637
1883
|
id: true
|
|
1638
1884
|
},
|
|
1639
1885
|
where: {
|
|
1640
|
-
id: (0,
|
|
1886
|
+
id: (0,typeorm__rspack_import_4.In)(fields.map((f)=>f.id))
|
|
1641
1887
|
},
|
|
1642
1888
|
withDeleted: true
|
|
1643
1889
|
})).map((f)=>f.id));
|
|
@@ -1647,6 +1893,7 @@ class DefinitionBundleService {
|
|
|
1647
1893
|
]);
|
|
1648
1894
|
}
|
|
1649
1895
|
/** Saves the flow (a Request step keeps this environment's API key, or gets a new one shown once) and publishes it. */ async applyFlowStep(planned, note, apiKeys, user) {
|
|
1896
|
+
if (planned.kind === 'deactivate_flow') return this.deactivateFlow(planned.existingId, note, user);
|
|
1650
1897
|
const existing = planned.existingId ? await this.flows.findOrFail(planned.existingId) : undefined;
|
|
1651
1898
|
const keys = storedApiKeys(existing);
|
|
1652
1899
|
const generated = [];
|
|
@@ -1664,7 +1911,7 @@ class DefinitionBundleService {
|
|
|
1664
1911
|
generated.push({
|
|
1665
1912
|
flowSlug: planned.flow.slug,
|
|
1666
1913
|
nodeId: node.id,
|
|
1667
|
-
path: triggerPath(node),
|
|
1914
|
+
path: (0,_flow_engine_flow_types_js__rspack_import_10/* .triggerPath */.um)(node),
|
|
1668
1915
|
key
|
|
1669
1916
|
});
|
|
1670
1917
|
return {
|
|
@@ -1688,23 +1935,38 @@ class DefinitionBundleService {
|
|
|
1688
1935
|
apiKeys.push(...generated);
|
|
1689
1936
|
await this.flows.publish(id, note, user);
|
|
1690
1937
|
}
|
|
1938
|
+
/** Publishes the live flow switched off; an unpublished draft is replaced, never published along with it. */ async deactivateFlow(id, note, user) {
|
|
1939
|
+
const live = (0,_flow_definition_service_js__rspack_import_15/* .snapshotOf */.eY)(await this.flows.findOrFail(id));
|
|
1940
|
+
await this.flows.update({
|
|
1941
|
+
...live,
|
|
1942
|
+
description: live.description ?? undefined,
|
|
1943
|
+
isActive: false,
|
|
1944
|
+
id
|
|
1945
|
+
}, user);
|
|
1946
|
+
await this.flows.publish(id, note, user);
|
|
1947
|
+
}
|
|
1691
1948
|
}
|
|
1692
1949
|
DefinitionBundleService = _ts_decorate([
|
|
1693
|
-
(0,
|
|
1694
|
-
scope:
|
|
1950
|
+
(0,_nestjs_common__rspack_import_2.Injectable)({
|
|
1951
|
+
scope: _nestjs_common__rspack_import_2.Scope.REQUEST
|
|
1695
1952
|
}),
|
|
1696
|
-
_ts_param(0, (0,
|
|
1697
|
-
_ts_param(1, (0,
|
|
1698
|
-
_ts_param(2, (0,
|
|
1699
|
-
_ts_param(3, (0,
|
|
1700
|
-
_ts_param(4, (0,
|
|
1953
|
+
_ts_param(0, (0,_nestjs_common__rspack_import_2.Inject)(_entity_builder_datasource_provider_js__rspack_import_13/* .EntityBuilderDataSourceProvider */.D)),
|
|
1954
|
+
_ts_param(1, (0,_nestjs_common__rspack_import_2.Inject)(_entity_builder_config_service_js__rspack_import_12/* .EntityBuilderConfigService */.E)),
|
|
1955
|
+
_ts_param(2, (0,_nestjs_common__rspack_import_2.Inject)(_schema_sync_service_js__rspack_import_18/* .SchemaSyncService */.W)),
|
|
1956
|
+
_ts_param(3, (0,_nestjs_common__rspack_import_2.Inject)(_schema_evolution_service_js__rspack_import_17/* .SchemaEvolutionService */._)),
|
|
1957
|
+
_ts_param(4, (0,_nestjs_common__rspack_import_2.Inject)(_flow_definition_service_js__rspack_import_15/* .FlowDefinitionService */.Tk)),
|
|
1958
|
+
_ts_param(5, (0,_nestjs_common__rspack_import_2.Inject)(_schema_dialect_adapter_service_js__rspack_import_16/* .SchemaDialectAdapterService */.I)),
|
|
1959
|
+
_ts_param(6, (0,_nestjs_common__rspack_import_2.Optional)()),
|
|
1960
|
+
_ts_param(6, (0,_nestjs_common__rspack_import_2.Inject)(_flusys_nestjs_shared_interfaces__rspack_import_0.PERMISSION_ACTION_REGISTRY)),
|
|
1701
1961
|
_ts_metadata("design:type", Function),
|
|
1702
1962
|
_ts_metadata("design:paramtypes", [
|
|
1703
|
-
typeof
|
|
1704
|
-
typeof
|
|
1705
|
-
typeof
|
|
1706
|
-
typeof
|
|
1707
|
-
typeof
|
|
1963
|
+
typeof _entity_builder_datasource_provider_js__rspack_import_13/* .EntityBuilderDataSourceProvider */.D === "undefined" ? Object : _entity_builder_datasource_provider_js__rspack_import_13/* .EntityBuilderDataSourceProvider */.D,
|
|
1964
|
+
typeof _entity_builder_config_service_js__rspack_import_12/* .EntityBuilderConfigService */.E === "undefined" ? Object : _entity_builder_config_service_js__rspack_import_12/* .EntityBuilderConfigService */.E,
|
|
1965
|
+
typeof _schema_sync_service_js__rspack_import_18/* .SchemaSyncService */.W === "undefined" ? Object : _schema_sync_service_js__rspack_import_18/* .SchemaSyncService */.W,
|
|
1966
|
+
typeof _schema_evolution_service_js__rspack_import_17/* .SchemaEvolutionService */._ === "undefined" ? Object : _schema_evolution_service_js__rspack_import_17/* .SchemaEvolutionService */._,
|
|
1967
|
+
typeof _flow_definition_service_js__rspack_import_15/* .FlowDefinitionService */.Tk === "undefined" ? Object : _flow_definition_service_js__rspack_import_15/* .FlowDefinitionService */.Tk,
|
|
1968
|
+
typeof _schema_dialect_adapter_service_js__rspack_import_16/* .SchemaDialectAdapterService */.I === "undefined" ? Object : _schema_dialect_adapter_service_js__rspack_import_16/* .SchemaDialectAdapterService */.I,
|
|
1969
|
+
typeof IPermissionActionRegistry === "undefined" ? Object : IPermissionActionRegistry
|
|
1708
1970
|
])
|
|
1709
1971
|
], DefinitionBundleService);
|
|
1710
1972
|
|
|
@@ -1746,7 +2008,7 @@ function _ts_param(paramIndex, decorator) {
|
|
|
1746
2008
|
|
|
1747
2009
|
|
|
1748
2010
|
|
|
1749
|
-
/** Every cached definition list/get. Schema changes write these rows inside a transaction, past the cached services. */ const CACHED_DEFINITIONS = [
|
|
2011
|
+
/** Every cached definition list/get. Schema changes write these rows inside a transaction, past the cached services, so they are bumped after it commits. */ const CACHED_DEFINITIONS = [
|
|
1750
2012
|
_config_event_actions_js__rspack_import_3/* .ENTITY_BUILDER_EVENT_ENTITIES.ENTITY_DEFINITION */.P.ENTITY_DEFINITION,
|
|
1751
2013
|
_config_event_actions_js__rspack_import_3/* .ENTITY_BUILDER_EVENT_ENTITIES.FIELD_DEFINITION */.P.FIELD_DEFINITION,
|
|
1752
2014
|
_config_event_actions_js__rspack_import_3/* .ENTITY_BUILDER_EVENT_ENTITIES.FLOW_DEFINITION */.P.FLOW_DEFINITION
|
|
@@ -1759,7 +2021,7 @@ class DefinitionCacheService {
|
|
|
1759
2021
|
this.utilsService = utilsService;
|
|
1760
2022
|
}
|
|
1761
2023
|
/** `tenantId` is the tenant whose definitions changed (see `cacheTenantId`); caches are keyed per tenant. */ async clearAll(tenantId) {
|
|
1762
|
-
await Promise.all(CACHED_DEFINITIONS.map((name)=>this.utilsService.clearCache(name, this.cacheManager,
|
|
2024
|
+
await Promise.all(CACHED_DEFINITIONS.map((name)=>this.utilsService.clearCache(name, this.cacheManager, tenantId)));
|
|
1763
2025
|
}
|
|
1764
2026
|
}
|
|
1765
2027
|
DefinitionCacheService = _ts_decorate([
|
|
@@ -1885,11 +2147,10 @@ const DEFAULT_ENDPOINT_NAME = {
|
|
|
1885
2147
|
delete: 'Delete'
|
|
1886
2148
|
};
|
|
1887
2149
|
const DEFAULT_INPUT_LABEL = {
|
|
1888
|
-
id: 'Id'
|
|
1889
|
-
ids: 'Ids',
|
|
1890
|
-
search: 'Search'
|
|
2150
|
+
id: 'Id'
|
|
1891
2151
|
};
|
|
1892
2152
|
const DEFAULT_NO_MATCH_MESSAGE = 'No record matches the filter.';
|
|
2153
|
+
const DEFAULT_DELETE_ONLY_MESSAGE = 'Only delete is supported: a flow cannot restore or permanently delete a record.';
|
|
1893
2154
|
const MAX_NAME_LENGTH = 255;
|
|
1894
2155
|
/** A supplied text when it has content, the English default otherwise. */ const textOr = (value, fallback)=>value?.trim() || fallback;
|
|
1895
2156
|
const INPUT_TYPE = {
|
|
@@ -1908,6 +2169,18 @@ const INPUT_TYPE = {
|
|
|
1908
2169
|
[_enums_field_type_enum_js__rspack_import_2/* .FieldType.FILE */.P.FILE]: 'uuid'
|
|
1909
2170
|
};
|
|
1910
2171
|
const LIST_LIMIT = 50;
|
|
2172
|
+
/** The API controller endpoint each endpoint's Request step stands in for. */ const ENDPOINT_CONTRACTS = {
|
|
2173
|
+
insert: 'insert',
|
|
2174
|
+
insertMany: 'insert_many',
|
|
2175
|
+
getById: 'get_by_id',
|
|
2176
|
+
getByIds: 'get_by_ids',
|
|
2177
|
+
getAll: 'get_all',
|
|
2178
|
+
getByFilter: 'get_by_filter',
|
|
2179
|
+
update: 'update',
|
|
2180
|
+
updateMany: 'update_many',
|
|
2181
|
+
bulkUpsert: 'bulk_upsert',
|
|
2182
|
+
delete: 'delete'
|
|
2183
|
+
};
|
|
1911
2184
|
const READ_ENDPOINTS = new Set([
|
|
1912
2185
|
'getById',
|
|
1913
2186
|
'getByIds',
|
|
@@ -1967,10 +2240,6 @@ const ref = (path)=>({
|
|
|
1967
2240
|
type: 'field_ref',
|
|
1968
2241
|
ref: path
|
|
1969
2242
|
});
|
|
1970
|
-
const lit = (value)=>({
|
|
1971
|
-
type: 'literal',
|
|
1972
|
-
value
|
|
1973
|
-
});
|
|
1974
2243
|
/** `customer_ticket` -> `customer-ticket`: an entity code always fits a flow URL name. */ const entityFlowSlug = (entityCode)=>entityCode.replace(/_/g, '-');
|
|
1975
2244
|
function optionValues(options) {
|
|
1976
2245
|
return (options ?? []).map((o)=>o && typeof o === 'object' && 'value' in o ? String(o.value) : String(o));
|
|
@@ -2041,38 +2310,38 @@ const optionalFieldInputs = (fields)=>fields.map((f)=>fieldInput(f, false));
|
|
|
2041
2310
|
const idInput = (t)=>({
|
|
2042
2311
|
name: 'id',
|
|
2043
2312
|
label: t.inputLabels.id,
|
|
2044
|
-
type: 'uuid'
|
|
2045
|
-
required: true
|
|
2313
|
+
type: 'uuid'
|
|
2046
2314
|
});
|
|
2047
|
-
const
|
|
2048
|
-
name: 'ids',
|
|
2049
|
-
label: t.inputLabels.ids,
|
|
2050
|
-
type: 'array',
|
|
2051
|
-
itemType: 'uuid',
|
|
2052
|
-
required: true
|
|
2053
|
-
});
|
|
2054
|
-
const respond = (id, column, statusCode, body, row)=>({
|
|
2315
|
+
/** An API controller envelope answer: `<entity>.<action>.success` (or the given key), like the generic controller's. */ const respond = (id, column, statusCode, format, message, messageKey, rest = {}, row)=>({
|
|
2055
2316
|
id,
|
|
2056
2317
|
type: 'respond',
|
|
2057
2318
|
column,
|
|
2058
2319
|
row,
|
|
2059
|
-
config:
|
|
2060
|
-
statusCode,
|
|
2061
|
-
bodyExpression: body
|
|
2062
|
-
} : {
|
|
2320
|
+
config: {
|
|
2063
2321
|
statusCode,
|
|
2064
|
-
|
|
2322
|
+
format,
|
|
2323
|
+
message,
|
|
2324
|
+
messageKey,
|
|
2325
|
+
...rest
|
|
2065
2326
|
}
|
|
2066
2327
|
});
|
|
2067
2328
|
/**
|
|
2068
|
-
* The steps of one endpoint
|
|
2069
|
-
* flow never share a step. A bulk body is
|
|
2070
|
-
*
|
|
2071
|
-
* answered with the saved records in order.
|
|
2329
|
+
* The steps of one endpoint, answering the generic API controller's envelope and message keys. Its step
|
|
2330
|
+
* ids are `<endpoint>_<step>` (`id()`), so the endpoints of one flow never share a step. A bulk body is
|
|
2331
|
+
* the list itself (the declared fields check each item), written by one step that walks it (`target:
|
|
2332
|
+
* many`, each item read as `loop.item`), all or nothing, and answered with the saved records in order.
|
|
2333
|
+
* getAll reads the request's filter, sort, paging, select, withDeleted and `?q=` search, getById and
|
|
2334
|
+
* getByIds their select (getByIds answers one page of every match, like the controller); delete takes a list of ids and refuses restore / permanent, which no flow step does.
|
|
2072
2335
|
*/ function endpointGraph(entity, endpoint, texts) {
|
|
2073
2336
|
const id = (step)=>`${endpoint}_${step}`;
|
|
2074
|
-
const entityCode = entity
|
|
2075
|
-
const
|
|
2337
|
+
const { code: entityCode, label } = entity;
|
|
2338
|
+
const key = (action)=>`${entityCode}.${action}.success`;
|
|
2339
|
+
const count = (step)=>({
|
|
2340
|
+
messageVariables: {
|
|
2341
|
+
count: ref(`context.${id(step)}.count`)
|
|
2342
|
+
}
|
|
2343
|
+
});
|
|
2344
|
+
const single = (inputSchema, step, message, action, statusCode = 200)=>({
|
|
2076
2345
|
inputSchema,
|
|
2077
2346
|
steps: [
|
|
2078
2347
|
{
|
|
@@ -2080,7 +2349,9 @@ const respond = (id, column, statusCode, body, row)=>({
|
|
|
2080
2349
|
id: id('record'),
|
|
2081
2350
|
column: 1
|
|
2082
2351
|
},
|
|
2083
|
-
respond(id('respond'), 2, statusCode,
|
|
2352
|
+
respond(id('respond'), 2, statusCode, 'single', message, key(action), {
|
|
2353
|
+
bodyExpression: ref(`context.${id('record')}`)
|
|
2354
|
+
})
|
|
2084
2355
|
],
|
|
2085
2356
|
edges: [
|
|
2086
2357
|
[
|
|
@@ -2090,8 +2361,7 @@ const respond = (id, column, statusCode, body, row)=>({
|
|
|
2090
2361
|
]
|
|
2091
2362
|
]
|
|
2092
2363
|
});
|
|
2093
|
-
const bulk = (itemSchema, step, statusCode)=>({
|
|
2094
|
-
bodyType: 'list',
|
|
2364
|
+
const bulk = (itemSchema, step, message, action, statusCode)=>({
|
|
2095
2365
|
inputSchema: itemSchema,
|
|
2096
2366
|
steps: [
|
|
2097
2367
|
{
|
|
@@ -2104,7 +2374,39 @@ const respond = (id, column, statusCode, body, row)=>({
|
|
|
2104
2374
|
items: ref('input')
|
|
2105
2375
|
}
|
|
2106
2376
|
},
|
|
2107
|
-
respond(id('respond'), 2, statusCode,
|
|
2377
|
+
respond(id('respond'), 2, statusCode, 'bulk', message, key(action), {
|
|
2378
|
+
bodyExpression: ref(`context.${id('records')}.items`),
|
|
2379
|
+
...count('records')
|
|
2380
|
+
})
|
|
2381
|
+
],
|
|
2382
|
+
edges: [
|
|
2383
|
+
[
|
|
2384
|
+
id('records'),
|
|
2385
|
+
'out',
|
|
2386
|
+
id('respond')
|
|
2387
|
+
]
|
|
2388
|
+
]
|
|
2389
|
+
});
|
|
2390
|
+
const list = (query, message, action, paged = true)=>({
|
|
2391
|
+
inputSchema: [],
|
|
2392
|
+
steps: [
|
|
2393
|
+
{
|
|
2394
|
+
id: id('records'),
|
|
2395
|
+
type: 'entity_query',
|
|
2396
|
+
column: 1,
|
|
2397
|
+
config: {
|
|
2398
|
+
entityCode,
|
|
2399
|
+
...query
|
|
2400
|
+
}
|
|
2401
|
+
},
|
|
2402
|
+
respond(id('respond'), 2, 200, 'list', message, key(action), {
|
|
2403
|
+
bodyExpression: ref(`context.${id('records')}.items`),
|
|
2404
|
+
...paged ? {
|
|
2405
|
+
total: ref(`context.${id('records')}.total`),
|
|
2406
|
+
page: ref(`context.${id('records')}.page`),
|
|
2407
|
+
pageSize: ref(`context.${id('records')}.pageSize`)
|
|
2408
|
+
} : {}
|
|
2409
|
+
})
|
|
2108
2410
|
],
|
|
2109
2411
|
edges: [
|
|
2110
2412
|
[
|
|
@@ -2124,7 +2426,7 @@ const respond = (id, column, statusCode, body, row)=>({
|
|
|
2124
2426
|
entityCode,
|
|
2125
2427
|
fields: fieldMap(entity.fields, 'input')
|
|
2126
2428
|
}
|
|
2127
|
-
}, 201);
|
|
2429
|
+
}, `${label} created successfully`, 'create', 201);
|
|
2128
2430
|
case 'insertMany':
|
|
2129
2431
|
return bulk(insertInputs, {
|
|
2130
2432
|
type: 'entity_insert',
|
|
@@ -2132,57 +2434,36 @@ const respond = (id, column, statusCode, body, row)=>({
|
|
|
2132
2434
|
entityCode,
|
|
2133
2435
|
fields: fieldMap(entity.fields, 'loop.item')
|
|
2134
2436
|
}
|
|
2135
|
-
}, 201);
|
|
2437
|
+
}, `${label} records created successfully`, 'create.many', 201);
|
|
2136
2438
|
case 'getById':
|
|
2137
|
-
return single([
|
|
2138
|
-
idInput(texts)
|
|
2139
|
-
], {
|
|
2439
|
+
return single([], {
|
|
2140
2440
|
type: 'entity_get',
|
|
2141
2441
|
config: {
|
|
2142
2442
|
entityCode,
|
|
2143
|
-
id: ref('input.id')
|
|
2443
|
+
id: ref('input.id'),
|
|
2444
|
+
fromRequest: true
|
|
2144
2445
|
}
|
|
2145
|
-
});
|
|
2446
|
+
}, `${label} retrieved successfully`, 'get');
|
|
2146
2447
|
case 'getByIds':
|
|
2147
|
-
return
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
filter: {
|
|
2154
|
-
id: {
|
|
2155
|
-
op: 'in',
|
|
2156
|
-
value: ref('input.ids')
|
|
2157
|
-
}
|
|
2158
|
-
},
|
|
2159
|
-
limit: _flow_engine_flow_types_js__rspack_import_1/* .FLOW_LIMITS.MAX_QUERY_LIMIT */.Or.MAX_QUERY_LIMIT
|
|
2160
|
-
}
|
|
2161
|
-
});
|
|
2162
|
-
case 'getAll':
|
|
2163
|
-
return single([
|
|
2164
|
-
{
|
|
2165
|
-
name: 'search',
|
|
2166
|
-
label: texts.inputLabels.search,
|
|
2167
|
-
type: 'string'
|
|
2448
|
+
return list({
|
|
2449
|
+
filter: {
|
|
2450
|
+
id: {
|
|
2451
|
+
op: 'in',
|
|
2452
|
+
value: ref('input.ids')
|
|
2453
|
+
}
|
|
2168
2454
|
},
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
}
|
|
2178
|
-
});
|
|
2455
|
+
fromRequest: true,
|
|
2456
|
+
limit: _flow_engine_flow_types_js__rspack_import_1/* .FLOW_LIMITS.MAX_QUERY_LIMIT */.Or.MAX_QUERY_LIMIT
|
|
2457
|
+
}, `${label} records retrieved successfully`, 'get.by.ids', false);
|
|
2458
|
+
case 'getAll':
|
|
2459
|
+
return list({
|
|
2460
|
+
fromRequest: true,
|
|
2461
|
+
limit: LIST_LIMIT
|
|
2462
|
+
}, `${label} records retrieved successfully`, 'get.all');
|
|
2179
2463
|
case 'getByFilter':
|
|
2180
2464
|
return {
|
|
2181
2465
|
inputSchema: [
|
|
2182
|
-
|
|
2183
|
-
...idInput(texts),
|
|
2184
|
-
required: false
|
|
2185
|
-
},
|
|
2466
|
+
idInput(texts),
|
|
2186
2467
|
...filterInputs(filterFields)
|
|
2187
2468
|
],
|
|
2188
2469
|
steps: [
|
|
@@ -2215,12 +2496,10 @@ const respond = (id, column, statusCode, body, row)=>({
|
|
|
2215
2496
|
}
|
|
2216
2497
|
}
|
|
2217
2498
|
},
|
|
2218
|
-
respond(id('respond'), 3, 200,
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
messageKey: lit(_config_message_keys_js__rspack_import_4/* .GENERIC_RECORD_MESSAGES.NO_MATCH */.j4.NO_MATCH)
|
|
2223
|
-
}, 1)
|
|
2499
|
+
respond(id('respond'), 3, 200, 'single', `${label} retrieved successfully`, key('get.by.filter'), {
|
|
2500
|
+
bodyExpression: ref(`context.${id('records')}.items.0`)
|
|
2501
|
+
}),
|
|
2502
|
+
respond(id('not_found'), 3, 404, 'message', texts.noMatchMessage, _config_message_keys_js__rspack_import_4/* .GENERIC_RECORD_MESSAGES.NO_MATCH */.j4.NO_MATCH, {}, 1)
|
|
2224
2503
|
],
|
|
2225
2504
|
edges: [
|
|
2226
2505
|
[
|
|
@@ -2241,34 +2520,25 @@ const respond = (id, column, statusCode, body, row)=>({
|
|
|
2241
2520
|
]
|
|
2242
2521
|
};
|
|
2243
2522
|
case 'update':
|
|
2244
|
-
return single(
|
|
2245
|
-
idInput(texts),
|
|
2246
|
-
...optionalFieldInputs(entity.fields)
|
|
2247
|
-
], {
|
|
2523
|
+
return single(optionalFieldInputs(entity.fields), {
|
|
2248
2524
|
type: 'entity_update',
|
|
2249
2525
|
config: {
|
|
2250
2526
|
entityCode,
|
|
2251
2527
|
id: ref('input.id'),
|
|
2252
2528
|
fields: fieldMap(entity.fields, 'input')
|
|
2253
2529
|
}
|
|
2254
|
-
});
|
|
2530
|
+
}, `${label} updated successfully`, 'update');
|
|
2255
2531
|
case 'updateMany':
|
|
2256
|
-
return bulk(
|
|
2257
|
-
idInput(texts),
|
|
2258
|
-
...optionalFieldInputs(entity.fields)
|
|
2259
|
-
], {
|
|
2532
|
+
return bulk(optionalFieldInputs(entity.fields), {
|
|
2260
2533
|
type: 'entity_update',
|
|
2261
2534
|
config: {
|
|
2262
2535
|
entityCode,
|
|
2263
2536
|
fields: fieldMap(entity.fields, 'loop.item')
|
|
2264
2537
|
}
|
|
2265
|
-
}, 200);
|
|
2538
|
+
}, `${label} records updated successfully`, 'update.many', 200);
|
|
2266
2539
|
case 'bulkUpsert':
|
|
2267
2540
|
return bulk([
|
|
2268
|
-
|
|
2269
|
-
...idInput(texts),
|
|
2270
|
-
required: false
|
|
2271
|
-
},
|
|
2541
|
+
idInput(texts),
|
|
2272
2542
|
...optionalFieldInputs(entity.fields)
|
|
2273
2543
|
], {
|
|
2274
2544
|
type: 'entity_update',
|
|
@@ -2277,17 +2547,60 @@ const respond = (id, column, statusCode, body, row)=>({
|
|
|
2277
2547
|
fields: fieldMap(entity.fields, 'loop.item'),
|
|
2278
2548
|
onNotFound: 'insert'
|
|
2279
2549
|
}
|
|
2280
|
-
}, 200);
|
|
2550
|
+
}, `${label} records upserted successfully`, 'upsert.many', 200);
|
|
2281
2551
|
case 'delete':
|
|
2282
|
-
return
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2552
|
+
return {
|
|
2553
|
+
inputSchema: [],
|
|
2554
|
+
steps: [
|
|
2555
|
+
{
|
|
2556
|
+
id: id('type'),
|
|
2557
|
+
type: 'validate',
|
|
2558
|
+
column: 1,
|
|
2559
|
+
config: {
|
|
2560
|
+
checks: [
|
|
2561
|
+
{
|
|
2562
|
+
id: 'type',
|
|
2563
|
+
field: 'type',
|
|
2564
|
+
message: texts.deleteOnlyMessage,
|
|
2565
|
+
rule: {
|
|
2566
|
+
operator: 'AND',
|
|
2567
|
+
conditions: [
|
|
2568
|
+
{
|
|
2569
|
+
field: 'input.type',
|
|
2570
|
+
comparison: 'equals',
|
|
2571
|
+
value: 'delete'
|
|
2572
|
+
}
|
|
2573
|
+
]
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
]
|
|
2577
|
+
}
|
|
2578
|
+
},
|
|
2579
|
+
{
|
|
2580
|
+
id: id('records'),
|
|
2581
|
+
type: 'entity_delete',
|
|
2582
|
+
column: 2,
|
|
2583
|
+
config: {
|
|
2584
|
+
entityCode,
|
|
2585
|
+
target: 'many',
|
|
2586
|
+
items: ref('input.id')
|
|
2587
|
+
}
|
|
2588
|
+
},
|
|
2589
|
+
respond(id('respond'), 3, 200, 'message', `${label} records deleted successfully`, key('delete'), count('records'))
|
|
2590
|
+
],
|
|
2591
|
+
edges: [
|
|
2592
|
+
[
|
|
2593
|
+
id('type'),
|
|
2594
|
+
'out',
|
|
2595
|
+
id('records')
|
|
2596
|
+
],
|
|
2597
|
+
[
|
|
2598
|
+
id('records'),
|
|
2599
|
+
'out',
|
|
2600
|
+
id('respond')
|
|
2601
|
+
]
|
|
2602
|
+
]
|
|
2603
|
+
};
|
|
2291
2604
|
}
|
|
2292
2605
|
}
|
|
2293
2606
|
/**
|
|
@@ -2302,11 +2615,10 @@ const respond = (id, column, statusCode, body, row)=>({
|
|
|
2302
2615
|
*/ function buildEntityFlow(entity, endpoints, texts = {}) {
|
|
2303
2616
|
const resolved = {
|
|
2304
2617
|
inputLabels: {
|
|
2305
|
-
id: textOr(texts.inputLabels?.id, DEFAULT_INPUT_LABEL.id)
|
|
2306
|
-
ids: textOr(texts.inputLabels?.ids, DEFAULT_INPUT_LABEL.ids),
|
|
2307
|
-
search: textOr(texts.inputLabels?.search, DEFAULT_INPUT_LABEL.search)
|
|
2618
|
+
id: textOr(texts.inputLabels?.id, DEFAULT_INPUT_LABEL.id)
|
|
2308
2619
|
},
|
|
2309
|
-
noMatchMessage: textOr(texts.noMatchMessage, DEFAULT_NO_MATCH_MESSAGE)
|
|
2620
|
+
noMatchMessage: textOr(texts.noMatchMessage, DEFAULT_NO_MATCH_MESSAGE),
|
|
2621
|
+
deleteOnlyMessage: textOr(texts.deleteOnlyMessage, DEFAULT_DELETE_ONLY_MESSAGE)
|
|
2310
2622
|
};
|
|
2311
2623
|
const nodes = [];
|
|
2312
2624
|
const edges = [];
|
|
@@ -2319,8 +2631,10 @@ const respond = (id, column, statusCode, body, row)=>({
|
|
|
2319
2631
|
});
|
|
2320
2632
|
const config = {
|
|
2321
2633
|
path: ENTITY_FLOW_PATHS[endpoint],
|
|
2322
|
-
|
|
2323
|
-
|
|
2634
|
+
contract: ENDPOINT_CONTRACTS[endpoint],
|
|
2635
|
+
...graph.inputSchema.length ? {
|
|
2636
|
+
inputSchema: graph.inputSchema
|
|
2637
|
+
} : {},
|
|
2324
2638
|
authMode: 'permission',
|
|
2325
2639
|
permissions: endpointPermissions(entity.code, endpoint),
|
|
2326
2640
|
...READ_ENDPOINTS.has(endpoint) ? {} : {
|
|
@@ -2405,6 +2719,7 @@ const CRUD_ACTIONS = [
|
|
|
2405
2719
|
'update',
|
|
2406
2720
|
'delete'
|
|
2407
2721
|
];
|
|
2722
|
+
/** The create / read / update / delete permission codes of a dynamic entity. */ const entityPermissionCodes = (entityCode)=>CRUD_ACTIONS.map((op)=>(0,_flusys_nestjs_shared__rspack_import_0.dynamicEntityPermission)(entityCode, op));
|
|
2408
2723
|
class EntityPermissionService {
|
|
2409
2724
|
actionRegistry;
|
|
2410
2725
|
constructor(actionRegistry){
|
|
@@ -2424,7 +2739,7 @@ class EntityPermissionService {
|
|
|
2424
2739
|
if (!this.actionRegistry) return;
|
|
2425
2740
|
await this.actionRegistry.removeActionsByCodes([
|
|
2426
2741
|
(0,_flusys_nestjs_shared__rspack_import_0.dynamicEntityPermissionParent)(definition.code),
|
|
2427
|
-
...
|
|
2742
|
+
...entityPermissionCodes(definition.code)
|
|
2428
2743
|
]);
|
|
2429
2744
|
}
|
|
2430
2745
|
}
|
|
@@ -2440,6 +2755,8 @@ EntityPermissionService = _ts_decorate([
|
|
|
2440
2755
|
|
|
2441
2756
|
__webpack_require__.d(__webpack_exports__, {
|
|
2442
2757
|
u: () => (EntityPermissionService)
|
|
2758
|
+
}, {
|
|
2759
|
+
Z: entityPermissionCodes
|
|
2443
2760
|
});
|
|
2444
2761
|
|
|
2445
2762
|
|
|
@@ -3094,8 +3411,13 @@ class SchemaChangeExecutor {
|
|
|
3094
3411
|
].toSorted().map((code)=>`eb_entity:${code}`);
|
|
3095
3412
|
const heldLocks = [];
|
|
3096
3413
|
const qr = await this.dataSourceProvider.getQueryRunner();
|
|
3097
|
-
|
|
3098
|
-
|
|
3414
|
+
try {
|
|
3415
|
+
await qr.connect();
|
|
3416
|
+
await qr.startTransaction();
|
|
3417
|
+
} catch (error) {
|
|
3418
|
+
await qr.release().catch(()=>undefined);
|
|
3419
|
+
throw (0,_database_error_mapper_js__rspack_import_6/* .mapSchemaError */.i)(error);
|
|
3420
|
+
}
|
|
3099
3421
|
const state = this.newState(target, options.impact ?? []);
|
|
3100
3422
|
let committed = false;
|
|
3101
3423
|
try {
|
|
@@ -3139,9 +3461,7 @@ class SchemaChangeExecutor {
|
|
|
3139
3461
|
}));
|
|
3140
3462
|
await qr.commitTransaction();
|
|
3141
3463
|
committed = true;
|
|
3142
|
-
|
|
3143
|
-
if (entityId) this.schemaRegistry.invalidate(entityId);
|
|
3144
|
-
await this.definitionCache.clearAll((0,_flusys_nestjs_shared_utils__rspack_import_0.cacheTenantId)(this.dataSourceProvider));
|
|
3464
|
+
await this.invalidateDefinitions(target, outcome.entityDefinitionId ?? target.entityDefinitionId, state.warnings);
|
|
3145
3465
|
try {
|
|
3146
3466
|
await outcome.afterCommit?.();
|
|
3147
3467
|
} catch (postCommitError) {
|
|
@@ -3158,6 +3478,8 @@ class SchemaChangeExecutor {
|
|
|
3158
3478
|
} catch (error) {
|
|
3159
3479
|
if (!committed) await qr.rollbackTransaction().catch(()=>undefined);
|
|
3160
3480
|
const undo = !committed && driver === 'mysql' && state.undo.length > 0 ? await this.compensate(qr, target, state) : null;
|
|
3481
|
+
// MySQL commits implicitly at each DDL statement, so metadata written before one may have stayed.
|
|
3482
|
+
if (undo) await this.invalidateDefinitions(target, target.entityDefinitionId, []);
|
|
3161
3483
|
await this.logFailure(target, error, state.steps, undo);
|
|
3162
3484
|
throw this.withUndo((0,_database_error_mapper_js__rspack_import_6/* .mapSchemaError */.i)(error), undo);
|
|
3163
3485
|
} finally{
|
|
@@ -3165,6 +3487,17 @@ class SchemaChangeExecutor {
|
|
|
3165
3487
|
await qr.release();
|
|
3166
3488
|
}
|
|
3167
3489
|
}
|
|
3490
|
+
/** Runs only once the change's transaction has ended: a bump inside it would let a concurrent reader re-cache the old definitions. */ async invalidateDefinitions(target, entityId, warnings) {
|
|
3491
|
+
try {
|
|
3492
|
+
if (entityId) this.schemaRegistry.invalidate(entityId);
|
|
3493
|
+
await this.definitionCache.clearAll((0,_flusys_nestjs_shared_utils__rspack_import_0.cacheTenantId)(this.dataSourceProvider));
|
|
3494
|
+
} catch (cacheError) {
|
|
3495
|
+
this.logger.error(`Clearing the definition cache failed after '${target.action}' on '${target.entityCode}': ${cacheError.message}`);
|
|
3496
|
+
warnings.push((0,_flusys_nestjs_shared_utils__rspack_import_0.messageRef)(_config_message_keys_js__rspack_import_11/* .SCHEMA_WARNING_MESSAGES.FOLLOW_UP_FAILED */.Uu.FOLLOW_UP_FAILED, {
|
|
3497
|
+
detail: (0,_flow_engine_flow_errors_js__rspack_import_3/* .errorRef */.O$)(cacheError)
|
|
3498
|
+
}));
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3168
3501
|
/**
|
|
3169
3502
|
* MySQL only: runs the reverse of every DDL statement that had run, newest first, still under
|
|
3170
3503
|
* the entity lock. Stops at the first undo statement that fails - running later ones against an
|
|
@@ -3575,8 +3908,8 @@ class SchemaEvolutionService {
|
|
|
3575
3908
|
const { definition, fields } = await this.loadEntity(entityDefinitionId);
|
|
3576
3909
|
const driver = await this.dataSourceProvider.getDriver();
|
|
3577
3910
|
const qr = await this.dataSourceProvider.getQueryRunner();
|
|
3578
|
-
await qr.connect();
|
|
3579
3911
|
try {
|
|
3912
|
+
await qr.connect();
|
|
3580
3913
|
const table = await qr.getTable(definition.tableName);
|
|
3581
3914
|
const findings = this.diff(definition, fields, table ?? undefined, driver);
|
|
3582
3915
|
let rowCount = null;
|