@oronts/vendure-data-hub-plugin 0.1.1 → 0.1.2
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/CHANGELOG.md +5 -0
- package/dist/dashboard/components/common/ConnectionConfigEditor.tsx +589 -0
- package/dist/dashboard/components/common/HeadersEditor.tsx +90 -0
- package/dist/dashboard/components/common/ValidationFeedback.tsx +17 -0
- package/dist/dashboard/components/common/index.ts +10 -0
- package/dist/dashboard/components/pipelines/PipelineEditor.tsx +504 -0
- package/dist/dashboard/components/pipelines/PipelineExport.tsx +63 -0
- package/dist/dashboard/components/pipelines/PipelineImport.tsx +87 -0
- package/dist/dashboard/components/pipelines/ReactFlowPipelineEditor.tsx +539 -0
- package/dist/dashboard/components/pipelines/shared/NodePropertiesPanel.tsx +146 -0
- package/dist/dashboard/components/pipelines/shared/PipelineNode.tsx +155 -0
- package/dist/dashboard/components/pipelines/shared/PipelineSettingsPanel.tsx +392 -0
- package/dist/dashboard/components/pipelines/shared/StepListItem.tsx +144 -0
- package/dist/dashboard/components/pipelines/shared/index.ts +33 -0
- package/dist/dashboard/components/pipelines/shared/visual-node-config.ts +169 -0
- package/dist/dashboard/components/shared/LoadMoreButton.tsx +18 -0
- package/dist/dashboard/components/shared/entity-selector/EntitySelector.tsx +59 -0
- package/dist/dashboard/components/shared/entity-selector/index.ts +1 -0
- package/dist/dashboard/components/shared/error-boundary/ErrorBoundary.tsx +90 -0
- package/dist/dashboard/components/shared/error-boundary/index.ts +1 -0
- package/dist/dashboard/components/shared/feedback/EmptyState.tsx +36 -0
- package/dist/dashboard/components/shared/feedback/ErrorState.tsx +69 -0
- package/dist/dashboard/components/shared/feedback/LoadingState.tsx +104 -0
- package/dist/dashboard/components/shared/feedback/ValidationErrorDisplay.tsx +29 -0
- package/dist/dashboard/components/shared/feedback/index.ts +4 -0
- package/dist/dashboard/components/shared/file-dropzone/FileDropzone.tsx +167 -0
- package/dist/dashboard/components/shared/file-dropzone/index.ts +1 -0
- package/dist/dashboard/components/shared/filter-conditions-editor/FilterConditionsEditor.tsx +226 -0
- package/dist/dashboard/components/shared/filter-conditions-editor/index.ts +1 -0
- package/dist/dashboard/components/shared/index.ts +45 -0
- package/dist/dashboard/components/shared/schema-form/SchemaFormRenderer.tsx +248 -0
- package/dist/dashboard/components/shared/schema-form/fields/BooleanField.tsx +26 -0
- package/dist/dashboard/components/shared/schema-form/fields/FieldWrapper.tsx +28 -0
- package/dist/dashboard/components/shared/schema-form/fields/FileUploadField.tsx +171 -0
- package/dist/dashboard/components/shared/schema-form/fields/JsonField.tsx +132 -0
- package/dist/dashboard/components/shared/schema-form/fields/NumberField.tsx +33 -0
- package/dist/dashboard/components/shared/schema-form/fields/SelectField.tsx +70 -0
- package/dist/dashboard/components/shared/schema-form/fields/StringField.tsx +36 -0
- package/dist/dashboard/components/shared/schema-form/fields/TextareaField.tsx +31 -0
- package/dist/dashboard/components/shared/schema-form/fields/index.ts +23 -0
- package/dist/dashboard/components/shared/schema-form/index.ts +2 -0
- package/dist/dashboard/components/shared/schema-form/utils.ts +3 -0
- package/dist/dashboard/components/shared/selectable-card/SelectableCard.tsx +65 -0
- package/dist/dashboard/components/shared/selectable-card/index.ts +1 -0
- package/dist/dashboard/components/shared/stat-card/StatCard.tsx +121 -0
- package/dist/dashboard/components/shared/stat-card/index.ts +1 -0
- package/dist/dashboard/components/shared/step-config/AdapterRequiredWarning.tsx +25 -0
- package/dist/dashboard/components/shared/step-config/AdapterSelector.tsx +109 -0
- package/dist/dashboard/components/shared/step-config/AdvancedEditors.tsx +634 -0
- package/dist/dashboard/components/shared/step-config/EnrichConfigComponent.tsx +295 -0
- package/dist/dashboard/components/shared/step-config/ExtractTestResults.tsx +143 -0
- package/dist/dashboard/components/shared/step-config/GateConfigComponent.tsx +127 -0
- package/dist/dashboard/components/shared/step-config/LoadTestResults.tsx +104 -0
- package/dist/dashboard/components/shared/step-config/OperatorCard.tsx +266 -0
- package/dist/dashboard/components/shared/step-config/OperatorCheatSheetButton.tsx +54 -0
- package/dist/dashboard/components/shared/step-config/OperatorFieldInput.tsx +209 -0
- package/dist/dashboard/components/shared/step-config/RetrySettingsComponent.tsx +111 -0
- package/dist/dashboard/components/shared/step-config/RouteConfigComponent.tsx +125 -0
- package/dist/dashboard/components/shared/step-config/StepConfigPanel.tsx +564 -0
- package/dist/dashboard/components/shared/step-config/StepTester.tsx +165 -0
- package/dist/dashboard/components/shared/step-config/TestResultContainer.tsx +57 -0
- package/dist/dashboard/components/shared/step-config/TransformTestResults.tsx +130 -0
- package/dist/dashboard/components/shared/step-config/ValidateConfigComponent.tsx +334 -0
- package/dist/dashboard/components/shared/step-config/index.ts +29 -0
- package/dist/dashboard/components/shared/step-config/step-test-handlers.ts +297 -0
- package/dist/dashboard/components/shared/trigger-config/TriggerForm.tsx +478 -0
- package/dist/dashboard/components/shared/trigger-config/index.ts +1 -0
- package/dist/dashboard/components/shared/triggers-panel/TriggersPanel.tsx +281 -0
- package/dist/dashboard/components/shared/triggers-panel/index.ts +1 -0
- package/dist/dashboard/components/shared/wizard/ConfigurationNameCard.tsx +59 -0
- package/dist/dashboard/components/shared/wizard/SummaryCard.tsx +53 -0
- package/dist/dashboard/components/shared/wizard/WizardFooter.tsx +47 -0
- package/dist/dashboard/components/shared/wizard/WizardProgressBar.tsx +78 -0
- package/dist/dashboard/components/shared/wizard/index.ts +4 -0
- package/dist/dashboard/components/shared/wizard-trigger/TriggerSchemaFields.tsx +128 -0
- package/dist/dashboard/components/shared/wizard-trigger/TriggerSelector.tsx +33 -0
- package/dist/dashboard/components/shared/wizard-trigger/index.ts +2 -0
- package/dist/dashboard/components/templates/TemplateGallery.tsx +210 -0
- package/dist/dashboard/components/templates/TemplatePreview.tsx +214 -0
- package/dist/dashboard/components/templates/index.ts +4 -0
- package/dist/dashboard/components/wizards/export-wizard/DestinationStep.tsx +207 -0
- package/dist/dashboard/components/wizards/export-wizard/ExportWizard.tsx +221 -0
- package/dist/dashboard/components/wizards/export-wizard/FieldsStep.tsx +159 -0
- package/dist/dashboard/components/wizards/export-wizard/FormatStep.tsx +246 -0
- package/dist/dashboard/components/wizards/export-wizard/ReviewStep.tsx +231 -0
- package/dist/dashboard/components/wizards/export-wizard/SourceStep.tsx +154 -0
- package/dist/dashboard/components/wizards/export-wizard/TriggerStep.tsx +234 -0
- package/dist/dashboard/components/wizards/export-wizard/constants.ts +73 -0
- package/dist/dashboard/components/wizards/export-wizard/index.ts +2 -0
- package/dist/dashboard/components/wizards/export-wizard/types.ts +39 -0
- package/dist/dashboard/components/wizards/import-wizard/ImportWizard.tsx +350 -0
- package/dist/dashboard/components/wizards/import-wizard/MappingStep.tsx +286 -0
- package/dist/dashboard/components/wizards/import-wizard/PreviewStep.tsx +79 -0
- package/dist/dashboard/components/wizards/import-wizard/ReviewStep.tsx +266 -0
- package/dist/dashboard/components/wizards/import-wizard/SourceStep.tsx +537 -0
- package/dist/dashboard/components/wizards/import-wizard/StrategyStep.tsx +328 -0
- package/dist/dashboard/components/wizards/import-wizard/TargetStep.tsx +76 -0
- package/dist/dashboard/components/wizards/import-wizard/TemplateStep.tsx +116 -0
- package/dist/dashboard/components/wizards/import-wizard/TransformStep.tsx +666 -0
- package/dist/dashboard/components/wizards/import-wizard/TriggerStep.tsx +51 -0
- package/dist/dashboard/components/wizards/import-wizard/constants.ts +104 -0
- package/dist/dashboard/components/wizards/import-wizard/index.ts +3 -0
- package/dist/dashboard/components/wizards/import-wizard/types.ts +35 -0
- package/dist/dashboard/components/wizards/index.ts +7 -0
- package/dist/dashboard/components/wizards/shared/WizardStepContainer.tsx +27 -0
- package/dist/dashboard/components/wizards/shared/constants.ts +16 -0
- package/dist/dashboard/components/wizards/shared/index.ts +10 -0
- package/dist/dashboard/constants/colors.ts +25 -0
- package/dist/dashboard/constants/connection-defaults.ts +7 -0
- package/dist/dashboard/constants/connection-types.ts +1 -0
- package/dist/dashboard/constants/defaults.ts +18 -0
- package/dist/dashboard/constants/editor.ts +69 -0
- package/dist/dashboard/constants/enum-maps.ts +18 -0
- package/dist/dashboard/constants/fallbacks.ts +44 -0
- package/dist/dashboard/constants/file-format-registry.ts +206 -0
- package/dist/dashboard/constants/index.ts +24 -0
- package/dist/dashboard/constants/navigation.ts +29 -0
- package/dist/dashboard/constants/permissions.ts +41 -0
- package/dist/dashboard/constants/placeholders.ts +77 -0
- package/dist/dashboard/constants/routes.ts +12 -0
- package/dist/dashboard/constants/run-status.ts +1 -0
- package/dist/dashboard/constants/sentinel-values.ts +12 -0
- package/dist/dashboard/constants/step-configs.ts +9 -0
- package/dist/dashboard/constants/step-mappings.ts +170 -0
- package/dist/dashboard/constants/steps.ts +37 -0
- package/dist/dashboard/constants/toast-messages.ts +149 -0
- package/dist/dashboard/constants/triggers.ts +5 -0
- package/dist/dashboard/constants/ui-config.ts +139 -0
- package/dist/dashboard/constants/ui-dimensions.ts +145 -0
- package/dist/dashboard/constants/ui-states.ts +28 -0
- package/dist/dashboard/constants/ui-types.ts +85 -0
- package/dist/dashboard/constants/validation-patterns.ts +26 -0
- package/dist/dashboard/gql/gql.ts +370 -0
- package/dist/dashboard/gql/graphql.ts +10378 -0
- package/dist/dashboard/gql/index.ts +1 -0
- package/dist/dashboard/hooks/api/index.ts +115 -0
- package/dist/dashboard/hooks/api/mutation-helpers.ts +34 -0
- package/dist/dashboard/hooks/api/use-adapters.ts +92 -0
- package/dist/dashboard/hooks/api/use-config-options.ts +513 -0
- package/dist/dashboard/hooks/api/use-connections.ts +84 -0
- package/dist/dashboard/hooks/api/use-entity-field-schemas.ts +99 -0
- package/dist/dashboard/hooks/api/use-entity-loaders.ts +45 -0
- package/dist/dashboard/hooks/api/use-hooks.ts +68 -0
- package/dist/dashboard/hooks/api/use-logs.ts +102 -0
- package/dist/dashboard/hooks/api/use-pipeline-runs.ts +221 -0
- package/dist/dashboard/hooks/api/use-pipelines.ts +279 -0
- package/dist/dashboard/hooks/api/use-queues.ts +141 -0
- package/dist/dashboard/hooks/api/use-secrets.ts +75 -0
- package/dist/dashboard/hooks/api/use-settings.ts +55 -0
- package/dist/dashboard/hooks/api/use-step-tester.ts +79 -0
- package/dist/dashboard/hooks/index.ts +13 -0
- package/dist/dashboard/hooks/use-adapter-catalog.ts +253 -0
- package/dist/dashboard/hooks/use-export-templates.ts +80 -0
- package/dist/dashboard/hooks/use-import-templates.ts +139 -0
- package/dist/dashboard/hooks/use-load-more.ts +29 -0
- package/dist/dashboard/hooks/use-stable-keys.ts +54 -0
- package/dist/dashboard/hooks/use-trigger-types.ts +100 -0
- package/dist/dashboard/hooks/use-wizard-navigation.ts +128 -0
- package/dist/dashboard/index.tsx +55 -0
- package/dist/dashboard/routes/adapters/AdapterCard.tsx +102 -0
- package/dist/dashboard/routes/adapters/AdapterConstants.tsx +20 -0
- package/dist/dashboard/routes/adapters/AdapterDetail.tsx +208 -0
- package/dist/dashboard/routes/adapters/AdapterTypeSection.tsx +105 -0
- package/dist/dashboard/routes/adapters/AdaptersPage.tsx +276 -0
- package/dist/dashboard/routes/adapters/AdaptersTable.tsx +107 -0
- package/dist/dashboard/routes/adapters/index.ts +1 -0
- package/dist/dashboard/routes/connections/ConnectionDetail.tsx +218 -0
- package/dist/dashboard/routes/connections/ConnectionsList.tsx +34 -0
- package/dist/dashboard/routes/connections/index.ts +2 -0
- package/dist/dashboard/routes/hooks/Hooks.tsx +425 -0
- package/dist/dashboard/routes/hooks/hook-stages.ts +52 -0
- package/dist/dashboard/routes/hooks/index.ts +1 -0
- package/dist/dashboard/routes/index.ts +8 -0
- package/dist/dashboard/routes/logs/Logs.tsx +93 -0
- package/dist/dashboard/routes/logs/components/LogDetailDrawer.tsx +118 -0
- package/dist/dashboard/routes/logs/components/LogExplorerTab.tsx +367 -0
- package/dist/dashboard/routes/logs/components/LogLevelBadge.tsx +34 -0
- package/dist/dashboard/routes/logs/components/LogTableRow.tsx +70 -0
- package/dist/dashboard/routes/logs/components/LogsOverviewTab.tsx +178 -0
- package/dist/dashboard/routes/logs/components/RealtimeLogTab.tsx +122 -0
- package/dist/dashboard/routes/logs/index.ts +1 -0
- package/dist/dashboard/routes/pipelines/ErrorAuditList.tsx +39 -0
- package/dist/dashboard/routes/pipelines/ExportWizardPage.tsx +96 -0
- package/dist/dashboard/routes/pipelines/ImportWizardPage.tsx +104 -0
- package/dist/dashboard/routes/pipelines/PipelineDetail.tsx +211 -0
- package/dist/dashboard/routes/pipelines/PipelineRunsBlock.tsx +377 -0
- package/dist/dashboard/routes/pipelines/PipelinesList.tsx +87 -0
- package/dist/dashboard/routes/pipelines/RetryPatchHelper.tsx +51 -0
- package/dist/dashboard/routes/pipelines/RunDetailsPanel.tsx +238 -0
- package/dist/dashboard/routes/pipelines/RunErrorsList.tsx +116 -0
- package/dist/dashboard/routes/pipelines/StepCounters.tsx +24 -0
- package/dist/dashboard/routes/pipelines/StepSummaryTable.tsx +36 -0
- package/dist/dashboard/routes/pipelines/components/DryRunDialog.tsx +341 -0
- package/dist/dashboard/routes/pipelines/components/PipelineActionButtons.tsx +201 -0
- package/dist/dashboard/routes/pipelines/components/PipelineEditorToggle.tsx +116 -0
- package/dist/dashboard/routes/pipelines/components/PipelineFormFields.tsx +156 -0
- package/dist/dashboard/routes/pipelines/components/PipelineWebhookInfo.tsx +111 -0
- package/dist/dashboard/routes/pipelines/components/ReviewActionsPanel.tsx +342 -0
- package/dist/dashboard/routes/pipelines/components/ValidationPanel.tsx +121 -0
- package/dist/dashboard/routes/pipelines/components/VersionHistoryDialog.tsx +131 -0
- package/dist/dashboard/routes/pipelines/components/index.ts +25 -0
- package/dist/dashboard/routes/pipelines/hooks/index.ts +1 -0
- package/dist/dashboard/routes/pipelines/hooks/use-pipeline-validation.ts +114 -0
- package/dist/dashboard/routes/pipelines/index.ts +4 -0
- package/dist/dashboard/routes/pipelines/utils/index.ts +1 -0
- package/dist/dashboard/routes/pipelines/utils/pipeline-conversion.ts +261 -0
- package/dist/dashboard/routes/queues/ConsumersTable.tsx +134 -0
- package/dist/dashboard/routes/queues/DeadLettersTable.tsx +118 -0
- package/dist/dashboard/routes/queues/FailedRunsTable.tsx +74 -0
- package/dist/dashboard/routes/queues/QueuesPage.tsx +290 -0
- package/dist/dashboard/routes/queues/index.ts +1 -0
- package/dist/dashboard/routes/queues/types.ts +22 -0
- package/dist/dashboard/routes/secrets/SecretDetail.tsx +278 -0
- package/dist/dashboard/routes/secrets/SecretsList.tsx +34 -0
- package/dist/dashboard/routes/secrets/index.ts +2 -0
- package/dist/dashboard/routes/settings/Settings.tsx +343 -0
- package/dist/dashboard/routes/settings/index.ts +1 -0
- package/dist/dashboard/types/index.ts +89 -0
- package/dist/dashboard/types/pipeline.ts +51 -0
- package/dist/dashboard/types/ui-types.ts +400 -0
- package/dist/dashboard/types/wizard.ts +235 -0
- package/dist/dashboard/utils/adapter-grouping.ts +43 -0
- package/dist/dashboard/utils/column-analysis.ts +11 -0
- package/dist/dashboard/utils/field-preparation.ts +31 -0
- package/dist/dashboard/utils/form-validation.ts +373 -0
- package/dist/dashboard/utils/formatters.ts +92 -0
- package/dist/dashboard/utils/icon-resolver.ts +35 -0
- package/dist/dashboard/utils/index.ts +60 -0
- package/dist/dashboard/utils/query-key-factory.ts +54 -0
- package/dist/dashboard/utils/step-helpers.ts +32 -0
- package/dist/dashboard/utils/string-helpers.ts +4 -0
- package/dist/dashboard/utils/template-helpers.ts +26 -0
- package/dist/dashboard/utils/trigger-sync.ts +138 -0
- package/dist/dashboard/utils/wizard-to-pipeline.ts +569 -0
- package/package.json +2 -2
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import type { TransformationType } from '../../shared/types';
|
|
2
|
+
import type { FileType } from './ui-types';
|
|
3
|
+
|
|
4
|
+
export interface WizardStep {
|
|
5
|
+
id: string;
|
|
6
|
+
label: string;
|
|
7
|
+
icon: React.FC<{ className?: string }>;
|
|
8
|
+
description?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface ImportSourceConfig {
|
|
12
|
+
type: 'FILE' | 'API' | 'DATABASE' | 'WEBHOOK' | 'CDC' | (string & {});
|
|
13
|
+
fileConfig?: FileSourceConfig;
|
|
14
|
+
apiConfig?: ApiSourceConfig;
|
|
15
|
+
databaseConfig?: DatabaseSourceConfig;
|
|
16
|
+
webhookConfig?: WebhookSourceConfig;
|
|
17
|
+
cdcConfig?: CdcSourceConfig;
|
|
18
|
+
/** Dynamic source types store their config under `${type.toLowerCase()}Config`. */
|
|
19
|
+
[key: string]: unknown;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface CdcSourceConfig {
|
|
23
|
+
connectionId: string;
|
|
24
|
+
databaseType: string;
|
|
25
|
+
table: string;
|
|
26
|
+
trackingColumn: string;
|
|
27
|
+
trackingType: 'TIMESTAMP' | 'VERSION';
|
|
28
|
+
columns?: string[];
|
|
29
|
+
pollIntervalMs?: number;
|
|
30
|
+
batchSize?: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface FileSourceConfig {
|
|
34
|
+
format: NonNullable<FileType>;
|
|
35
|
+
hasHeaders: boolean;
|
|
36
|
+
delimiter?: string;
|
|
37
|
+
encoding?: string;
|
|
38
|
+
sheetName?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ApiSourceConfig {
|
|
42
|
+
url: string;
|
|
43
|
+
method: 'GET' | 'POST';
|
|
44
|
+
headers?: Record<string, string>;
|
|
45
|
+
body?: string;
|
|
46
|
+
pagination?: {
|
|
47
|
+
type: 'OFFSET' | 'CURSOR' | 'PAGE';
|
|
48
|
+
pageSize: number;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface DatabaseSourceConfig {
|
|
53
|
+
connectionCode: string;
|
|
54
|
+
databaseType: string;
|
|
55
|
+
query: string;
|
|
56
|
+
host?: string;
|
|
57
|
+
port?: number;
|
|
58
|
+
database?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface WebhookSourceConfig {
|
|
62
|
+
path: string;
|
|
63
|
+
secret?: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface ImportFieldMapping {
|
|
67
|
+
sourceField: string;
|
|
68
|
+
targetField: string;
|
|
69
|
+
transformation?: string;
|
|
70
|
+
defaultValue?: unknown;
|
|
71
|
+
required: boolean;
|
|
72
|
+
preview?: unknown[];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface ImportStrategies {
|
|
76
|
+
existingRecords: 'SKIP' | 'UPDATE' | 'REPLACE' | 'ERROR';
|
|
77
|
+
lookupFields: string[];
|
|
78
|
+
newRecords: 'CREATE' | 'SKIP' | 'ERROR';
|
|
79
|
+
publishAfterImport: boolean;
|
|
80
|
+
publishDelay?: number;
|
|
81
|
+
cleanupStrategy: 'NONE' | 'UNPUBLISH_MISSING' | 'DELETE_MISSING';
|
|
82
|
+
batchSize: number;
|
|
83
|
+
parallelBatches: number;
|
|
84
|
+
errorThreshold: number;
|
|
85
|
+
continueOnError: boolean;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface ImportTriggerConfig {
|
|
89
|
+
type: 'MANUAL' | 'SCHEDULE' | 'WEBHOOK' | 'FILE';
|
|
90
|
+
schedule?: string;
|
|
91
|
+
webhookPath?: string;
|
|
92
|
+
fileWatchPath?: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface QueryConfig {
|
|
96
|
+
type: 'all' | 'query' | 'graphql';
|
|
97
|
+
limit?: number;
|
|
98
|
+
orderBy?: string;
|
|
99
|
+
orderDirection?: 'ASC' | 'DESC';
|
|
100
|
+
customQuery?: string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface ExportField {
|
|
104
|
+
sourceField: string;
|
|
105
|
+
outputName: string;
|
|
106
|
+
transformation?: string;
|
|
107
|
+
format?: string;
|
|
108
|
+
include: boolean;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface ExportFormatConfig {
|
|
112
|
+
/** Known: CSV, JSON, XML, GOOGLE_SHOPPING, META_CATALOG, AMAZON. Dynamic adapters may add more. */
|
|
113
|
+
type: 'CSV' | 'JSON' | 'XML' | 'GOOGLE_SHOPPING' | 'META_CATALOG' | 'AMAZON' | (string & {});
|
|
114
|
+
/** Schema-driven format options. Keys come from exporter adapter schema fields with group 'format-options'. */
|
|
115
|
+
options: Record<string, unknown> & {
|
|
116
|
+
/** Feed template identifier for feed-based formats */
|
|
117
|
+
feedTemplate?: string;
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface DestinationConfig {
|
|
122
|
+
type: 'FILE' | 'SFTP' | 'FTP' | 'HTTP' | 'S3' | 'WEBHOOK' | 'DOWNLOAD' | 'EMAIL' | 'LOCAL' | (string & {});
|
|
123
|
+
fileConfig?: FileDestinationConfig;
|
|
124
|
+
sftpConfig?: SftpDestinationConfig;
|
|
125
|
+
ftpConfig?: FtpDestinationConfig;
|
|
126
|
+
httpConfig?: HttpDestinationConfig;
|
|
127
|
+
s3Config?: S3DestinationConfig;
|
|
128
|
+
webhookConfig?: WebhookDestinationConfig;
|
|
129
|
+
emailConfig?: EmailDestinationConfig;
|
|
130
|
+
localConfig?: LocalDestinationConfig;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface FileDestinationConfig {
|
|
134
|
+
directory: string;
|
|
135
|
+
filename: string;
|
|
136
|
+
filenamePattern?: string;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface SftpDestinationConfig {
|
|
140
|
+
host: string;
|
|
141
|
+
port: number;
|
|
142
|
+
username: string;
|
|
143
|
+
passwordSecretCode?: string;
|
|
144
|
+
privateKeySecretCode?: string;
|
|
145
|
+
remotePath: string;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export interface HttpDestinationConfig {
|
|
149
|
+
url: string;
|
|
150
|
+
method: 'POST' | 'PUT';
|
|
151
|
+
headers?: Record<string, string>;
|
|
152
|
+
authType?: 'NONE' | 'BASIC' | 'BEARER' | 'API_KEY';
|
|
153
|
+
authSecretId?: string;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export interface S3DestinationConfig {
|
|
157
|
+
bucket: string;
|
|
158
|
+
region: string;
|
|
159
|
+
key: string;
|
|
160
|
+
accessKeyIdSecretCode?: string;
|
|
161
|
+
secretAccessKeySecretCode?: string;
|
|
162
|
+
endpoint?: string;
|
|
163
|
+
forcePathStyle?: boolean;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export interface FtpDestinationConfig {
|
|
167
|
+
host: string;
|
|
168
|
+
port: number;
|
|
169
|
+
username: string;
|
|
170
|
+
passwordSecretCode?: string;
|
|
171
|
+
remotePath: string;
|
|
172
|
+
secure?: boolean;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export interface WebhookDestinationConfig {
|
|
176
|
+
url: string;
|
|
177
|
+
includeMetadata?: boolean;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export interface EmailDestinationConfig {
|
|
181
|
+
to: string;
|
|
182
|
+
subject: string;
|
|
183
|
+
body?: string;
|
|
184
|
+
attachFile?: boolean;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export interface LocalDestinationConfig {
|
|
188
|
+
directory: string;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export interface ExportTriggerConfig {
|
|
192
|
+
type: 'MANUAL' | 'SCHEDULE' | 'EVENT' | 'WEBHOOK';
|
|
193
|
+
schedule?: string;
|
|
194
|
+
events?: string[];
|
|
195
|
+
webhookPath?: string;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export interface CacheConfig {
|
|
199
|
+
enabled: boolean;
|
|
200
|
+
ttl: number;
|
|
201
|
+
invalidateOn?: string[];
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export interface ExportOptions {
|
|
205
|
+
batchSize: number;
|
|
206
|
+
includeMetadata: boolean;
|
|
207
|
+
compression?: 'NONE' | 'GZIP' | 'ZIP';
|
|
208
|
+
notifyOnComplete?: boolean;
|
|
209
|
+
retryOnFailure?: boolean;
|
|
210
|
+
maxRetries?: number;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export interface WizardTransformationStep {
|
|
214
|
+
id: string;
|
|
215
|
+
type: TransformationType;
|
|
216
|
+
config: Record<string, unknown>;
|
|
217
|
+
enabled?: boolean;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export interface ParsedData {
|
|
221
|
+
headers: string[];
|
|
222
|
+
rows: Record<string, unknown>[];
|
|
223
|
+
totalRows: number;
|
|
224
|
+
sampleRows: number;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export interface FeedTemplate {
|
|
228
|
+
id: string;
|
|
229
|
+
name: string;
|
|
230
|
+
icon: React.FC<{ className?: string }>;
|
|
231
|
+
description: string;
|
|
232
|
+
format: string;
|
|
233
|
+
requiredFields: string[];
|
|
234
|
+
optionalFields?: string[];
|
|
235
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { StepType, AdapterListItem } from '../types';
|
|
2
|
+
|
|
3
|
+
function groupAdaptersByCategory(adapters: AdapterListItem[] = []): Record<string, AdapterListItem[]> {
|
|
4
|
+
return (adapters ?? []).reduce((acc, adapter) => {
|
|
5
|
+
const category = adapter.category || 'other';
|
|
6
|
+
if (!acc[category]) {
|
|
7
|
+
acc[category] = [];
|
|
8
|
+
}
|
|
9
|
+
acc[category].push(adapter);
|
|
10
|
+
return acc;
|
|
11
|
+
}, {} as Record<string, AdapterListItem[]>);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface FilterOptions {
|
|
15
|
+
stepType?: StepType;
|
|
16
|
+
includeDescription?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function filterAdapters(
|
|
20
|
+
adapters: AdapterListItem[] = [],
|
|
21
|
+
searchQuery: string,
|
|
22
|
+
options: FilterOptions = {}
|
|
23
|
+
): AdapterListItem[] {
|
|
24
|
+
const { stepType, includeDescription = true } = options;
|
|
25
|
+
const query = searchQuery.toLowerCase();
|
|
26
|
+
return (adapters ?? []).filter(adapter => {
|
|
27
|
+
const matchesSearch = !query ||
|
|
28
|
+
(adapter.name?.toLowerCase().includes(query) ?? false) ||
|
|
29
|
+
adapter.code.toLowerCase().includes(query) ||
|
|
30
|
+
(includeDescription && adapter.description?.toLowerCase().includes(query));
|
|
31
|
+
const matchesType = !stepType || adapter.type.toUpperCase() === stepType;
|
|
32
|
+
return matchesSearch && matchesType;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function filterAndGroupAdaptersByCategory(
|
|
37
|
+
adapters: AdapterListItem[],
|
|
38
|
+
searchQuery: string,
|
|
39
|
+
options: FilterOptions = {}
|
|
40
|
+
): Record<string, AdapterListItem[]> {
|
|
41
|
+
const filtered = filterAdapters(adapters, searchQuery, options);
|
|
42
|
+
return groupAdaptersByCategory(filtered);
|
|
43
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { JsonValue } from '../../shared/types';
|
|
2
|
+
|
|
3
|
+
export type FileType = 'CSV' | 'JSON' | 'XLSX' | 'XML' | 'NDJSON' | null;
|
|
4
|
+
|
|
5
|
+
export interface ParsedColumn {
|
|
6
|
+
name: string;
|
|
7
|
+
type: 'string' | 'number' | 'boolean' | 'date' | 'array' | 'object' | 'unknown';
|
|
8
|
+
sampleValues: JsonValue[];
|
|
9
|
+
nullCount: number;
|
|
10
|
+
uniqueCount: number;
|
|
11
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { AdapterSchemaField, SelectOption } from '../../shared/types';
|
|
2
|
+
|
|
3
|
+
interface PrepareDynamicFieldsOptions {
|
|
4
|
+
baseFields: AdapterSchemaField[];
|
|
5
|
+
connectionCodes?: string[];
|
|
6
|
+
secretCodes?: string[];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function prepareDynamicFields(options: PrepareDynamicFieldsOptions): AdapterSchemaField[] {
|
|
10
|
+
const { baseFields, connectionCodes = [], secretCodes = [] } = options;
|
|
11
|
+
|
|
12
|
+
return baseFields.map((field) => {
|
|
13
|
+
if (field.key === 'connectionCode') {
|
|
14
|
+
const options: SelectOption[] = connectionCodes.map((code) => ({
|
|
15
|
+
value: code,
|
|
16
|
+
label: code,
|
|
17
|
+
}));
|
|
18
|
+
return { ...field, type: 'SELECT' as const, options };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (field.key.endsWith('SecretCode')) {
|
|
22
|
+
const options: SelectOption[] = secretCodes.map((code) => ({
|
|
23
|
+
value: code,
|
|
24
|
+
label: code,
|
|
25
|
+
}));
|
|
26
|
+
return { ...field, type: 'SECRET' as const, options };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return field;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
import { isEmpty, isValidUrl } from '../../shared';
|
|
2
|
+
import { CODE_PATTERN } from '../../shared';
|
|
3
|
+
import { SOURCE_TYPE } from '../constants';
|
|
4
|
+
import type { DestinationSchema, TypedOptionValue } from '../hooks/api/use-config-options';
|
|
5
|
+
|
|
6
|
+
export { CODE_PATTERN };
|
|
7
|
+
|
|
8
|
+
interface FieldValidationError {
|
|
9
|
+
field: string;
|
|
10
|
+
message: string;
|
|
11
|
+
type: 'required' | 'format' | 'range' | 'custom';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface FormValidationResult {
|
|
15
|
+
isValid: boolean;
|
|
16
|
+
errors: FieldValidationError[];
|
|
17
|
+
errorsByField: Record<string, string>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const ERROR_MESSAGES = {
|
|
21
|
+
required: (field: string) => `${field} is required`,
|
|
22
|
+
invalidUrl: 'Please enter a valid URL (e.g., https://example.com)',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function createValidationResult(errors: FieldValidationError[]): FormValidationResult {
|
|
26
|
+
const errorsByField: Record<string, string> = {};
|
|
27
|
+
for (const error of errors) {
|
|
28
|
+
errorsByField[error.field] = error.message;
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
isValid: errors.length === 0,
|
|
32
|
+
errors,
|
|
33
|
+
errorsByField,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function validateRequired(value: unknown, fieldName: string): FieldValidationError | null {
|
|
38
|
+
if (isEmpty(value)) {
|
|
39
|
+
return {
|
|
40
|
+
field: fieldName,
|
|
41
|
+
message: ERROR_MESSAGES.required(fieldName),
|
|
42
|
+
type: 'required',
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function validateUrl(value: string, fieldName: string = 'URL'): FieldValidationError | null {
|
|
49
|
+
if (isEmpty(value)) return null;
|
|
50
|
+
|
|
51
|
+
if (!isValidUrl(value)) {
|
|
52
|
+
return {
|
|
53
|
+
field: fieldName,
|
|
54
|
+
message: ERROR_MESSAGES.invalidUrl,
|
|
55
|
+
type: 'format',
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const HOSTNAME_PATTERN = /^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$/;
|
|
62
|
+
|
|
63
|
+
export function validateHostname(value: string, fieldName: string = 'Hostname'): FieldValidationError | null {
|
|
64
|
+
if (isEmpty(value)) return null;
|
|
65
|
+
|
|
66
|
+
if (!HOSTNAME_PATTERN.test(value)) {
|
|
67
|
+
return {
|
|
68
|
+
field: fieldName,
|
|
69
|
+
message: 'Please enter a valid hostname',
|
|
70
|
+
type: 'format',
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function validatePort(value: string | number, fieldName: string = 'Port'): FieldValidationError | null {
|
|
77
|
+
if (isEmpty(value)) return null;
|
|
78
|
+
|
|
79
|
+
const portNum = typeof value === 'string' ? parseInt(value, 10) : value;
|
|
80
|
+
if (isNaN(portNum) || portNum < 1 || portNum > 65535) {
|
|
81
|
+
return {
|
|
82
|
+
field: fieldName,
|
|
83
|
+
message: 'Please enter a valid port number (1-65535)',
|
|
84
|
+
type: 'format',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function validateTriggerConfig(
|
|
91
|
+
trigger?: { type?: string; [key: string]: unknown },
|
|
92
|
+
triggerSchemas?: TypedOptionValue[],
|
|
93
|
+
): FieldValidationError[] {
|
|
94
|
+
if (!trigger?.type) return [];
|
|
95
|
+
const errors: FieldValidationError[] = [];
|
|
96
|
+
|
|
97
|
+
// Schema-driven validation: validate required fields from schema
|
|
98
|
+
if (triggerSchemas?.length) {
|
|
99
|
+
const schema = triggerSchemas.find(s => s.value === trigger.type);
|
|
100
|
+
if (schema) {
|
|
101
|
+
for (const field of schema.fields) {
|
|
102
|
+
if (field.required) {
|
|
103
|
+
const value = trigger[field.key];
|
|
104
|
+
if (value === undefined || value === null || value === '') {
|
|
105
|
+
errors.push({
|
|
106
|
+
field: field.key,
|
|
107
|
+
message: `${field.label} is required`,
|
|
108
|
+
type: 'required',
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return errors;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function validateReviewStep(name?: string): FieldValidationError[] {
|
|
120
|
+
const err = validateRequired(name, 'Name');
|
|
121
|
+
return err ? [err] : [];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Resolve the backend adapter code for a wizard source type.
|
|
126
|
+
* Searches provided adapter schemas first (case-insensitive match),
|
|
127
|
+
* then falls back to lowercase convention.
|
|
128
|
+
*/
|
|
129
|
+
function getAdapterCodeForType(
|
|
130
|
+
sourceType: string,
|
|
131
|
+
adapterSchemas?: Array<{ code: string }>,
|
|
132
|
+
): string {
|
|
133
|
+
const match = adapterSchemas?.find(a => a.code.toUpperCase() === sourceType.toUpperCase());
|
|
134
|
+
if (match) return match.code;
|
|
135
|
+
return sourceType.toLowerCase();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function validateImportWizardStep(
|
|
139
|
+
step: string,
|
|
140
|
+
config: {
|
|
141
|
+
name?: string;
|
|
142
|
+
source?: {
|
|
143
|
+
type?: string;
|
|
144
|
+
[key: string]: unknown;
|
|
145
|
+
};
|
|
146
|
+
trigger?: { type?: string; [key: string]: unknown };
|
|
147
|
+
targetEntity?: string;
|
|
148
|
+
mappings?: Array<{ sourceField?: string; targetField?: string; required?: boolean }>;
|
|
149
|
+
strategies?: { lookupFields?: string[] };
|
|
150
|
+
},
|
|
151
|
+
uploadedFile?: File | null,
|
|
152
|
+
adapterSchemas?: Array<{ code: string; schema?: { fields: Array<{ key: string; required?: boolean }> } }>,
|
|
153
|
+
triggerSchemas?: TypedOptionValue[],
|
|
154
|
+
): FormValidationResult {
|
|
155
|
+
const errors: FieldValidationError[] = [];
|
|
156
|
+
|
|
157
|
+
switch (step) {
|
|
158
|
+
case 'source':
|
|
159
|
+
if (config.source?.type === SOURCE_TYPE.FILE && !uploadedFile) {
|
|
160
|
+
errors.push({
|
|
161
|
+
field: 'file',
|
|
162
|
+
message: 'Please upload a file',
|
|
163
|
+
type: 'required',
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
// Generic validation for all schema-driven sources (API, DATABASE, WEBHOOK, CDC, and dynamic types)
|
|
167
|
+
if (config.source?.type
|
|
168
|
+
&& config.source.type !== SOURCE_TYPE.FILE) {
|
|
169
|
+
const configKey = `${config.source.type.toLowerCase()}Config`;
|
|
170
|
+
const sourceConfig = (config.source as Record<string, unknown>)[configKey];
|
|
171
|
+
if (!sourceConfig || typeof sourceConfig !== 'object') {
|
|
172
|
+
errors.push({
|
|
173
|
+
field: configKey,
|
|
174
|
+
message: 'Source configuration is required',
|
|
175
|
+
type: 'required',
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
// When adapter schemas are provided, validate required fields from the schema
|
|
179
|
+
if (adapterSchemas) {
|
|
180
|
+
const adapterCode = getAdapterCodeForType(config.source.type, adapterSchemas);
|
|
181
|
+
const adapter = adapterSchemas.find(a => a.code === adapterCode);
|
|
182
|
+
if (adapter?.schema?.fields) {
|
|
183
|
+
const cfgObj = ((sourceConfig ?? {}) as Record<string, unknown>);
|
|
184
|
+
for (const field of adapter.schema.fields) {
|
|
185
|
+
if (field.required && isEmpty(cfgObj[field.key])) {
|
|
186
|
+
errors.push({
|
|
187
|
+
field: field.key,
|
|
188
|
+
message: `${field.key} is required`,
|
|
189
|
+
type: 'required',
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
// Validate URL format for URL-typed fields
|
|
193
|
+
if (/url/i.test(field.key) && !isEmpty(cfgObj[field.key])) {
|
|
194
|
+
const urlError = validateUrl(String(cfgObj[field.key]), field.key);
|
|
195
|
+
if (urlError) errors.push(urlError);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
} else if (!adapter) {
|
|
199
|
+
// Adapter schemas loaded but this specific adapter was not found
|
|
200
|
+
errors.push({
|
|
201
|
+
field: 'adapterCode',
|
|
202
|
+
message: `Unknown source adapter: ${adapterCode}`,
|
|
203
|
+
type: 'custom',
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
break;
|
|
209
|
+
|
|
210
|
+
case 'target': {
|
|
211
|
+
const targetError = validateRequired(config.targetEntity, 'Target Entity');
|
|
212
|
+
if (targetError) errors.push(targetError);
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
case 'mapping': {
|
|
217
|
+
const mappedFields = config.mappings?.filter(m => m.sourceField && m.targetField) ?? [];
|
|
218
|
+
if (mappedFields.length === 0) {
|
|
219
|
+
errors.push({
|
|
220
|
+
field: 'mappings',
|
|
221
|
+
message: 'At least one field mapping is required',
|
|
222
|
+
type: 'required',
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
const requiredUnmapped = config.mappings?.filter(m => m.required && !m.sourceField) ?? [];
|
|
226
|
+
if (requiredUnmapped.length > 0) {
|
|
227
|
+
errors.push({
|
|
228
|
+
field: 'mappings',
|
|
229
|
+
message: `Required fields must be mapped: ${requiredUnmapped.map(m => m.targetField).join(', ')}`,
|
|
230
|
+
type: 'required',
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
case 'strategy':
|
|
237
|
+
if ((config.strategies?.lookupFields?.length ?? 0) === 0) {
|
|
238
|
+
errors.push({
|
|
239
|
+
field: 'lookupFields',
|
|
240
|
+
message: 'At least one lookup field is required to identify existing records',
|
|
241
|
+
type: 'required',
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
break;
|
|
245
|
+
|
|
246
|
+
case 'trigger':
|
|
247
|
+
errors.push(...validateTriggerConfig(config.trigger, triggerSchemas));
|
|
248
|
+
break;
|
|
249
|
+
|
|
250
|
+
case 'review':
|
|
251
|
+
errors.push(...validateReviewStep(config.name));
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return createValidationResult(errors);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Validate destination fields using backend destination schemas.
|
|
260
|
+
* Loops over schema-defined required fields instead of hardcoding per-type validation.
|
|
261
|
+
* For URL-typed fields (key contains 'url', case-insensitive), also validates format.
|
|
262
|
+
*/
|
|
263
|
+
function validateDestinationFromSchema(
|
|
264
|
+
destination: { type?: string; [key: string]: unknown },
|
|
265
|
+
schemas: DestinationSchema[],
|
|
266
|
+
): FieldValidationError[] {
|
|
267
|
+
if (!destination.type) return [];
|
|
268
|
+
const schema = schemas.find(s => s.type === destination.type);
|
|
269
|
+
if (!schema || schema.fields.length === 0) return [];
|
|
270
|
+
|
|
271
|
+
const configObj = (destination[schema.configKey] ?? {}) as Record<string, unknown>;
|
|
272
|
+
const errors: FieldValidationError[] = [];
|
|
273
|
+
|
|
274
|
+
for (const field of schema.fields) {
|
|
275
|
+
const value = configObj[field.key];
|
|
276
|
+
if (field.required && isEmpty(value)) {
|
|
277
|
+
errors.push({
|
|
278
|
+
field: field.key,
|
|
279
|
+
message: `${field.label} is required`,
|
|
280
|
+
type: 'required',
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
// Validate URL format for URL-typed fields
|
|
284
|
+
if (/url/i.test(field.key) && !isEmpty(value)) {
|
|
285
|
+
const urlError = validateUrl(String(value), field.label);
|
|
286
|
+
if (urlError) errors.push(urlError);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return errors;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export function validateExportWizardStep(
|
|
294
|
+
step: string,
|
|
295
|
+
config: {
|
|
296
|
+
name?: string;
|
|
297
|
+
sourceEntity?: string;
|
|
298
|
+
fields?: Array<{ include?: boolean }>;
|
|
299
|
+
format?: { type?: string };
|
|
300
|
+
trigger?: { type?: string; [key: string]: unknown };
|
|
301
|
+
destination?: { type?: string; [key: string]: unknown };
|
|
302
|
+
},
|
|
303
|
+
destinationSchemas?: DestinationSchema[],
|
|
304
|
+
triggerSchemas?: TypedOptionValue[],
|
|
305
|
+
): FormValidationResult {
|
|
306
|
+
const errors: FieldValidationError[] = [];
|
|
307
|
+
|
|
308
|
+
switch (step) {
|
|
309
|
+
case 'source': {
|
|
310
|
+
const entityError = validateRequired(config.sourceEntity, 'Source Entity');
|
|
311
|
+
if (entityError) errors.push(entityError);
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
case 'fields': {
|
|
316
|
+
const includedFields = config.fields?.filter(f => f.include) ?? [];
|
|
317
|
+
if (includedFields.length === 0) {
|
|
318
|
+
errors.push({
|
|
319
|
+
field: 'fields',
|
|
320
|
+
message: 'At least one field must be selected for export',
|
|
321
|
+
type: 'required',
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
// Validate each included field has a non-empty outputName
|
|
325
|
+
for (let i = 0; i < includedFields.length; i++) {
|
|
326
|
+
const field = includedFields[i] as { outputName?: string };
|
|
327
|
+
if (!field.outputName || field.outputName.trim() === '') {
|
|
328
|
+
errors.push({
|
|
329
|
+
field: `fields[${i}].outputName`,
|
|
330
|
+
message: 'Output name is required for all included fields',
|
|
331
|
+
type: 'required',
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
// Check for duplicate outputName values
|
|
336
|
+
const outputNames = includedFields
|
|
337
|
+
.map((f: { outputName?: string }) => f.outputName?.trim())
|
|
338
|
+
.filter((name): name is string => Boolean(name));
|
|
339
|
+
const duplicates = outputNames.filter((name, index) => outputNames.indexOf(name) !== index);
|
|
340
|
+
if (duplicates.length > 0) {
|
|
341
|
+
errors.push({
|
|
342
|
+
field: 'fields',
|
|
343
|
+
message: `Duplicate output names found: ${[...new Set(duplicates)].join(', ')}`,
|
|
344
|
+
type: 'custom',
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
case 'format': {
|
|
351
|
+
const formatError = validateRequired(config.format?.type, 'Export Format');
|
|
352
|
+
if (formatError) errors.push(formatError);
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
case 'destination':
|
|
357
|
+
if (config.destination && destinationSchemas) {
|
|
358
|
+
errors.push(...validateDestinationFromSchema(config.destination, destinationSchemas));
|
|
359
|
+
}
|
|
360
|
+
break;
|
|
361
|
+
|
|
362
|
+
case 'trigger':
|
|
363
|
+
errors.push(...validateTriggerConfig(config.trigger, triggerSchemas));
|
|
364
|
+
break;
|
|
365
|
+
|
|
366
|
+
case 'review':
|
|
367
|
+
errors.push(...validateReviewStep(config.name));
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return createValidationResult(errors);
|
|
372
|
+
}
|
|
373
|
+
|