@acorex/connectivity 21.0.0-next.5 → 21.0.0-next.7
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/fesm2022/acorex-connectivity-mock.mjs +2465 -1845
- package/fesm2022/acorex-connectivity-mock.mjs.map +1 -1
- package/mock/index.d.ts +47 -28
- package/package.json +2 -2
|
@@ -14,11 +14,10 @@ import { AXMSubscriptionManagementSubscriptionEntityService, AXMSubscriptionMana
|
|
|
14
14
|
import { AXPSystemStatusType, AXPFileStorageStatus, AXPFileStorageService, AXVChangeType as AXVChangeType$1, AXP_SEARCH_PROVIDER, AXPLockService, AXPVersioningService, AXPSystemStatuses, AXPAppVersionService } from '@acorex/platform/common';
|
|
15
15
|
import { AXMTenantService, AXMTenantManagementTenantUserEntityService, AXMTenantManagementPermissionsKeys, RootConfig as RootConfig$q } from '@acorex/modules/tenant-management';
|
|
16
16
|
import { RootConfig as RootConfig$1 } from '@acorex/modules/calendar-management';
|
|
17
|
+
import { provideCommandSetups, AXPCommandService, provideQuerySetups, AXPQueryService, AXPRuntimeModule } from '@acorex/platform/runtime';
|
|
17
18
|
import Dexie from 'dexie';
|
|
18
19
|
import { AXFileService } from '@acorex/core/file';
|
|
19
|
-
import {
|
|
20
|
-
import { AXPCommandService, provideCommandSetups, provideQuerySetups, AXPQueryService, AXPRuntimeModule } from '@acorex/platform/runtime';
|
|
21
|
-
import { AXMPermissionsKeys as AXMPermissionsKeys$3, AXMDistributionServiceImpl, AXMDocumentService, AXMDistributionInteractionService, RootConfig as RootConfig$6, AXMFolderServiceImpl, AXMDocumentServiceImpl, AXMReviewService, AXMReviewServiceImpl, AXMDistributionInteractionServiceImpl, AXMFolderService, AXMDistributionService } from '@acorex/modules/document-management';
|
|
20
|
+
import { AXMPermissionsKeys as AXMPermissionsKeys$3, AXMDistributionInteractionServiceImpl, AXMDistributionServiceImpl, AXMDocumentService, AXMDistributionInteractionService, AXMFolderServiceImpl, AXMDocumentServiceImpl, AXMReviewService, AXMDocumentTypeServiceImpl, RootConfig as RootConfig$6, AXMReviewServiceImpl, AXMFolderService, AXMDistributionService, AXPDocumentManagementService } from '@acorex/modules/document-management';
|
|
22
21
|
import { AXMPermissionsKeys as AXMPermissionsKeys$2, RootConfig as RootConfig$5, AXMMetaDataDefinitionService, META_DATA_SELECTOR_FIELD, AXMQueryServiceImpl, AXMQueryService, AXMDataManagementFeatureKeys } from '@acorex/modules/data-management';
|
|
23
22
|
import { AXMPermissionsKeys as AXMPermissionsKeys$4, RootConfig as RootConfig$a, AXPHumanCapitalManagementFeatureKeys } from '@acorex/modules/human-capital-management';
|
|
24
23
|
import { AXMSettingsManagementPermissionKeys } from '@acorex/modules/settings-management';
|
|
@@ -60,6 +59,7 @@ import { RootConfig as RootConfig$v, AXM_ORDER_NUMBER_IDENTIFIER_RULE } from '@a
|
|
|
60
59
|
import { RootConfig as RootConfig$x } from '@acorex/modules/help-desk';
|
|
61
60
|
import { AXCExternalAuthorizationService } from '@acorex/connectivity/utils';
|
|
62
61
|
import { RootConfig as RootConfig$y } from '@acorex/modules/workflow-management';
|
|
62
|
+
import { AXP_WORKFLOW_CATEGORY_PROVIDER, AXP_WORKFLOW_PROVIDER } from '@acorex/platform/workflow';
|
|
63
63
|
import { AXCalendarService } from '@acorex/core/date-time';
|
|
64
64
|
|
|
65
65
|
//#region ---- Constants ----
|
|
@@ -611,6 +611,9 @@ class MockFeatureChecker {
|
|
|
611
611
|
check(keys, context, baseResult) {
|
|
612
612
|
const user = context.user;
|
|
613
613
|
//console.log('[Feature Checker]', keys, user?.name, baseResult);
|
|
614
|
+
// if (keys.includes("SecurityManagement")) {
|
|
615
|
+
// return false;
|
|
616
|
+
// }
|
|
614
617
|
// Root user always has all features enabled
|
|
615
618
|
if (user?.name === 'super-root') {
|
|
616
619
|
return true;
|
|
@@ -1609,6 +1612,219 @@ class EntitySearchProvider {
|
|
|
1609
1612
|
}
|
|
1610
1613
|
}
|
|
1611
1614
|
|
|
1615
|
+
//#endregion
|
|
1616
|
+
//#region ---- Update Status Command ----
|
|
1617
|
+
/**
|
|
1618
|
+
* Generic command to update entity status
|
|
1619
|
+
* Supports both single entity (id) and bulk updates (ids)
|
|
1620
|
+
*
|
|
1621
|
+
* Options:
|
|
1622
|
+
* - entity: string - Entity key (e.g., 'OrderManagement.GroupOrder')
|
|
1623
|
+
* - id?: string - Single entity ID
|
|
1624
|
+
* - ids?: string[] - Multiple entity IDs (for bulk updates)
|
|
1625
|
+
* - status: string - Target status value
|
|
1626
|
+
* - statusField?: string - Status field name (default: 'statusId')
|
|
1627
|
+
*/
|
|
1628
|
+
class AXCEntityUpdateStatusCommand {
|
|
1629
|
+
constructor() {
|
|
1630
|
+
this.entityService = inject(AXPEntityService);
|
|
1631
|
+
}
|
|
1632
|
+
async execute(input) {
|
|
1633
|
+
try {
|
|
1634
|
+
const { entity, id, ids, status, statusField = 'statusId' } = input;
|
|
1635
|
+
// Validate input
|
|
1636
|
+
if (!entity) {
|
|
1637
|
+
throw new Error('Entity name is required');
|
|
1638
|
+
}
|
|
1639
|
+
if (!id && (!ids || ids.length === 0)) {
|
|
1640
|
+
throw new Error('Either id or ids must be provided');
|
|
1641
|
+
}
|
|
1642
|
+
if (!status) {
|
|
1643
|
+
throw new Error('Status value is required');
|
|
1644
|
+
}
|
|
1645
|
+
const dataAccessor = this.entityService.withEntity(entity).data();
|
|
1646
|
+
const updateData = { [statusField]: status };
|
|
1647
|
+
// Handle bulk update
|
|
1648
|
+
if (ids && ids.length > 0) {
|
|
1649
|
+
const results = [];
|
|
1650
|
+
for (const entityId of ids) {
|
|
1651
|
+
const updated = await dataAccessor.update(entityId, updateData);
|
|
1652
|
+
results.push(updated);
|
|
1653
|
+
}
|
|
1654
|
+
return {
|
|
1655
|
+
success: true,
|
|
1656
|
+
data: results,
|
|
1657
|
+
};
|
|
1658
|
+
}
|
|
1659
|
+
// Handle single update
|
|
1660
|
+
if (id) {
|
|
1661
|
+
const updated = await dataAccessor.update(id, updateData);
|
|
1662
|
+
return {
|
|
1663
|
+
success: true,
|
|
1664
|
+
data: updated,
|
|
1665
|
+
message: {
|
|
1666
|
+
text: 'Entity status updated successfully',
|
|
1667
|
+
},
|
|
1668
|
+
};
|
|
1669
|
+
}
|
|
1670
|
+
throw new Error('Invalid input: neither id nor ids provided');
|
|
1671
|
+
}
|
|
1672
|
+
catch (error) {
|
|
1673
|
+
return {
|
|
1674
|
+
success: false,
|
|
1675
|
+
message: {
|
|
1676
|
+
text: error instanceof Error ? error.message : 'Failed to update entity status',
|
|
1677
|
+
},
|
|
1678
|
+
};
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCEntityUpdateStatusCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1682
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCEntityUpdateStatusCommand, providedIn: 'root' }); }
|
|
1683
|
+
}
|
|
1684
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCEntityUpdateStatusCommand, decorators: [{
|
|
1685
|
+
type: Injectable,
|
|
1686
|
+
args: [{ providedIn: 'root' }]
|
|
1687
|
+
}] });
|
|
1688
|
+
|
|
1689
|
+
class FileStorageDatabase extends Dexie {
|
|
1690
|
+
constructor() {
|
|
1691
|
+
super('ACoreXFileStorage');
|
|
1692
|
+
this.version(1).stores({
|
|
1693
|
+
files: 'fileId,refId,refType,category,isPrimary,status',
|
|
1694
|
+
});
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
const db = new FileStorageDatabase();
|
|
1698
|
+
class AXCFileStorageService {
|
|
1699
|
+
async mapToFileStorageInfo(record) {
|
|
1700
|
+
if (!record) {
|
|
1701
|
+
throw new Error('Record not found');
|
|
1702
|
+
}
|
|
1703
|
+
const { binary, ...fileInfo } = record;
|
|
1704
|
+
const url = await this.fileService.blobToBase64(binary);
|
|
1705
|
+
return { ...record, url };
|
|
1706
|
+
}
|
|
1707
|
+
async save(request) {
|
|
1708
|
+
const fileId = AXPDataGenerator.uuid();
|
|
1709
|
+
const fileInfo = {
|
|
1710
|
+
fileId,
|
|
1711
|
+
refId: request.refId,
|
|
1712
|
+
refType: request.refType,
|
|
1713
|
+
category: request.category,
|
|
1714
|
+
size: request.file.size,
|
|
1715
|
+
mimeType: request.file.type,
|
|
1716
|
+
uploadedAt: new Date(),
|
|
1717
|
+
isPublic: request.metadata?.['isPublic'] || true,
|
|
1718
|
+
isPrimary: request.isPrimary || false,
|
|
1719
|
+
status: request.status ?? AXPFileStorageStatus.Temporary, // Use enum
|
|
1720
|
+
name: request.name,
|
|
1721
|
+
binary: request.file,
|
|
1722
|
+
};
|
|
1723
|
+
// Save the binary content along with metadata in Dexie
|
|
1724
|
+
await db.files.add({ ...fileInfo, binary: request.file });
|
|
1725
|
+
return fileInfo;
|
|
1726
|
+
}
|
|
1727
|
+
async commit(fileId) {
|
|
1728
|
+
const file = await db.files.get(fileId);
|
|
1729
|
+
if (!file) {
|
|
1730
|
+
throw new Error('File not found');
|
|
1731
|
+
}
|
|
1732
|
+
file.status = AXPFileStorageStatus.Committed; // Use enum
|
|
1733
|
+
await db.files.put(file);
|
|
1734
|
+
}
|
|
1735
|
+
async markForDeletion(fileId) {
|
|
1736
|
+
const file = await db.files.get(fileId);
|
|
1737
|
+
if (!file) {
|
|
1738
|
+
throw new Error('File not found');
|
|
1739
|
+
}
|
|
1740
|
+
file.status = AXPFileStorageStatus.PendingDeletion; // Use enum
|
|
1741
|
+
await db.files.put(file);
|
|
1742
|
+
}
|
|
1743
|
+
async update(request) {
|
|
1744
|
+
const file = await db.files.get(request.fileId);
|
|
1745
|
+
if (!file) {
|
|
1746
|
+
throw new Error('File not found');
|
|
1747
|
+
}
|
|
1748
|
+
const updatedFileInfo = {
|
|
1749
|
+
...file,
|
|
1750
|
+
...request.metadata,
|
|
1751
|
+
name: request.name !== undefined ? request.name : file.name,
|
|
1752
|
+
isPrimary: request.isPrimary !== undefined ? request.isPrimary : file.isPrimary,
|
|
1753
|
+
};
|
|
1754
|
+
await db.files.put(updatedFileInfo);
|
|
1755
|
+
return this.mapToFileStorageInfo(updatedFileInfo);
|
|
1756
|
+
}
|
|
1757
|
+
async find(request) {
|
|
1758
|
+
const files = await db.files.toArray();
|
|
1759
|
+
const filteredFiles = files.filter((file) => {
|
|
1760
|
+
return ((!request.refId || file.refId === request.refId) &&
|
|
1761
|
+
(!request.refType || file.refType === request.refType) &&
|
|
1762
|
+
(!request.category || file.category === request.category) &&
|
|
1763
|
+
(!request.isPrimary || file.isPrimary === request.isPrimary) &&
|
|
1764
|
+
(!request.isPublic || file.isPublic === request.isPublic) &&
|
|
1765
|
+
(!request.mimeType || file.mimeType === request.mimeType) &&
|
|
1766
|
+
(!request.uploadedAtRange ||
|
|
1767
|
+
(file.uploadedAt >= request.uploadedAtRange.from && file.uploadedAt <= request.uploadedAtRange.to)) &&
|
|
1768
|
+
file.status === AXPFileStorageStatus.Committed // Use enum
|
|
1769
|
+
);
|
|
1770
|
+
});
|
|
1771
|
+
// Map all filtered files to `AXPFileStorageInfo`
|
|
1772
|
+
return Promise.all(filteredFiles.map((file) => this.mapToFileStorageInfo(file)));
|
|
1773
|
+
}
|
|
1774
|
+
async getInfo(fileId) {
|
|
1775
|
+
// Simulate a delay of 2 seconds (2000 milliseconds)
|
|
1776
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1777
|
+
const file = await db.files.get(fileId);
|
|
1778
|
+
return this.mapToFileStorageInfo(file);
|
|
1779
|
+
}
|
|
1780
|
+
async remove(fileId) {
|
|
1781
|
+
await db.files.delete(fileId);
|
|
1782
|
+
}
|
|
1783
|
+
async cleanupTemporaryFiles() {
|
|
1784
|
+
const files = await db.files.toArray();
|
|
1785
|
+
const temporaryFiles = files.filter((file) => file.status === AXPFileStorageStatus.Temporary);
|
|
1786
|
+
for (const file of temporaryFiles) {
|
|
1787
|
+
await db.files.delete(file.fileId);
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
async cleanupMarkedFiles(gracePeriod) {
|
|
1791
|
+
const now = new Date();
|
|
1792
|
+
const files = await db.files.toArray();
|
|
1793
|
+
for (const file of files) {
|
|
1794
|
+
if (file.status === AXPFileStorageStatus.PendingDeletion &&
|
|
1795
|
+
now.getTime() - file.uploadedAt.getTime() > gracePeriod) {
|
|
1796
|
+
await db.files.delete(file.fileId);
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
// Semi-background cleanup job
|
|
1801
|
+
constructor() {
|
|
1802
|
+
this.fileService = inject(AXFileService);
|
|
1803
|
+
setInterval(() => {
|
|
1804
|
+
this.cleanupMarkedFiles(5 * 60 * 1000) // Grace period: 5 minutes in milliseconds
|
|
1805
|
+
.catch((error) => console.error('Error during cleanup:', error));
|
|
1806
|
+
this.cleanupTemporaryFiles() // Ensure temporary files are cleaned
|
|
1807
|
+
.catch((error) => console.error('Error during cleanup:', error));
|
|
1808
|
+
}, 5 * 60 * 1000); // Runs every 5 minutes
|
|
1809
|
+
}
|
|
1810
|
+
async findMany(ids) {
|
|
1811
|
+
const files = await db.files.where('fileId').anyOf(ids).toArray();
|
|
1812
|
+
return files.map((file) => ({
|
|
1813
|
+
id: file.fileId,
|
|
1814
|
+
fileId: file.fileId,
|
|
1815
|
+
refId: file.refId,
|
|
1816
|
+
refType: file.refType,
|
|
1817
|
+
size: file.size,
|
|
1818
|
+
name: file.name,
|
|
1819
|
+
}));
|
|
1820
|
+
}
|
|
1821
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCFileStorageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1822
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCFileStorageService }); }
|
|
1823
|
+
}
|
|
1824
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCFileStorageService, decorators: [{
|
|
1825
|
+
type: Injectable
|
|
1826
|
+
}], ctorParameters: () => [] });
|
|
1827
|
+
|
|
1612
1828
|
class AXCLockService {
|
|
1613
1829
|
constructor() {
|
|
1614
1830
|
// Local Dexie database for storing lock information independent of AXPEntityStorageService
|
|
@@ -1760,145 +1976,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
|
|
|
1760
1976
|
type: Injectable
|
|
1761
1977
|
}], ctorParameters: () => [] });
|
|
1762
1978
|
|
|
1763
|
-
class FileStorageDatabase extends Dexie {
|
|
1764
|
-
constructor() {
|
|
1765
|
-
super('ACoreXFileStorage');
|
|
1766
|
-
this.version(1).stores({
|
|
1767
|
-
files: 'fileId,refId,refType,category,isPrimary,status',
|
|
1768
|
-
});
|
|
1769
|
-
}
|
|
1770
|
-
}
|
|
1771
|
-
const db = new FileStorageDatabase();
|
|
1772
|
-
class AXCFileStorageService {
|
|
1773
|
-
async mapToFileStorageInfo(record) {
|
|
1774
|
-
if (!record) {
|
|
1775
|
-
throw new Error('Record not found');
|
|
1776
|
-
}
|
|
1777
|
-
const { binary, ...fileInfo } = record;
|
|
1778
|
-
const url = await this.fileService.blobToBase64(binary);
|
|
1779
|
-
return { ...record, url };
|
|
1780
|
-
}
|
|
1781
|
-
async save(request) {
|
|
1782
|
-
const fileId = AXPDataGenerator.uuid();
|
|
1783
|
-
const fileInfo = {
|
|
1784
|
-
fileId,
|
|
1785
|
-
refId: request.refId,
|
|
1786
|
-
refType: request.refType,
|
|
1787
|
-
category: request.category,
|
|
1788
|
-
size: request.file.size,
|
|
1789
|
-
mimeType: request.file.type,
|
|
1790
|
-
uploadedAt: new Date(),
|
|
1791
|
-
isPublic: request.metadata?.['isPublic'] || true,
|
|
1792
|
-
isPrimary: request.isPrimary || false,
|
|
1793
|
-
status: request.status ?? AXPFileStorageStatus.Temporary, // Use enum
|
|
1794
|
-
name: request.name,
|
|
1795
|
-
binary: request.file,
|
|
1796
|
-
};
|
|
1797
|
-
// Save the binary content along with metadata in Dexie
|
|
1798
|
-
await db.files.add({ ...fileInfo, binary: request.file });
|
|
1799
|
-
return fileInfo;
|
|
1800
|
-
}
|
|
1801
|
-
async commit(fileId) {
|
|
1802
|
-
const file = await db.files.get(fileId);
|
|
1803
|
-
if (!file) {
|
|
1804
|
-
throw new Error('File not found');
|
|
1805
|
-
}
|
|
1806
|
-
file.status = AXPFileStorageStatus.Committed; // Use enum
|
|
1807
|
-
await db.files.put(file);
|
|
1808
|
-
}
|
|
1809
|
-
async markForDeletion(fileId) {
|
|
1810
|
-
const file = await db.files.get(fileId);
|
|
1811
|
-
if (!file) {
|
|
1812
|
-
throw new Error('File not found');
|
|
1813
|
-
}
|
|
1814
|
-
file.status = AXPFileStorageStatus.PendingDeletion; // Use enum
|
|
1815
|
-
await db.files.put(file);
|
|
1816
|
-
}
|
|
1817
|
-
async update(request) {
|
|
1818
|
-
const file = await db.files.get(request.fileId);
|
|
1819
|
-
if (!file) {
|
|
1820
|
-
throw new Error('File not found');
|
|
1821
|
-
}
|
|
1822
|
-
const updatedFileInfo = {
|
|
1823
|
-
...file,
|
|
1824
|
-
...request.metadata,
|
|
1825
|
-
name: request.name !== undefined ? request.name : file.name,
|
|
1826
|
-
isPrimary: request.isPrimary !== undefined ? request.isPrimary : file.isPrimary,
|
|
1827
|
-
};
|
|
1828
|
-
await db.files.put(updatedFileInfo);
|
|
1829
|
-
return this.mapToFileStorageInfo(updatedFileInfo);
|
|
1830
|
-
}
|
|
1831
|
-
async find(request) {
|
|
1832
|
-
const files = await db.files.toArray();
|
|
1833
|
-
const filteredFiles = files.filter((file) => {
|
|
1834
|
-
return ((!request.refId || file.refId === request.refId) &&
|
|
1835
|
-
(!request.refType || file.refType === request.refType) &&
|
|
1836
|
-
(!request.category || file.category === request.category) &&
|
|
1837
|
-
(!request.isPrimary || file.isPrimary === request.isPrimary) &&
|
|
1838
|
-
(!request.isPublic || file.isPublic === request.isPublic) &&
|
|
1839
|
-
(!request.mimeType || file.mimeType === request.mimeType) &&
|
|
1840
|
-
(!request.uploadedAtRange ||
|
|
1841
|
-
(file.uploadedAt >= request.uploadedAtRange.from && file.uploadedAt <= request.uploadedAtRange.to)) &&
|
|
1842
|
-
file.status === AXPFileStorageStatus.Committed // Use enum
|
|
1843
|
-
);
|
|
1844
|
-
});
|
|
1845
|
-
// Map all filtered files to `AXPFileStorageInfo`
|
|
1846
|
-
return Promise.all(filteredFiles.map((file) => this.mapToFileStorageInfo(file)));
|
|
1847
|
-
}
|
|
1848
|
-
async getInfo(fileId) {
|
|
1849
|
-
// Simulate a delay of 2 seconds (2000 milliseconds)
|
|
1850
|
-
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1851
|
-
const file = await db.files.get(fileId);
|
|
1852
|
-
return this.mapToFileStorageInfo(file);
|
|
1853
|
-
}
|
|
1854
|
-
async remove(fileId) {
|
|
1855
|
-
await db.files.delete(fileId);
|
|
1856
|
-
}
|
|
1857
|
-
async cleanupTemporaryFiles() {
|
|
1858
|
-
const files = await db.files.toArray();
|
|
1859
|
-
const temporaryFiles = files.filter((file) => file.status === AXPFileStorageStatus.Temporary);
|
|
1860
|
-
for (const file of temporaryFiles) {
|
|
1861
|
-
await db.files.delete(file.fileId);
|
|
1862
|
-
}
|
|
1863
|
-
}
|
|
1864
|
-
async cleanupMarkedFiles(gracePeriod) {
|
|
1865
|
-
const now = new Date();
|
|
1866
|
-
const files = await db.files.toArray();
|
|
1867
|
-
for (const file of files) {
|
|
1868
|
-
if (file.status === AXPFileStorageStatus.PendingDeletion &&
|
|
1869
|
-
now.getTime() - file.uploadedAt.getTime() > gracePeriod) {
|
|
1870
|
-
await db.files.delete(file.fileId);
|
|
1871
|
-
}
|
|
1872
|
-
}
|
|
1873
|
-
}
|
|
1874
|
-
// Semi-background cleanup job
|
|
1875
|
-
constructor() {
|
|
1876
|
-
this.fileService = inject(AXFileService);
|
|
1877
|
-
setInterval(() => {
|
|
1878
|
-
this.cleanupMarkedFiles(5 * 60 * 1000) // Grace period: 5 minutes in milliseconds
|
|
1879
|
-
.catch((error) => console.error('Error during cleanup:', error));
|
|
1880
|
-
this.cleanupTemporaryFiles() // Ensure temporary files are cleaned
|
|
1881
|
-
.catch((error) => console.error('Error during cleanup:', error));
|
|
1882
|
-
}, 5 * 60 * 1000); // Runs every 5 minutes
|
|
1883
|
-
}
|
|
1884
|
-
async findMany(ids) {
|
|
1885
|
-
const files = await db.files.where('fileId').anyOf(ids).toArray();
|
|
1886
|
-
return files.map((file) => ({
|
|
1887
|
-
id: file.fileId,
|
|
1888
|
-
fileId: file.fileId,
|
|
1889
|
-
refId: file.refId,
|
|
1890
|
-
refType: file.refType,
|
|
1891
|
-
size: file.size,
|
|
1892
|
-
name: file.name,
|
|
1893
|
-
}));
|
|
1894
|
-
}
|
|
1895
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCFileStorageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1896
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCFileStorageService }); }
|
|
1897
|
-
}
|
|
1898
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCFileStorageService, decorators: [{
|
|
1899
|
-
type: Injectable
|
|
1900
|
-
}], ctorParameters: () => [] });
|
|
1901
|
-
|
|
1902
1979
|
//#region ---- Imports ----
|
|
1903
1980
|
var AXVChangeType;
|
|
1904
1981
|
(function (AXVChangeType) {
|
|
@@ -2322,1004 +2399,6 @@ function buildSummaryFromChanges(changes, changeType) {
|
|
|
2322
2399
|
return result;
|
|
2323
2400
|
}
|
|
2324
2401
|
|
|
2325
|
-
//#region ---- Dexie DB ----
|
|
2326
|
-
/**
|
|
2327
|
-
* Dexie database for workflow instances.
|
|
2328
|
-
*/
|
|
2329
|
-
class AXCWorkflowExecutionDB extends Dexie {
|
|
2330
|
-
constructor() {
|
|
2331
|
-
super('ACoreXWorkflowExecutions');
|
|
2332
|
-
this.version(2).stores({
|
|
2333
|
-
instances: 'id, definitionId, status, subStatus, createdAt, updatedAt',
|
|
2334
|
-
});
|
|
2335
|
-
// Migration from v1 to v2: clear old executions table
|
|
2336
|
-
this.version(1).stores({
|
|
2337
|
-
executions: 'executionId, workflowId, status, currentStepId, lastUpdated',
|
|
2338
|
-
});
|
|
2339
|
-
}
|
|
2340
|
-
}
|
|
2341
|
-
/**
|
|
2342
|
-
* Singleton instance of workflow execution database.
|
|
2343
|
-
*/
|
|
2344
|
-
const axWorkflowExecutionDB = new AXCWorkflowExecutionDB();
|
|
2345
|
-
//#endregion
|
|
2346
|
-
|
|
2347
|
-
/**
|
|
2348
|
-
* Mock implementation of AXPWorkflowExecutionService.
|
|
2349
|
-
*
|
|
2350
|
-
* Uses IndexedDB (via Dexie) to store workflow execution states.
|
|
2351
|
-
*
|
|
2352
|
-
* @example
|
|
2353
|
-
* ```typescript
|
|
2354
|
-
* // In module providers
|
|
2355
|
-
* {
|
|
2356
|
-
* provide: AXPWorkflowExecutionService,
|
|
2357
|
-
* useClass: AXCWorkflowExecutionService,
|
|
2358
|
-
* }
|
|
2359
|
-
* ```
|
|
2360
|
-
*/
|
|
2361
|
-
class AXCWorkflowExecutionService {
|
|
2362
|
-
constructor() {
|
|
2363
|
-
this.db = axWorkflowExecutionDB;
|
|
2364
|
-
this.commandService = inject(AXPCommandService);
|
|
2365
|
-
this.entityStorageService = inject(AXPEntityStorageService);
|
|
2366
|
-
}
|
|
2367
|
-
/**
|
|
2368
|
-
* Start a new workflow execution.
|
|
2369
|
-
* Creates a workflow instance and stores it in IndexedDB.
|
|
2370
|
-
*/
|
|
2371
|
-
async startExecution(request) {
|
|
2372
|
-
const instanceId = AXPDataGenerator.uuid();
|
|
2373
|
-
const now = new Date().toISOString();
|
|
2374
|
-
// Get workflow definition to find first step
|
|
2375
|
-
const workflowResponse = await this.getWorkflowDefinition({
|
|
2376
|
-
workflowId: request.workflowId
|
|
2377
|
-
});
|
|
2378
|
-
if (!workflowResponse.workflow) {
|
|
2379
|
-
throw new Error(`Workflow definition not found: ${request.workflowId}`);
|
|
2380
|
-
}
|
|
2381
|
-
const workflow = workflowResponse.workflow;
|
|
2382
|
-
// Find first step from Flowchart (activity with no incoming connections)
|
|
2383
|
-
const flowchart = workflow.root;
|
|
2384
|
-
const activities = flowchart.activities || [];
|
|
2385
|
-
const connections = flowchart.connections || [];
|
|
2386
|
-
// Build connection map: target -> source[]
|
|
2387
|
-
const incomingConnections = new Map();
|
|
2388
|
-
connections.forEach((conn) => {
|
|
2389
|
-
const targetId = conn.target.activity;
|
|
2390
|
-
if (!incomingConnections.has(targetId)) {
|
|
2391
|
-
incomingConnections.set(targetId, []);
|
|
2392
|
-
}
|
|
2393
|
-
incomingConnections.get(targetId).push(conn.source.activity);
|
|
2394
|
-
});
|
|
2395
|
-
// Find root activity (activity with no incoming connections, or first activity)
|
|
2396
|
-
let firstActivity = null;
|
|
2397
|
-
for (const activity of activities) {
|
|
2398
|
-
if (!incomingConnections.has(activity.id)) {
|
|
2399
|
-
firstActivity = activity;
|
|
2400
|
-
break;
|
|
2401
|
-
}
|
|
2402
|
-
}
|
|
2403
|
-
// If no root found, use first activity
|
|
2404
|
-
if (!firstActivity && activities.length > 0) {
|
|
2405
|
-
firstActivity = activities[0];
|
|
2406
|
-
}
|
|
2407
|
-
const firstStepId = firstActivity?.id || 'root';
|
|
2408
|
-
// Create workflow state
|
|
2409
|
-
const workflowState = {
|
|
2410
|
-
id: `state-${instanceId}`,
|
|
2411
|
-
definitionId: workflow.name, // Use name as definitionId (backward compatibility)
|
|
2412
|
-
//definitionVersion: workflow.version,
|
|
2413
|
-
definitionVersion: 1,
|
|
2414
|
-
correlationId: null,
|
|
2415
|
-
status: 'Running',
|
|
2416
|
-
subStatus: 'Executing',
|
|
2417
|
-
bookmarks: [],
|
|
2418
|
-
incidents: [],
|
|
2419
|
-
fault: null,
|
|
2420
|
-
completionCallbacks: [],
|
|
2421
|
-
activityExecutionContexts: firstActivity ? [{
|
|
2422
|
-
id: `ctx-${firstStepId}`,
|
|
2423
|
-
parentContextId: null,
|
|
2424
|
-
scheduledActivityNodeId: firstStepId,
|
|
2425
|
-
ownerActivityNodeId: null,
|
|
2426
|
-
properties: {},
|
|
2427
|
-
activityState: null,
|
|
2428
|
-
dynamicVariables: [],
|
|
2429
|
-
status: 'Running',
|
|
2430
|
-
isExecuting: false,
|
|
2431
|
-
faultCount: 0,
|
|
2432
|
-
startedAt: now,
|
|
2433
|
-
completedAt: null,
|
|
2434
|
-
tag: null
|
|
2435
|
-
}] : [],
|
|
2436
|
-
input: request.input || {},
|
|
2437
|
-
output: {},
|
|
2438
|
-
properties: {},
|
|
2439
|
-
createdAt: now,
|
|
2440
|
-
updatedAt: now,
|
|
2441
|
-
finishedAt: null
|
|
2442
|
-
};
|
|
2443
|
-
// Create workflow instance
|
|
2444
|
-
const instance = {
|
|
2445
|
-
$schema: 'https://elsaworkflows.io/schemas/workflow-instance/v3.0.0/schema.json',
|
|
2446
|
-
id: instanceId,
|
|
2447
|
-
definitionId: workflow.name, // Use name as definitionId (backward compatibility)
|
|
2448
|
-
//definitionVersionId: workflow.id,
|
|
2449
|
-
//version: workflow.version,
|
|
2450
|
-
version: 1,
|
|
2451
|
-
definitionVersionId: '1',
|
|
2452
|
-
parentWorkflowInstanceId: null,
|
|
2453
|
-
workflowState,
|
|
2454
|
-
status: 'Running',
|
|
2455
|
-
subStatus: 'Executing',
|
|
2456
|
-
correlationId: null,
|
|
2457
|
-
name: workflow.name || null,
|
|
2458
|
-
incidentCount: 0,
|
|
2459
|
-
createdAt: now,
|
|
2460
|
-
updatedAt: now,
|
|
2461
|
-
finishedAt: null
|
|
2462
|
-
};
|
|
2463
|
-
// Store instance in IndexedDB
|
|
2464
|
-
await this.db.instances.add(instance, instance.id);
|
|
2465
|
-
// 🎯 Also store in entity service for entity-based access
|
|
2466
|
-
await this.saveInstanceToEntityService(instance);
|
|
2467
|
-
// Convert to execution state for response
|
|
2468
|
-
let state = this.instanceToExecutionState(instance);
|
|
2469
|
-
// 🎯 KEY LOGIC: Execute all backend activities until we reach a frontend activity
|
|
2470
|
-
let currentActivity = firstActivity;
|
|
2471
|
-
let executionCount = 0;
|
|
2472
|
-
const maxExecutions = 100; // Prevent infinite loops
|
|
2473
|
-
while (currentActivity && executionCount < maxExecutions) {
|
|
2474
|
-
executionCount++;
|
|
2475
|
-
// Map activity type to ACoreX format
|
|
2476
|
-
let activityType = currentActivity.type;
|
|
2477
|
-
if (activityType.startsWith('Elsa.')) {
|
|
2478
|
-
let type = activityType.replace(/^Elsa\./, '');
|
|
2479
|
-
type = type.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
|
|
2480
|
-
activityType = `workflow-activity:${type}`;
|
|
2481
|
-
}
|
|
2482
|
-
// Determine execution mode
|
|
2483
|
-
const executionMode = this.determineExecutionMode(activityType, currentActivity);
|
|
2484
|
-
// If frontend activity → stop and return as pendingTask
|
|
2485
|
-
if (executionMode === 'frontend') {
|
|
2486
|
-
const pendingTask = this.createTaskFromActivity(currentActivity, state);
|
|
2487
|
-
// Update state to suspended (waiting for frontend)
|
|
2488
|
-
state = {
|
|
2489
|
-
...state,
|
|
2490
|
-
status: 'suspended',
|
|
2491
|
-
currentStepId: currentActivity.id,
|
|
2492
|
-
};
|
|
2493
|
-
await this.updateInstanceFromExecutionState(instanceId, state);
|
|
2494
|
-
return {
|
|
2495
|
-
executionId: instanceId,
|
|
2496
|
-
state,
|
|
2497
|
-
pendingTask
|
|
2498
|
-
};
|
|
2499
|
-
}
|
|
2500
|
-
// Backend activity → execute it
|
|
2501
|
-
console.log(`[WorkflowExecutionService] Executing backend activity '${currentActivity.name || currentActivity.id}' (${activityType})`);
|
|
2502
|
-
const executionResult = await this.executeBackendActivityLocally(currentActivity, { ...state.variables, ...state.input }, state);
|
|
2503
|
-
// Update variables with output
|
|
2504
|
-
state = {
|
|
2505
|
-
...state,
|
|
2506
|
-
variables: {
|
|
2507
|
-
...state.variables,
|
|
2508
|
-
...executionResult.output
|
|
2509
|
-
},
|
|
2510
|
-
currentStepId: currentActivity.id,
|
|
2511
|
-
lastUpdated: new Date()
|
|
2512
|
-
};
|
|
2513
|
-
// Find next activity based on outcome
|
|
2514
|
-
const outcome = Object.keys(executionResult.outcomes)[0] || 'Done';
|
|
2515
|
-
const nextConnection = connections.find((conn) => conn.source.activity === currentActivity.id &&
|
|
2516
|
-
(conn.source.port === outcome || (!conn.source.port && outcome === 'Done')));
|
|
2517
|
-
if (!nextConnection) {
|
|
2518
|
-
// No next activity → workflow completed
|
|
2519
|
-
state = {
|
|
2520
|
-
...state,
|
|
2521
|
-
status: 'completed',
|
|
2522
|
-
currentStepId: undefined,
|
|
2523
|
-
output: state.variables
|
|
2524
|
-
};
|
|
2525
|
-
await this.updateInstanceFromExecutionState(instanceId, state);
|
|
2526
|
-
return {
|
|
2527
|
-
executionId: instanceId,
|
|
2528
|
-
state,
|
|
2529
|
-
pendingTask: null
|
|
2530
|
-
};
|
|
2531
|
-
}
|
|
2532
|
-
// Find next activity
|
|
2533
|
-
currentActivity = this.findActivityInFlowchart(flowchart, nextConnection.target.activity);
|
|
2534
|
-
}
|
|
2535
|
-
// If we exit loop without frontend activity, workflow is complete
|
|
2536
|
-
state = {
|
|
2537
|
-
...state,
|
|
2538
|
-
status: 'completed',
|
|
2539
|
-
currentStepId: undefined,
|
|
2540
|
-
output: state.variables
|
|
2541
|
-
};
|
|
2542
|
-
await this.updateInstanceFromExecutionState(instanceId, state);
|
|
2543
|
-
return {
|
|
2544
|
-
executionId: instanceId,
|
|
2545
|
-
state,
|
|
2546
|
-
pendingTask: null
|
|
2547
|
-
};
|
|
2548
|
-
}
|
|
2549
|
-
/**
|
|
2550
|
-
* Find activity in Flowchart by ID.
|
|
2551
|
-
*/
|
|
2552
|
-
findActivityInFlowchart(flowchart, activityId) {
|
|
2553
|
-
const activities = flowchart.activities || [];
|
|
2554
|
-
return activities.find(a => a.id === activityId) || null;
|
|
2555
|
-
}
|
|
2556
|
-
/**
|
|
2557
|
-
* Resume a suspended workflow execution.
|
|
2558
|
-
*
|
|
2559
|
-
* After frontend completes its task:
|
|
2560
|
-
* 1. Validates taskToken
|
|
2561
|
-
* 2. Finds next activity based on outcome
|
|
2562
|
-
* 3. Executes ALL backend activities in sequence
|
|
2563
|
-
* 4. Stops at first frontend activity and returns it as pendingTask
|
|
2564
|
-
*/
|
|
2565
|
-
async resumeExecution(request) {
|
|
2566
|
-
// Get current state
|
|
2567
|
-
let state = await this.getExecutionState({
|
|
2568
|
-
executionId: request.executionId,
|
|
2569
|
-
});
|
|
2570
|
-
if (!state) {
|
|
2571
|
-
throw new Error(`Workflow execution not found: ${request.executionId}`);
|
|
2572
|
-
}
|
|
2573
|
-
if (state.status !== 'suspended') {
|
|
2574
|
-
throw new Error(`Cannot resume workflow with status: ${state.status}. Expected: suspended`);
|
|
2575
|
-
}
|
|
2576
|
-
// TODO: Validate taskToken here for security
|
|
2577
|
-
// For now, just verify stepId matches
|
|
2578
|
-
if (state.currentStepId !== request.stepId) {
|
|
2579
|
-
throw new Error(`Step mismatch: expected '${state.currentStepId}', got '${request.stepId}'`);
|
|
2580
|
-
}
|
|
2581
|
-
// Get workflow definition
|
|
2582
|
-
const workflowResponse = await this.getWorkflowDefinition({
|
|
2583
|
-
workflowId: state.workflowId
|
|
2584
|
-
});
|
|
2585
|
-
const workflow = workflowResponse.workflow;
|
|
2586
|
-
const connections = workflow.root.connections || [];
|
|
2587
|
-
// Update state with frontend activity outcome and user input
|
|
2588
|
-
state = {
|
|
2589
|
-
...state,
|
|
2590
|
-
variables: {
|
|
2591
|
-
...state.variables,
|
|
2592
|
-
[`${request.stepId}_outcome`]: request.outcome,
|
|
2593
|
-
[`${request.stepId}_userInput`]: request.userInput || {},
|
|
2594
|
-
},
|
|
2595
|
-
lastUpdated: new Date()
|
|
2596
|
-
};
|
|
2597
|
-
// Find next activity based on outcome from frontend
|
|
2598
|
-
const nextConnection = connections.find((conn) => conn.source.activity === request.stepId &&
|
|
2599
|
-
(conn.source.port === request.outcome || (!conn.source.port && request.outcome === 'Done')));
|
|
2600
|
-
console.log('[AXCWorkflowExecutionService] 🔍 Looking for next connection', {
|
|
2601
|
-
stepId: request.stepId,
|
|
2602
|
-
outcome: request.outcome,
|
|
2603
|
-
connectionsCount: connections.length,
|
|
2604
|
-
foundConnection: !!nextConnection
|
|
2605
|
-
});
|
|
2606
|
-
if (!nextConnection) {
|
|
2607
|
-
// No next activity → workflow completed
|
|
2608
|
-
console.log('[AXCWorkflowExecutionService] ✅ No next activity found - workflow completed');
|
|
2609
|
-
state = {
|
|
2610
|
-
...state,
|
|
2611
|
-
status: 'completed',
|
|
2612
|
-
currentStepId: undefined,
|
|
2613
|
-
output: state.variables
|
|
2614
|
-
};
|
|
2615
|
-
console.log('[AXCWorkflowExecutionService] 📝 Updating instance to completed state');
|
|
2616
|
-
await this.updateInstanceFromExecutionState(request.executionId, state);
|
|
2617
|
-
return {
|
|
2618
|
-
output: { outcome: request.outcome, userInput: request.userInput },
|
|
2619
|
-
outcomes: { [request.outcome]: true },
|
|
2620
|
-
nextTask: null,
|
|
2621
|
-
state
|
|
2622
|
-
};
|
|
2623
|
-
}
|
|
2624
|
-
// 🎯 KEY LOGIC: Execute all backend activities until we reach next frontend activity
|
|
2625
|
-
let currentActivity = this.findActivityInFlowchart(workflow.root, nextConnection.target.activity);
|
|
2626
|
-
let executionCount = 0;
|
|
2627
|
-
const maxExecutions = 100;
|
|
2628
|
-
while (currentActivity && executionCount < maxExecutions) {
|
|
2629
|
-
executionCount++;
|
|
2630
|
-
// Map activity type
|
|
2631
|
-
let activityType = currentActivity.type;
|
|
2632
|
-
if (activityType.startsWith('Elsa.')) {
|
|
2633
|
-
let type = activityType.replace(/^Elsa\./, '');
|
|
2634
|
-
type = type.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
|
|
2635
|
-
activityType = `workflow-activity:${type}`;
|
|
2636
|
-
}
|
|
2637
|
-
// Determine execution mode
|
|
2638
|
-
const executionMode = this.determineExecutionMode(activityType, currentActivity);
|
|
2639
|
-
// If frontend activity → stop and return as pendingTask
|
|
2640
|
-
if (executionMode === 'frontend') {
|
|
2641
|
-
const pendingTask = this.createTaskFromActivity(currentActivity, state);
|
|
2642
|
-
state = {
|
|
2643
|
-
...state,
|
|
2644
|
-
status: 'suspended',
|
|
2645
|
-
currentStepId: currentActivity.id,
|
|
2646
|
-
};
|
|
2647
|
-
await this.updateInstanceFromExecutionState(request.executionId, state);
|
|
2648
|
-
return {
|
|
2649
|
-
output: { outcome: request.outcome, userInput: request.userInput },
|
|
2650
|
-
outcomes: { [request.outcome]: true },
|
|
2651
|
-
nextTask: pendingTask,
|
|
2652
|
-
state
|
|
2653
|
-
};
|
|
2654
|
-
}
|
|
2655
|
-
// Backend activity → execute it
|
|
2656
|
-
console.log(`[WorkflowExecutionService] Executing backend activity '${currentActivity.name || currentActivity.id}' (${activityType})`);
|
|
2657
|
-
const executionResult = await this.executeBackendActivityLocally(currentActivity, { ...state.variables, ...state.input }, state);
|
|
2658
|
-
// Update variables with output
|
|
2659
|
-
state = {
|
|
2660
|
-
...state,
|
|
2661
|
-
variables: {
|
|
2662
|
-
...state.variables,
|
|
2663
|
-
...executionResult.output
|
|
2664
|
-
},
|
|
2665
|
-
currentStepId: currentActivity.id,
|
|
2666
|
-
lastUpdated: new Date()
|
|
2667
|
-
};
|
|
2668
|
-
// Find next activity based on outcome
|
|
2669
|
-
const outcome = Object.keys(executionResult.outcomes)[0] || 'Done';
|
|
2670
|
-
const nextConn = connections.find((conn) => conn.source.activity === currentActivity.id &&
|
|
2671
|
-
(conn.source.port === outcome || (!conn.source.port && outcome === 'Done')));
|
|
2672
|
-
if (!nextConn) {
|
|
2673
|
-
// No next activity → workflow completed
|
|
2674
|
-
state = {
|
|
2675
|
-
...state,
|
|
2676
|
-
status: 'completed',
|
|
2677
|
-
currentStepId: undefined,
|
|
2678
|
-
output: state.variables
|
|
2679
|
-
};
|
|
2680
|
-
await this.updateInstanceFromExecutionState(request.executionId, state);
|
|
2681
|
-
return {
|
|
2682
|
-
output: state.variables,
|
|
2683
|
-
outcomes: { Done: true },
|
|
2684
|
-
nextTask: null,
|
|
2685
|
-
state
|
|
2686
|
-
};
|
|
2687
|
-
}
|
|
2688
|
-
// Find next activity
|
|
2689
|
-
currentActivity = this.findActivityInFlowchart(workflow.root, nextConn.target.activity);
|
|
2690
|
-
}
|
|
2691
|
-
// If we exit loop, workflow is complete
|
|
2692
|
-
state = {
|
|
2693
|
-
...state,
|
|
2694
|
-
status: 'completed',
|
|
2695
|
-
currentStepId: undefined,
|
|
2696
|
-
output: state.variables
|
|
2697
|
-
};
|
|
2698
|
-
await this.updateInstanceFromExecutionState(request.executionId, state);
|
|
2699
|
-
return {
|
|
2700
|
-
output: state.variables,
|
|
2701
|
-
outcomes: { Done: true },
|
|
2702
|
-
nextTask: null,
|
|
2703
|
-
state
|
|
2704
|
-
};
|
|
2705
|
-
}
|
|
2706
|
-
/**
|
|
2707
|
-
* Get current workflow execution state.
|
|
2708
|
-
*/
|
|
2709
|
-
async getExecutionState(request) {
|
|
2710
|
-
// Get instance from IndexedDB
|
|
2711
|
-
const instance = await this.getInstance(request.executionId);
|
|
2712
|
-
if (!instance) {
|
|
2713
|
-
throw new Error(`Workflow execution not found: ${request.executionId}`);
|
|
2714
|
-
}
|
|
2715
|
-
// Convert instance to execution state
|
|
2716
|
-
return this.instanceToExecutionState(instance);
|
|
2717
|
-
}
|
|
2718
|
-
/**
|
|
2719
|
-
* Get workflow definition by ID.
|
|
2720
|
-
*
|
|
2721
|
-
* In mock implementation, this retrieves workflow definition from localStorage.
|
|
2722
|
-
* In production, this would make an HTTP call to backend API.
|
|
2723
|
-
*
|
|
2724
|
-
* @param request - Get workflow definition request
|
|
2725
|
-
* @returns Workflow definition
|
|
2726
|
-
*/
|
|
2727
|
-
async getWorkflowDefinition(request) {
|
|
2728
|
-
// In mock, try to get from localStorage first (for workflow-studio testing)
|
|
2729
|
-
const storedWorkflow = localStorage.getItem(`workflow:${request.workflowId}`);
|
|
2730
|
-
if (storedWorkflow) {
|
|
2731
|
-
try {
|
|
2732
|
-
const workflowDefinition = JSON.parse(storedWorkflow);
|
|
2733
|
-
console.log('[AXCWorkflowExecutionService] 📄 Parsed workflow definition', {
|
|
2734
|
-
hasRoot: !!workflowDefinition.root,
|
|
2735
|
-
rootType: workflowDefinition.root?.type
|
|
2736
|
-
});
|
|
2737
|
-
// Validate format - if root is already a Flowchart
|
|
2738
|
-
if (workflowDefinition.root && workflowDefinition.root.type === 'workflow-activity:flowchart') {
|
|
2739
|
-
return { workflow: workflowDefinition };
|
|
2740
|
-
}
|
|
2741
|
-
// If stored in materializerContext.stringData, parse it
|
|
2742
|
-
if (workflowDefinition.materializerContext?.stringData) {
|
|
2743
|
-
try {
|
|
2744
|
-
const rootFlowchart = JSON.parse(workflowDefinition.materializerContext.stringData);
|
|
2745
|
-
if (rootFlowchart.type === 'workflow-activity:flowchart') {
|
|
2746
|
-
return {
|
|
2747
|
-
workflow: {
|
|
2748
|
-
...workflowDefinition,
|
|
2749
|
-
root: rootFlowchart
|
|
2750
|
-
}
|
|
2751
|
-
};
|
|
2752
|
-
}
|
|
2753
|
-
}
|
|
2754
|
-
catch (error) {
|
|
2755
|
-
console.warn('[AXCWorkflowExecutionService] ⚠️ Failed to parse stringData from materializerContext:', error);
|
|
2756
|
-
}
|
|
2757
|
-
}
|
|
2758
|
-
// 🎯 NEW: If root is a single activity (not Flowchart), convert it to Flowchart
|
|
2759
|
-
if (workflowDefinition.root && workflowDefinition.root.type !== 'workflow-activity:flowchart') {
|
|
2760
|
-
console.log('[AXCWorkflowExecutionService] 🔄 Converting single activity to Flowchart', workflowDefinition.root);
|
|
2761
|
-
const singleActivity = workflowDefinition.root;
|
|
2762
|
-
// Create a Flowchart with this single activity
|
|
2763
|
-
const flowchart = {
|
|
2764
|
-
id: 'root-flowchart',
|
|
2765
|
-
nodeId: 'root-flowchart',
|
|
2766
|
-
type: 'workflow-activity:flowchart',
|
|
2767
|
-
version: 1,
|
|
2768
|
-
activities: [singleActivity],
|
|
2769
|
-
connections: [],
|
|
2770
|
-
variables: []
|
|
2771
|
-
};
|
|
2772
|
-
console.log('[AXCWorkflowExecutionService] ✅ Created Flowchart from single activity', {
|
|
2773
|
-
activitiesCount: flowchart.activities.length,
|
|
2774
|
-
activityType: singleActivity.type
|
|
2775
|
-
});
|
|
2776
|
-
return {
|
|
2777
|
-
workflow: {
|
|
2778
|
-
...workflowDefinition,
|
|
2779
|
-
root: flowchart
|
|
2780
|
-
}
|
|
2781
|
-
};
|
|
2782
|
-
}
|
|
2783
|
-
}
|
|
2784
|
-
catch (error) {
|
|
2785
|
-
console.warn(`[AXCWorkflowExecutionService] ⚠️ Failed to parse stored workflow ${request.workflowId}:`, error);
|
|
2786
|
-
}
|
|
2787
|
-
}
|
|
2788
|
-
// If not found in localStorage, return a default mock workflow
|
|
2789
|
-
// In production, this would be an HTTP call to backend
|
|
2790
|
-
const now = new Date().toISOString();
|
|
2791
|
-
const defaultWorkflow = {
|
|
2792
|
-
name: request.workflowId, // workflowId is the name (unique key)
|
|
2793
|
-
title: `Workflow ${request.workflowId}`, // Display title
|
|
2794
|
-
description: 'Default mock workflow',
|
|
2795
|
-
variables: [],
|
|
2796
|
-
inputs: [],
|
|
2797
|
-
outputs: [],
|
|
2798
|
-
outcomes: ['Done'],
|
|
2799
|
-
customProperties: {},
|
|
2800
|
-
options: {
|
|
2801
|
-
usableAsActivity: false,
|
|
2802
|
-
autoUpdateConsumingWorkflows: false
|
|
2803
|
-
},
|
|
2804
|
-
root: {
|
|
2805
|
-
id: 'root-flowchart',
|
|
2806
|
-
nodeId: 'root-flowchart',
|
|
2807
|
-
type: 'workflow-activity:flowchart',
|
|
2808
|
-
version: 1,
|
|
2809
|
-
activities: [],
|
|
2810
|
-
connections: [],
|
|
2811
|
-
variables: []
|
|
2812
|
-
}
|
|
2813
|
-
};
|
|
2814
|
-
return { workflow: defaultWorkflow };
|
|
2815
|
-
}
|
|
2816
|
-
//#region ---- Task-Based Helper Methods ----
|
|
2817
|
-
/**
|
|
2818
|
-
* Generate unique task token for task-based execution.
|
|
2819
|
-
* Inspired by Temporal's task tokens for secure task completion.
|
|
2820
|
-
*/
|
|
2821
|
-
generateTaskToken(executionId, activityId) {
|
|
2822
|
-
return `${executionId}:${activityId}:${Date.now()}`;
|
|
2823
|
-
}
|
|
2824
|
-
/**
|
|
2825
|
-
* Determine execution mode for an activity.
|
|
2826
|
-
*
|
|
2827
|
-
* Priority (highest to lowest):
|
|
2828
|
-
* 1. Activity instance's executionMode (from AXPActivity in workflow definition)
|
|
2829
|
-
* 2. ActivityDescriptor's executionMode (from ActivityProviderService)
|
|
2830
|
-
* 3. Default: 'frontend'
|
|
2831
|
-
*
|
|
2832
|
-
* @param activityType - Activity type (e.g., 'workflow-activity:write-line')
|
|
2833
|
-
* @param activity - Activity from workflow definition (AXPActivity)
|
|
2834
|
-
* @returns Execution mode: 'frontend', 'backend', or 'both'
|
|
2835
|
-
*/
|
|
2836
|
-
determineExecutionMode(activityType, activity) {
|
|
2837
|
-
// 1. Check if executionMode is explicitly set in activity instance (highest priority)
|
|
2838
|
-
if (activity?.executionMode) {
|
|
2839
|
-
return activity.executionMode;
|
|
2840
|
-
}
|
|
2841
|
-
// 2. Check ActivityDescriptor from ActivityProviderService
|
|
2842
|
-
// const descriptor = this.activityProviderService.getMetadata(activityType);
|
|
2843
|
-
// if (descriptor?.executionMode) {
|
|
2844
|
-
// return descriptor.executionMode;
|
|
2845
|
-
// }
|
|
2846
|
-
// 3. Default to frontend (UI activities are more common)
|
|
2847
|
-
return 'frontend';
|
|
2848
|
-
}
|
|
2849
|
-
/**
|
|
2850
|
-
* Create a task from a workflow activity.
|
|
2851
|
-
* Inspired by Temporal's Activity Task and Elsa's Bookmark pattern.
|
|
2852
|
-
*/
|
|
2853
|
-
createTaskFromActivity(activity, state) {
|
|
2854
|
-
// Extract properties from activity
|
|
2855
|
-
// Activities have properties as AXPInputValue objects or primitives
|
|
2856
|
-
const properties = {};
|
|
2857
|
-
// Iterate through all properties of the activity
|
|
2858
|
-
Object.keys(activity).forEach(key => {
|
|
2859
|
-
// Skip standard Elsa fields
|
|
2860
|
-
if (['id', 'nodeId', 'name', 'type', 'version', 'customProperties', 'metadata', 'executionMode'].includes(key)) {
|
|
2861
|
-
return;
|
|
2862
|
-
}
|
|
2863
|
-
const value = activity[key];
|
|
2864
|
-
// 🎯 SPECIAL HANDLING: If key is 'properties' and it's an object, flatten it
|
|
2865
|
-
// This handles workflow-studio format where properties are nested
|
|
2866
|
-
if (key === 'properties' && value && typeof value === 'object' && !('typeName' in value) && !('expression' in value)) {
|
|
2867
|
-
// Flatten properties object
|
|
2868
|
-
Object.keys(value).forEach(propKey => {
|
|
2869
|
-
const propValue = value[propKey];
|
|
2870
|
-
// Handle AXPInputValue format within properties
|
|
2871
|
-
if (propValue && typeof propValue === 'object' && 'typeName' in propValue && 'expression' in propValue) {
|
|
2872
|
-
if (propValue.expression && propValue.expression.value !== undefined) {
|
|
2873
|
-
properties[propKey] = propValue.expression.value;
|
|
2874
|
-
}
|
|
2875
|
-
else {
|
|
2876
|
-
properties[propKey] = null;
|
|
2877
|
-
}
|
|
2878
|
-
}
|
|
2879
|
-
else {
|
|
2880
|
-
// Direct value
|
|
2881
|
-
properties[propKey] = propValue;
|
|
2882
|
-
}
|
|
2883
|
-
});
|
|
2884
|
-
return;
|
|
2885
|
-
}
|
|
2886
|
-
// Handle AXPInputValue format
|
|
2887
|
-
if (value && typeof value === 'object' && 'typeName' in value && 'expression' in value) {
|
|
2888
|
-
// It's an AXPInputValue - extract the expression value
|
|
2889
|
-
if (value.expression && value.expression.value !== undefined) {
|
|
2890
|
-
properties[key] = value.expression.value;
|
|
2891
|
-
}
|
|
2892
|
-
else {
|
|
2893
|
-
properties[key] = null;
|
|
2894
|
-
}
|
|
2895
|
-
}
|
|
2896
|
-
else {
|
|
2897
|
-
// Direct value
|
|
2898
|
-
properties[key] = value;
|
|
2899
|
-
}
|
|
2900
|
-
});
|
|
2901
|
-
console.log('[AXCWorkflowExecutionService] 📦 Extracted properties for task', properties);
|
|
2902
|
-
// Map activity type to ACoreX activity type
|
|
2903
|
-
// Example: "Elsa.WriteLine" -> "workflow-activity:write-line"
|
|
2904
|
-
let activityType = activity.type;
|
|
2905
|
-
if (activityType.startsWith('Elsa.')) {
|
|
2906
|
-
let type = activityType.replace(/^Elsa\./, '');
|
|
2907
|
-
// Convert PascalCase to kebab-case
|
|
2908
|
-
type = type.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
|
|
2909
|
-
activityType = `workflow-activity:${type}`;
|
|
2910
|
-
}
|
|
2911
|
-
// Get executionMode from activity descriptor
|
|
2912
|
-
const executionMode = this.determineExecutionMode(activityType, activity);
|
|
2913
|
-
return {
|
|
2914
|
-
taskToken: this.generateTaskToken(state.executionId, activity.id),
|
|
2915
|
-
activityId: activity.id,
|
|
2916
|
-
activityType,
|
|
2917
|
-
activityName: activity.name || undefined,
|
|
2918
|
-
executionMode,
|
|
2919
|
-
input: (executionMode === 'frontend' || executionMode === 'both') ? properties : undefined,
|
|
2920
|
-
config: (executionMode === 'frontend' || executionMode === 'both') ? properties : undefined
|
|
2921
|
-
};
|
|
2922
|
-
}
|
|
2923
|
-
/**
|
|
2924
|
-
* Save workflow instance to entity service for entity-based access.
|
|
2925
|
-
*/
|
|
2926
|
-
async saveInstanceToEntityService(instance) {
|
|
2927
|
-
try {
|
|
2928
|
-
const entityModel = {
|
|
2929
|
-
id: instance.id,
|
|
2930
|
-
definitionId: instance.definitionId,
|
|
2931
|
-
definitionVersionId: instance.definitionVersionId,
|
|
2932
|
-
version: instance.version,
|
|
2933
|
-
status: instance.status,
|
|
2934
|
-
subStatus: instance.subStatus,
|
|
2935
|
-
correlationId: instance.correlationId || undefined,
|
|
2936
|
-
name: instance.name || undefined,
|
|
2937
|
-
incidentCount: instance.incidentCount,
|
|
2938
|
-
parentWorkflowInstanceId: instance.parentWorkflowInstanceId || undefined,
|
|
2939
|
-
createdAt: instance.createdAt,
|
|
2940
|
-
updatedAt: instance.updatedAt,
|
|
2941
|
-
finishedAt: instance.finishedAt || undefined,
|
|
2942
|
-
workflowInstance: instance
|
|
2943
|
-
};
|
|
2944
|
-
const entityName = 'WorkflowManagement.WorkflowInstance';
|
|
2945
|
-
// Check if instance already exists
|
|
2946
|
-
try {
|
|
2947
|
-
const existing = await this.entityStorageService.getOne(entityName, entityModel.id);
|
|
2948
|
-
if (existing) {
|
|
2949
|
-
// Already exists - don't insert again, will be updated later
|
|
2950
|
-
console.log('[AXCWorkflowExecutionService] ℹ️ Instance already exists in entity service, will be updated later', {
|
|
2951
|
-
instanceId: instance.id
|
|
2952
|
-
});
|
|
2953
|
-
return;
|
|
2954
|
-
}
|
|
2955
|
-
}
|
|
2956
|
-
catch (getError) {
|
|
2957
|
-
// Instance doesn't exist - proceed with insert
|
|
2958
|
-
}
|
|
2959
|
-
// Insert new instance
|
|
2960
|
-
await this.entityStorageService.insertOne(entityName, entityModel);
|
|
2961
|
-
console.log('[AXCWorkflowExecutionService] ✅ Inserted new instance in entity service', {
|
|
2962
|
-
instanceId: instance.id,
|
|
2963
|
-
status: instance.status,
|
|
2964
|
-
subStatus: instance.subStatus
|
|
2965
|
-
});
|
|
2966
|
-
}
|
|
2967
|
-
catch (error) {
|
|
2968
|
-
console.warn('[AXCWorkflowExecutionService] Failed to save instance to entity service:', error);
|
|
2969
|
-
// Don't throw - IndexedDB is primary storage
|
|
2970
|
-
}
|
|
2971
|
-
}
|
|
2972
|
-
/**
|
|
2973
|
-
* Update workflow instance in entity service.
|
|
2974
|
-
* Uses upsert pattern: try update first, if fails then insert.
|
|
2975
|
-
*/
|
|
2976
|
-
async updateInstanceInEntityService(instance) {
|
|
2977
|
-
try {
|
|
2978
|
-
const entityModel = {
|
|
2979
|
-
id: instance.id,
|
|
2980
|
-
definitionId: instance.definitionId,
|
|
2981
|
-
definitionVersionId: instance.definitionVersionId,
|
|
2982
|
-
version: instance.version,
|
|
2983
|
-
status: instance.status,
|
|
2984
|
-
subStatus: instance.subStatus,
|
|
2985
|
-
correlationId: instance.correlationId || undefined,
|
|
2986
|
-
name: instance.name || undefined,
|
|
2987
|
-
incidentCount: instance.incidentCount,
|
|
2988
|
-
parentWorkflowInstanceId: instance.parentWorkflowInstanceId || undefined,
|
|
2989
|
-
createdAt: instance.createdAt,
|
|
2990
|
-
updatedAt: instance.updatedAt,
|
|
2991
|
-
finishedAt: instance.finishedAt || undefined,
|
|
2992
|
-
workflowInstance: instance
|
|
2993
|
-
};
|
|
2994
|
-
const entityName = 'WorkflowManagement.WorkflowInstance';
|
|
2995
|
-
// Try update first (upsert pattern)
|
|
2996
|
-
try {
|
|
2997
|
-
await this.entityStorageService.updateOne(entityName, entityModel.id, entityModel);
|
|
2998
|
-
console.log('[AXCWorkflowExecutionService] ✅ Updated instance in entity service', {
|
|
2999
|
-
instanceId: instance.id,
|
|
3000
|
-
status: instance.status,
|
|
3001
|
-
subStatus: instance.subStatus
|
|
3002
|
-
});
|
|
3003
|
-
return; // Successfully updated, exit
|
|
3004
|
-
}
|
|
3005
|
-
catch (updateError) {
|
|
3006
|
-
// Update failed - instance doesn't exist, try insert
|
|
3007
|
-
console.log('[AXCWorkflowExecutionService] ℹ️ Update failed, trying insert', {
|
|
3008
|
-
instanceId: instance.id,
|
|
3009
|
-
error: updateError.message
|
|
3010
|
-
});
|
|
3011
|
-
}
|
|
3012
|
-
// Try insert
|
|
3013
|
-
try {
|
|
3014
|
-
await this.entityStorageService.insertOne(entityName, entityModel);
|
|
3015
|
-
console.log('[AXCWorkflowExecutionService] ✅ Inserted instance in entity service', {
|
|
3016
|
-
instanceId: instance.id,
|
|
3017
|
-
status: instance.status,
|
|
3018
|
-
subStatus: instance.subStatus
|
|
3019
|
-
});
|
|
3020
|
-
}
|
|
3021
|
-
catch (insertError) {
|
|
3022
|
-
// Insert might fail if instance was created between update and insert
|
|
3023
|
-
// Try update again as fallback
|
|
3024
|
-
try {
|
|
3025
|
-
await this.entityStorageService.updateOne(entityName, entityModel.id, entityModel);
|
|
3026
|
-
console.log('[AXCWorkflowExecutionService] ✅ Updated instance in entity service (after insert failed)', {
|
|
3027
|
-
instanceId: instance.id,
|
|
3028
|
-
status: instance.status,
|
|
3029
|
-
subStatus: instance.subStatus
|
|
3030
|
-
});
|
|
3031
|
-
}
|
|
3032
|
-
catch (finalError) {
|
|
3033
|
-
console.warn('[AXCWorkflowExecutionService] Failed to upsert instance in entity service:', finalError);
|
|
3034
|
-
}
|
|
3035
|
-
}
|
|
3036
|
-
}
|
|
3037
|
-
catch (error) {
|
|
3038
|
-
console.warn('[AXCWorkflowExecutionService] Failed to update instance in entity service:', error);
|
|
3039
|
-
// Don't throw - IndexedDB is primary storage
|
|
3040
|
-
}
|
|
3041
|
-
}
|
|
3042
|
-
//#endregion
|
|
3043
|
-
//#region ---- Backend Activity Executors ----
|
|
3044
|
-
/**
|
|
3045
|
-
* Execute backend activities locally using CommandBus.
|
|
3046
|
-
*
|
|
3047
|
-
* Backend activities are registered as Commands and executed via CommandService.
|
|
3048
|
-
* This provides a unified execution channel for all activities.
|
|
3049
|
-
*/
|
|
3050
|
-
async executeBackendActivityLocally(activity, input, state) {
|
|
3051
|
-
console.log('[AXCWorkflowExecutionService] 🔧 executeBackendActivityLocally', {
|
|
3052
|
-
activityId: activity.id,
|
|
3053
|
-
activityType: activity.type,
|
|
3054
|
-
input
|
|
3055
|
-
});
|
|
3056
|
-
// Extract properties from activity
|
|
3057
|
-
const properties = {};
|
|
3058
|
-
Object.keys(activity).forEach(key => {
|
|
3059
|
-
if (['id', 'nodeId', 'name', 'type', 'version', 'customProperties', 'metadata', 'executionMode'].includes(key)) {
|
|
3060
|
-
return;
|
|
3061
|
-
}
|
|
3062
|
-
const value = activity[key];
|
|
3063
|
-
if (value && typeof value === 'object' && 'typeName' in value && 'expression' in value) {
|
|
3064
|
-
if (value.expression && value.expression.value !== undefined) {
|
|
3065
|
-
properties[key] = value.expression.value;
|
|
3066
|
-
}
|
|
3067
|
-
else {
|
|
3068
|
-
properties[key] = null;
|
|
3069
|
-
}
|
|
3070
|
-
}
|
|
3071
|
-
else {
|
|
3072
|
-
properties[key] = value;
|
|
3073
|
-
}
|
|
3074
|
-
});
|
|
3075
|
-
console.log('[AXCWorkflowExecutionService] 📦 Extracted properties', properties);
|
|
3076
|
-
// Map activity type to ACoreX activity type
|
|
3077
|
-
let activityType = activity.type;
|
|
3078
|
-
if (activityType.startsWith('Elsa.')) {
|
|
3079
|
-
let type = activityType.replace(/^Elsa\./, '');
|
|
3080
|
-
type = type.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
|
|
3081
|
-
activityType = `workflow-activity:${type}`;
|
|
3082
|
-
}
|
|
3083
|
-
console.log('[AXCWorkflowExecutionService] 🔀 Activity type mapped', {
|
|
3084
|
-
original: activity.type,
|
|
3085
|
-
mapped: activityType
|
|
3086
|
-
});
|
|
3087
|
-
// Check if activity is registered as a Command
|
|
3088
|
-
const commandExists = this.commandService.exists(activityType);
|
|
3089
|
-
console.log('[AXCWorkflowExecutionService] 🔍 Command exists?', commandExists, activityType);
|
|
3090
|
-
if (commandExists) {
|
|
3091
|
-
console.log(`[AXCWorkflowExecutionService] ⚡ Executing '${activityType}' via CommandBus`);
|
|
3092
|
-
try {
|
|
3093
|
-
// Execute via CommandBus
|
|
3094
|
-
const result = await this.commandService.execute(activityType, properties);
|
|
3095
|
-
const finalResult = {
|
|
3096
|
-
output: result?.data?.output || result,
|
|
3097
|
-
outcomes: result?.data?.outcomes || { Done: true }
|
|
3098
|
-
};
|
|
3099
|
-
console.log('[AXCWorkflowExecutionService] ✅ Final result', finalResult);
|
|
3100
|
-
return finalResult;
|
|
3101
|
-
}
|
|
3102
|
-
catch (error) {
|
|
3103
|
-
console.error(`[AXCWorkflowExecutionService] ❌ Backend activity '${activityType}' failed:`, error);
|
|
3104
|
-
return {
|
|
3105
|
-
output: { error: error.message || 'Unknown error' },
|
|
3106
|
-
outcomes: { Failed: true }
|
|
3107
|
-
};
|
|
3108
|
-
}
|
|
3109
|
-
}
|
|
3110
|
-
// Fallback: Handle built-in activities that aren't Commands yet
|
|
3111
|
-
console.warn(`[WorkflowExecutionService] Activity '${activityType}' not registered as Command, using fallback`);
|
|
3112
|
-
switch (activityType) {
|
|
3113
|
-
case 'workflow-activity:set-variable':
|
|
3114
|
-
return this.executeSetVariable({ type: activityType, properties }, input);
|
|
3115
|
-
case 'workflow-activity:http-request':
|
|
3116
|
-
return this.executeHttpRequest({ type: activityType, properties }, input);
|
|
3117
|
-
default:
|
|
3118
|
-
return { output: null, outcomes: { Done: true } };
|
|
3119
|
-
}
|
|
3120
|
-
}
|
|
3121
|
-
/**
|
|
3122
|
-
* Set variable activity - stores value in workflow state.
|
|
3123
|
-
*/
|
|
3124
|
-
async executeSetVariable(activity, input) {
|
|
3125
|
-
const variableName = activity.properties?.variableName;
|
|
3126
|
-
const value = activity.properties?.value;
|
|
3127
|
-
return {
|
|
3128
|
-
output: { [variableName]: value },
|
|
3129
|
-
outcomes: { Done: true }
|
|
3130
|
-
};
|
|
3131
|
-
}
|
|
3132
|
-
/**
|
|
3133
|
-
* Execute HTTP request - makes real HTTP call.
|
|
3134
|
-
* Supports different HTTP methods and handles timeouts.
|
|
3135
|
-
*/
|
|
3136
|
-
async executeHttpRequest(activity, input) {
|
|
3137
|
-
const url = activity.properties?.url;
|
|
3138
|
-
const method = activity.properties?.method || 'GET';
|
|
3139
|
-
const timeout = activity.properties?.timeout || 30000;
|
|
3140
|
-
const headers = activity.properties?.headers || {};
|
|
3141
|
-
const body = activity.properties?.body;
|
|
3142
|
-
try {
|
|
3143
|
-
const controller = new AbortController();
|
|
3144
|
-
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
3145
|
-
const fetchOptions = {
|
|
3146
|
-
method,
|
|
3147
|
-
signal: controller.signal,
|
|
3148
|
-
headers: {
|
|
3149
|
-
'Content-Type': 'application/json',
|
|
3150
|
-
...headers
|
|
3151
|
-
}
|
|
3152
|
-
};
|
|
3153
|
-
if (body && method !== 'GET' && method !== 'HEAD') {
|
|
3154
|
-
fetchOptions.body = typeof body === 'string' ? body : JSON.stringify(body);
|
|
3155
|
-
}
|
|
3156
|
-
const response = await fetch(url, fetchOptions);
|
|
3157
|
-
clearTimeout(timeoutId);
|
|
3158
|
-
const statusCode = response.status.toString();
|
|
3159
|
-
let data;
|
|
3160
|
-
try {
|
|
3161
|
-
data = await response.json();
|
|
3162
|
-
}
|
|
3163
|
-
catch {
|
|
3164
|
-
data = await response.text();
|
|
3165
|
-
}
|
|
3166
|
-
// Convert headers to object
|
|
3167
|
-
const responseHeaders = {};
|
|
3168
|
-
response.headers.forEach((value, key) => {
|
|
3169
|
-
responseHeaders[key] = value;
|
|
3170
|
-
});
|
|
3171
|
-
return {
|
|
3172
|
-
output: {
|
|
3173
|
-
statusCode,
|
|
3174
|
-
data,
|
|
3175
|
-
status: response.status,
|
|
3176
|
-
headers: responseHeaders
|
|
3177
|
-
},
|
|
3178
|
-
outcomes: {
|
|
3179
|
-
[statusCode]: true,
|
|
3180
|
-
Done: true
|
|
3181
|
-
}
|
|
3182
|
-
};
|
|
3183
|
-
}
|
|
3184
|
-
catch (error) {
|
|
3185
|
-
if (error.name === 'AbortError') {
|
|
3186
|
-
return {
|
|
3187
|
-
output: { error: 'Request timeout' },
|
|
3188
|
-
outcomes: { Timeout: true }
|
|
3189
|
-
};
|
|
3190
|
-
}
|
|
3191
|
-
return {
|
|
3192
|
-
output: { error: error.message },
|
|
3193
|
-
outcomes: { Failed: true }
|
|
3194
|
-
};
|
|
3195
|
-
}
|
|
3196
|
-
}
|
|
3197
|
-
//#endregion
|
|
3198
|
-
//#region ---- Instance Management ----
|
|
3199
|
-
/**
|
|
3200
|
-
* Convert workflow instance to execution state (for backward compatibility).
|
|
3201
|
-
*/
|
|
3202
|
-
instanceToExecutionState(instance) {
|
|
3203
|
-
const state = instance.workflowState;
|
|
3204
|
-
// Find current activity from execution contexts
|
|
3205
|
-
const currentContext = state.activityExecutionContexts.find(ctx => ctx.status === 'Running' && !ctx.completedAt);
|
|
3206
|
-
// Map status
|
|
3207
|
-
let status = 'running';
|
|
3208
|
-
if (instance.status === 'Finished') {
|
|
3209
|
-
if (instance.subStatus === 'Faulted') {
|
|
3210
|
-
status = 'error';
|
|
3211
|
-
}
|
|
3212
|
-
else {
|
|
3213
|
-
status = 'completed';
|
|
3214
|
-
}
|
|
3215
|
-
}
|
|
3216
|
-
else if (instance.subStatus === 'Suspended') {
|
|
3217
|
-
status = 'suspended';
|
|
3218
|
-
}
|
|
3219
|
-
return {
|
|
3220
|
-
executionId: instance.id,
|
|
3221
|
-
workflowId: instance.definitionId,
|
|
3222
|
-
status,
|
|
3223
|
-
currentStepId: currentContext?.scheduledActivityNodeId,
|
|
3224
|
-
variables: state.properties || {},
|
|
3225
|
-
input: state.input || {},
|
|
3226
|
-
output: state.output || {},
|
|
3227
|
-
lastUpdated: new Date(instance.updatedAt)
|
|
3228
|
-
};
|
|
3229
|
-
}
|
|
3230
|
-
/**
|
|
3231
|
-
* Update execution state in instance and save to IndexedDB.
|
|
3232
|
-
*/
|
|
3233
|
-
async updateInstanceFromExecutionState(executionId, executionState) {
|
|
3234
|
-
const instance = await this.getInstance(executionId);
|
|
3235
|
-
if (!instance) {
|
|
3236
|
-
throw new Error(`Workflow instance not found: ${executionId}`);
|
|
3237
|
-
}
|
|
3238
|
-
// Update instance from execution state
|
|
3239
|
-
const now = new Date().toISOString();
|
|
3240
|
-
// Map execution state status to instance status
|
|
3241
|
-
let status = 'Running';
|
|
3242
|
-
let subStatus = 'Executing';
|
|
3243
|
-
console.log('[AXCWorkflowExecutionService] 🔄 updateInstanceFromExecutionState', {
|
|
3244
|
-
executionStateStatus: executionState.status,
|
|
3245
|
-
instanceId: instance.id,
|
|
3246
|
-
currentInstanceStatus: instance.status
|
|
3247
|
-
});
|
|
3248
|
-
if (executionState.status === 'completed') {
|
|
3249
|
-
console.log('[AXCWorkflowExecutionService] ✅ Marking workflow as Finished');
|
|
3250
|
-
status = 'Finished';
|
|
3251
|
-
subStatus = 'Finished';
|
|
3252
|
-
instance.finishedAt = now;
|
|
3253
|
-
}
|
|
3254
|
-
else if (executionState.status === 'error') {
|
|
3255
|
-
status = 'Finished';
|
|
3256
|
-
subStatus = 'Faulted';
|
|
3257
|
-
instance.finishedAt = now;
|
|
3258
|
-
}
|
|
3259
|
-
else if (executionState.status === 'suspended') {
|
|
3260
|
-
subStatus = 'Suspended';
|
|
3261
|
-
}
|
|
3262
|
-
instance.status = status;
|
|
3263
|
-
instance.subStatus = subStatus;
|
|
3264
|
-
instance.updatedAt = now;
|
|
3265
|
-
// Update workflow state
|
|
3266
|
-
instance.workflowState.status = status;
|
|
3267
|
-
instance.workflowState.subStatus = subStatus;
|
|
3268
|
-
instance.workflowState.input = executionState.input || {};
|
|
3269
|
-
instance.workflowState.output = executionState.output || {};
|
|
3270
|
-
instance.workflowState.properties = executionState.variables || {};
|
|
3271
|
-
instance.workflowState.updatedAt = now;
|
|
3272
|
-
if (executionState.status === 'completed' || executionState.status === 'error') {
|
|
3273
|
-
instance.workflowState.finishedAt = now;
|
|
3274
|
-
}
|
|
3275
|
-
// Update activity execution context if currentStepId changed
|
|
3276
|
-
if (executionState.currentStepId) {
|
|
3277
|
-
const existingContext = instance.workflowState.activityExecutionContexts.find(ctx => ctx.scheduledActivityNodeId === executionState.currentStepId);
|
|
3278
|
-
if (!existingContext) {
|
|
3279
|
-
// Add new context
|
|
3280
|
-
instance.workflowState.activityExecutionContexts.push({
|
|
3281
|
-
id: `ctx-${executionState.currentStepId}`,
|
|
3282
|
-
parentContextId: null,
|
|
3283
|
-
scheduledActivityNodeId: executionState.currentStepId,
|
|
3284
|
-
ownerActivityNodeId: null,
|
|
3285
|
-
properties: {},
|
|
3286
|
-
activityState: null,
|
|
3287
|
-
dynamicVariables: [],
|
|
3288
|
-
status: 'Running',
|
|
3289
|
-
isExecuting: false,
|
|
3290
|
-
faultCount: 0,
|
|
3291
|
-
startedAt: now,
|
|
3292
|
-
completedAt: null,
|
|
3293
|
-
tag: null
|
|
3294
|
-
});
|
|
3295
|
-
}
|
|
3296
|
-
}
|
|
3297
|
-
// Save to IndexedDB
|
|
3298
|
-
await this.updateInstance(instance);
|
|
3299
|
-
// 🎯 Also update in entity service
|
|
3300
|
-
await this.updateInstanceInEntityService(instance);
|
|
3301
|
-
return instance;
|
|
3302
|
-
}
|
|
3303
|
-
/**
|
|
3304
|
-
* Get workflow instance from IndexedDB.
|
|
3305
|
-
*/
|
|
3306
|
-
async getInstance(executionId) {
|
|
3307
|
-
return await this.db.instances.get(executionId) || null;
|
|
3308
|
-
}
|
|
3309
|
-
/**
|
|
3310
|
-
* Update workflow instance in IndexedDB.
|
|
3311
|
-
*/
|
|
3312
|
-
async updateInstance(instance) {
|
|
3313
|
-
instance.updatedAt = new Date().toISOString();
|
|
3314
|
-
await this.db.instances.update(instance.id, instance);
|
|
3315
|
-
}
|
|
3316
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowExecutionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3317
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowExecutionService }); }
|
|
3318
|
-
}
|
|
3319
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowExecutionService, decorators: [{
|
|
3320
|
-
type: Injectable
|
|
3321
|
-
}] });
|
|
3322
|
-
|
|
3323
2402
|
/**
|
|
3324
2403
|
* Mock workflow definitions for testing and demonstration.
|
|
3325
2404
|
*/
|
|
@@ -3812,259 +2891,1084 @@ const workflowDefinitionMock = [
|
|
|
3812
2891
|
},
|
|
3813
2892
|
];
|
|
3814
2893
|
/**
|
|
3815
|
-
* Export definitionId list for easy access
|
|
2894
|
+
* Export definitionId list for easy access
|
|
2895
|
+
*/
|
|
2896
|
+
const workflowDefinitionIds = {
|
|
2897
|
+
createUser: 'create-user-workflow',
|
|
2898
|
+
welcome: 'welcome-workflow',
|
|
2899
|
+
dataProcessing: 'data-processing-workflow',
|
|
2900
|
+
};
|
|
2901
|
+
|
|
2902
|
+
/**
|
|
2903
|
+
* Seeds workflow definitions into localStorage for mock/demo purposes.
|
|
2904
|
+
*
|
|
2905
|
+
* Workflow definitions are stored in localStorage with key pattern:
|
|
2906
|
+
* `workflow:{definitionId}`
|
|
2907
|
+
*
|
|
2908
|
+
* This allows the mock workflow execution service to retrieve definitions
|
|
2909
|
+
* without making HTTP calls.
|
|
2910
|
+
*/
|
|
2911
|
+
let AXCWorkflowDefinitionDataSeeder$1 = class AXCWorkflowDefinitionDataSeeder {
|
|
2912
|
+
constructor() {
|
|
2913
|
+
this.storageService = inject(AXPEntityStorageService);
|
|
2914
|
+
}
|
|
2915
|
+
async seed() {
|
|
2916
|
+
// Store each workflow definition in localStorage
|
|
2917
|
+
// for (const workflow of workflowDefinitionMock) {
|
|
2918
|
+
// localStorage.setItem(`workflow:${workflow.definitionId}`, JSON.stringify(workflow));
|
|
2919
|
+
// console.log(`[WorkflowDefinitionSeeder] Seeded workflow: ${workflow.name} (${workflow.definitionId})`);
|
|
2920
|
+
// }
|
|
2921
|
+
// Also store in entity storage for entity-based access
|
|
2922
|
+
await this.storageService.initial('Workflow.WorkflowDefinition', workflowDefinitionMock);
|
|
2923
|
+
}
|
|
2924
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowDefinitionDataSeeder, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2925
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowDefinitionDataSeeder }); }
|
|
2926
|
+
};
|
|
2927
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowDefinitionDataSeeder$1, decorators: [{
|
|
2928
|
+
type: Injectable
|
|
2929
|
+
}] });
|
|
2930
|
+
|
|
2931
|
+
class AXCCommonMockModule {
|
|
2932
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCCommonMockModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
2933
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.12", ngImport: i0, type: AXCCommonMockModule }); }
|
|
2934
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCCommonMockModule, providers: [
|
|
2935
|
+
{
|
|
2936
|
+
provide: AXP_ACTIVITY_LOG_PROVIDER,
|
|
2937
|
+
useClass: AXCPActivityLogService,
|
|
2938
|
+
multi: true,
|
|
2939
|
+
},
|
|
2940
|
+
{
|
|
2941
|
+
provide: AXP_SEARCH_PROVIDER,
|
|
2942
|
+
useClass: EntitySearchProvider,
|
|
2943
|
+
multi: true,
|
|
2944
|
+
},
|
|
2945
|
+
{
|
|
2946
|
+
provide: AXPLockService,
|
|
2947
|
+
useClass: AXCLockService,
|
|
2948
|
+
},
|
|
2949
|
+
{
|
|
2950
|
+
provide: AXPFileStorageService,
|
|
2951
|
+
useClass: AXCFileStorageService,
|
|
2952
|
+
},
|
|
2953
|
+
{
|
|
2954
|
+
provide: AXPVersioningService,
|
|
2955
|
+
useClass: AXCVersioningService,
|
|
2956
|
+
},
|
|
2957
|
+
// {
|
|
2958
|
+
// provide: AXP_ACTIVITY_PROVIDER,
|
|
2959
|
+
// useClass: AXCActivityProvider,
|
|
2960
|
+
// multi: true,
|
|
2961
|
+
// },
|
|
2962
|
+
//TODO ADD after testing workflow local engine
|
|
2963
|
+
// {
|
|
2964
|
+
// provide: AXP_WORKFLOW_ENGINE,
|
|
2965
|
+
// useClass: AXCWorkflowRuntimeService,
|
|
2966
|
+
// },
|
|
2967
|
+
{
|
|
2968
|
+
provide: AXP_DATA_SEEDER_TOKEN,
|
|
2969
|
+
useClass: AXCWorkflowDefinitionDataSeeder$1,
|
|
2970
|
+
multi: true,
|
|
2971
|
+
},
|
|
2972
|
+
// Register generic Entity:UpdateStatus command
|
|
2973
|
+
provideCommandSetups([{ key: 'Entity:UpdateStatus', command: () => AXCEntityUpdateStatusCommand }]),
|
|
2974
|
+
AXCEntityUpdateStatusCommand,
|
|
2975
|
+
] }); }
|
|
2976
|
+
}
|
|
2977
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCCommonMockModule, decorators: [{
|
|
2978
|
+
type: NgModule,
|
|
2979
|
+
args: [{
|
|
2980
|
+
imports: [],
|
|
2981
|
+
exports: [],
|
|
2982
|
+
declarations: [],
|
|
2983
|
+
providers: [
|
|
2984
|
+
{
|
|
2985
|
+
provide: AXP_ACTIVITY_LOG_PROVIDER,
|
|
2986
|
+
useClass: AXCPActivityLogService,
|
|
2987
|
+
multi: true,
|
|
2988
|
+
},
|
|
2989
|
+
{
|
|
2990
|
+
provide: AXP_SEARCH_PROVIDER,
|
|
2991
|
+
useClass: EntitySearchProvider,
|
|
2992
|
+
multi: true,
|
|
2993
|
+
},
|
|
2994
|
+
{
|
|
2995
|
+
provide: AXPLockService,
|
|
2996
|
+
useClass: AXCLockService,
|
|
2997
|
+
},
|
|
2998
|
+
{
|
|
2999
|
+
provide: AXPFileStorageService,
|
|
3000
|
+
useClass: AXCFileStorageService,
|
|
3001
|
+
},
|
|
3002
|
+
{
|
|
3003
|
+
provide: AXPVersioningService,
|
|
3004
|
+
useClass: AXCVersioningService,
|
|
3005
|
+
},
|
|
3006
|
+
// {
|
|
3007
|
+
// provide: AXP_ACTIVITY_PROVIDER,
|
|
3008
|
+
// useClass: AXCActivityProvider,
|
|
3009
|
+
// multi: true,
|
|
3010
|
+
// },
|
|
3011
|
+
//TODO ADD after testing workflow local engine
|
|
3012
|
+
// {
|
|
3013
|
+
// provide: AXP_WORKFLOW_ENGINE,
|
|
3014
|
+
// useClass: AXCWorkflowRuntimeService,
|
|
3015
|
+
// },
|
|
3016
|
+
{
|
|
3017
|
+
provide: AXP_DATA_SEEDER_TOKEN,
|
|
3018
|
+
useClass: AXCWorkflowDefinitionDataSeeder$1,
|
|
3019
|
+
multi: true,
|
|
3020
|
+
},
|
|
3021
|
+
// Register generic Entity:UpdateStatus command
|
|
3022
|
+
provideCommandSetups([{ key: 'Entity:UpdateStatus', command: () => AXCEntityUpdateStatusCommand }]),
|
|
3023
|
+
AXCEntityUpdateStatusCommand,
|
|
3024
|
+
],
|
|
3025
|
+
}]
|
|
3026
|
+
}] });
|
|
3027
|
+
|
|
3028
|
+
//#region ---- Imports ----
|
|
3029
|
+
function toUiRows(changes) {
|
|
3030
|
+
return changes.map((c) => ({
|
|
3031
|
+
badge: toBadge(c.op),
|
|
3032
|
+
path: c.path,
|
|
3033
|
+
summary: toSummary(c),
|
|
3034
|
+
oldValue: c.oldValue,
|
|
3035
|
+
newValue: c.newValue,
|
|
3036
|
+
}));
|
|
3037
|
+
}
|
|
3038
|
+
function toBadge(op) {
|
|
3039
|
+
switch (op) {
|
|
3040
|
+
case 'add':
|
|
3041
|
+
return 'Added';
|
|
3042
|
+
case 'remove':
|
|
3043
|
+
return 'Removed';
|
|
3044
|
+
case 'replace':
|
|
3045
|
+
return 'Replaced';
|
|
3046
|
+
case 'rename':
|
|
3047
|
+
return 'Renamed';
|
|
3048
|
+
case 'metadata':
|
|
3049
|
+
return 'Metadata';
|
|
3050
|
+
default:
|
|
3051
|
+
return 'Updated';
|
|
3052
|
+
}
|
|
3053
|
+
}
|
|
3054
|
+
function toSummary(c) {
|
|
3055
|
+
switch (c.op) {
|
|
3056
|
+
case 'add':
|
|
3057
|
+
return 'Added';
|
|
3058
|
+
case 'remove':
|
|
3059
|
+
return 'Removed';
|
|
3060
|
+
case 'replace':
|
|
3061
|
+
return 'File replaced';
|
|
3062
|
+
case 'rename':
|
|
3063
|
+
return 'File renamed';
|
|
3064
|
+
case 'metadata':
|
|
3065
|
+
return 'File metadata updated';
|
|
3066
|
+
default:
|
|
3067
|
+
return 'Updated';
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
//#endregion
|
|
3071
|
+
|
|
3072
|
+
//#region ---- Dexie DB ----
|
|
3073
|
+
/**
|
|
3074
|
+
* Dexie database for workflow instances.
|
|
3816
3075
|
*/
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3076
|
+
class AXCWorkflowExecutionDB extends Dexie {
|
|
3077
|
+
constructor() {
|
|
3078
|
+
super('ACoreXWorkflowExecutions');
|
|
3079
|
+
this.version(2).stores({
|
|
3080
|
+
instances: 'id, definitionId, status, subStatus, createdAt, updatedAt',
|
|
3081
|
+
});
|
|
3082
|
+
// Migration from v1 to v2: clear old executions table
|
|
3083
|
+
this.version(1).stores({
|
|
3084
|
+
executions: 'executionId, workflowId, status, currentStepId, lastUpdated',
|
|
3085
|
+
});
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
/**
|
|
3089
|
+
* Singleton instance of workflow execution database.
|
|
3090
|
+
*/
|
|
3091
|
+
const axWorkflowExecutionDB = new AXCWorkflowExecutionDB();
|
|
3092
|
+
//#endregion
|
|
3822
3093
|
|
|
3823
3094
|
/**
|
|
3824
|
-
*
|
|
3095
|
+
* Mock implementation of AXPWorkflowEngine.
|
|
3825
3096
|
*
|
|
3826
|
-
*
|
|
3827
|
-
* `workflow:{definitionId}`
|
|
3097
|
+
* Uses IndexedDB (via Dexie) to store workflow instance states.
|
|
3828
3098
|
*
|
|
3829
|
-
*
|
|
3830
|
-
*
|
|
3099
|
+
* @example
|
|
3100
|
+
* ```typescript
|
|
3101
|
+
* // In module providers
|
|
3102
|
+
* {
|
|
3103
|
+
* provide: AXP_WORKFLOW_ENGINE,
|
|
3104
|
+
* useClass: AXCWorkflowRuntimeService,
|
|
3105
|
+
* }
|
|
3106
|
+
* ```
|
|
3831
3107
|
*/
|
|
3832
|
-
|
|
3108
|
+
class AXCWorkflowRuntimeService {
|
|
3833
3109
|
constructor() {
|
|
3834
|
-
this.
|
|
3110
|
+
this.db = axWorkflowExecutionDB;
|
|
3111
|
+
this.commandService = inject(AXPCommandService);
|
|
3112
|
+
this.entityStorageService = inject(AXPEntityStorageService);
|
|
3835
3113
|
}
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3114
|
+
/**
|
|
3115
|
+
* Start a new workflow instance.
|
|
3116
|
+
* Creates a workflow instance and stores it in IndexedDB.
|
|
3117
|
+
*/
|
|
3118
|
+
async start(request) {
|
|
3119
|
+
const instanceId = AXPDataGenerator.uuid();
|
|
3120
|
+
const now = new Date().toISOString();
|
|
3121
|
+
// Get workflow definition from entity storage service
|
|
3122
|
+
const workflowEntity = await this.entityStorageService.getOne('WorkflowManagement.WorkflowDefinition', request.workflowId);
|
|
3123
|
+
if (!workflowEntity) {
|
|
3124
|
+
throw new Error(`Workflow definition not found: ${request.workflowId}`);
|
|
3125
|
+
}
|
|
3126
|
+
// Convert entity model to workflow definition
|
|
3127
|
+
const workflow = workflowEntity;
|
|
3128
|
+
// Find first step from Flowchart (activity with no incoming connections)
|
|
3129
|
+
const flowchart = workflow.root;
|
|
3130
|
+
const activities = flowchart.activities || [];
|
|
3131
|
+
const connections = flowchart.connections || [];
|
|
3132
|
+
// Build connection map: target -> source[]
|
|
3133
|
+
const incomingConnections = new Map();
|
|
3134
|
+
connections.forEach((conn) => {
|
|
3135
|
+
const targetId = conn.target.activity;
|
|
3136
|
+
if (!incomingConnections.has(targetId)) {
|
|
3137
|
+
incomingConnections.set(targetId, []);
|
|
3138
|
+
}
|
|
3139
|
+
incomingConnections.get(targetId).push(conn.source.activity);
|
|
3140
|
+
});
|
|
3141
|
+
// Find root activity (activity with no incoming connections, or first activity)
|
|
3142
|
+
let firstActivity = null;
|
|
3143
|
+
for (const activity of activities) {
|
|
3144
|
+
if (!incomingConnections.has(activity.id)) {
|
|
3145
|
+
firstActivity = activity;
|
|
3146
|
+
break;
|
|
3147
|
+
}
|
|
3148
|
+
}
|
|
3149
|
+
// If no root found, use first activity
|
|
3150
|
+
if (!firstActivity && activities.length > 0) {
|
|
3151
|
+
firstActivity = activities[0];
|
|
3152
|
+
}
|
|
3153
|
+
const firstStepId = firstActivity?.id || 'root';
|
|
3154
|
+
// Create workflow state
|
|
3155
|
+
const workflowState = {
|
|
3156
|
+
id: `state-${instanceId}`,
|
|
3157
|
+
definitionId: workflow.name, // Use name as definitionId (backward compatibility)
|
|
3158
|
+
//definitionVersion: workflow.version,
|
|
3159
|
+
definitionVersion: 1,
|
|
3160
|
+
correlationId: null,
|
|
3161
|
+
status: 'Running',
|
|
3162
|
+
subStatus: 'Executing',
|
|
3163
|
+
bookmarks: [],
|
|
3164
|
+
incidents: [],
|
|
3165
|
+
fault: null,
|
|
3166
|
+
completionCallbacks: [],
|
|
3167
|
+
activityExecutionContexts: firstActivity ? [{
|
|
3168
|
+
id: `ctx-${firstStepId}`,
|
|
3169
|
+
parentContextId: null,
|
|
3170
|
+
scheduledActivityNodeId: firstStepId,
|
|
3171
|
+
ownerActivityNodeId: null,
|
|
3172
|
+
properties: {},
|
|
3173
|
+
activityState: null,
|
|
3174
|
+
dynamicVariables: [],
|
|
3175
|
+
status: 'Running',
|
|
3176
|
+
isExecuting: false,
|
|
3177
|
+
faultCount: 0,
|
|
3178
|
+
startedAt: now,
|
|
3179
|
+
completedAt: null,
|
|
3180
|
+
tag: null
|
|
3181
|
+
}] : [],
|
|
3182
|
+
input: request.input || {},
|
|
3183
|
+
output: {},
|
|
3184
|
+
properties: {},
|
|
3185
|
+
createdAt: now,
|
|
3186
|
+
updatedAt: now,
|
|
3187
|
+
finishedAt: null
|
|
3188
|
+
};
|
|
3189
|
+
// Create workflow instance
|
|
3190
|
+
const instance = {
|
|
3191
|
+
$schema: 'https://elsaworkflows.io/schemas/workflow-instance/v3.0.0/schema.json',
|
|
3192
|
+
id: instanceId,
|
|
3193
|
+
definitionId: workflow.name, // Use name as definitionId (backward compatibility)
|
|
3194
|
+
//definitionVersionId: workflow.id,
|
|
3195
|
+
//version: workflow.version,
|
|
3196
|
+
version: 1,
|
|
3197
|
+
definitionVersionId: '1',
|
|
3198
|
+
parentWorkflowInstanceId: null,
|
|
3199
|
+
workflowState,
|
|
3200
|
+
status: 'Running',
|
|
3201
|
+
subStatus: 'Executing',
|
|
3202
|
+
correlationId: null,
|
|
3203
|
+
name: workflow.name || null,
|
|
3204
|
+
incidentCount: 0,
|
|
3205
|
+
createdAt: now,
|
|
3206
|
+
updatedAt: now,
|
|
3207
|
+
finishedAt: null
|
|
3208
|
+
};
|
|
3209
|
+
// Store instance in IndexedDB
|
|
3210
|
+
await this.db.instances.add(instance, instance.id);
|
|
3211
|
+
// 🎯 Also store in entity service for entity-based access
|
|
3212
|
+
await this.saveInstanceToEntityService(instance);
|
|
3213
|
+
// Convert to instance state for response
|
|
3214
|
+
let state = this.instanceToInstanceState(instance);
|
|
3215
|
+
// 🎯 KEY LOGIC: Execute all backend activities until we reach a frontend activity
|
|
3216
|
+
let currentActivity = firstActivity;
|
|
3217
|
+
let executionCount = 0;
|
|
3218
|
+
const maxExecutions = 100; // Prevent infinite loops
|
|
3219
|
+
while (currentActivity && executionCount < maxExecutions) {
|
|
3220
|
+
executionCount++;
|
|
3221
|
+
// Map activity type to ACoreX format
|
|
3222
|
+
let activityType = currentActivity.type;
|
|
3223
|
+
if (activityType.startsWith('Elsa.')) {
|
|
3224
|
+
let type = activityType.replace(/^Elsa\./, '');
|
|
3225
|
+
type = type.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
|
|
3226
|
+
activityType = `workflow-activity:${type}`;
|
|
3227
|
+
}
|
|
3228
|
+
// Determine execution mode
|
|
3229
|
+
const executionMode = this.determineExecutionMode(activityType, currentActivity);
|
|
3230
|
+
// If frontend activity → stop and return as pendingTask
|
|
3231
|
+
if (executionMode === 'frontend') {
|
|
3232
|
+
const pendingTask = this.createTaskFromActivity(currentActivity, state);
|
|
3233
|
+
// Update state to suspended (waiting for frontend)
|
|
3234
|
+
state = {
|
|
3235
|
+
...state,
|
|
3236
|
+
status: 'suspended',
|
|
3237
|
+
currentStepId: currentActivity.id,
|
|
3238
|
+
};
|
|
3239
|
+
await this.updateInstanceFromState(instanceId, state);
|
|
3240
|
+
return {
|
|
3241
|
+
instanceId: instanceId,
|
|
3242
|
+
state,
|
|
3243
|
+
pendingTask
|
|
3244
|
+
};
|
|
3245
|
+
}
|
|
3246
|
+
// Backend activity → execute it
|
|
3247
|
+
console.log(`[WorkflowExecutionService] Executing backend activity '${currentActivity.name || currentActivity.id}' (${activityType})`);
|
|
3248
|
+
const executionResult = await this.executeBackendActivityLocally(currentActivity, { ...state.variables, ...state.input }, state);
|
|
3249
|
+
// Update variables with output
|
|
3250
|
+
state = {
|
|
3251
|
+
...state,
|
|
3252
|
+
variables: {
|
|
3253
|
+
...state.variables,
|
|
3254
|
+
...executionResult.output
|
|
3255
|
+
},
|
|
3256
|
+
currentStepId: currentActivity.id,
|
|
3257
|
+
lastUpdated: new Date()
|
|
3258
|
+
};
|
|
3259
|
+
// Find next activity based on outcome
|
|
3260
|
+
const outcome = Object.keys(executionResult.outcomes)[0] || 'Done';
|
|
3261
|
+
const nextConnection = connections.find((conn) => conn.source.activity === currentActivity.id &&
|
|
3262
|
+
(conn.source.port === outcome || (!conn.source.port && outcome === 'Done')));
|
|
3263
|
+
if (!nextConnection) {
|
|
3264
|
+
// No next activity → workflow completed
|
|
3265
|
+
state = {
|
|
3266
|
+
...state,
|
|
3267
|
+
status: 'completed',
|
|
3268
|
+
currentStepId: undefined,
|
|
3269
|
+
output: state.variables
|
|
3270
|
+
};
|
|
3271
|
+
await this.updateInstanceFromState(instanceId, state);
|
|
3272
|
+
return {
|
|
3273
|
+
instanceId: instanceId,
|
|
3274
|
+
state,
|
|
3275
|
+
pendingTask: null
|
|
3276
|
+
};
|
|
3277
|
+
}
|
|
3278
|
+
// Find next activity
|
|
3279
|
+
currentActivity = this.findActivityInFlowchart(flowchart, nextConnection.target.activity);
|
|
3280
|
+
}
|
|
3281
|
+
// If we exit loop without frontend activity, workflow is complete
|
|
3282
|
+
state = {
|
|
3283
|
+
...state,
|
|
3284
|
+
status: 'completed',
|
|
3285
|
+
currentStepId: undefined,
|
|
3286
|
+
output: state.variables
|
|
3287
|
+
};
|
|
3288
|
+
await this.updateInstanceFromState(instanceId, state);
|
|
3289
|
+
return {
|
|
3290
|
+
instanceId: instanceId,
|
|
3291
|
+
state,
|
|
3292
|
+
pendingTask: null
|
|
3293
|
+
};
|
|
3294
|
+
}
|
|
3295
|
+
/**
|
|
3296
|
+
* Find activity in Flowchart by ID.
|
|
3297
|
+
*/
|
|
3298
|
+
findActivityInFlowchart(flowchart, activityId) {
|
|
3299
|
+
const activities = flowchart.activities || [];
|
|
3300
|
+
return activities.find(a => a.id === activityId) || null;
|
|
3301
|
+
}
|
|
3302
|
+
/**
|
|
3303
|
+
* Resume a suspended workflow instance.
|
|
3304
|
+
*
|
|
3305
|
+
* After frontend completes its task:
|
|
3306
|
+
* 1. Validates taskToken
|
|
3307
|
+
* 2. Finds next activity based on outcome
|
|
3308
|
+
* 3. Executes ALL backend activities in sequence
|
|
3309
|
+
* 4. Stops at first frontend activity and returns it as pendingTask
|
|
3310
|
+
*/
|
|
3311
|
+
async resume(request) {
|
|
3312
|
+
// Get current state
|
|
3313
|
+
let state = await this.getState({
|
|
3314
|
+
instanceId: request.instanceId,
|
|
3315
|
+
});
|
|
3316
|
+
if (!state) {
|
|
3317
|
+
throw new Error(`Workflow instance not found: ${request.instanceId}`);
|
|
3318
|
+
}
|
|
3319
|
+
if (state.status !== 'suspended') {
|
|
3320
|
+
throw new Error(`Cannot resume workflow with status: ${state.status}. Expected: suspended`);
|
|
3321
|
+
}
|
|
3322
|
+
// TODO: Validate taskToken here for security
|
|
3323
|
+
// For now, just verify stepId matches
|
|
3324
|
+
if (state.currentStepId !== request.stepId) {
|
|
3325
|
+
throw new Error(`Step mismatch: expected '${state.currentStepId}', got '${request.stepId}'`);
|
|
3326
|
+
}
|
|
3327
|
+
// Get workflow definition from entity storage service
|
|
3328
|
+
const workflowEntity = await this.entityStorageService.getOne('WorkflowManagement.WorkflowDefinition', state.workflowId);
|
|
3329
|
+
if (!workflowEntity) {
|
|
3330
|
+
throw new Error(`Workflow definition not found: ${state.workflowId}`);
|
|
3331
|
+
}
|
|
3332
|
+
// Convert entity model to workflow definition
|
|
3333
|
+
const workflow = workflowEntity;
|
|
3334
|
+
const connections = workflow.root.connections || [];
|
|
3335
|
+
// Update state with frontend activity outcome and user input
|
|
3336
|
+
state = {
|
|
3337
|
+
...state,
|
|
3338
|
+
variables: {
|
|
3339
|
+
...state.variables,
|
|
3340
|
+
[`${request.stepId}_outcome`]: request.outcome,
|
|
3341
|
+
[`${request.stepId}_userInput`]: request.userInput || {},
|
|
3342
|
+
},
|
|
3343
|
+
lastUpdated: new Date()
|
|
3344
|
+
};
|
|
3345
|
+
// Find next activity based on outcome from frontend
|
|
3346
|
+
const nextConnection = connections.find((conn) => conn.source.activity === request.stepId &&
|
|
3347
|
+
(conn.source.port === request.outcome || (!conn.source.port && request.outcome === 'Done')));
|
|
3348
|
+
console.log('[AXCWorkflowExecutionService] 🔍 Looking for next connection', {
|
|
3349
|
+
stepId: request.stepId,
|
|
3350
|
+
outcome: request.outcome,
|
|
3351
|
+
connectionsCount: connections.length,
|
|
3352
|
+
foundConnection: !!nextConnection
|
|
3353
|
+
});
|
|
3354
|
+
if (!nextConnection) {
|
|
3355
|
+
// No next activity → workflow completed
|
|
3356
|
+
console.log('[AXCWorkflowExecutionService] ✅ No next activity found - workflow completed');
|
|
3357
|
+
state = {
|
|
3358
|
+
...state,
|
|
3359
|
+
status: 'completed',
|
|
3360
|
+
currentStepId: undefined,
|
|
3361
|
+
output: state.variables
|
|
3362
|
+
};
|
|
3363
|
+
console.log('[AXCWorkflowExecutionService] 📝 Updating instance to completed state');
|
|
3364
|
+
await this.updateInstanceFromState(request.instanceId, state);
|
|
3365
|
+
return {
|
|
3366
|
+
output: { outcome: request.outcome, userInput: request.userInput },
|
|
3367
|
+
outcomes: { [request.outcome]: true },
|
|
3368
|
+
nextTask: null,
|
|
3369
|
+
state
|
|
3370
|
+
};
|
|
3371
|
+
}
|
|
3372
|
+
// 🎯 KEY LOGIC: Execute all backend activities until we reach next frontend activity
|
|
3373
|
+
let currentActivity = this.findActivityInFlowchart(workflow.root, nextConnection.target.activity);
|
|
3374
|
+
let executionCount = 0;
|
|
3375
|
+
const maxExecutions = 100;
|
|
3376
|
+
while (currentActivity && executionCount < maxExecutions) {
|
|
3377
|
+
executionCount++;
|
|
3378
|
+
// Map activity type
|
|
3379
|
+
let activityType = currentActivity.type;
|
|
3380
|
+
if (activityType.startsWith('Elsa.')) {
|
|
3381
|
+
let type = activityType.replace(/^Elsa\./, '');
|
|
3382
|
+
type = type.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
|
|
3383
|
+
activityType = `workflow-activity:${type}`;
|
|
3384
|
+
}
|
|
3385
|
+
// Determine execution mode
|
|
3386
|
+
const executionMode = this.determineExecutionMode(activityType, currentActivity);
|
|
3387
|
+
// If frontend activity → stop and return as pendingTask
|
|
3388
|
+
if (executionMode === 'frontend') {
|
|
3389
|
+
const pendingTask = this.createTaskFromActivity(currentActivity, state);
|
|
3390
|
+
state = {
|
|
3391
|
+
...state,
|
|
3392
|
+
status: 'suspended',
|
|
3393
|
+
currentStepId: currentActivity.id,
|
|
3394
|
+
};
|
|
3395
|
+
await this.updateInstanceFromState(request.instanceId, state);
|
|
3396
|
+
return {
|
|
3397
|
+
output: { outcome: request.outcome, userInput: request.userInput },
|
|
3398
|
+
outcomes: { [request.outcome]: true },
|
|
3399
|
+
nextTask: pendingTask,
|
|
3400
|
+
state
|
|
3401
|
+
};
|
|
3402
|
+
}
|
|
3403
|
+
// Backend activity → execute it
|
|
3404
|
+
console.log(`[WorkflowExecutionService] Executing backend activity '${currentActivity.name || currentActivity.id}' (${activityType})`);
|
|
3405
|
+
const executionResult = await this.executeBackendActivityLocally(currentActivity, { ...state.variables, ...state.input }, state);
|
|
3406
|
+
// Update variables with output
|
|
3407
|
+
state = {
|
|
3408
|
+
...state,
|
|
3409
|
+
variables: {
|
|
3410
|
+
...state.variables,
|
|
3411
|
+
...executionResult.output
|
|
3412
|
+
},
|
|
3413
|
+
currentStepId: currentActivity.id,
|
|
3414
|
+
lastUpdated: new Date()
|
|
3415
|
+
};
|
|
3416
|
+
// Find next activity based on outcome
|
|
3417
|
+
const outcome = Object.keys(executionResult.outcomes)[0] || 'Done';
|
|
3418
|
+
const nextConn = connections.find((conn) => conn.source.activity === currentActivity.id &&
|
|
3419
|
+
(conn.source.port === outcome || (!conn.source.port && outcome === 'Done')));
|
|
3420
|
+
if (!nextConn) {
|
|
3421
|
+
// No next activity → workflow completed
|
|
3422
|
+
state = {
|
|
3423
|
+
...state,
|
|
3424
|
+
status: 'completed',
|
|
3425
|
+
currentStepId: undefined,
|
|
3426
|
+
output: state.variables
|
|
3427
|
+
};
|
|
3428
|
+
await this.updateInstanceFromState(request.instanceId, state);
|
|
3429
|
+
return {
|
|
3430
|
+
output: state.variables,
|
|
3431
|
+
outcomes: { Done: true },
|
|
3432
|
+
nextTask: null,
|
|
3433
|
+
state
|
|
3434
|
+
};
|
|
3435
|
+
}
|
|
3436
|
+
// Find next activity
|
|
3437
|
+
currentActivity = this.findActivityInFlowchart(workflow.root, nextConn.target.activity);
|
|
3438
|
+
}
|
|
3439
|
+
// If we exit loop, workflow is complete
|
|
3440
|
+
state = {
|
|
3441
|
+
...state,
|
|
3442
|
+
status: 'completed',
|
|
3443
|
+
currentStepId: undefined,
|
|
3444
|
+
output: state.variables
|
|
3445
|
+
};
|
|
3446
|
+
await this.updateInstanceFromState(request.instanceId, state);
|
|
3447
|
+
return {
|
|
3448
|
+
output: state.variables,
|
|
3449
|
+
outcomes: { Done: true },
|
|
3450
|
+
nextTask: null,
|
|
3451
|
+
state
|
|
3452
|
+
};
|
|
3453
|
+
}
|
|
3454
|
+
/**
|
|
3455
|
+
* Get current workflow execution state.
|
|
3456
|
+
*/
|
|
3457
|
+
async getState(request) {
|
|
3458
|
+
// Get instance from IndexedDB
|
|
3459
|
+
const instance = await this.getInstance(request.instanceId);
|
|
3460
|
+
if (!instance) {
|
|
3461
|
+
throw new Error(`Workflow instance not found: ${request.instanceId}`);
|
|
3462
|
+
}
|
|
3463
|
+
// Convert instance to state
|
|
3464
|
+
return this.instanceToInstanceState(instance);
|
|
3465
|
+
}
|
|
3466
|
+
//#region ---- Task-Based Helper Methods ----
|
|
3467
|
+
/**
|
|
3468
|
+
* Generate unique task token for task-based execution.
|
|
3469
|
+
* Inspired by Temporal's task tokens for secure task completion.
|
|
3470
|
+
*/
|
|
3471
|
+
generateTaskToken(instanceId, activityId) {
|
|
3472
|
+
return `${instanceId}:${activityId}:${Date.now()}`;
|
|
3473
|
+
}
|
|
3474
|
+
/**
|
|
3475
|
+
* Determine execution mode for an activity.
|
|
3476
|
+
*
|
|
3477
|
+
* Priority (highest to lowest):
|
|
3478
|
+
* 1. Activity instance's executionMode (from AXPActivity in workflow definition)
|
|
3479
|
+
* 2. ActivityDescriptor's executionMode (from ActivityProviderService)
|
|
3480
|
+
* 3. Default: 'frontend'
|
|
3481
|
+
*
|
|
3482
|
+
* @param activityType - Activity type (e.g., 'workflow-activity:write-line')
|
|
3483
|
+
* @param activity - Activity from workflow definition (AXPActivity)
|
|
3484
|
+
* @returns Execution mode: 'frontend', 'backend', or 'both'
|
|
3485
|
+
*/
|
|
3486
|
+
determineExecutionMode(activityType, activity) {
|
|
3487
|
+
// 1. Check if executionMode is explicitly set in activity instance (highest priority)
|
|
3488
|
+
if (activity?.executionMode) {
|
|
3489
|
+
return activity.executionMode;
|
|
3490
|
+
}
|
|
3491
|
+
// 2. Check ActivityDescriptor from ActivityProviderService
|
|
3492
|
+
// const descriptor = this.activityProviderService.getMetadata(activityType);
|
|
3493
|
+
// if (descriptor?.executionMode) {
|
|
3494
|
+
// return descriptor.executionMode;
|
|
3841
3495
|
// }
|
|
3842
|
-
//
|
|
3843
|
-
|
|
3496
|
+
// 3. Default to frontend (UI activities are more common)
|
|
3497
|
+
return 'frontend';
|
|
3844
3498
|
}
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3499
|
+
/**
|
|
3500
|
+
* Create a task from a workflow activity.
|
|
3501
|
+
* Inspired by Temporal's Activity Task and Elsa's Bookmark pattern.
|
|
3502
|
+
*/
|
|
3503
|
+
createTaskFromActivity(activity, state) {
|
|
3504
|
+
// Extract properties from activity
|
|
3505
|
+
// Activities have properties as AXPInputValue objects or primitives
|
|
3506
|
+
const properties = {};
|
|
3507
|
+
// Iterate through all properties of the activity
|
|
3508
|
+
Object.keys(activity).forEach(key => {
|
|
3509
|
+
// Skip standard Elsa fields
|
|
3510
|
+
if (['id', 'nodeId', 'name', 'type', 'version', 'customProperties', 'metadata', 'executionMode'].includes(key)) {
|
|
3511
|
+
return;
|
|
3512
|
+
}
|
|
3513
|
+
const value = activity[key];
|
|
3514
|
+
// 🎯 SPECIAL HANDLING: If key is 'properties' and it's an object, flatten it
|
|
3515
|
+
// This handles workflow-studio format where properties are nested
|
|
3516
|
+
if (key === 'properties' && value && typeof value === 'object' && !('typeName' in value) && !('expression' in value)) {
|
|
3517
|
+
// Flatten properties object
|
|
3518
|
+
Object.keys(value).forEach(propKey => {
|
|
3519
|
+
const propValue = value[propKey];
|
|
3520
|
+
// Handle AXPInputValue format within properties
|
|
3521
|
+
if (propValue && typeof propValue === 'object' && 'typeName' in propValue && 'expression' in propValue) {
|
|
3522
|
+
if (propValue.expression && propValue.expression.value !== undefined) {
|
|
3523
|
+
properties[propKey] = propValue.expression.value;
|
|
3524
|
+
}
|
|
3525
|
+
else {
|
|
3526
|
+
properties[propKey] = null;
|
|
3527
|
+
}
|
|
3528
|
+
}
|
|
3529
|
+
else {
|
|
3530
|
+
// Direct value
|
|
3531
|
+
properties[propKey] = propValue;
|
|
3532
|
+
}
|
|
3533
|
+
});
|
|
3534
|
+
return;
|
|
3535
|
+
}
|
|
3536
|
+
// Handle AXPInputValue format
|
|
3537
|
+
if (value && typeof value === 'object' && 'typeName' in value && 'expression' in value) {
|
|
3538
|
+
// It's an AXPInputValue - extract the expression value
|
|
3539
|
+
if (value.expression && value.expression.value !== undefined) {
|
|
3540
|
+
properties[key] = value.expression.value;
|
|
3541
|
+
}
|
|
3542
|
+
else {
|
|
3543
|
+
properties[key] = null;
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3546
|
+
else {
|
|
3547
|
+
// Direct value
|
|
3548
|
+
properties[key] = value;
|
|
3549
|
+
}
|
|
3550
|
+
});
|
|
3551
|
+
console.log('[AXCWorkflowExecutionService] 📦 Extracted properties for task', properties);
|
|
3552
|
+
// Map activity type to ACoreX activity type
|
|
3553
|
+
// Example: "Elsa.WriteLine" -> "workflow-activity:write-line"
|
|
3554
|
+
let activityType = activity.type;
|
|
3555
|
+
if (activityType.startsWith('Elsa.')) {
|
|
3556
|
+
let type = activityType.replace(/^Elsa\./, '');
|
|
3557
|
+
// Convert PascalCase to kebab-case
|
|
3558
|
+
type = type.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
|
|
3559
|
+
activityType = `workflow-activity:${type}`;
|
|
3560
|
+
}
|
|
3561
|
+
// Get executionMode from activity descriptor
|
|
3562
|
+
const executionMode = this.determineExecutionMode(activityType, activity);
|
|
3563
|
+
return {
|
|
3564
|
+
taskToken: this.generateTaskToken(state.instanceId, activity.id),
|
|
3565
|
+
activityId: activity.id,
|
|
3566
|
+
activityType,
|
|
3567
|
+
activityName: activity.name || undefined,
|
|
3568
|
+
executionMode,
|
|
3569
|
+
input: (executionMode === 'frontend' || executionMode === 'both') ? properties : undefined,
|
|
3570
|
+
config: (executionMode === 'frontend' || executionMode === 'both') ? properties : undefined
|
|
3571
|
+
};
|
|
3572
|
+
}
|
|
3573
|
+
/**
|
|
3574
|
+
* Save workflow instance to entity service for entity-based access.
|
|
3575
|
+
*/
|
|
3576
|
+
async saveInstanceToEntityService(instance) {
|
|
3577
|
+
try {
|
|
3578
|
+
const entityModel = {
|
|
3579
|
+
id: instance.id,
|
|
3580
|
+
definitionId: instance.definitionId,
|
|
3581
|
+
definitionVersionId: instance.definitionVersionId,
|
|
3582
|
+
version: instance.version,
|
|
3583
|
+
status: instance.status,
|
|
3584
|
+
subStatus: instance.subStatus,
|
|
3585
|
+
correlationId: instance.correlationId || undefined,
|
|
3586
|
+
name: instance.name || undefined,
|
|
3587
|
+
incidentCount: instance.incidentCount,
|
|
3588
|
+
parentWorkflowInstanceId: instance.parentWorkflowInstanceId || undefined,
|
|
3589
|
+
createdAt: instance.createdAt,
|
|
3590
|
+
updatedAt: instance.updatedAt,
|
|
3591
|
+
finishedAt: instance.finishedAt || undefined,
|
|
3592
|
+
workflowInstance: instance
|
|
3593
|
+
};
|
|
3594
|
+
const entityName = 'WorkflowManagement.WorkflowInstance';
|
|
3595
|
+
// Check if instance already exists
|
|
3596
|
+
try {
|
|
3597
|
+
const existing = await this.entityStorageService.getOne(entityName, entityModel.id);
|
|
3598
|
+
if (existing) {
|
|
3599
|
+
// Already exists - don't insert again, will be updated later
|
|
3600
|
+
console.log('[AXCWorkflowExecutionService] ℹ️ Instance already exists in entity service, will be updated later', {
|
|
3601
|
+
instanceId: instance.id
|
|
3602
|
+
});
|
|
3603
|
+
return;
|
|
3604
|
+
}
|
|
3605
|
+
}
|
|
3606
|
+
catch (getError) {
|
|
3607
|
+
// Instance doesn't exist - proceed with insert
|
|
3608
|
+
}
|
|
3609
|
+
// Insert new instance
|
|
3610
|
+
await this.entityStorageService.insertOne(entityName, entityModel);
|
|
3611
|
+
console.log('[AXCWorkflowExecutionService] ✅ Inserted new instance in entity service', {
|
|
3612
|
+
instanceId: instance.id,
|
|
3613
|
+
status: instance.status,
|
|
3614
|
+
subStatus: instance.subStatus
|
|
3615
|
+
});
|
|
3616
|
+
}
|
|
3617
|
+
catch (error) {
|
|
3618
|
+
console.warn('[AXCWorkflowExecutionService] Failed to save instance to entity service:', error);
|
|
3619
|
+
// Don't throw - IndexedDB is primary storage
|
|
3620
|
+
}
|
|
3868
3621
|
}
|
|
3869
|
-
|
|
3622
|
+
/**
|
|
3623
|
+
* Update workflow instance in entity service.
|
|
3624
|
+
* Uses upsert pattern: try update first, if fails then insert.
|
|
3625
|
+
*/
|
|
3626
|
+
async updateInstanceInEntityService(instance) {
|
|
3870
3627
|
try {
|
|
3871
|
-
const
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3628
|
+
const entityModel = {
|
|
3629
|
+
id: instance.id,
|
|
3630
|
+
definitionId: instance.definitionId,
|
|
3631
|
+
definitionVersionId: instance.definitionVersionId,
|
|
3632
|
+
version: instance.version,
|
|
3633
|
+
status: instance.status,
|
|
3634
|
+
subStatus: instance.subStatus,
|
|
3635
|
+
correlationId: instance.correlationId || undefined,
|
|
3636
|
+
name: instance.name || undefined,
|
|
3637
|
+
incidentCount: instance.incidentCount,
|
|
3638
|
+
parentWorkflowInstanceId: instance.parentWorkflowInstanceId || undefined,
|
|
3639
|
+
createdAt: instance.createdAt,
|
|
3640
|
+
updatedAt: instance.updatedAt,
|
|
3641
|
+
finishedAt: instance.finishedAt || undefined,
|
|
3642
|
+
workflowInstance: instance
|
|
3643
|
+
};
|
|
3644
|
+
const entityName = 'WorkflowManagement.WorkflowInstance';
|
|
3645
|
+
// Try update first (upsert pattern)
|
|
3646
|
+
try {
|
|
3647
|
+
await this.entityStorageService.updateOne(entityName, entityModel.id, entityModel);
|
|
3648
|
+
console.log('[AXCWorkflowExecutionService] ✅ Updated instance in entity service', {
|
|
3649
|
+
instanceId: instance.id,
|
|
3650
|
+
status: instance.status,
|
|
3651
|
+
subStatus: instance.subStatus
|
|
3652
|
+
});
|
|
3653
|
+
return; // Successfully updated, exit
|
|
3875
3654
|
}
|
|
3876
|
-
|
|
3877
|
-
|
|
3655
|
+
catch (updateError) {
|
|
3656
|
+
// Update failed - instance doesn't exist, try insert
|
|
3657
|
+
console.log('[AXCWorkflowExecutionService] ℹ️ Update failed, trying insert', {
|
|
3658
|
+
instanceId: instance.id,
|
|
3659
|
+
error: updateError.message
|
|
3660
|
+
});
|
|
3878
3661
|
}
|
|
3879
|
-
|
|
3880
|
-
|
|
3662
|
+
// Try insert
|
|
3663
|
+
try {
|
|
3664
|
+
await this.entityStorageService.insertOne(entityName, entityModel);
|
|
3665
|
+
console.log('[AXCWorkflowExecutionService] ✅ Inserted instance in entity service', {
|
|
3666
|
+
instanceId: instance.id,
|
|
3667
|
+
status: instance.status,
|
|
3668
|
+
subStatus: instance.subStatus
|
|
3669
|
+
});
|
|
3881
3670
|
}
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3671
|
+
catch (insertError) {
|
|
3672
|
+
// Insert might fail if instance was created between update and insert
|
|
3673
|
+
// Try update again as fallback
|
|
3674
|
+
try {
|
|
3675
|
+
await this.entityStorageService.updateOne(entityName, entityModel.id, entityModel);
|
|
3676
|
+
console.log('[AXCWorkflowExecutionService] ✅ Updated instance in entity service (after insert failed)', {
|
|
3677
|
+
instanceId: instance.id,
|
|
3678
|
+
status: instance.status,
|
|
3679
|
+
subStatus: instance.subStatus
|
|
3680
|
+
});
|
|
3890
3681
|
}
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3682
|
+
catch (finalError) {
|
|
3683
|
+
console.warn('[AXCWorkflowExecutionService] Failed to upsert instance in entity service:', finalError);
|
|
3684
|
+
}
|
|
3685
|
+
}
|
|
3686
|
+
}
|
|
3687
|
+
catch (error) {
|
|
3688
|
+
console.warn('[AXCWorkflowExecutionService] Failed to update instance in entity service:', error);
|
|
3689
|
+
// Don't throw - IndexedDB is primary storage
|
|
3690
|
+
}
|
|
3691
|
+
}
|
|
3692
|
+
//#endregion
|
|
3693
|
+
//#region ---- Backend Activity Executors ----
|
|
3694
|
+
/**
|
|
3695
|
+
* Execute backend activities locally using CommandBus.
|
|
3696
|
+
*
|
|
3697
|
+
* Backend activities are registered as Commands and executed via CommandService.
|
|
3698
|
+
* This provides a unified execution channel for all activities.
|
|
3699
|
+
*/
|
|
3700
|
+
async executeBackendActivityLocally(activity, input, state) {
|
|
3701
|
+
console.log('[AXCWorkflowExecutionService] 🔧 executeBackendActivityLocally', {
|
|
3702
|
+
activityId: activity.id,
|
|
3703
|
+
activityType: activity.type,
|
|
3704
|
+
input
|
|
3705
|
+
});
|
|
3706
|
+
// Extract properties from activity
|
|
3707
|
+
const properties = {};
|
|
3708
|
+
Object.keys(activity).forEach(key => {
|
|
3709
|
+
if (['id', 'nodeId', 'name', 'type', 'version', 'customProperties', 'metadata', 'executionMode'].includes(key)) {
|
|
3710
|
+
return;
|
|
3711
|
+
}
|
|
3712
|
+
const value = activity[key];
|
|
3713
|
+
if (value && typeof value === 'object' && 'typeName' in value && 'expression' in value) {
|
|
3714
|
+
if (value.expression && value.expression.value !== undefined) {
|
|
3715
|
+
properties[key] = value.expression.value;
|
|
3716
|
+
}
|
|
3717
|
+
else {
|
|
3718
|
+
properties[key] = null;
|
|
3719
|
+
}
|
|
3720
|
+
}
|
|
3721
|
+
else {
|
|
3722
|
+
properties[key] = value;
|
|
3723
|
+
}
|
|
3724
|
+
});
|
|
3725
|
+
console.log('[AXCWorkflowExecutionService] 📦 Extracted properties', properties);
|
|
3726
|
+
// Map activity type to ACoreX activity type
|
|
3727
|
+
let activityType = activity.type;
|
|
3728
|
+
if (activityType.startsWith('Elsa.')) {
|
|
3729
|
+
let type = activityType.replace(/^Elsa\./, '');
|
|
3730
|
+
type = type.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
|
|
3731
|
+
activityType = `workflow-activity:${type}`;
|
|
3732
|
+
}
|
|
3733
|
+
console.log('[AXCWorkflowExecutionService] 🔀 Activity type mapped', {
|
|
3734
|
+
original: activity.type,
|
|
3735
|
+
mapped: activityType
|
|
3736
|
+
});
|
|
3737
|
+
// Check if activity is registered as a Command
|
|
3738
|
+
const commandExists = this.commandService.exists(activityType);
|
|
3739
|
+
console.log('[AXCWorkflowExecutionService] 🔍 Command exists?', commandExists, activityType);
|
|
3740
|
+
if (commandExists) {
|
|
3741
|
+
console.log(`[AXCWorkflowExecutionService] ⚡ Executing '${activityType}' via CommandBus`);
|
|
3742
|
+
try {
|
|
3743
|
+
// Execute via CommandBus
|
|
3744
|
+
const result = await this.commandService.execute(activityType, properties);
|
|
3745
|
+
const finalResult = {
|
|
3746
|
+
output: result?.data?.output || result,
|
|
3747
|
+
outcomes: result?.data?.outcomes || { Done: true }
|
|
3894
3748
|
};
|
|
3749
|
+
console.log('[AXCWorkflowExecutionService] ✅ Final result', finalResult);
|
|
3750
|
+
return finalResult;
|
|
3895
3751
|
}
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
const updated = await dataAccessor.update(id, updateData);
|
|
3752
|
+
catch (error) {
|
|
3753
|
+
console.error(`[AXCWorkflowExecutionService] ❌ Backend activity '${activityType}' failed:`, error);
|
|
3899
3754
|
return {
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
message: {
|
|
3903
|
-
text: 'Entity status updated successfully',
|
|
3904
|
-
},
|
|
3755
|
+
output: { error: error.message || 'Unknown error' },
|
|
3756
|
+
outcomes: { Failed: true }
|
|
3905
3757
|
};
|
|
3906
3758
|
}
|
|
3907
|
-
throw new Error('Invalid input: neither id nor ids provided');
|
|
3908
3759
|
}
|
|
3909
|
-
|
|
3760
|
+
// Fallback: Handle built-in activities that aren't Commands yet
|
|
3761
|
+
console.warn(`[WorkflowExecutionService] Activity '${activityType}' not registered as Command, using fallback`);
|
|
3762
|
+
switch (activityType) {
|
|
3763
|
+
case 'workflow-activity:set-variable':
|
|
3764
|
+
return this.executeSetVariable({ type: activityType, properties }, input);
|
|
3765
|
+
case 'workflow-activity:http-request':
|
|
3766
|
+
return this.executeHttpRequest({ type: activityType, properties }, input);
|
|
3767
|
+
default:
|
|
3768
|
+
return { output: null, outcomes: { Done: true } };
|
|
3769
|
+
}
|
|
3770
|
+
}
|
|
3771
|
+
/**
|
|
3772
|
+
* Set variable activity - stores value in workflow state.
|
|
3773
|
+
*/
|
|
3774
|
+
async executeSetVariable(activity, input) {
|
|
3775
|
+
const variableName = activity.properties?.variableName;
|
|
3776
|
+
const value = activity.properties?.value;
|
|
3777
|
+
return {
|
|
3778
|
+
output: { [variableName]: value },
|
|
3779
|
+
outcomes: { Done: true }
|
|
3780
|
+
};
|
|
3781
|
+
}
|
|
3782
|
+
/**
|
|
3783
|
+
* Execute HTTP request - makes real HTTP call.
|
|
3784
|
+
* Supports different HTTP methods and handles timeouts.
|
|
3785
|
+
*/
|
|
3786
|
+
async executeHttpRequest(activity, input) {
|
|
3787
|
+
const url = activity.properties?.url;
|
|
3788
|
+
const method = activity.properties?.method || 'GET';
|
|
3789
|
+
const timeout = activity.properties?.timeout || 30000;
|
|
3790
|
+
const headers = activity.properties?.headers || {};
|
|
3791
|
+
const body = activity.properties?.body;
|
|
3792
|
+
try {
|
|
3793
|
+
const controller = new AbortController();
|
|
3794
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
3795
|
+
const fetchOptions = {
|
|
3796
|
+
method,
|
|
3797
|
+
signal: controller.signal,
|
|
3798
|
+
headers: {
|
|
3799
|
+
'Content-Type': 'application/json',
|
|
3800
|
+
...headers
|
|
3801
|
+
}
|
|
3802
|
+
};
|
|
3803
|
+
if (body && method !== 'GET' && method !== 'HEAD') {
|
|
3804
|
+
fetchOptions.body = typeof body === 'string' ? body : JSON.stringify(body);
|
|
3805
|
+
}
|
|
3806
|
+
const response = await fetch(url, fetchOptions);
|
|
3807
|
+
clearTimeout(timeoutId);
|
|
3808
|
+
const statusCode = response.status.toString();
|
|
3809
|
+
let data;
|
|
3810
|
+
try {
|
|
3811
|
+
data = await response.json();
|
|
3812
|
+
}
|
|
3813
|
+
catch {
|
|
3814
|
+
data = await response.text();
|
|
3815
|
+
}
|
|
3816
|
+
// Convert headers to object
|
|
3817
|
+
const responseHeaders = {};
|
|
3818
|
+
response.headers.forEach((value, key) => {
|
|
3819
|
+
responseHeaders[key] = value;
|
|
3820
|
+
});
|
|
3910
3821
|
return {
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3822
|
+
output: {
|
|
3823
|
+
statusCode,
|
|
3824
|
+
data,
|
|
3825
|
+
status: response.status,
|
|
3826
|
+
headers: responseHeaders
|
|
3914
3827
|
},
|
|
3828
|
+
outcomes: {
|
|
3829
|
+
[statusCode]: true,
|
|
3830
|
+
Done: true
|
|
3831
|
+
}
|
|
3832
|
+
};
|
|
3833
|
+
}
|
|
3834
|
+
catch (error) {
|
|
3835
|
+
if (error.name === 'AbortError') {
|
|
3836
|
+
return {
|
|
3837
|
+
output: { error: 'Request timeout' },
|
|
3838
|
+
outcomes: { Timeout: true }
|
|
3839
|
+
};
|
|
3840
|
+
}
|
|
3841
|
+
return {
|
|
3842
|
+
output: { error: error.message },
|
|
3843
|
+
outcomes: { Failed: true }
|
|
3915
3844
|
};
|
|
3916
3845
|
}
|
|
3917
3846
|
}
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
{
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
},
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
useClass: AXCVersioningService,
|
|
3951
|
-
},
|
|
3952
|
-
// {
|
|
3953
|
-
// provide: AXP_ACTIVITY_PROVIDER,
|
|
3954
|
-
// useClass: AXCActivityProvider,
|
|
3955
|
-
// multi: true,
|
|
3956
|
-
// },
|
|
3957
|
-
{
|
|
3958
|
-
provide: AXPWorkflowExecutionService,
|
|
3959
|
-
useClass: AXCWorkflowExecutionService,
|
|
3960
|
-
},
|
|
3961
|
-
{
|
|
3962
|
-
provide: AXP_DATA_SEEDER_TOKEN,
|
|
3963
|
-
useClass: AXCWorkflowDefinitionDataSeeder$1,
|
|
3964
|
-
multi: true,
|
|
3965
|
-
},
|
|
3966
|
-
// Register generic Entity:UpdateStatus command
|
|
3967
|
-
provideCommandSetups([
|
|
3968
|
-
{ key: 'Entity:UpdateStatus', command: () => AXCEntityUpdateStatusCommand },
|
|
3969
|
-
]),
|
|
3970
|
-
AXCEntityUpdateStatusCommand,
|
|
3971
|
-
] }); }
|
|
3972
|
-
}
|
|
3973
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCCommonMockModule, decorators: [{
|
|
3974
|
-
type: NgModule,
|
|
3975
|
-
args: [{
|
|
3976
|
-
imports: [],
|
|
3977
|
-
exports: [],
|
|
3978
|
-
declarations: [],
|
|
3979
|
-
providers: [
|
|
3980
|
-
{
|
|
3981
|
-
provide: AXP_ACTIVITY_LOG_PROVIDER,
|
|
3982
|
-
useClass: AXCPActivityLogService,
|
|
3983
|
-
multi: true,
|
|
3984
|
-
},
|
|
3985
|
-
{
|
|
3986
|
-
provide: AXP_SEARCH_PROVIDER,
|
|
3987
|
-
useClass: EntitySearchProvider,
|
|
3988
|
-
multi: true,
|
|
3989
|
-
},
|
|
3990
|
-
{
|
|
3991
|
-
provide: AXPLockService,
|
|
3992
|
-
useClass: AXCLockService,
|
|
3993
|
-
},
|
|
3994
|
-
{
|
|
3995
|
-
provide: AXPFileStorageService,
|
|
3996
|
-
useClass: AXCFileStorageService,
|
|
3997
|
-
},
|
|
3998
|
-
{
|
|
3999
|
-
provide: AXPVersioningService,
|
|
4000
|
-
useClass: AXCVersioningService,
|
|
4001
|
-
},
|
|
4002
|
-
// {
|
|
4003
|
-
// provide: AXP_ACTIVITY_PROVIDER,
|
|
4004
|
-
// useClass: AXCActivityProvider,
|
|
4005
|
-
// multi: true,
|
|
4006
|
-
// },
|
|
4007
|
-
{
|
|
4008
|
-
provide: AXPWorkflowExecutionService,
|
|
4009
|
-
useClass: AXCWorkflowExecutionService,
|
|
4010
|
-
},
|
|
4011
|
-
{
|
|
4012
|
-
provide: AXP_DATA_SEEDER_TOKEN,
|
|
4013
|
-
useClass: AXCWorkflowDefinitionDataSeeder$1,
|
|
4014
|
-
multi: true,
|
|
4015
|
-
},
|
|
4016
|
-
// Register generic Entity:UpdateStatus command
|
|
4017
|
-
provideCommandSetups([
|
|
4018
|
-
{ key: 'Entity:UpdateStatus', command: () => AXCEntityUpdateStatusCommand },
|
|
4019
|
-
]),
|
|
4020
|
-
AXCEntityUpdateStatusCommand,
|
|
4021
|
-
],
|
|
4022
|
-
}]
|
|
4023
|
-
}] });
|
|
4024
|
-
|
|
4025
|
-
//#region ---- Imports ----
|
|
4026
|
-
function toUiRows(changes) {
|
|
4027
|
-
return changes.map((c) => ({
|
|
4028
|
-
badge: toBadge(c.op),
|
|
4029
|
-
path: c.path,
|
|
4030
|
-
summary: toSummary(c),
|
|
4031
|
-
oldValue: c.oldValue,
|
|
4032
|
-
newValue: c.newValue,
|
|
4033
|
-
}));
|
|
4034
|
-
}
|
|
4035
|
-
function toBadge(op) {
|
|
4036
|
-
switch (op) {
|
|
4037
|
-
case 'add':
|
|
4038
|
-
return 'Added';
|
|
4039
|
-
case 'remove':
|
|
4040
|
-
return 'Removed';
|
|
4041
|
-
case 'replace':
|
|
4042
|
-
return 'Replaced';
|
|
4043
|
-
case 'rename':
|
|
4044
|
-
return 'Renamed';
|
|
4045
|
-
case 'metadata':
|
|
4046
|
-
return 'Metadata';
|
|
4047
|
-
default:
|
|
4048
|
-
return 'Updated';
|
|
3847
|
+
//#endregion
|
|
3848
|
+
//#region ---- Instance Management ----
|
|
3849
|
+
/**
|
|
3850
|
+
* Convert workflow instance to instance state.
|
|
3851
|
+
*/
|
|
3852
|
+
instanceToInstanceState(instance) {
|
|
3853
|
+
const state = instance.workflowState;
|
|
3854
|
+
// Find current activity from execution contexts
|
|
3855
|
+
const currentContext = state.activityExecutionContexts.find(ctx => ctx.status === 'Running' && !ctx.completedAt);
|
|
3856
|
+
// Map status
|
|
3857
|
+
let status = 'running';
|
|
3858
|
+
if (instance.status === 'Finished') {
|
|
3859
|
+
if (instance.subStatus === 'Faulted') {
|
|
3860
|
+
status = 'error';
|
|
3861
|
+
}
|
|
3862
|
+
else {
|
|
3863
|
+
status = 'completed';
|
|
3864
|
+
}
|
|
3865
|
+
}
|
|
3866
|
+
else if (instance.subStatus === 'Suspended') {
|
|
3867
|
+
status = 'suspended';
|
|
3868
|
+
}
|
|
3869
|
+
return {
|
|
3870
|
+
instanceId: instance.id,
|
|
3871
|
+
workflowId: instance.definitionId,
|
|
3872
|
+
status,
|
|
3873
|
+
currentStepId: currentContext?.scheduledActivityNodeId,
|
|
3874
|
+
variables: state.properties || {},
|
|
3875
|
+
input: state.input || {},
|
|
3876
|
+
output: state.output || {},
|
|
3877
|
+
lastUpdated: new Date(instance.updatedAt)
|
|
3878
|
+
};
|
|
4049
3879
|
}
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
3880
|
+
/**
|
|
3881
|
+
* Update instance state in instance and save to IndexedDB.
|
|
3882
|
+
*/
|
|
3883
|
+
async updateInstanceFromState(instanceId, instanceState) {
|
|
3884
|
+
const instance = await this.getInstance(instanceId);
|
|
3885
|
+
if (!instance) {
|
|
3886
|
+
throw new Error(`Workflow instance not found: ${instanceId}`);
|
|
3887
|
+
}
|
|
3888
|
+
// Update instance from state
|
|
3889
|
+
const now = new Date().toISOString();
|
|
3890
|
+
// Map state status to instance status
|
|
3891
|
+
let status = 'Running';
|
|
3892
|
+
let subStatus = 'Executing';
|
|
3893
|
+
console.log('[AXCWorkflowRuntimeService] 🔄 updateInstanceFromState', {
|
|
3894
|
+
instanceStateStatus: instanceState.status,
|
|
3895
|
+
instanceId: instance.id,
|
|
3896
|
+
currentInstanceStatus: instance.status
|
|
3897
|
+
});
|
|
3898
|
+
if (instanceState.status === 'completed') {
|
|
3899
|
+
console.log('[AXCWorkflowRuntimeService] ✅ Marking workflow as Finished');
|
|
3900
|
+
status = 'Finished';
|
|
3901
|
+
subStatus = 'Finished';
|
|
3902
|
+
instance.finishedAt = now;
|
|
3903
|
+
}
|
|
3904
|
+
else if (instanceState.status === 'error') {
|
|
3905
|
+
status = 'Finished';
|
|
3906
|
+
subStatus = 'Faulted';
|
|
3907
|
+
instance.finishedAt = now;
|
|
3908
|
+
}
|
|
3909
|
+
else if (instanceState.status === 'suspended') {
|
|
3910
|
+
subStatus = 'Suspended';
|
|
3911
|
+
}
|
|
3912
|
+
instance.status = status;
|
|
3913
|
+
instance.subStatus = subStatus;
|
|
3914
|
+
instance.updatedAt = now;
|
|
3915
|
+
// Update workflow state
|
|
3916
|
+
instance.workflowState.status = status;
|
|
3917
|
+
instance.workflowState.subStatus = subStatus;
|
|
3918
|
+
instance.workflowState.input = instanceState.input || {};
|
|
3919
|
+
instance.workflowState.output = instanceState.output || {};
|
|
3920
|
+
instance.workflowState.properties = instanceState.variables || {};
|
|
3921
|
+
instance.workflowState.updatedAt = now;
|
|
3922
|
+
if (instanceState.status === 'completed' || instanceState.status === 'error') {
|
|
3923
|
+
instance.workflowState.finishedAt = now;
|
|
3924
|
+
}
|
|
3925
|
+
// Update activity execution context if currentStepId changed
|
|
3926
|
+
if (instanceState.currentStepId) {
|
|
3927
|
+
const existingContext = instance.workflowState.activityExecutionContexts.find(ctx => ctx.scheduledActivityNodeId === instanceState.currentStepId);
|
|
3928
|
+
if (!existingContext) {
|
|
3929
|
+
// Add new context
|
|
3930
|
+
instance.workflowState.activityExecutionContexts.push({
|
|
3931
|
+
id: `ctx-${instanceState.currentStepId}`,
|
|
3932
|
+
parentContextId: null,
|
|
3933
|
+
scheduledActivityNodeId: instanceState.currentStepId,
|
|
3934
|
+
ownerActivityNodeId: null,
|
|
3935
|
+
properties: {},
|
|
3936
|
+
activityState: null,
|
|
3937
|
+
dynamicVariables: [],
|
|
3938
|
+
status: 'Running',
|
|
3939
|
+
isExecuting: false,
|
|
3940
|
+
faultCount: 0,
|
|
3941
|
+
startedAt: now,
|
|
3942
|
+
completedAt: null,
|
|
3943
|
+
tag: null
|
|
3944
|
+
});
|
|
3945
|
+
}
|
|
3946
|
+
}
|
|
3947
|
+
// Save to IndexedDB
|
|
3948
|
+
await this.updateInstance(instance);
|
|
3949
|
+
// 🎯 Also update in entity service
|
|
3950
|
+
await this.updateInstanceInEntityService(instance);
|
|
3951
|
+
return instance;
|
|
3952
|
+
}
|
|
3953
|
+
/**
|
|
3954
|
+
* Get workflow instance from IndexedDB.
|
|
3955
|
+
*/
|
|
3956
|
+
async getInstance(instanceId) {
|
|
3957
|
+
return await this.db.instances.get(instanceId) || null;
|
|
3958
|
+
}
|
|
3959
|
+
/**
|
|
3960
|
+
* Update workflow instance in IndexedDB.
|
|
3961
|
+
*/
|
|
3962
|
+
async updateInstance(instance) {
|
|
3963
|
+
instance.updatedAt = new Date().toISOString();
|
|
3964
|
+
await this.db.instances.update(instance.id, instance);
|
|
4065
3965
|
}
|
|
3966
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowRuntimeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3967
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowRuntimeService }); }
|
|
4066
3968
|
}
|
|
4067
|
-
|
|
3969
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowRuntimeService, decorators: [{
|
|
3970
|
+
type: Injectable
|
|
3971
|
+
}] });
|
|
4068
3972
|
|
|
4069
3973
|
//#region ---- Imports ----
|
|
4070
3974
|
//#endregion
|
|
@@ -10219,6 +10123,198 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
|
|
|
10219
10123
|
}]
|
|
10220
10124
|
}] });
|
|
10221
10125
|
|
|
10126
|
+
class AXCDistributionInteractionMockService extends AXMDistributionInteractionServiceImpl {
|
|
10127
|
+
constructor() {
|
|
10128
|
+
super(...arguments);
|
|
10129
|
+
//#region ---- Services & Dependencies ----
|
|
10130
|
+
this.usersService = inject(AXMUsersEntityService);
|
|
10131
|
+
this.sessionService = inject(AXPSessionService);
|
|
10132
|
+
}
|
|
10133
|
+
//#endregion
|
|
10134
|
+
async query(request) {
|
|
10135
|
+
// Raw mode: return ungrouped interaction items (used for bulk aggregation)
|
|
10136
|
+
if (request?.params?.raw === true) {
|
|
10137
|
+
const rawRes = await super.query({
|
|
10138
|
+
filter: request.filter,
|
|
10139
|
+
sort: request.sort,
|
|
10140
|
+
params: request.params,
|
|
10141
|
+
take: 100000,
|
|
10142
|
+
skip: 0,
|
|
10143
|
+
});
|
|
10144
|
+
const total = rawRes.items.length;
|
|
10145
|
+
const skip = request?.skip ?? 0;
|
|
10146
|
+
const take = request?.take ?? total;
|
|
10147
|
+
const pagedItems = rawRes.items.slice(skip, skip + take);
|
|
10148
|
+
return {
|
|
10149
|
+
items: pagedItems,
|
|
10150
|
+
total,
|
|
10151
|
+
};
|
|
10152
|
+
}
|
|
10153
|
+
const items = (await super.query({
|
|
10154
|
+
filter: request.filter,
|
|
10155
|
+
sort: request.sort,
|
|
10156
|
+
params: request.params,
|
|
10157
|
+
take: 100000,
|
|
10158
|
+
skip: 0,
|
|
10159
|
+
})).items;
|
|
10160
|
+
// Extract documentDistributionId from request filter
|
|
10161
|
+
let documentDistributionId;
|
|
10162
|
+
if (request.filter && typeof request.filter === 'object') {
|
|
10163
|
+
if (request.filter.filters && request.filter.filters.length > 0 && request.filter.filters[0].field === 'documentDistributionId') {
|
|
10164
|
+
documentDistributionId = request.filter.filters[0].value;
|
|
10165
|
+
}
|
|
10166
|
+
}
|
|
10167
|
+
// Get the distribution record to access assignedTo property using storage service to avoid circular calls
|
|
10168
|
+
if (documentDistributionId) {
|
|
10169
|
+
try {
|
|
10170
|
+
// Use storage service directly to get distribution record
|
|
10171
|
+
const distribution = await this.storageService.getOne('DocumentManagement.Distribution', documentDistributionId);
|
|
10172
|
+
if (distribution && distribution.assignedTo && Array.isArray(distribution.assignedTo)) {
|
|
10173
|
+
// Get all assigned user IDs
|
|
10174
|
+
const assignedUserIds = distribution.assignedTo.map((user) => user.id || user);
|
|
10175
|
+
// Get existing interaction user IDs (from created.user.id)
|
|
10176
|
+
const existingUserIds = new Set(items
|
|
10177
|
+
.map((item) => item?.created?.user?.id)
|
|
10178
|
+
.filter((id) => !!id));
|
|
10179
|
+
// Find users who are assigned but don't have interaction records
|
|
10180
|
+
const missingUserIds = assignedUserIds.filter((userId) => !existingUserIds.has(userId));
|
|
10181
|
+
// Create missing interaction records
|
|
10182
|
+
for (const userId of missingUserIds) {
|
|
10183
|
+
let fullName = 'Unknown';
|
|
10184
|
+
try {
|
|
10185
|
+
const userRes = await this.usersService.query({
|
|
10186
|
+
skip: 0,
|
|
10187
|
+
take: 1,
|
|
10188
|
+
filter: { field: 'id', operator: { type: 'equal' }, value: userId },
|
|
10189
|
+
});
|
|
10190
|
+
const u = userRes.items?.[0];
|
|
10191
|
+
if (u) {
|
|
10192
|
+
const candidate = `${u.firstName ?? ''} ${u.lastName ?? ''}`.trim();
|
|
10193
|
+
fullName = candidate || u.username || u.title || u.name || fullName;
|
|
10194
|
+
}
|
|
10195
|
+
}
|
|
10196
|
+
catch { }
|
|
10197
|
+
const missingRecord = {
|
|
10198
|
+
id: `missing-${documentDistributionId}-${userId}`,
|
|
10199
|
+
documentDistributionId: documentDistributionId,
|
|
10200
|
+
auditInfo: {
|
|
10201
|
+
created: { at: new Date(), by: { id: userId, type: 'user', fullName } },
|
|
10202
|
+
updated: { at: new Date(), by: { id: userId, type: 'user', fullName } },
|
|
10203
|
+
},
|
|
10204
|
+
action: 'Fake', // Default action, but with false flags
|
|
10205
|
+
};
|
|
10206
|
+
// Add the missing record to the items array
|
|
10207
|
+
items.push(missingRecord);
|
|
10208
|
+
}
|
|
10209
|
+
}
|
|
10210
|
+
}
|
|
10211
|
+
catch (error) {
|
|
10212
|
+
}
|
|
10213
|
+
}
|
|
10214
|
+
// Group items by creator user id
|
|
10215
|
+
const groupedItems = this.groupItemsByCreatedBy(items);
|
|
10216
|
+
// Collect distinct user IDs from grouped items
|
|
10217
|
+
const userIds = Array.from(new Set(groupedItems.map((g) => g.createdBy).filter((id) => !!id)));
|
|
10218
|
+
// Fetch users by IDs (in operator)
|
|
10219
|
+
let usersMap = new Map();
|
|
10220
|
+
if (userIds.length > 0) {
|
|
10221
|
+
try {
|
|
10222
|
+
const usersRes = await this.usersService.query({
|
|
10223
|
+
skip: 0,
|
|
10224
|
+
take: userIds.length,
|
|
10225
|
+
filter: {
|
|
10226
|
+
field: 'id',
|
|
10227
|
+
operator: { type: 'in' },
|
|
10228
|
+
value: userIds,
|
|
10229
|
+
},
|
|
10230
|
+
});
|
|
10231
|
+
usersMap = new Map(usersRes.items.map((u) => [
|
|
10232
|
+
u.id,
|
|
10233
|
+
{
|
|
10234
|
+
id: u.id,
|
|
10235
|
+
name: u.username,
|
|
10236
|
+
title: `${u.firstName} ${u.lastName}`,
|
|
10237
|
+
},
|
|
10238
|
+
]));
|
|
10239
|
+
}
|
|
10240
|
+
catch {
|
|
10241
|
+
// If fetching users fails, continue without enrichment
|
|
10242
|
+
usersMap = new Map();
|
|
10243
|
+
}
|
|
10244
|
+
}
|
|
10245
|
+
// Convert grouped items back to the expected format with user enrichment
|
|
10246
|
+
const enrichedItems = groupedItems.map((summary) => ({
|
|
10247
|
+
id: summary.id,
|
|
10248
|
+
createdBy: summary.createdBy,
|
|
10249
|
+
hasViewed: summary.hasViewed,
|
|
10250
|
+
lastViewDate: summary.lastViewDate,
|
|
10251
|
+
hasSigned: summary.hasSigned,
|
|
10252
|
+
lastSignDate: summary.lastSignDate,
|
|
10253
|
+
totalInteractions: summary.totalInteractions,
|
|
10254
|
+
user: usersMap.get(summary.createdBy) ?? {
|
|
10255
|
+
id: this.sessionService?.user?.id ?? '',
|
|
10256
|
+
name: this.sessionService?.user?.name ?? '',
|
|
10257
|
+
title: this.sessionService?.user?.title ?? '',
|
|
10258
|
+
},
|
|
10259
|
+
}));
|
|
10260
|
+
// Apply pagination using request.skip and request.take
|
|
10261
|
+
const total = enrichedItems.length;
|
|
10262
|
+
const skip = request?.skip ?? 0;
|
|
10263
|
+
const take = request?.take ?? total;
|
|
10264
|
+
const pagedItems = enrichedItems.slice(skip, skip + take);
|
|
10265
|
+
return {
|
|
10266
|
+
items: pagedItems,
|
|
10267
|
+
total,
|
|
10268
|
+
};
|
|
10269
|
+
}
|
|
10270
|
+
/**
|
|
10271
|
+
* Groups distribution interaction items by createdBy and creates a summary
|
|
10272
|
+
*/
|
|
10273
|
+
groupItemsByCreatedBy(items) {
|
|
10274
|
+
console.log(items);
|
|
10275
|
+
const groupedMap = new Map();
|
|
10276
|
+
for (const item of items) {
|
|
10277
|
+
const createdBy = item?.created?.user?.id || 'unknown';
|
|
10278
|
+
if (!groupedMap.has(createdBy)) {
|
|
10279
|
+
// Initialize summary for new createdBy
|
|
10280
|
+
groupedMap.set(createdBy, {
|
|
10281
|
+
id: createdBy, // Use createdBy as the ID for the summary
|
|
10282
|
+
createdBy: createdBy,
|
|
10283
|
+
hasViewed: false,
|
|
10284
|
+
lastViewDate: undefined,
|
|
10285
|
+
hasSigned: false,
|
|
10286
|
+
lastSignDate: undefined,
|
|
10287
|
+
totalInteractions: 0,
|
|
10288
|
+
});
|
|
10289
|
+
}
|
|
10290
|
+
const summary = groupedMap.get(createdBy);
|
|
10291
|
+
summary.totalInteractions++;
|
|
10292
|
+
// Check for view actions
|
|
10293
|
+
if (item.action === 'Viewed') {
|
|
10294
|
+
summary.hasViewed = true;
|
|
10295
|
+
// Update last view date if this is more recent
|
|
10296
|
+
if (!summary.lastViewDate || item.actionTime && item.actionTime > summary.lastViewDate) {
|
|
10297
|
+
summary.lastViewDate = item.actionTime;
|
|
10298
|
+
}
|
|
10299
|
+
}
|
|
10300
|
+
// Check for sign actions
|
|
10301
|
+
if (item.action === 'Signed') {
|
|
10302
|
+
summary.hasSigned = true;
|
|
10303
|
+
// Update last sign date if this is more recent
|
|
10304
|
+
if (!summary.lastSignDate || item.actionTime && item.actionTime > summary.lastSignDate) {
|
|
10305
|
+
summary.lastSignDate = item.actionTime;
|
|
10306
|
+
}
|
|
10307
|
+
}
|
|
10308
|
+
}
|
|
10309
|
+
return Array.from(groupedMap.values());
|
|
10310
|
+
}
|
|
10311
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDistributionInteractionMockService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
10312
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDistributionInteractionMockService }); }
|
|
10313
|
+
}
|
|
10314
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDistributionInteractionMockService, decorators: [{
|
|
10315
|
+
type: Injectable
|
|
10316
|
+
}] });
|
|
10317
|
+
|
|
10222
10318
|
class AXCDistributionMockService extends AXMDistributionServiceImpl {
|
|
10223
10319
|
constructor() {
|
|
10224
10320
|
super(...arguments);
|
|
@@ -10328,153 +10424,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
|
|
|
10328
10424
|
type: Injectable
|
|
10329
10425
|
}] });
|
|
10330
10426
|
|
|
10331
|
-
const DOCUMENT_TYPES = [
|
|
10332
|
-
{
|
|
10333
|
-
id: AXPDataGenerator.uuid(),
|
|
10334
|
-
name: 'risk-assessment-report',
|
|
10335
|
-
title: 'Risk Assessment Report',
|
|
10336
|
-
maxSize: 5000,
|
|
10337
|
-
isEncrypted: false,
|
|
10338
|
-
enableVersioning: true,
|
|
10339
|
-
enableExpiration: false,
|
|
10340
|
-
requiresReview: true,
|
|
10341
|
-
requiresDistribution: false,
|
|
10342
|
-
metaDataList: [
|
|
10343
|
-
{
|
|
10344
|
-
id: AXPDataGenerator.uuid(),
|
|
10345
|
-
text: 'Description',
|
|
10346
|
-
// "parentId": "9e3c7586-4722-4170-bc55-e612ca2ab4b5",
|
|
10347
|
-
hasCheckbox: true,
|
|
10348
|
-
isSelected: true,
|
|
10349
|
-
interface: '{"type":"text-editor","name":"description-editor1","meta":{"header":true},"options":{"multiple":false,"disabled":false,"hasClearButton":false},"defaultValue":"","path":"description"}',
|
|
10350
|
-
name: 'description',
|
|
10351
|
-
title: 'Description',
|
|
10352
|
-
isRequired: true,
|
|
10353
|
-
placeholder: '',
|
|
10354
|
-
status: 'active',
|
|
10355
|
-
},
|
|
10356
|
-
],
|
|
10357
|
-
type: {
|
|
10358
|
-
selectedItems: [
|
|
10359
|
-
{
|
|
10360
|
-
name: 'image.jpg',
|
|
10361
|
-
title: 'Image/JPEG',
|
|
10362
|
-
icon: 'fa-light fa-image ax-text-purple-500',
|
|
10363
|
-
},
|
|
10364
|
-
{
|
|
10365
|
-
name: 'image.png',
|
|
10366
|
-
title: 'Image/PNG',
|
|
10367
|
-
icon: 'fa-light fa-image ax-text-purple-500',
|
|
10368
|
-
},
|
|
10369
|
-
{
|
|
10370
|
-
name: 'image.gif',
|
|
10371
|
-
title: 'Image/GIF',
|
|
10372
|
-
icon: 'fa-light fa-image ax-text-purple-500',
|
|
10373
|
-
},
|
|
10374
|
-
],
|
|
10375
|
-
context: {
|
|
10376
|
-
image: {
|
|
10377
|
-
width: '22',
|
|
10378
|
-
height: '33',
|
|
10379
|
-
},
|
|
10380
|
-
gift: {
|
|
10381
|
-
duration: '11',
|
|
10382
|
-
},
|
|
10383
|
-
},
|
|
10384
|
-
},
|
|
10385
|
-
},
|
|
10386
|
-
{
|
|
10387
|
-
id: AXPDataGenerator.uuid(),
|
|
10388
|
-
name: 'any',
|
|
10389
|
-
title: 'Upload File',
|
|
10390
|
-
maxSize: 50000,
|
|
10391
|
-
isEncrypted: false,
|
|
10392
|
-
enableVersioning: false,
|
|
10393
|
-
enableExpiration: false,
|
|
10394
|
-
requiresReview: false,
|
|
10395
|
-
requiresDistribution: false,
|
|
10396
|
-
control: { isSystem: true },
|
|
10397
|
-
metaDataList: [],
|
|
10398
|
-
type: {
|
|
10399
|
-
selectedItems: [],
|
|
10400
|
-
context: {
|
|
10401
|
-
audio: {
|
|
10402
|
-
bitrate: '320',
|
|
10403
|
-
duration: '180',
|
|
10404
|
-
},
|
|
10405
|
-
},
|
|
10406
|
-
},
|
|
10407
|
-
},
|
|
10408
|
-
{
|
|
10409
|
-
id: AXPDataGenerator.uuid(),
|
|
10410
|
-
name: 'file-meeting',
|
|
10411
|
-
title: 'File Meeting',
|
|
10412
|
-
maxSize: 50000,
|
|
10413
|
-
isEncrypted: false,
|
|
10414
|
-
enableVersioning: true,
|
|
10415
|
-
enableExpiration: true,
|
|
10416
|
-
requiresReview: true,
|
|
10417
|
-
requiresDistribution: true,
|
|
10418
|
-
metaDataList: [
|
|
10419
|
-
{
|
|
10420
|
-
id: AXPDataGenerator.uuid(),
|
|
10421
|
-
text: 'Description',
|
|
10422
|
-
// "parentId": "9e3c7586-4722-4170-bc55-e612ca2ab4b5",
|
|
10423
|
-
hasCheckbox: true,
|
|
10424
|
-
isSelected: true,
|
|
10425
|
-
interface: '{"type":"text-editor","name":"description-editor1","meta":{"header":true},"options":{"multiple":false,"disabled":false,"hasClearButton":false},"defaultValue":"","path":"description"}',
|
|
10426
|
-
name: 'description',
|
|
10427
|
-
title: 'Description',
|
|
10428
|
-
isRequired: true,
|
|
10429
|
-
placeholder: '',
|
|
10430
|
-
status: 'active',
|
|
10431
|
-
},
|
|
10432
|
-
],
|
|
10433
|
-
type: {
|
|
10434
|
-
selectedItems: [
|
|
10435
|
-
{
|
|
10436
|
-
name: 'image.jpg',
|
|
10437
|
-
title: 'Image/JPEG',
|
|
10438
|
-
icon: 'fa-light fa-image ax-text-purple-500',
|
|
10439
|
-
},
|
|
10440
|
-
{
|
|
10441
|
-
name: 'image.png',
|
|
10442
|
-
title: 'Image/PNG',
|
|
10443
|
-
icon: 'fa-light fa-image ax-text-purple-500',
|
|
10444
|
-
},
|
|
10445
|
-
{
|
|
10446
|
-
name: 'image.gif',
|
|
10447
|
-
title: 'Image/GIF',
|
|
10448
|
-
icon: 'fa-light fa-image ax-text-purple-500',
|
|
10449
|
-
},
|
|
10450
|
-
],
|
|
10451
|
-
context: {
|
|
10452
|
-
image: {
|
|
10453
|
-
width: '22',
|
|
10454
|
-
height: '33',
|
|
10455
|
-
},
|
|
10456
|
-
gift: {
|
|
10457
|
-
duration: '11',
|
|
10458
|
-
},
|
|
10459
|
-
},
|
|
10460
|
-
},
|
|
10461
|
-
},
|
|
10462
|
-
];
|
|
10463
|
-
|
|
10464
|
-
class AXCDocumentTypeDataSeeder {
|
|
10465
|
-
constructor() {
|
|
10466
|
-
this.storageService = inject(AXPEntityStorageService);
|
|
10467
|
-
}
|
|
10468
|
-
async seed() {
|
|
10469
|
-
await this.storageService.initial(`${RootConfig$6.module.name}.${RootConfig$6.entities.documentType.name}`, DOCUMENT_TYPES);
|
|
10470
|
-
}
|
|
10471
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDocumentTypeDataSeeder, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
10472
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDocumentTypeDataSeeder }); }
|
|
10473
|
-
}
|
|
10474
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDocumentTypeDataSeeder, decorators: [{
|
|
10475
|
-
type: Injectable
|
|
10476
|
-
}] });
|
|
10477
|
-
|
|
10478
10427
|
class AXCFolderMockService extends AXMFolderServiceImpl {
|
|
10479
10428
|
async updateParentFolders(folder) {
|
|
10480
10429
|
if (folder.parentId) {
|
|
@@ -10535,24 +10484,6 @@ class AXCFolderMockService extends AXMFolderServiceImpl {
|
|
|
10535
10484
|
await super.deleteOne(id);
|
|
10536
10485
|
}
|
|
10537
10486
|
}
|
|
10538
|
-
async getPath(folderId, stopFolderId) {
|
|
10539
|
-
const path = [];
|
|
10540
|
-
let currentId = folderId;
|
|
10541
|
-
while (currentId) {
|
|
10542
|
-
const folder = await super.getOne(currentId);
|
|
10543
|
-
if (!folder) {
|
|
10544
|
-
// Folder not found in persisted storage
|
|
10545
|
-
break;
|
|
10546
|
-
}
|
|
10547
|
-
path.push(folder);
|
|
10548
|
-
if (currentId == stopFolderId) {
|
|
10549
|
-
// Stop if we reach the specified stop folder ID
|
|
10550
|
-
break;
|
|
10551
|
-
}
|
|
10552
|
-
currentId = folder.parentId ?? null;
|
|
10553
|
-
}
|
|
10554
|
-
return path.reverse();
|
|
10555
|
-
}
|
|
10556
10487
|
/**
|
|
10557
10488
|
* Finds the first non-inheriting folder in the hierarchy
|
|
10558
10489
|
* @param folderId ID of the starting folder
|
|
@@ -10645,46 +10576,6 @@ class AXCFolderMockService extends AXMFolderServiceImpl {
|
|
|
10645
10576
|
}
|
|
10646
10577
|
return folder;
|
|
10647
10578
|
}
|
|
10648
|
-
/**
|
|
10649
|
-
* Search folders with specified name using recursive method
|
|
10650
|
-
* @param searchTerm search term
|
|
10651
|
-
* @param rootFolderId ID of the root folder to start the search
|
|
10652
|
-
*/
|
|
10653
|
-
async searchFolders(searchTerm, rootFolderId) {
|
|
10654
|
-
const result = [];
|
|
10655
|
-
await this.searchFoldersRecursive(searchTerm, rootFolderId, result);
|
|
10656
|
-
return result;
|
|
10657
|
-
}
|
|
10658
|
-
/**
|
|
10659
|
-
* Recursive folder search
|
|
10660
|
-
* @param searchTerm search term
|
|
10661
|
-
* @param folderId current folder ID
|
|
10662
|
-
* @param result results array
|
|
10663
|
-
*/
|
|
10664
|
-
async searchFoldersRecursive(searchTerm, folderId, result) {
|
|
10665
|
-
try {
|
|
10666
|
-
// Get current folder
|
|
10667
|
-
const currentFolder = await this.getOne(folderId);
|
|
10668
|
-
if (!currentFolder) {
|
|
10669
|
-
return;
|
|
10670
|
-
}
|
|
10671
|
-
// Check subfolders
|
|
10672
|
-
const subFolders = currentFolder.folders || [];
|
|
10673
|
-
// Add folders that match the search term
|
|
10674
|
-
for (const folder of subFolders) {
|
|
10675
|
-
if (folder.name.toLowerCase().includes(searchTerm.toLowerCase())) {
|
|
10676
|
-
result.push(folder);
|
|
10677
|
-
}
|
|
10678
|
-
// Recursive search in subfolders
|
|
10679
|
-
if (folder.id) {
|
|
10680
|
-
await this.searchFoldersRecursive(searchTerm, folder.id, result);
|
|
10681
|
-
}
|
|
10682
|
-
}
|
|
10683
|
-
}
|
|
10684
|
-
catch (error) {
|
|
10685
|
-
console.error('Error searching folders:', error);
|
|
10686
|
-
}
|
|
10687
|
-
}
|
|
10688
10579
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCFolderMockService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
10689
10580
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCFolderMockService }); }
|
|
10690
10581
|
}
|
|
@@ -10751,12 +10642,78 @@ class AXMFileMockService extends AXMDocumentServiceImpl {
|
|
|
10751
10642
|
await super.deleteOne(id);
|
|
10752
10643
|
}
|
|
10753
10644
|
}
|
|
10754
|
-
async
|
|
10755
|
-
const
|
|
10645
|
+
async query(request) {
|
|
10646
|
+
const result = await super.query(request);
|
|
10647
|
+
// Use lazy injection to avoid circular dependency
|
|
10648
|
+
const reviewService = this.injector.get(AXMReviewService, null);
|
|
10649
|
+
if (reviewService) {
|
|
10650
|
+
// Process items sequentially to avoid performance issues
|
|
10651
|
+
for (const item of result.items) {
|
|
10652
|
+
try {
|
|
10653
|
+
if (item.documentType?.requiresReview) {
|
|
10654
|
+
const reviewResult = await reviewService.query({
|
|
10655
|
+
skip: 0,
|
|
10656
|
+
take: 1,
|
|
10657
|
+
filter: {
|
|
10658
|
+
logic: 'and',
|
|
10659
|
+
filters: [
|
|
10660
|
+
{
|
|
10661
|
+
field: 'documentId',
|
|
10662
|
+
operator: {
|
|
10663
|
+
type: 'equal',
|
|
10664
|
+
},
|
|
10665
|
+
value: item.id,
|
|
10666
|
+
},
|
|
10667
|
+
{
|
|
10668
|
+
field: 'version',
|
|
10669
|
+
operator: {
|
|
10670
|
+
type: 'equal',
|
|
10671
|
+
},
|
|
10672
|
+
value: item.version,
|
|
10673
|
+
},
|
|
10674
|
+
],
|
|
10675
|
+
},
|
|
10676
|
+
});
|
|
10677
|
+
item.documentReview = reviewResult.items[0];
|
|
10678
|
+
}
|
|
10679
|
+
}
|
|
10680
|
+
catch (error) {
|
|
10681
|
+
// Continue processing other items if one fails
|
|
10682
|
+
}
|
|
10683
|
+
}
|
|
10684
|
+
}
|
|
10685
|
+
// Map result items to fileData structure
|
|
10686
|
+
const mappedResult = {
|
|
10687
|
+
...result,
|
|
10688
|
+
items: result.items.map((item) => ({
|
|
10689
|
+
...item,
|
|
10690
|
+
// fileId: 'file:' + item.fileId,
|
|
10691
|
+
})),
|
|
10692
|
+
};
|
|
10693
|
+
return mappedResult;
|
|
10694
|
+
}
|
|
10695
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMFileMockService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
10696
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMFileMockService }); }
|
|
10697
|
+
}
|
|
10698
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMFileMockService, decorators: [{
|
|
10699
|
+
type: Injectable
|
|
10700
|
+
}], ctorParameters: () => [] });
|
|
10701
|
+
|
|
10702
|
+
class AXCDocumentManagementService {
|
|
10703
|
+
constructor() {
|
|
10704
|
+
// Create instances directly to avoid circular dependency
|
|
10705
|
+
// These services all use the same Dexie database, so they share data
|
|
10706
|
+
this.folderService = new AXCFolderMockService();
|
|
10707
|
+
this.documentService = new AXMFileMockService();
|
|
10708
|
+
this.documentTypeService = new AXMDocumentTypeServiceImpl();
|
|
10709
|
+
}
|
|
10710
|
+
//#region ---- Document Methods ----
|
|
10711
|
+
async getDocumentPath(documentId, stopFolderId) {
|
|
10712
|
+
const file = await this.documentService.getOne(documentId);
|
|
10756
10713
|
if (!file) {
|
|
10757
10714
|
throw new Error('File not found');
|
|
10758
10715
|
}
|
|
10759
|
-
return await this.
|
|
10716
|
+
return await this.getFolderPath(file.parentId, stopFolderId);
|
|
10760
10717
|
}
|
|
10761
10718
|
async searchFiles(searchTerm, rootFolderId) {
|
|
10762
10719
|
const result = [];
|
|
@@ -10764,10 +10721,10 @@ class AXMFileMockService extends AXMDocumentServiceImpl {
|
|
|
10764
10721
|
return result;
|
|
10765
10722
|
}
|
|
10766
10723
|
/**
|
|
10767
|
-
*
|
|
10768
|
-
* @param searchTerm
|
|
10769
|
-
* @param folderId
|
|
10770
|
-
* @param result
|
|
10724
|
+
* Recursive file search
|
|
10725
|
+
* @param searchTerm search term
|
|
10726
|
+
* @param folderId current folder ID
|
|
10727
|
+
* @param result results array
|
|
10771
10728
|
*/
|
|
10772
10729
|
async searchFilesRecursive(searchTerm, folderId, result) {
|
|
10773
10730
|
try {
|
|
@@ -10792,63 +10749,247 @@ class AXMFileMockService extends AXMDocumentServiceImpl {
|
|
|
10792
10749
|
console.error('Error searching files:', error);
|
|
10793
10750
|
}
|
|
10794
10751
|
}
|
|
10795
|
-
async
|
|
10796
|
-
const
|
|
10797
|
-
|
|
10798
|
-
|
|
10799
|
-
|
|
10800
|
-
|
|
10801
|
-
|
|
10802
|
-
|
|
10803
|
-
|
|
10804
|
-
|
|
10805
|
-
|
|
10806
|
-
|
|
10807
|
-
|
|
10808
|
-
|
|
10809
|
-
|
|
10810
|
-
|
|
10811
|
-
|
|
10812
|
-
|
|
10813
|
-
|
|
10814
|
-
|
|
10815
|
-
|
|
10816
|
-
|
|
10817
|
-
|
|
10818
|
-
|
|
10819
|
-
|
|
10820
|
-
|
|
10821
|
-
|
|
10822
|
-
|
|
10823
|
-
|
|
10824
|
-
|
|
10825
|
-
|
|
10826
|
-
|
|
10827
|
-
|
|
10828
|
-
|
|
10752
|
+
async findManyDocuments(ids) {
|
|
10753
|
+
const resp = await this.documentService.query({
|
|
10754
|
+
skip: 0,
|
|
10755
|
+
take: ids.length || 1000,
|
|
10756
|
+
filter: {
|
|
10757
|
+
field: 'id',
|
|
10758
|
+
operator: { type: 'in' },
|
|
10759
|
+
value: ids,
|
|
10760
|
+
},
|
|
10761
|
+
});
|
|
10762
|
+
return (resp?.items ?? []);
|
|
10763
|
+
}
|
|
10764
|
+
//#endregion
|
|
10765
|
+
//#region ---- Folder Methods ----
|
|
10766
|
+
async getFolderPath(folderId, stopFolderId) {
|
|
10767
|
+
const path = [];
|
|
10768
|
+
let currentId = folderId;
|
|
10769
|
+
while (currentId) {
|
|
10770
|
+
const folder = await this.folderService.getOne(currentId);
|
|
10771
|
+
if (!folder) {
|
|
10772
|
+
// Folder not found in persisted storage
|
|
10773
|
+
break;
|
|
10774
|
+
}
|
|
10775
|
+
path.push(folder);
|
|
10776
|
+
if (currentId == stopFolderId) {
|
|
10777
|
+
// Stop if we reach the specified stop folder ID
|
|
10778
|
+
break;
|
|
10779
|
+
}
|
|
10780
|
+
currentId = folder.parentId ?? null;
|
|
10781
|
+
}
|
|
10782
|
+
return path.reverse();
|
|
10783
|
+
}
|
|
10784
|
+
async searchFolders(searchTerm, rootFolderId) {
|
|
10785
|
+
const result = [];
|
|
10786
|
+
await this.searchFoldersRecursive(searchTerm, rootFolderId, result);
|
|
10787
|
+
return result;
|
|
10788
|
+
}
|
|
10789
|
+
/**
|
|
10790
|
+
* Recursive folder search
|
|
10791
|
+
* @param searchTerm search term
|
|
10792
|
+
* @param folderId current folder ID
|
|
10793
|
+
* @param result results array
|
|
10794
|
+
*/
|
|
10795
|
+
async searchFoldersRecursive(searchTerm, folderId, result) {
|
|
10796
|
+
try {
|
|
10797
|
+
// Get current folder
|
|
10798
|
+
const currentFolder = await this.folderService.getOne(folderId);
|
|
10799
|
+
if (!currentFolder) {
|
|
10800
|
+
return;
|
|
10801
|
+
}
|
|
10802
|
+
// Check subfolders
|
|
10803
|
+
const subFolders = currentFolder.folders || [];
|
|
10804
|
+
// Add folders that match the search term
|
|
10805
|
+
for (const folder of subFolders) {
|
|
10806
|
+
if (folder.name.toLowerCase().includes(searchTerm.toLowerCase())) {
|
|
10807
|
+
result.push(folder);
|
|
10829
10808
|
}
|
|
10830
|
-
|
|
10831
|
-
|
|
10809
|
+
// Recursive search in subfolders
|
|
10810
|
+
if (folder.id) {
|
|
10811
|
+
await this.searchFoldersRecursive(searchTerm, folder.id, result);
|
|
10832
10812
|
}
|
|
10833
10813
|
}
|
|
10834
10814
|
}
|
|
10835
|
-
|
|
10836
|
-
|
|
10837
|
-
|
|
10838
|
-
items: result.items.map((item) => ({
|
|
10839
|
-
...item,
|
|
10840
|
-
// fileId: 'file:' + item.fileId,
|
|
10841
|
-
})),
|
|
10842
|
-
};
|
|
10843
|
-
return mappedResult;
|
|
10815
|
+
catch (error) {
|
|
10816
|
+
console.error('Error searching folders:', error);
|
|
10817
|
+
}
|
|
10844
10818
|
}
|
|
10845
|
-
|
|
10846
|
-
|
|
10819
|
+
//#endregion
|
|
10820
|
+
//#region ---- DocumentType Methods ----
|
|
10821
|
+
async getDocumentTypeByName(name) {
|
|
10822
|
+
return (await this.documentTypeService.query({
|
|
10823
|
+
skip: 0,
|
|
10824
|
+
take: 1,
|
|
10825
|
+
filter: {
|
|
10826
|
+
logic: 'and',
|
|
10827
|
+
filters: [
|
|
10828
|
+
{
|
|
10829
|
+
field: 'name',
|
|
10830
|
+
operator: {
|
|
10831
|
+
type: 'equal',
|
|
10832
|
+
},
|
|
10833
|
+
value: name,
|
|
10834
|
+
},
|
|
10835
|
+
],
|
|
10836
|
+
},
|
|
10837
|
+
})).items[0];
|
|
10838
|
+
}
|
|
10839
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDocumentManagementService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
10840
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDocumentManagementService }); }
|
|
10847
10841
|
}
|
|
10848
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type:
|
|
10842
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDocumentManagementService, decorators: [{
|
|
10849
10843
|
type: Injectable
|
|
10850
10844
|
}], ctorParameters: () => [] });
|
|
10851
10845
|
|
|
10846
|
+
const DOCUMENT_TYPES = [
|
|
10847
|
+
{
|
|
10848
|
+
id: AXPDataGenerator.uuid(),
|
|
10849
|
+
name: 'risk-assessment-report',
|
|
10850
|
+
title: 'Risk Assessment Report',
|
|
10851
|
+
maxSize: 5000,
|
|
10852
|
+
isEncrypted: false,
|
|
10853
|
+
enableVersioning: true,
|
|
10854
|
+
enableExpiration: false,
|
|
10855
|
+
requiresReview: true,
|
|
10856
|
+
requiresDistribution: false,
|
|
10857
|
+
metaDataList: [
|
|
10858
|
+
{
|
|
10859
|
+
id: AXPDataGenerator.uuid(),
|
|
10860
|
+
text: 'Description',
|
|
10861
|
+
// "parentId": "9e3c7586-4722-4170-bc55-e612ca2ab4b5",
|
|
10862
|
+
hasCheckbox: true,
|
|
10863
|
+
isSelected: true,
|
|
10864
|
+
interface: '{"type":"text-editor","name":"description-editor1","meta":{"header":true},"options":{"multiple":false,"disabled":false,"hasClearButton":false},"defaultValue":"","path":"description"}',
|
|
10865
|
+
name: 'description',
|
|
10866
|
+
title: 'Description',
|
|
10867
|
+
isRequired: true,
|
|
10868
|
+
placeholder: '',
|
|
10869
|
+
status: 'active',
|
|
10870
|
+
},
|
|
10871
|
+
],
|
|
10872
|
+
type: {
|
|
10873
|
+
selectedItems: [
|
|
10874
|
+
{
|
|
10875
|
+
name: 'image.jpg',
|
|
10876
|
+
title: 'Image/JPEG',
|
|
10877
|
+
icon: 'fa-light fa-image ax-text-purple-500',
|
|
10878
|
+
},
|
|
10879
|
+
{
|
|
10880
|
+
name: 'image.png',
|
|
10881
|
+
title: 'Image/PNG',
|
|
10882
|
+
icon: 'fa-light fa-image ax-text-purple-500',
|
|
10883
|
+
},
|
|
10884
|
+
{
|
|
10885
|
+
name: 'image.gif',
|
|
10886
|
+
title: 'Image/GIF',
|
|
10887
|
+
icon: 'fa-light fa-image ax-text-purple-500',
|
|
10888
|
+
},
|
|
10889
|
+
],
|
|
10890
|
+
context: {
|
|
10891
|
+
image: {
|
|
10892
|
+
width: '22',
|
|
10893
|
+
height: '33',
|
|
10894
|
+
},
|
|
10895
|
+
gift: {
|
|
10896
|
+
duration: '11',
|
|
10897
|
+
},
|
|
10898
|
+
},
|
|
10899
|
+
},
|
|
10900
|
+
},
|
|
10901
|
+
{
|
|
10902
|
+
id: AXPDataGenerator.uuid(),
|
|
10903
|
+
name: 'any',
|
|
10904
|
+
title: 'Upload File',
|
|
10905
|
+
maxSize: 50000,
|
|
10906
|
+
isEncrypted: false,
|
|
10907
|
+
enableVersioning: false,
|
|
10908
|
+
enableExpiration: false,
|
|
10909
|
+
requiresReview: false,
|
|
10910
|
+
requiresDistribution: false,
|
|
10911
|
+
control: { isSystem: true },
|
|
10912
|
+
metaDataList: [],
|
|
10913
|
+
type: {
|
|
10914
|
+
selectedItems: [],
|
|
10915
|
+
context: {
|
|
10916
|
+
audio: {
|
|
10917
|
+
bitrate: '320',
|
|
10918
|
+
duration: '180',
|
|
10919
|
+
},
|
|
10920
|
+
},
|
|
10921
|
+
},
|
|
10922
|
+
},
|
|
10923
|
+
{
|
|
10924
|
+
id: AXPDataGenerator.uuid(),
|
|
10925
|
+
name: 'file-meeting',
|
|
10926
|
+
title: 'File Meeting',
|
|
10927
|
+
maxSize: 50000,
|
|
10928
|
+
isEncrypted: false,
|
|
10929
|
+
enableVersioning: true,
|
|
10930
|
+
enableExpiration: true,
|
|
10931
|
+
requiresReview: true,
|
|
10932
|
+
requiresDistribution: true,
|
|
10933
|
+
metaDataList: [
|
|
10934
|
+
{
|
|
10935
|
+
id: AXPDataGenerator.uuid(),
|
|
10936
|
+
text: 'Description',
|
|
10937
|
+
// "parentId": "9e3c7586-4722-4170-bc55-e612ca2ab4b5",
|
|
10938
|
+
hasCheckbox: true,
|
|
10939
|
+
isSelected: true,
|
|
10940
|
+
interface: '{"type":"text-editor","name":"description-editor1","meta":{"header":true},"options":{"multiple":false,"disabled":false,"hasClearButton":false},"defaultValue":"","path":"description"}',
|
|
10941
|
+
name: 'description',
|
|
10942
|
+
title: 'Description',
|
|
10943
|
+
isRequired: true,
|
|
10944
|
+
placeholder: '',
|
|
10945
|
+
status: 'active',
|
|
10946
|
+
},
|
|
10947
|
+
],
|
|
10948
|
+
type: {
|
|
10949
|
+
selectedItems: [
|
|
10950
|
+
{
|
|
10951
|
+
name: 'image.jpg',
|
|
10952
|
+
title: 'Image/JPEG',
|
|
10953
|
+
icon: 'fa-light fa-image ax-text-purple-500',
|
|
10954
|
+
},
|
|
10955
|
+
{
|
|
10956
|
+
name: 'image.png',
|
|
10957
|
+
title: 'Image/PNG',
|
|
10958
|
+
icon: 'fa-light fa-image ax-text-purple-500',
|
|
10959
|
+
},
|
|
10960
|
+
{
|
|
10961
|
+
name: 'image.gif',
|
|
10962
|
+
title: 'Image/GIF',
|
|
10963
|
+
icon: 'fa-light fa-image ax-text-purple-500',
|
|
10964
|
+
},
|
|
10965
|
+
],
|
|
10966
|
+
context: {
|
|
10967
|
+
image: {
|
|
10968
|
+
width: '22',
|
|
10969
|
+
height: '33',
|
|
10970
|
+
},
|
|
10971
|
+
gift: {
|
|
10972
|
+
duration: '11',
|
|
10973
|
+
},
|
|
10974
|
+
},
|
|
10975
|
+
},
|
|
10976
|
+
},
|
|
10977
|
+
];
|
|
10978
|
+
|
|
10979
|
+
class AXCDocumentTypeDataSeeder {
|
|
10980
|
+
constructor() {
|
|
10981
|
+
this.storageService = inject(AXPEntityStorageService);
|
|
10982
|
+
}
|
|
10983
|
+
async seed() {
|
|
10984
|
+
await this.storageService.initial(`${RootConfig$6.module.name}.${RootConfig$6.entities.documentType.name}`, DOCUMENT_TYPES);
|
|
10985
|
+
}
|
|
10986
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDocumentTypeDataSeeder, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
10987
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDocumentTypeDataSeeder }); }
|
|
10988
|
+
}
|
|
10989
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDocumentTypeDataSeeder, decorators: [{
|
|
10990
|
+
type: Injectable
|
|
10991
|
+
}] });
|
|
10992
|
+
|
|
10852
10993
|
// Section: Constants and Initial Setup
|
|
10853
10994
|
const checkbox = '{"type":"document-layout","children":[{"type":"page-layout","children":[{"type":"checkbox-editor","name":"checkbox-editor1","options":{"label":"i accept","readonly":false,"disabled":false},"defaultValue":false,"path":"accept-check-box"}],"name":"page-layout1"}],"name":"document"}';
|
|
10854
10995
|
const allTypes = DOCUMENT_TYPES;
|
|
@@ -11438,198 +11579,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
|
|
|
11438
11579
|
type: Injectable
|
|
11439
11580
|
}] });
|
|
11440
11581
|
|
|
11441
|
-
class AXCDistributionInteractionMockService extends AXMDistributionInteractionServiceImpl {
|
|
11442
|
-
constructor() {
|
|
11443
|
-
super(...arguments);
|
|
11444
|
-
//#region ---- Services & Dependencies ----
|
|
11445
|
-
this.usersService = inject(AXMUsersEntityService);
|
|
11446
|
-
this.sessionService = inject(AXPSessionService);
|
|
11447
|
-
}
|
|
11448
|
-
//#endregion
|
|
11449
|
-
async query(request) {
|
|
11450
|
-
// Raw mode: return ungrouped interaction items (used for bulk aggregation)
|
|
11451
|
-
if (request?.params?.raw === true) {
|
|
11452
|
-
const rawRes = await super.query({
|
|
11453
|
-
filter: request.filter,
|
|
11454
|
-
sort: request.sort,
|
|
11455
|
-
params: request.params,
|
|
11456
|
-
take: 100000,
|
|
11457
|
-
skip: 0,
|
|
11458
|
-
});
|
|
11459
|
-
const total = rawRes.items.length;
|
|
11460
|
-
const skip = request?.skip ?? 0;
|
|
11461
|
-
const take = request?.take ?? total;
|
|
11462
|
-
const pagedItems = rawRes.items.slice(skip, skip + take);
|
|
11463
|
-
return {
|
|
11464
|
-
items: pagedItems,
|
|
11465
|
-
total,
|
|
11466
|
-
};
|
|
11467
|
-
}
|
|
11468
|
-
const items = (await super.query({
|
|
11469
|
-
filter: request.filter,
|
|
11470
|
-
sort: request.sort,
|
|
11471
|
-
params: request.params,
|
|
11472
|
-
take: 100000,
|
|
11473
|
-
skip: 0,
|
|
11474
|
-
})).items;
|
|
11475
|
-
// Extract documentDistributionId from request filter
|
|
11476
|
-
let documentDistributionId;
|
|
11477
|
-
if (request.filter && typeof request.filter === 'object') {
|
|
11478
|
-
if (request.filter.filters && request.filter.filters.length > 0 && request.filter.filters[0].field === 'documentDistributionId') {
|
|
11479
|
-
documentDistributionId = request.filter.filters[0].value;
|
|
11480
|
-
}
|
|
11481
|
-
}
|
|
11482
|
-
// Get the distribution record to access assignedTo property using storage service to avoid circular calls
|
|
11483
|
-
if (documentDistributionId) {
|
|
11484
|
-
try {
|
|
11485
|
-
// Use storage service directly to get distribution record
|
|
11486
|
-
const distribution = await this.storageService.getOne('DocumentManagement.Distribution', documentDistributionId);
|
|
11487
|
-
if (distribution && distribution.assignedTo && Array.isArray(distribution.assignedTo)) {
|
|
11488
|
-
// Get all assigned user IDs
|
|
11489
|
-
const assignedUserIds = distribution.assignedTo.map((user) => user.id || user);
|
|
11490
|
-
// Get existing interaction user IDs (from created.user.id)
|
|
11491
|
-
const existingUserIds = new Set(items
|
|
11492
|
-
.map((item) => item?.created?.user?.id)
|
|
11493
|
-
.filter((id) => !!id));
|
|
11494
|
-
// Find users who are assigned but don't have interaction records
|
|
11495
|
-
const missingUserIds = assignedUserIds.filter((userId) => !existingUserIds.has(userId));
|
|
11496
|
-
// Create missing interaction records
|
|
11497
|
-
for (const userId of missingUserIds) {
|
|
11498
|
-
let fullName = 'Unknown';
|
|
11499
|
-
try {
|
|
11500
|
-
const userRes = await this.usersService.query({
|
|
11501
|
-
skip: 0,
|
|
11502
|
-
take: 1,
|
|
11503
|
-
filter: { field: 'id', operator: { type: 'equal' }, value: userId },
|
|
11504
|
-
});
|
|
11505
|
-
const u = userRes.items?.[0];
|
|
11506
|
-
if (u) {
|
|
11507
|
-
const candidate = `${u.firstName ?? ''} ${u.lastName ?? ''}`.trim();
|
|
11508
|
-
fullName = candidate || u.username || u.title || u.name || fullName;
|
|
11509
|
-
}
|
|
11510
|
-
}
|
|
11511
|
-
catch { }
|
|
11512
|
-
const missingRecord = {
|
|
11513
|
-
id: `missing-${documentDistributionId}-${userId}`,
|
|
11514
|
-
documentDistributionId: documentDistributionId,
|
|
11515
|
-
auditInfo: {
|
|
11516
|
-
created: { at: new Date(), by: { id: userId, type: 'user', fullName } },
|
|
11517
|
-
updated: { at: new Date(), by: { id: userId, type: 'user', fullName } },
|
|
11518
|
-
},
|
|
11519
|
-
action: 'Fake', // Default action, but with false flags
|
|
11520
|
-
};
|
|
11521
|
-
// Add the missing record to the items array
|
|
11522
|
-
items.push(missingRecord);
|
|
11523
|
-
}
|
|
11524
|
-
}
|
|
11525
|
-
}
|
|
11526
|
-
catch (error) {
|
|
11527
|
-
}
|
|
11528
|
-
}
|
|
11529
|
-
// Group items by creator user id
|
|
11530
|
-
const groupedItems = this.groupItemsByCreatedBy(items);
|
|
11531
|
-
// Collect distinct user IDs from grouped items
|
|
11532
|
-
const userIds = Array.from(new Set(groupedItems.map((g) => g.createdBy).filter((id) => !!id)));
|
|
11533
|
-
// Fetch users by IDs (in operator)
|
|
11534
|
-
let usersMap = new Map();
|
|
11535
|
-
if (userIds.length > 0) {
|
|
11536
|
-
try {
|
|
11537
|
-
const usersRes = await this.usersService.query({
|
|
11538
|
-
skip: 0,
|
|
11539
|
-
take: userIds.length,
|
|
11540
|
-
filter: {
|
|
11541
|
-
field: 'id',
|
|
11542
|
-
operator: { type: 'in' },
|
|
11543
|
-
value: userIds,
|
|
11544
|
-
},
|
|
11545
|
-
});
|
|
11546
|
-
usersMap = new Map(usersRes.items.map((u) => [
|
|
11547
|
-
u.id,
|
|
11548
|
-
{
|
|
11549
|
-
id: u.id,
|
|
11550
|
-
name: u.username,
|
|
11551
|
-
title: `${u.firstName} ${u.lastName}`,
|
|
11552
|
-
},
|
|
11553
|
-
]));
|
|
11554
|
-
}
|
|
11555
|
-
catch {
|
|
11556
|
-
// If fetching users fails, continue without enrichment
|
|
11557
|
-
usersMap = new Map();
|
|
11558
|
-
}
|
|
11559
|
-
}
|
|
11560
|
-
// Convert grouped items back to the expected format with user enrichment
|
|
11561
|
-
const enrichedItems = groupedItems.map((summary) => ({
|
|
11562
|
-
id: summary.id,
|
|
11563
|
-
createdBy: summary.createdBy,
|
|
11564
|
-
hasViewed: summary.hasViewed,
|
|
11565
|
-
lastViewDate: summary.lastViewDate,
|
|
11566
|
-
hasSigned: summary.hasSigned,
|
|
11567
|
-
lastSignDate: summary.lastSignDate,
|
|
11568
|
-
totalInteractions: summary.totalInteractions,
|
|
11569
|
-
user: usersMap.get(summary.createdBy) ?? {
|
|
11570
|
-
id: this.sessionService?.user?.id ?? '',
|
|
11571
|
-
name: this.sessionService?.user?.name ?? '',
|
|
11572
|
-
title: this.sessionService?.user?.title ?? '',
|
|
11573
|
-
},
|
|
11574
|
-
}));
|
|
11575
|
-
// Apply pagination using request.skip and request.take
|
|
11576
|
-
const total = enrichedItems.length;
|
|
11577
|
-
const skip = request?.skip ?? 0;
|
|
11578
|
-
const take = request?.take ?? total;
|
|
11579
|
-
const pagedItems = enrichedItems.slice(skip, skip + take);
|
|
11580
|
-
return {
|
|
11581
|
-
items: pagedItems,
|
|
11582
|
-
total,
|
|
11583
|
-
};
|
|
11584
|
-
}
|
|
11585
|
-
/**
|
|
11586
|
-
* Groups distribution interaction items by createdBy and creates a summary
|
|
11587
|
-
*/
|
|
11588
|
-
groupItemsByCreatedBy(items) {
|
|
11589
|
-
console.log(items);
|
|
11590
|
-
const groupedMap = new Map();
|
|
11591
|
-
for (const item of items) {
|
|
11592
|
-
const createdBy = item?.created?.user?.id || 'unknown';
|
|
11593
|
-
if (!groupedMap.has(createdBy)) {
|
|
11594
|
-
// Initialize summary for new createdBy
|
|
11595
|
-
groupedMap.set(createdBy, {
|
|
11596
|
-
id: createdBy, // Use createdBy as the ID for the summary
|
|
11597
|
-
createdBy: createdBy,
|
|
11598
|
-
hasViewed: false,
|
|
11599
|
-
lastViewDate: undefined,
|
|
11600
|
-
hasSigned: false,
|
|
11601
|
-
lastSignDate: undefined,
|
|
11602
|
-
totalInteractions: 0,
|
|
11603
|
-
});
|
|
11604
|
-
}
|
|
11605
|
-
const summary = groupedMap.get(createdBy);
|
|
11606
|
-
summary.totalInteractions++;
|
|
11607
|
-
// Check for view actions
|
|
11608
|
-
if (item.action === 'Viewed') {
|
|
11609
|
-
summary.hasViewed = true;
|
|
11610
|
-
// Update last view date if this is more recent
|
|
11611
|
-
if (!summary.lastViewDate || item.actionTime && item.actionTime > summary.lastViewDate) {
|
|
11612
|
-
summary.lastViewDate = item.actionTime;
|
|
11613
|
-
}
|
|
11614
|
-
}
|
|
11615
|
-
// Check for sign actions
|
|
11616
|
-
if (item.action === 'Signed') {
|
|
11617
|
-
summary.hasSigned = true;
|
|
11618
|
-
// Update last sign date if this is more recent
|
|
11619
|
-
if (!summary.lastSignDate || item.actionTime && item.actionTime > summary.lastSignDate) {
|
|
11620
|
-
summary.lastSignDate = item.actionTime;
|
|
11621
|
-
}
|
|
11622
|
-
}
|
|
11623
|
-
}
|
|
11624
|
-
return Array.from(groupedMap.values());
|
|
11625
|
-
}
|
|
11626
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDistributionInteractionMockService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
11627
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDistributionInteractionMockService }); }
|
|
11628
|
-
}
|
|
11629
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDistributionInteractionMockService, decorators: [{
|
|
11630
|
-
type: Injectable
|
|
11631
|
-
}] });
|
|
11632
|
-
|
|
11633
11582
|
class AXCDocumentManagementMockModule {
|
|
11634
11583
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDocumentManagementMockModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
11635
11584
|
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.12", ngImport: i0, type: AXCDocumentManagementMockModule }); }
|
|
@@ -11640,6 +11589,10 @@ class AXCDocumentManagementMockModule {
|
|
|
11640
11589
|
{ provide: AXMDocumentService, useClass: AXMFileMockService },
|
|
11641
11590
|
{ provide: AXMDistributionService, useClass: AXCDistributionMockService },
|
|
11642
11591
|
{ provide: AXMReviewService, useClass: AXCReviewMockService },
|
|
11592
|
+
{
|
|
11593
|
+
provide: AXPDocumentManagementService,
|
|
11594
|
+
useClass: AXCDocumentManagementService,
|
|
11595
|
+
},
|
|
11643
11596
|
{ provide: AXMDistributionInteractionService, useClass: AXCDistributionInteractionMockService },
|
|
11644
11597
|
] }); }
|
|
11645
11598
|
}
|
|
@@ -11656,6 +11609,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
|
|
|
11656
11609
|
{ provide: AXMDocumentService, useClass: AXMFileMockService },
|
|
11657
11610
|
{ provide: AXMDistributionService, useClass: AXCDistributionMockService },
|
|
11658
11611
|
{ provide: AXMReviewService, useClass: AXCReviewMockService },
|
|
11612
|
+
{
|
|
11613
|
+
provide: AXPDocumentManagementService,
|
|
11614
|
+
useClass: AXCDocumentManagementService,
|
|
11615
|
+
},
|
|
11659
11616
|
{ provide: AXMDistributionInteractionService, useClass: AXCDistributionInteractionMockService },
|
|
11660
11617
|
],
|
|
11661
11618
|
}]
|
|
@@ -12521,7 +12478,7 @@ class AXMFormDataSourcesProvider {
|
|
|
12521
12478
|
}
|
|
12522
12479
|
}
|
|
12523
12480
|
|
|
12524
|
-
function generateCategories$
|
|
12481
|
+
function generateCategories$4() {
|
|
12525
12482
|
const categories = [];
|
|
12526
12483
|
const ids = new Map();
|
|
12527
12484
|
// Root categories - reduced to 5 main areas
|
|
@@ -12667,7 +12624,7 @@ function generateCategories$3() {
|
|
|
12667
12624
|
});
|
|
12668
12625
|
return categories;
|
|
12669
12626
|
}
|
|
12670
|
-
const TEMPLATE_CATEGORIES = generateCategories$
|
|
12627
|
+
const TEMPLATE_CATEGORIES = generateCategories$4();
|
|
12671
12628
|
|
|
12672
12629
|
class AXMFormTemplateCategoryDataSeeder {
|
|
12673
12630
|
constructor() {
|
|
@@ -39109,7 +39066,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
|
|
|
39109
39066
|
* - itemsCount: Direct reports in this category
|
|
39110
39067
|
* - No need for boolean flags (hasChild, hasReport)
|
|
39111
39068
|
*/
|
|
39112
|
-
function generateCategories$
|
|
39069
|
+
function generateCategories$3() {
|
|
39113
39070
|
const categories = [];
|
|
39114
39071
|
// Root categories with child count metadata
|
|
39115
39072
|
const rootCategories = [
|
|
@@ -39162,7 +39119,7 @@ function generateCategories$2() {
|
|
|
39162
39119
|
});
|
|
39163
39120
|
return categories;
|
|
39164
39121
|
}
|
|
39165
|
-
const PRODUCT_REPORT_CATEGORIES = generateCategories$
|
|
39122
|
+
const PRODUCT_REPORT_CATEGORIES = generateCategories$3();
|
|
39166
39123
|
//#endregion
|
|
39167
39124
|
//#region ---- Mock Parameter Groups ----
|
|
39168
39125
|
const productCatalogParameterGroups = [
|
|
@@ -39852,6 +39809,8 @@ const APPLICATION_TREE = [
|
|
|
39852
39809
|
'DocumentManagement',
|
|
39853
39810
|
'AssessmentManagement',
|
|
39854
39811
|
'SecurityManagement',
|
|
39812
|
+
'CalendarManagement',
|
|
39813
|
+
'TaskManagement',
|
|
39855
39814
|
AXPCommonFeatureKeys.GlobalSearch,
|
|
39856
39815
|
AXPHumanCapitalManagementFeatureKeys.EmployeeManagement,
|
|
39857
39816
|
AXPHumanCapitalManagementFeatureKeys.LeaveManagement,
|
|
@@ -41117,7 +41076,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
|
|
|
41117
41076
|
* - itemsCount: Direct reports in this category
|
|
41118
41077
|
* - No need for boolean flags (hasChild, hasReport)
|
|
41119
41078
|
*/
|
|
41120
|
-
function generateCategories$
|
|
41079
|
+
function generateCategories$2() {
|
|
41121
41080
|
const categories = [];
|
|
41122
41081
|
// Root category: Platform Analytics
|
|
41123
41082
|
const rootCategories = [
|
|
@@ -41141,7 +41100,7 @@ function generateCategories$1() {
|
|
|
41141
41100
|
});
|
|
41142
41101
|
return categories;
|
|
41143
41102
|
}
|
|
41144
|
-
const PLATFORM_ANALYTICS_REPORT_CATEGORIES = generateCategories$
|
|
41103
|
+
const PLATFORM_ANALYTICS_REPORT_CATEGORIES = generateCategories$2();
|
|
41145
41104
|
//#endregion
|
|
41146
41105
|
//#region ---- Mock Parameter Groups ----
|
|
41147
41106
|
const platformAnalyticsParameterGroups = [
|
|
@@ -46333,6 +46292,14 @@ const createFileCastMiddleware = {
|
|
|
46333
46292
|
return undefined;
|
|
46334
46293
|
}
|
|
46335
46294
|
})();
|
|
46295
|
+
const documentManagementService = (() => {
|
|
46296
|
+
try {
|
|
46297
|
+
return inject(AXPDocumentManagementService);
|
|
46298
|
+
}
|
|
46299
|
+
catch {
|
|
46300
|
+
return undefined;
|
|
46301
|
+
}
|
|
46302
|
+
})();
|
|
46336
46303
|
const folderService = (() => {
|
|
46337
46304
|
try {
|
|
46338
46305
|
return inject(AXMFolderService);
|
|
@@ -46431,14 +46398,17 @@ const createFileCastMiddleware = {
|
|
|
46431
46398
|
try {
|
|
46432
46399
|
await fileStorageService.remove(fileId);
|
|
46433
46400
|
}
|
|
46434
|
-
catch (error) {
|
|
46435
|
-
}
|
|
46401
|
+
catch (error) { }
|
|
46436
46402
|
}
|
|
46437
46403
|
// Return null to indicate this file should be removed from the list
|
|
46438
46404
|
return null;
|
|
46439
46405
|
}
|
|
46440
46406
|
// Case 2: Reference-based file (handle document updates)
|
|
46441
|
-
if (file.source?.kind === 'reference' &&
|
|
46407
|
+
if (file.source?.kind === 'reference' &&
|
|
46408
|
+
file.source.value &&
|
|
46409
|
+
typeof file.source.value === 'object' &&
|
|
46410
|
+
'id' in file.source.value &&
|
|
46411
|
+
'type' in file.source.value) {
|
|
46442
46412
|
const reference = file.source.value;
|
|
46443
46413
|
// Only handle document references
|
|
46444
46414
|
if (reference.type === 'document' && documentService) {
|
|
@@ -46504,8 +46474,7 @@ const createFileCastMiddleware = {
|
|
|
46504
46474
|
});
|
|
46505
46475
|
return { id: docId, kind: 'document' };
|
|
46506
46476
|
}
|
|
46507
|
-
catch (error) {
|
|
46508
|
-
}
|
|
46477
|
+
catch (error) { }
|
|
46509
46478
|
}
|
|
46510
46479
|
else {
|
|
46511
46480
|
}
|
|
@@ -46553,8 +46522,7 @@ const createFileCastMiddleware = {
|
|
|
46553
46522
|
});
|
|
46554
46523
|
return { id: docId, kind: 'document' };
|
|
46555
46524
|
}
|
|
46556
|
-
catch (error) {
|
|
46557
|
-
}
|
|
46525
|
+
catch (error) { }
|
|
46558
46526
|
}
|
|
46559
46527
|
else {
|
|
46560
46528
|
}
|
|
@@ -46614,8 +46582,11 @@ const createFileCastMiddleware = {
|
|
|
46614
46582
|
const values = Object.values(candidate.meta);
|
|
46615
46583
|
if (!Array.isArray(values) || values.length === 0)
|
|
46616
46584
|
return false;
|
|
46617
|
-
return values.every((m) => m &&
|
|
46618
|
-
typeof m
|
|
46585
|
+
return values.every((m) => m &&
|
|
46586
|
+
typeof m === 'object' &&
|
|
46587
|
+
typeof m.name === 'string' &&
|
|
46588
|
+
typeof m.title === 'string' &&
|
|
46589
|
+
typeof m.value === 'string');
|
|
46619
46590
|
};
|
|
46620
46591
|
const ensureDocumentRecord = async (svc, payload) => {
|
|
46621
46592
|
// Resolve or create folder hierarchy: <Entity>/<Record Title|Name>
|
|
@@ -46623,9 +46594,7 @@ const createFileCastMiddleware = {
|
|
|
46623
46594
|
try {
|
|
46624
46595
|
if (folderService) {
|
|
46625
46596
|
const findFolderByName = async (name, parent) => {
|
|
46626
|
-
const filters = [
|
|
46627
|
-
{ field: 'name', operator: { type: 'equal' }, value: name },
|
|
46628
|
-
];
|
|
46597
|
+
const filters = [{ field: 'name', operator: { type: 'equal' }, value: name }];
|
|
46629
46598
|
if (parent) {
|
|
46630
46599
|
filters.push({ field: 'parentId', operator: { type: 'equal' }, value: parent });
|
|
46631
46600
|
}
|
|
@@ -46663,7 +46632,10 @@ const createFileCastMiddleware = {
|
|
|
46663
46632
|
if (root?.id) {
|
|
46664
46633
|
const entityFullName = ctx.entityName || 'Entity';
|
|
46665
46634
|
const entityNameOnly = entityFullName.split('.').pop() || entityFullName;
|
|
46666
|
-
const recordFolderName = (ctx?.data?.title ||
|
|
46635
|
+
const recordFolderName = (ctx?.data?.title ||
|
|
46636
|
+
ctx?.data?.name ||
|
|
46637
|
+
payload.name ||
|
|
46638
|
+
'Record').toString();
|
|
46667
46639
|
const entityFolderId = await ensureFolder(entityNameOnly, root.id);
|
|
46668
46640
|
parentId = await ensureFolder(recordFolderName, entityFolderId);
|
|
46669
46641
|
}
|
|
@@ -46824,8 +46796,8 @@ const createFileCastMiddleware = {
|
|
|
46824
46796
|
const allDocIds = [...documentIdList, ...referenceDocIds];
|
|
46825
46797
|
const [manyInfos, documents] = await Promise.all([
|
|
46826
46798
|
fileIdList.length ? fileStorageService.findMany(fileIdList) : Promise.resolve([]),
|
|
46827
|
-
allDocIds.length &&
|
|
46828
|
-
?
|
|
46799
|
+
allDocIds.length && documentManagementService
|
|
46800
|
+
? documentManagementService.findManyDocuments(allDocIds)
|
|
46829
46801
|
: Promise.resolve([]),
|
|
46830
46802
|
]);
|
|
46831
46803
|
const infoMap = new Map(manyInfos.map((i) => [i.fileId, i]));
|
|
@@ -46934,8 +46906,8 @@ const createFileCastMiddleware = {
|
|
|
46934
46906
|
else if (isDocumentId(value)) {
|
|
46935
46907
|
const actualDocId = removeDocumentPrefix(value);
|
|
46936
46908
|
try {
|
|
46937
|
-
if (
|
|
46938
|
-
const docs = await
|
|
46909
|
+
if (documentManagementService) {
|
|
46910
|
+
const docs = await documentManagementService.findManyDocuments([actualDocId]);
|
|
46939
46911
|
const doc = docs?.[0];
|
|
46940
46912
|
if (doc) {
|
|
46941
46913
|
const listItem = {
|
|
@@ -46970,7 +46942,11 @@ const createFileCastMiddleware = {
|
|
|
46970
46942
|
}
|
|
46971
46943
|
}
|
|
46972
46944
|
catch (error) {
|
|
46973
|
-
console.error('[file-cast] ❌ Failed to get document info', {
|
|
46945
|
+
console.error('[file-cast] ❌ Failed to get document info', {
|
|
46946
|
+
keyPath,
|
|
46947
|
+
documentId: actualDocId,
|
|
46948
|
+
error,
|
|
46949
|
+
});
|
|
46974
46950
|
setValueByPath(resultData, keyPath, {
|
|
46975
46951
|
id: actualDocId,
|
|
46976
46952
|
name: 'Unknown Document',
|
|
@@ -46981,9 +46957,9 @@ const createFileCastMiddleware = {
|
|
|
46981
46957
|
}
|
|
46982
46958
|
else if (isReference(value)) {
|
|
46983
46959
|
const reference = parseReference(value);
|
|
46984
|
-
if (reference && reference.type === 'document' &&
|
|
46960
|
+
if (reference && reference.type === 'document' && documentManagementService) {
|
|
46985
46961
|
try {
|
|
46986
|
-
const docs = await
|
|
46962
|
+
const docs = await documentManagementService.findManyDocuments([reference.id]);
|
|
46987
46963
|
const doc = docs?.[0];
|
|
46988
46964
|
if (doc) {
|
|
46989
46965
|
const listItem = {
|
|
@@ -47009,7 +46985,11 @@ const createFileCastMiddleware = {
|
|
|
47009
46985
|
}
|
|
47010
46986
|
}
|
|
47011
46987
|
catch (error) {
|
|
47012
|
-
console.error('[file-cast] ❌ Failed to get reference document info', {
|
|
46988
|
+
console.error('[file-cast] ❌ Failed to get reference document info', {
|
|
46989
|
+
keyPath,
|
|
46990
|
+
reference,
|
|
46991
|
+
error,
|
|
46992
|
+
});
|
|
47013
46993
|
setValueByPath(resultData, keyPath, {
|
|
47014
46994
|
id: reference.id,
|
|
47015
46995
|
name: 'Unknown Document',
|
|
@@ -48839,7 +48819,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
|
|
|
48839
48819
|
* - childrenCount: Direct child categories (for lazy loading)
|
|
48840
48820
|
* - itemsCount: Direct activities in this category
|
|
48841
48821
|
*/
|
|
48842
|
-
function generateCategories() {
|
|
48822
|
+
function generateCategories$1() {
|
|
48843
48823
|
const categories = [];
|
|
48844
48824
|
// Root categories
|
|
48845
48825
|
const rootCategories = [
|
|
@@ -48941,7 +48921,7 @@ function generateCategories() {
|
|
|
48941
48921
|
});
|
|
48942
48922
|
return categories;
|
|
48943
48923
|
}
|
|
48944
|
-
const ACTIVITY_CATEGORIES = generateCategories();
|
|
48924
|
+
const ACTIVITY_CATEGORIES = generateCategories$1();
|
|
48945
48925
|
//#endregion
|
|
48946
48926
|
|
|
48947
48927
|
class AXCActivityCategorySeeder {
|
|
@@ -48972,6 +48952,7 @@ function generateActivityDefinitions() {
|
|
|
48972
48952
|
const queryCategory = ACTIVITY_CATEGORIES.find(c => c.title === 'Query');
|
|
48973
48953
|
// Command category activities
|
|
48974
48954
|
if (commandCategory) {
|
|
48955
|
+
// Check Permission Activity
|
|
48975
48956
|
definitions.push({
|
|
48976
48957
|
name: 'check-permission',
|
|
48977
48958
|
type: 'workflow-activity:check-permission',
|
|
@@ -48979,8 +48960,73 @@ function generateActivityDefinitions() {
|
|
|
48979
48960
|
description: 'Checks if a user has a specific permission',
|
|
48980
48961
|
category: commandCategory.id,
|
|
48981
48962
|
executionMode: 'backend',
|
|
48963
|
+
icon: 'fa-shield-alt',
|
|
48982
48964
|
outcomes: ['HasPermission', 'NoPermission'],
|
|
48965
|
+
inputs: [
|
|
48966
|
+
{
|
|
48967
|
+
name: 'permission',
|
|
48968
|
+
title: 'Permission Key',
|
|
48969
|
+
description: 'Permission key to check (e.g., "Users.Create")',
|
|
48970
|
+
schema: {
|
|
48971
|
+
dataType: 'string',
|
|
48972
|
+
nullable: false,
|
|
48973
|
+
interface: {
|
|
48974
|
+
type: AXPWidgetsList.Editors.TextBox,
|
|
48975
|
+
options: {
|
|
48976
|
+
placeholder: 'Enter permission key (e.g., Users.Create)',
|
|
48977
|
+
},
|
|
48978
|
+
},
|
|
48979
|
+
},
|
|
48980
|
+
validations: [
|
|
48981
|
+
{
|
|
48982
|
+
rule: 'required',
|
|
48983
|
+
},
|
|
48984
|
+
],
|
|
48985
|
+
},
|
|
48986
|
+
{
|
|
48987
|
+
name: 'userId',
|
|
48988
|
+
title: 'User ID',
|
|
48989
|
+
description: 'User ID to check (optional, defaults to current user)',
|
|
48990
|
+
schema: {
|
|
48991
|
+
dataType: 'string',
|
|
48992
|
+
nullable: true,
|
|
48993
|
+
interface: {
|
|
48994
|
+
type: AXPWidgetsList.Editors.TextBox,
|
|
48995
|
+
options: {
|
|
48996
|
+
placeholder: 'Enter user ID (leave empty for current user)',
|
|
48997
|
+
},
|
|
48998
|
+
},
|
|
48999
|
+
},
|
|
49000
|
+
},
|
|
49001
|
+
],
|
|
49002
|
+
outputs: [
|
|
49003
|
+
{
|
|
49004
|
+
name: 'hasPermission',
|
|
49005
|
+
title: 'Has Permission',
|
|
49006
|
+
description: 'Whether user has the permission',
|
|
49007
|
+
schema: {
|
|
49008
|
+
dataType: 'boolean',
|
|
49009
|
+
},
|
|
49010
|
+
},
|
|
49011
|
+
{
|
|
49012
|
+
name: 'userId',
|
|
49013
|
+
title: 'User ID',
|
|
49014
|
+
description: 'User ID that was checked',
|
|
49015
|
+
schema: {
|
|
49016
|
+
dataType: 'string',
|
|
49017
|
+
},
|
|
49018
|
+
},
|
|
49019
|
+
{
|
|
49020
|
+
name: 'permission',
|
|
49021
|
+
title: 'Permission',
|
|
49022
|
+
description: 'Permission that was checked',
|
|
49023
|
+
schema: {
|
|
49024
|
+
dataType: 'string',
|
|
49025
|
+
},
|
|
49026
|
+
},
|
|
49027
|
+
],
|
|
48983
49028
|
});
|
|
49029
|
+
// Execute Command Activity
|
|
48984
49030
|
definitions.push({
|
|
48985
49031
|
name: 'execute-command',
|
|
48986
49032
|
type: 'workflow-activity:execute-command',
|
|
@@ -48988,8 +49034,82 @@ function generateActivityDefinitions() {
|
|
|
48988
49034
|
description: 'Executes a command operation',
|
|
48989
49035
|
category: commandCategory.id,
|
|
48990
49036
|
executionMode: 'backend',
|
|
49037
|
+
icon: 'fa-terminal',
|
|
48991
49038
|
outcomes: ['Done', 'Failed'],
|
|
49039
|
+
inputs: [
|
|
49040
|
+
{
|
|
49041
|
+
name: 'commandKey',
|
|
49042
|
+
title: 'Command Key',
|
|
49043
|
+
description: 'Command key to execute (e.g., "User:Create")',
|
|
49044
|
+
schema: {
|
|
49045
|
+
dataType: 'string',
|
|
49046
|
+
nullable: false,
|
|
49047
|
+
interface: {
|
|
49048
|
+
type: AXPWidgetsList.Editors.TextBox,
|
|
49049
|
+
options: {
|
|
49050
|
+
placeholder: 'Enter command key (e.g., User:Create)',
|
|
49051
|
+
},
|
|
49052
|
+
},
|
|
49053
|
+
},
|
|
49054
|
+
validations: [
|
|
49055
|
+
{
|
|
49056
|
+
rule: 'required',
|
|
49057
|
+
},
|
|
49058
|
+
],
|
|
49059
|
+
},
|
|
49060
|
+
{
|
|
49061
|
+
name: 'input',
|
|
49062
|
+
title: 'Command Input',
|
|
49063
|
+
description: 'Command input data as JSON object',
|
|
49064
|
+
schema: {
|
|
49065
|
+
dataType: 'object',
|
|
49066
|
+
nullable: true,
|
|
49067
|
+
interface: {
|
|
49068
|
+
type: AXPWidgetsList.Advanced.CodeEditor,
|
|
49069
|
+
options: {
|
|
49070
|
+
language: 'json',
|
|
49071
|
+
placeholder: 'Enter JSON object for command input',
|
|
49072
|
+
},
|
|
49073
|
+
},
|
|
49074
|
+
},
|
|
49075
|
+
},
|
|
49076
|
+
],
|
|
49077
|
+
outputs: [
|
|
49078
|
+
{
|
|
49079
|
+
name: 'result',
|
|
49080
|
+
title: 'Result',
|
|
49081
|
+
description: 'Command result data',
|
|
49082
|
+
schema: {
|
|
49083
|
+
dataType: 'object',
|
|
49084
|
+
},
|
|
49085
|
+
},
|
|
49086
|
+
{
|
|
49087
|
+
name: 'success',
|
|
49088
|
+
title: 'Success',
|
|
49089
|
+
description: 'Whether command executed successfully',
|
|
49090
|
+
schema: {
|
|
49091
|
+
dataType: 'boolean',
|
|
49092
|
+
},
|
|
49093
|
+
},
|
|
49094
|
+
{
|
|
49095
|
+
name: 'error',
|
|
49096
|
+
title: 'Error',
|
|
49097
|
+
description: 'Error message if command failed',
|
|
49098
|
+
schema: {
|
|
49099
|
+
dataType: 'string',
|
|
49100
|
+
},
|
|
49101
|
+
},
|
|
49102
|
+
{
|
|
49103
|
+
name: 'entityId',
|
|
49104
|
+
title: 'Entity ID',
|
|
49105
|
+
description: 'Created/Updated entity ID (if applicable)',
|
|
49106
|
+
schema: {
|
|
49107
|
+
dataType: 'string',
|
|
49108
|
+
},
|
|
49109
|
+
},
|
|
49110
|
+
],
|
|
48992
49111
|
});
|
|
49112
|
+
// Show Confirm Popup Activity
|
|
48993
49113
|
definitions.push({
|
|
48994
49114
|
name: 'show-confirm-popup',
|
|
48995
49115
|
type: 'workflow-activity:show-confirm-popup',
|
|
@@ -48997,11 +49117,272 @@ function generateActivityDefinitions() {
|
|
|
48997
49117
|
description: 'Shows a confirmation popup dialog with OK and Cancel buttons',
|
|
48998
49118
|
category: commandCategory.id,
|
|
48999
49119
|
executionMode: 'frontend',
|
|
49120
|
+
icon: 'fa-question-circle',
|
|
49000
49121
|
outcomes: ['ok', 'cancel'],
|
|
49122
|
+
inputs: [
|
|
49123
|
+
{
|
|
49124
|
+
name: 'title',
|
|
49125
|
+
title: 'Title',
|
|
49126
|
+
description: 'Title of the confirmation dialog',
|
|
49127
|
+
schema: {
|
|
49128
|
+
dataType: 'string',
|
|
49129
|
+
nullable: false,
|
|
49130
|
+
interface: {
|
|
49131
|
+
type: AXPWidgetsList.Editors.TextBox,
|
|
49132
|
+
options: {
|
|
49133
|
+
placeholder: 'Enter dialog title',
|
|
49134
|
+
},
|
|
49135
|
+
},
|
|
49136
|
+
},
|
|
49137
|
+
validations: [
|
|
49138
|
+
{
|
|
49139
|
+
rule: 'required',
|
|
49140
|
+
},
|
|
49141
|
+
],
|
|
49142
|
+
},
|
|
49143
|
+
{
|
|
49144
|
+
name: 'message',
|
|
49145
|
+
title: 'Message',
|
|
49146
|
+
description: 'Message/content of the confirmation dialog',
|
|
49147
|
+
schema: {
|
|
49148
|
+
dataType: 'string',
|
|
49149
|
+
nullable: false,
|
|
49150
|
+
interface: {
|
|
49151
|
+
type: AXPWidgetsList.Editors.LargeTextBox,
|
|
49152
|
+
options: {
|
|
49153
|
+
placeholder: 'Enter confirmation message',
|
|
49154
|
+
rows: 4,
|
|
49155
|
+
},
|
|
49156
|
+
},
|
|
49157
|
+
},
|
|
49158
|
+
validations: [
|
|
49159
|
+
{
|
|
49160
|
+
rule: 'required',
|
|
49161
|
+
},
|
|
49162
|
+
],
|
|
49163
|
+
},
|
|
49164
|
+
{
|
|
49165
|
+
name: 'type',
|
|
49166
|
+
title: 'Dialog Type',
|
|
49167
|
+
description: 'Dialog type/color (e.g., warning, danger, info, success)',
|
|
49168
|
+
schema: {
|
|
49169
|
+
dataType: 'string',
|
|
49170
|
+
nullable: true,
|
|
49171
|
+
defaultValue: 'warning',
|
|
49172
|
+
interface: {
|
|
49173
|
+
type: AXPWidgetsList.Editors.SelectBox,
|
|
49174
|
+
options: {
|
|
49175
|
+
valueField: 'value',
|
|
49176
|
+
textField: 'text',
|
|
49177
|
+
dataSource: [
|
|
49178
|
+
{ value: 'warning', text: 'Warning' },
|
|
49179
|
+
{ value: 'danger', text: 'Danger' },
|
|
49180
|
+
{ value: 'info', text: 'Info' },
|
|
49181
|
+
{ value: 'success', text: 'Success' },
|
|
49182
|
+
],
|
|
49183
|
+
},
|
|
49184
|
+
},
|
|
49185
|
+
},
|
|
49186
|
+
},
|
|
49187
|
+
{
|
|
49188
|
+
name: 'direction',
|
|
49189
|
+
title: 'Button Direction',
|
|
49190
|
+
description: 'Button layout direction',
|
|
49191
|
+
schema: {
|
|
49192
|
+
dataType: 'string',
|
|
49193
|
+
nullable: true,
|
|
49194
|
+
defaultValue: 'horizontal',
|
|
49195
|
+
interface: {
|
|
49196
|
+
type: AXPWidgetsList.Editors.SelectBox,
|
|
49197
|
+
options: {
|
|
49198
|
+
valueField: 'value',
|
|
49199
|
+
textField: 'text',
|
|
49200
|
+
dataSource: [
|
|
49201
|
+
{ value: 'horizontal', text: 'Horizontal' },
|
|
49202
|
+
{ value: 'vertical', text: 'Vertical' },
|
|
49203
|
+
],
|
|
49204
|
+
},
|
|
49205
|
+
},
|
|
49206
|
+
},
|
|
49207
|
+
},
|
|
49208
|
+
{
|
|
49209
|
+
name: 'closable',
|
|
49210
|
+
title: 'Closable',
|
|
49211
|
+
description: 'Whether dialog is closable',
|
|
49212
|
+
schema: {
|
|
49213
|
+
dataType: 'boolean',
|
|
49214
|
+
nullable: true,
|
|
49215
|
+
defaultValue: false,
|
|
49216
|
+
interface: {
|
|
49217
|
+
type: AXPWidgetsList.Editors.ToggleSwitch,
|
|
49218
|
+
},
|
|
49219
|
+
},
|
|
49220
|
+
},
|
|
49221
|
+
{
|
|
49222
|
+
name: 'defaultAction',
|
|
49223
|
+
title: 'Default Action',
|
|
49224
|
+
description: 'Default action button',
|
|
49225
|
+
schema: {
|
|
49226
|
+
dataType: 'string',
|
|
49227
|
+
nullable: true,
|
|
49228
|
+
defaultValue: 'cancel',
|
|
49229
|
+
interface: {
|
|
49230
|
+
type: AXPWidgetsList.Editors.SelectBox,
|
|
49231
|
+
options: {
|
|
49232
|
+
valueField: 'value',
|
|
49233
|
+
textField: 'text',
|
|
49234
|
+
dataSource: [
|
|
49235
|
+
{ value: 'confirm', text: 'Confirm' },
|
|
49236
|
+
{ value: 'cancel', text: 'Cancel' },
|
|
49237
|
+
],
|
|
49238
|
+
},
|
|
49239
|
+
},
|
|
49240
|
+
},
|
|
49241
|
+
},
|
|
49242
|
+
],
|
|
49243
|
+
outputs: [],
|
|
49244
|
+
});
|
|
49245
|
+
// Show Layout Popup Activity
|
|
49246
|
+
definitions.push({
|
|
49247
|
+
name: 'show-layout-popup',
|
|
49248
|
+
type: 'workflow-activity:show-layout-popup',
|
|
49249
|
+
title: 'Show Layout Popup',
|
|
49250
|
+
description: 'Shows a popup dialog with a custom layout built using layout builder. Actions are defined dynamically and each action creates a corresponding outcome.',
|
|
49251
|
+
category: commandCategory.id,
|
|
49252
|
+
executionMode: 'frontend',
|
|
49253
|
+
icon: 'fa-window-maximize',
|
|
49254
|
+
// Outcomes are dynamic based on actions - default outcomes shown for reference
|
|
49255
|
+
outcomes: ['submit', 'cancel'],
|
|
49256
|
+
inputs: [
|
|
49257
|
+
{
|
|
49258
|
+
name: 'layout',
|
|
49259
|
+
title: 'Layout Definition',
|
|
49260
|
+
description: 'Layout definition as JSON string or AXPWidgetNode object',
|
|
49261
|
+
schema: {
|
|
49262
|
+
dataType: 'string',
|
|
49263
|
+
nullable: false,
|
|
49264
|
+
interface: {
|
|
49265
|
+
type: AXPWidgetsList.Advanced.CodeEditor,
|
|
49266
|
+
options: {
|
|
49267
|
+
language: 'json',
|
|
49268
|
+
placeholder: 'Enter layout definition as JSON',
|
|
49269
|
+
},
|
|
49270
|
+
},
|
|
49271
|
+
},
|
|
49272
|
+
validations: [
|
|
49273
|
+
{
|
|
49274
|
+
rule: 'required',
|
|
49275
|
+
},
|
|
49276
|
+
],
|
|
49277
|
+
},
|
|
49278
|
+
{
|
|
49279
|
+
name: 'title',
|
|
49280
|
+
title: 'Dialog Title',
|
|
49281
|
+
description: 'Title of the dialog',
|
|
49282
|
+
schema: {
|
|
49283
|
+
dataType: 'string',
|
|
49284
|
+
nullable: true,
|
|
49285
|
+
interface: {
|
|
49286
|
+
type: AXPWidgetsList.Editors.TextBox,
|
|
49287
|
+
options: {
|
|
49288
|
+
placeholder: 'Enter dialog title',
|
|
49289
|
+
},
|
|
49290
|
+
},
|
|
49291
|
+
},
|
|
49292
|
+
},
|
|
49293
|
+
{
|
|
49294
|
+
name: 'size',
|
|
49295
|
+
title: 'Dialog Size',
|
|
49296
|
+
description: 'Size of the dialog',
|
|
49297
|
+
schema: {
|
|
49298
|
+
dataType: 'string',
|
|
49299
|
+
nullable: true,
|
|
49300
|
+
defaultValue: 'md',
|
|
49301
|
+
interface: {
|
|
49302
|
+
type: AXPWidgetsList.Editors.SelectBox,
|
|
49303
|
+
options: {
|
|
49304
|
+
valueField: 'value',
|
|
49305
|
+
textField: 'text',
|
|
49306
|
+
dataSource: [
|
|
49307
|
+
{ value: 'sm', text: 'Small' },
|
|
49308
|
+
{ value: 'md', text: 'Medium' },
|
|
49309
|
+
{ value: 'lg', text: 'Large' },
|
|
49310
|
+
{ value: 'xl', text: 'Extra Large' },
|
|
49311
|
+
{ value: 'xxl', text: 'Extra Extra Large' },
|
|
49312
|
+
],
|
|
49313
|
+
},
|
|
49314
|
+
},
|
|
49315
|
+
},
|
|
49316
|
+
},
|
|
49317
|
+
{
|
|
49318
|
+
name: 'closeButton',
|
|
49319
|
+
title: 'Show Close Button',
|
|
49320
|
+
description: 'Whether to show close button',
|
|
49321
|
+
schema: {
|
|
49322
|
+
dataType: 'boolean',
|
|
49323
|
+
nullable: true,
|
|
49324
|
+
defaultValue: true,
|
|
49325
|
+
interface: {
|
|
49326
|
+
type: AXPWidgetsList.Editors.ToggleSwitch,
|
|
49327
|
+
},
|
|
49328
|
+
},
|
|
49329
|
+
},
|
|
49330
|
+
{
|
|
49331
|
+
name: 'context',
|
|
49332
|
+
title: 'Initial Context',
|
|
49333
|
+
description: 'Initial context data for the form as JSON object',
|
|
49334
|
+
schema: {
|
|
49335
|
+
dataType: 'object',
|
|
49336
|
+
nullable: true,
|
|
49337
|
+
interface: {
|
|
49338
|
+
type: AXPWidgetsList.Advanced.CodeEditor,
|
|
49339
|
+
options: {
|
|
49340
|
+
language: 'json',
|
|
49341
|
+
placeholder: 'Enter initial context as JSON object',
|
|
49342
|
+
},
|
|
49343
|
+
},
|
|
49344
|
+
},
|
|
49345
|
+
},
|
|
49346
|
+
{
|
|
49347
|
+
name: 'actions',
|
|
49348
|
+
title: 'Dialog Actions',
|
|
49349
|
+
description: 'Dialog actions (buttons) to display in footer. Each action will create a corresponding outcome. Format: { prefix?: Array<Action>, suffix?: Array<Action> }',
|
|
49350
|
+
schema: {
|
|
49351
|
+
dataType: 'object',
|
|
49352
|
+
nullable: true,
|
|
49353
|
+
interface: {
|
|
49354
|
+
type: AXPWidgetsList.Advanced.CodeEditor,
|
|
49355
|
+
options: {
|
|
49356
|
+
language: 'json',
|
|
49357
|
+
placeholder: 'Enter actions as JSON object with prefix and/or suffix arrays',
|
|
49358
|
+
},
|
|
49359
|
+
},
|
|
49360
|
+
},
|
|
49361
|
+
},
|
|
49362
|
+
],
|
|
49363
|
+
outputs: [
|
|
49364
|
+
{
|
|
49365
|
+
name: 'result',
|
|
49366
|
+
title: 'Result',
|
|
49367
|
+
description: 'Result data from the dialog (form values)',
|
|
49368
|
+
schema: {
|
|
49369
|
+
dataType: 'object',
|
|
49370
|
+
},
|
|
49371
|
+
},
|
|
49372
|
+
{
|
|
49373
|
+
name: 'action',
|
|
49374
|
+
title: 'Action',
|
|
49375
|
+
description: 'Action that was clicked (e.g., submit, cancel, or custom action name)',
|
|
49376
|
+
schema: {
|
|
49377
|
+
dataType: 'string',
|
|
49378
|
+
},
|
|
49379
|
+
},
|
|
49380
|
+
],
|
|
49001
49381
|
});
|
|
49002
49382
|
}
|
|
49003
49383
|
// Query category activities
|
|
49004
49384
|
if (queryCategory) {
|
|
49385
|
+
// Execute Query Activity
|
|
49005
49386
|
definitions.push({
|
|
49006
49387
|
name: 'execute-query',
|
|
49007
49388
|
type: 'workflow-activity:execute-query',
|
|
@@ -49009,7 +49390,72 @@ function generateActivityDefinitions() {
|
|
|
49009
49390
|
description: 'Executes a query operation',
|
|
49010
49391
|
category: queryCategory.id,
|
|
49011
49392
|
executionMode: 'backend',
|
|
49393
|
+
icon: 'fa-search',
|
|
49012
49394
|
outcomes: ['Done', 'Valid', 'Exists', 'NotFound', 'Failed'],
|
|
49395
|
+
inputs: [
|
|
49396
|
+
{
|
|
49397
|
+
name: 'queryKey',
|
|
49398
|
+
title: 'Query Key',
|
|
49399
|
+
description: 'Query key to execute (e.g., "User:CheckEmailExists")',
|
|
49400
|
+
schema: {
|
|
49401
|
+
dataType: 'string',
|
|
49402
|
+
nullable: false,
|
|
49403
|
+
interface: {
|
|
49404
|
+
type: AXPWidgetsList.Editors.TextBox,
|
|
49405
|
+
options: {
|
|
49406
|
+
placeholder: 'Enter query key (e.g., User:CheckEmailExists)',
|
|
49407
|
+
},
|
|
49408
|
+
},
|
|
49409
|
+
},
|
|
49410
|
+
validations: [
|
|
49411
|
+
{
|
|
49412
|
+
rule: 'required',
|
|
49413
|
+
},
|
|
49414
|
+
],
|
|
49415
|
+
},
|
|
49416
|
+
{
|
|
49417
|
+
name: 'parameters',
|
|
49418
|
+
title: 'Query Parameters',
|
|
49419
|
+
description: 'Query parameters as JSON object',
|
|
49420
|
+
schema: {
|
|
49421
|
+
dataType: 'object',
|
|
49422
|
+
nullable: true,
|
|
49423
|
+
interface: {
|
|
49424
|
+
type: AXPWidgetsList.Advanced.CodeEditor,
|
|
49425
|
+
options: {
|
|
49426
|
+
language: 'json',
|
|
49427
|
+
placeholder: 'Enter query parameters as JSON object',
|
|
49428
|
+
},
|
|
49429
|
+
},
|
|
49430
|
+
},
|
|
49431
|
+
},
|
|
49432
|
+
],
|
|
49433
|
+
outputs: [
|
|
49434
|
+
{
|
|
49435
|
+
name: 'data',
|
|
49436
|
+
title: 'Data',
|
|
49437
|
+
description: 'Query result data',
|
|
49438
|
+
schema: {
|
|
49439
|
+
dataType: 'object',
|
|
49440
|
+
},
|
|
49441
|
+
},
|
|
49442
|
+
{
|
|
49443
|
+
name: 'success',
|
|
49444
|
+
title: 'Success',
|
|
49445
|
+
description: 'Whether query executed successfully',
|
|
49446
|
+
schema: {
|
|
49447
|
+
dataType: 'boolean',
|
|
49448
|
+
},
|
|
49449
|
+
},
|
|
49450
|
+
{
|
|
49451
|
+
name: 'error',
|
|
49452
|
+
title: 'Error',
|
|
49453
|
+
description: 'Error message if query failed',
|
|
49454
|
+
schema: {
|
|
49455
|
+
dataType: 'string',
|
|
49456
|
+
},
|
|
49457
|
+
},
|
|
49458
|
+
],
|
|
49013
49459
|
});
|
|
49014
49460
|
}
|
|
49015
49461
|
return definitions;
|
|
@@ -49034,6 +49480,156 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
|
|
|
49034
49480
|
type: Injectable
|
|
49035
49481
|
}] });
|
|
49036
49482
|
|
|
49483
|
+
//#region ---- Mock Categories ----
|
|
49484
|
+
/**
|
|
49485
|
+
* Generate workflow categories with optimized count metadata
|
|
49486
|
+
*
|
|
49487
|
+
* Uses childrenCount & itemsCount for performance:
|
|
49488
|
+
* - childrenCount: Direct child categories (for lazy loading)
|
|
49489
|
+
* - itemsCount: Direct workflows in this category
|
|
49490
|
+
*/
|
|
49491
|
+
function generateCategories() {
|
|
49492
|
+
const categories = [];
|
|
49493
|
+
// Root categories
|
|
49494
|
+
const rootCategories = [
|
|
49495
|
+
{
|
|
49496
|
+
title: 'Business Process',
|
|
49497
|
+
description: 'Business process workflows',
|
|
49498
|
+
childrenCount: 0,
|
|
49499
|
+
itemsCount: 2,
|
|
49500
|
+
},
|
|
49501
|
+
{
|
|
49502
|
+
title: 'Approval',
|
|
49503
|
+
description: 'Approval and review workflows',
|
|
49504
|
+
childrenCount: 0,
|
|
49505
|
+
itemsCount: 1,
|
|
49506
|
+
},
|
|
49507
|
+
{
|
|
49508
|
+
title: 'Data Processing',
|
|
49509
|
+
description: 'Data processing and transformation workflows',
|
|
49510
|
+
childrenCount: 0,
|
|
49511
|
+
itemsCount: 1,
|
|
49512
|
+
},
|
|
49513
|
+
{
|
|
49514
|
+
title: 'Integration',
|
|
49515
|
+
description: 'Integration and API workflows',
|
|
49516
|
+
childrenCount: 0,
|
|
49517
|
+
itemsCount: 0,
|
|
49518
|
+
},
|
|
49519
|
+
{
|
|
49520
|
+
title: 'Notification',
|
|
49521
|
+
description: 'Notification and communication workflows',
|
|
49522
|
+
childrenCount: 0,
|
|
49523
|
+
itemsCount: 0,
|
|
49524
|
+
},
|
|
49525
|
+
{
|
|
49526
|
+
title: 'General',
|
|
49527
|
+
description: 'General purpose workflows',
|
|
49528
|
+
childrenCount: 0,
|
|
49529
|
+
itemsCount: 0,
|
|
49530
|
+
},
|
|
49531
|
+
];
|
|
49532
|
+
rootCategories.forEach(({ title, description, childrenCount, itemsCount }) => {
|
|
49533
|
+
const categoryId = AXPDataGenerator.uuid();
|
|
49534
|
+
categories.push({
|
|
49535
|
+
id: categoryId,
|
|
49536
|
+
title,
|
|
49537
|
+
description,
|
|
49538
|
+
parentId: undefined,
|
|
49539
|
+
childrenCount,
|
|
49540
|
+
itemsCount,
|
|
49541
|
+
});
|
|
49542
|
+
});
|
|
49543
|
+
return categories;
|
|
49544
|
+
}
|
|
49545
|
+
const WORKFLOW_CATEGORIES = generateCategories();
|
|
49546
|
+
//#endregion
|
|
49547
|
+
|
|
49548
|
+
/**
|
|
49549
|
+
* Mock Workflow Category Provider
|
|
49550
|
+
*
|
|
49551
|
+
* Provides workflow categories from mock data.
|
|
49552
|
+
*/
|
|
49553
|
+
class AXCWorkflowCategoryProvider {
|
|
49554
|
+
async getList(parentId) {
|
|
49555
|
+
if (parentId === undefined) {
|
|
49556
|
+
// Return root categories
|
|
49557
|
+
return WORKFLOW_CATEGORIES.filter(cat => cat.parentId === undefined);
|
|
49558
|
+
}
|
|
49559
|
+
// Return child categories
|
|
49560
|
+
return WORKFLOW_CATEGORIES.filter(cat => cat.parentId === parentId);
|
|
49561
|
+
}
|
|
49562
|
+
async getById(id) {
|
|
49563
|
+
return WORKFLOW_CATEGORIES.find(cat => cat.id === id);
|
|
49564
|
+
}
|
|
49565
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowCategoryProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
49566
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowCategoryProvider }); }
|
|
49567
|
+
}
|
|
49568
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowCategoryProvider, decorators: [{
|
|
49569
|
+
type: Injectable
|
|
49570
|
+
}] });
|
|
49571
|
+
|
|
49572
|
+
/**
|
|
49573
|
+
* Mock Workflow Definition Provider
|
|
49574
|
+
*
|
|
49575
|
+
* Provides workflow definition metadata from mock data.
|
|
49576
|
+
* Converts workflow definitions to metadata format.
|
|
49577
|
+
*/
|
|
49578
|
+
class AXCWorkflowDefinitionMetadataProvider {
|
|
49579
|
+
constructor() {
|
|
49580
|
+
this.metadataCache = null;
|
|
49581
|
+
}
|
|
49582
|
+
getMetadataCache() {
|
|
49583
|
+
if (this.metadataCache) {
|
|
49584
|
+
return this.metadataCache;
|
|
49585
|
+
}
|
|
49586
|
+
this.metadataCache = new Map();
|
|
49587
|
+
// Find Business Process category (first category with itemsCount > 0)
|
|
49588
|
+
const businessProcessCategory = WORKFLOW_CATEGORIES.find(cat => cat.title === 'Business Process');
|
|
49589
|
+
const approvalCategory = WORKFLOW_CATEGORIES.find(cat => cat.title === 'Approval');
|
|
49590
|
+
const dataProcessingCategory = WORKFLOW_CATEGORIES.find(cat => cat.title === 'Data Processing');
|
|
49591
|
+
// Convert workflow definitions to metadata
|
|
49592
|
+
workflowDefinitionMock.forEach(def => {
|
|
49593
|
+
let categoryId;
|
|
49594
|
+
// Map workflow to category based on name/type
|
|
49595
|
+
if (def.name?.includes('approval') || def.name?.includes('review')) {
|
|
49596
|
+
categoryId = approvalCategory?.id;
|
|
49597
|
+
}
|
|
49598
|
+
else if (def.name?.includes('process') || def.name?.includes('data')) {
|
|
49599
|
+
categoryId = dataProcessingCategory?.id;
|
|
49600
|
+
}
|
|
49601
|
+
else {
|
|
49602
|
+
categoryId = businessProcessCategory?.id;
|
|
49603
|
+
}
|
|
49604
|
+
const metadata = {
|
|
49605
|
+
name: def.name || '',
|
|
49606
|
+
title: def.title || def.name || '',
|
|
49607
|
+
description: def.description || undefined,
|
|
49608
|
+
category: categoryId,
|
|
49609
|
+
icon: 'fa-light fa-diagram-project',
|
|
49610
|
+
version: 1,
|
|
49611
|
+
isPublished: true,
|
|
49612
|
+
isBrowsable: true,
|
|
49613
|
+
};
|
|
49614
|
+
this.metadataCache.set(metadata.name, metadata);
|
|
49615
|
+
});
|
|
49616
|
+
return this.metadataCache;
|
|
49617
|
+
}
|
|
49618
|
+
async getList(categoryId) {
|
|
49619
|
+
const cache = this.getMetadataCache();
|
|
49620
|
+
return Array.from(cache.values()).filter(meta => meta.category === categoryId);
|
|
49621
|
+
}
|
|
49622
|
+
async getById(name) {
|
|
49623
|
+
const cache = this.getMetadataCache();
|
|
49624
|
+
return cache.get(name);
|
|
49625
|
+
}
|
|
49626
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowDefinitionMetadataProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
49627
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowDefinitionMetadataProvider }); }
|
|
49628
|
+
}
|
|
49629
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowDefinitionMetadataProvider, decorators: [{
|
|
49630
|
+
type: Injectable
|
|
49631
|
+
}] });
|
|
49632
|
+
|
|
49037
49633
|
class AXCWorkflowManagementMockModule {
|
|
49038
49634
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowManagementMockModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
49039
49635
|
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowManagementMockModule }); }
|
|
@@ -49059,6 +49655,18 @@ class AXCWorkflowManagementMockModule {
|
|
|
49059
49655
|
useClass: AXCActivityDefinitionSeeder,
|
|
49060
49656
|
multi: true,
|
|
49061
49657
|
},
|
|
49658
|
+
// Workflow Category Provider
|
|
49659
|
+
{
|
|
49660
|
+
provide: AXP_WORKFLOW_CATEGORY_PROVIDER,
|
|
49661
|
+
useClass: AXCWorkflowCategoryProvider,
|
|
49662
|
+
multi: true,
|
|
49663
|
+
},
|
|
49664
|
+
// Workflow Definition Metadata Provider
|
|
49665
|
+
{
|
|
49666
|
+
provide: AXP_WORKFLOW_PROVIDER,
|
|
49667
|
+
useClass: AXCWorkflowDefinitionMetadataProvider,
|
|
49668
|
+
multi: true,
|
|
49669
|
+
},
|
|
49062
49670
|
] }); }
|
|
49063
49671
|
}
|
|
49064
49672
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowManagementMockModule, decorators: [{
|
|
@@ -49089,6 +49697,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
|
|
|
49089
49697
|
useClass: AXCActivityDefinitionSeeder,
|
|
49090
49698
|
multi: true,
|
|
49091
49699
|
},
|
|
49700
|
+
// Workflow Category Provider
|
|
49701
|
+
{
|
|
49702
|
+
provide: AXP_WORKFLOW_CATEGORY_PROVIDER,
|
|
49703
|
+
useClass: AXCWorkflowCategoryProvider,
|
|
49704
|
+
multi: true,
|
|
49705
|
+
},
|
|
49706
|
+
// Workflow Definition Metadata Provider
|
|
49707
|
+
{
|
|
49708
|
+
provide: AXP_WORKFLOW_PROVIDER,
|
|
49709
|
+
useClass: AXCWorkflowDefinitionMetadataProvider,
|
|
49710
|
+
multi: true,
|
|
49711
|
+
},
|
|
49092
49712
|
],
|
|
49093
49713
|
}]
|
|
49094
49714
|
}] });
|
|
@@ -49585,5 +50205,5 @@ const AXCMockEntityLogListener = {
|
|
|
49585
50205
|
* Generated bundle index. Do not edit.
|
|
49586
50206
|
*/
|
|
49587
50207
|
|
|
49588
|
-
export { ACTIVITY_CATEGORIES, ACTIVITY_DEFINITIONS, APPLICATIONS, AXCActivityCategorySeeder, AXCActivityDefinitionSeeder, AXCAppTermDataSeeder, AXCAppVersionDataSeeder, AXCApplicationDataSeeder, AXCApplicationManagementMockModule, AXCAssessmentManagementMockModule, AXCAssetManagementMockModule, AXCAuthMockModule, AXCCalendarDataSeeder, AXCCalendarEventDataSeeder, AXCCalendarManagementMockModule, AXCCommonMockModule, AXCContactManagementMockModule, AXCContentManagementMockModule, AXCConversationMockModule, AXCCustomerManagementMockModule, AXCDashboardManagementMockModule, AXCDataManagementMockModule, AXCDexieEntityStorageService, AXCDocumentManagementMockModule, AXCEditionDataSeeder, AXCFileStorageService, AXCFinancialCoreMockModule, AXCFormTemplateManagementMockModule, AXCGoogleStrategyMock, AXCHelpDeskMockModule, AXCHumanCapitalManagementMockModule, AXCIdentifierManagementMockModule, AXCJsaSeeder, AXCLearningManagementMockModule, AXCLocaleManagementMockModule, AXCLocationManagementMockModule, AXCLockService, AXCMeasurementCoreMockModule, AXCMeetingManagementMockModule, AXCMetaDataDefinitionDataSeeder, AXCMetadataCategorySeeder, AXCMiddlewaresModule, AXCMockEntityLogListener, AXCMockModule, AXCNotificationManagementMockModule, AXCOrderManagementMockModule, AXCOrganizationManagementMockModule, AXCPersonManagementMockModule, AXCPlatformManagementMockModule, AXCProcurementManagementMockModule, AXCProductCatalogMockModule, AXCProjectManagementMockModule, AXCQueryCategoryMetadataInheritanceQuery, AXCReportManagementMockModule, AXCSecurityManagementMockModule, AXCSubscriptionManagementMockModule, AXCSupplierManagementMockModule, AXCTaskManagementMockModule, AXCTenantManagementMockModule, AXCTenantSeeder, AXCTokensDataSeeder, AXCUserPassStrategyMock, AXCVersionDB, AXCVersioningService, AXCWorkflowDefinitionDataSeeder,
|
|
50208
|
+
export { ACTIVITY_CATEGORIES, ACTIVITY_DEFINITIONS, APPLICATIONS, AXCActivityCategorySeeder, AXCActivityDefinitionSeeder, AXCAppTermDataSeeder, AXCAppVersionDataSeeder, AXCApplicationDataSeeder, AXCApplicationManagementMockModule, AXCAssessmentManagementMockModule, AXCAssetManagementMockModule, AXCAuthMockModule, AXCCalendarDataSeeder, AXCCalendarEventDataSeeder, AXCCalendarManagementMockModule, AXCCommonMockModule, AXCContactManagementMockModule, AXCContentManagementMockModule, AXCConversationMockModule, AXCCustomerManagementMockModule, AXCDashboardManagementMockModule, AXCDataManagementMockModule, AXCDexieEntityStorageService, AXCDocumentManagementMockModule, AXCEditionDataSeeder, AXCFileStorageService, AXCFinancialCoreMockModule, AXCFormTemplateManagementMockModule, AXCGoogleStrategyMock, AXCHelpDeskMockModule, AXCHumanCapitalManagementMockModule, AXCIdentifierManagementMockModule, AXCJsaSeeder, AXCLearningManagementMockModule, AXCLocaleManagementMockModule, AXCLocationManagementMockModule, AXCLockService, AXCMeasurementCoreMockModule, AXCMeetingManagementMockModule, AXCMetaDataDefinitionDataSeeder, AXCMetadataCategorySeeder, AXCMiddlewaresModule, AXCMockEntityLogListener, AXCMockModule, AXCNotificationManagementMockModule, AXCOrderManagementMockModule, AXCOrganizationManagementMockModule, AXCPersonManagementMockModule, AXCPlatformManagementMockModule, AXCProcurementManagementMockModule, AXCProductCatalogMockModule, AXCProjectManagementMockModule, AXCQueryCategoryMetadataInheritanceQuery, AXCReportManagementMockModule, AXCSecurityManagementMockModule, AXCSubscriptionManagementMockModule, AXCSupplierManagementMockModule, AXCTaskManagementMockModule, AXCTenantManagementMockModule, AXCTenantSeeder, AXCTokensDataSeeder, AXCUserPassStrategyMock, AXCVersionDB, AXCVersioningService, AXCWorkflowCategoryProvider, AXCWorkflowDefinitionDataSeeder, AXCWorkflowDefinitionMetadataProvider, AXCWorkflowExecutionDB, AXCWorkflowManagementMockModule, AXCWorkflowRuntimeService, AXMAiResponderService, AXMAssessmentCaseDataSeeder, AXMAssessmentSessionDataSeeder, AXMBusinessUnitDataSeeder, AXMCalendarEventTypeSeeder, AXMEmployeeDataSeeder, AXMEmploymentTypeDataSeeder, AXMFormDataSourcesProvider, AXMJobDefinitionDataSeeder, AXMJobLevelDataSeeder, AXMLeaveRequestDataSeeder, AXMMeetingDataSeeder, AXMMeetingFilesDataSeeder, AXMMeetingParticipantDataSeeder, AXMMeetingRoleTypeDataSeeder, AXMMeetingSessionDataSeeder, AXMMeetingTypeDataSeeder, AXMMeetingTypeFileTemplateDataSeeder, AXMPositionAssignmentDataSeeder, AXMPositionDataSeeder, AXMQuestionBankItemCategoryDataSeeder, AXMQuestionBankItemDataSeeder, AXMQuestionnaireCategoryDataSeeder, AXMQuestionnaireDataSeeder, AXMReportExecuteCommand, AXMResponsibilityDataSeeder, AXMTagDataSeeder, AXMTeamDataSeeder, AXPDashboardDataSeeder, AXPIdentifierDB, AXPMessageDataSeeder, AXPMockChecksumProvider, AXPMockClockProvider, AXPMockIdentifierService, AXPMockLookupProvider, AXPMockPolicyProvider, AXPMockSequenceProvider, AXPRoomDataSeeder, AXPSecurityManagementRoleDataSeeder, AXPSecurityManagementUserDataSeeder, AXPTaskBoardPlatformManagementTaskProvider, AXPTaskBoardProjectManagementTaskProvider, AXVChangeType, BUSINESS_UNITS_MOCK, COSTMANAGER_ENTERPRISE, COSTMANAGER_PROFESSIONAL, COSTMANAGER_STANDARD, CRM_ENTERPRISE, DASHBOARDS, EDITIONS, EMPLOYEES_MOCK, EMPLOYMENT_TYPES_MOCK, FINANCE_BASIC, FINANCE_ENTERPRISE, FINANCE_PRO, HR_ENTERPRISE, JOB_DEFINITIONS_CATEGORY_MOCK, JOB_DEFINITIONS_MOCK, JOB_LEVELS_MOCK, LASER_PLUMBING_TENANT_ID, LEAVE_REQUESTS_MOCK, ORDERING_BASIC, ORDERING_ENTERPRISE, ORDERING_STANDARD, OWNERSHIP_FILTER_BYPASS, PLATFORM_CONSOLE, PLATFORM_TENANT_ID, POSITIONS_CATEGORY_MOCK, POSITIONS_MOCK, POSITION_ASSIGNMENTS_MOCK, QUESTIONNAIRE_CATEGORY_MOCK, QUESTION_BANK_ITEM_CATEGORY_MOCK, RESPONSIBILITIES_CATEGORY_MOCK, RESPONSIBILITIES_MOCK, SAFETYMINDER_BASIC, SAFETYMINDER_ENTERPRISE, SAFETYMINDER_PROFESSIONAL, SHOP_BASIC, SHOP_ENTERPRISE, SHOP_PRO, TAGS_MOCK, TASKS, TASK_STATUSES, TASK_TEMPLATES, TASK_TYPES, TEAMS_MOCK, TIMEPLICITY_TENANT_ID, TOKENS, VISIBILITY_FILTER_BYPASS, WORKFLOW_CATEGORIES, assessmentCaseMock, assessmentSessionMock, auditLoggerMiddleware, avatarInterface, axVersionDB, axWorkflowExecutionDB, bypassAllFilters, bypassOwnershipFilter, bypassVisibilityFilter, calendarEventMock, calendarEventTypeMockData, calendarMock, checkboxInterface, childCountMiddleware, colorInterface, computeDiff, contactInterface, createAndSaveDashboardForUser, createDashboardForUser, createFileCastMiddleware, dateInterface, dateTimeInterface, descriptionInterface, emailInterface, fileUploaderInterface, findContactMethod, findEmployeeById, findPersonById, generateUserDashboard, groupOrderItemCalculatorMiddleware, historyLoggerMiddleware, identifierCommitMiddleware, imageInterface, lockGuardMiddleware, longTextAnswerInterface, lookupInterface, lookupResolverMiddleware, mapInterface, meetingFilesMock, meetingIds, meetingMock, meetingParticipantMock, meetingRoleTypeMock, meetingSessionMock, meetingTimeSlotMock, meetingTypeFileTemplateMock, meetingTypeMock, mergeDetailRelationMiddleware, metadataCategoryMocks, mockRoleDefinitions, mockUsers, multipleChoiceInterface, multipleSelectInterface, normalizeAnyFileArrayForView, normalizeSnapshotFileFieldsForView, numberInterface, participantIds, passwordInterface, phoneInterface, primaryMiddleware, qrcodeInterface, questionBankItemMock, questionnaireMock, ratingInterface, richTextInterface, scaleInterface, selectInterface, selectionListInterface, signatureInterface, signatureLoaderMiddleware, tagInterface, tenantMocks, textAreaInterface, textInterface, timeDurationInterface, titleInterface, toUiRows, toggleInterface, urlInterface, versionInterface, visibilityFilterMiddleware, workflowDefinitionEntityMock, workflowDefinitionIds, workflowDefinitionMock, yesNoInterface };
|
|
49589
50209
|
//# sourceMappingURL=acorex-connectivity-mock.mjs.map
|