@finos/legend-extension-dsl-data-quality 2.1.34 → 2.1.36

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.
Files changed (109) hide show
  1. package/lib/components/DSL_DataQuality_LegendStudioApplicationPlugin.d.ts.map +1 -1
  2. package/lib/components/DSL_DataQuality_LegendStudioApplicationPlugin.js +45 -13
  3. package/lib/components/DSL_DataQuality_LegendStudioApplicationPlugin.js.map +1 -1
  4. package/lib/components/DataQualityCustomSelector.d.ts +16 -1
  5. package/lib/components/DataQualityCustomSelector.d.ts.map +1 -1
  6. package/lib/components/DataQualityCustomSelector.js +29 -4
  7. package/lib/components/DataQualityCustomSelector.js.map +1 -1
  8. package/lib/components/DataQualityRelationComparisonEditor.d.ts +19 -0
  9. package/lib/components/DataQualityRelationComparisonEditor.d.ts.map +1 -0
  10. package/lib/components/DataQualityRelationComparisonEditor.js +239 -0
  11. package/lib/components/DataQualityRelationComparisonEditor.js.map +1 -0
  12. package/lib/components/DataQualityRelationGridResult.d.ts +3 -0
  13. package/lib/components/DataQualityRelationGridResult.d.ts.map +1 -1
  14. package/lib/components/DataQualityRelationGridResult.js +1 -1
  15. package/lib/components/DataQualityRelationGridResult.js.map +1 -1
  16. package/lib/components/DataQualityRelationLambdaGUIDataTypeHandlers.d.ts +0 -10
  17. package/lib/components/DataQualityRelationLambdaGUIDataTypeHandlers.d.ts.map +1 -1
  18. package/lib/components/DataQualityRelationLambdaGUIDataTypeHandlers.js +11 -24
  19. package/lib/components/DataQualityRelationLambdaGUIDataTypeHandlers.js.map +1 -1
  20. package/lib/components/DataQualityRelationLambdaGUIValidationEditor.d.ts.map +1 -1
  21. package/lib/components/DataQualityRelationLambdaGUIValidationEditor.js +6 -4
  22. package/lib/components/DataQualityRelationLambdaGUIValidationEditor.js.map +1 -1
  23. package/lib/components/states/DataQualityRelationComparisonConfigurationState.d.ts +111 -0
  24. package/lib/components/states/DataQualityRelationComparisonConfigurationState.d.ts.map +1 -0
  25. package/lib/components/states/DataQualityRelationComparisonConfigurationState.js +495 -0
  26. package/lib/components/states/DataQualityRelationComparisonConfigurationState.js.map +1 -0
  27. package/lib/data-quality-custom-selector.css +2 -2
  28. package/lib/data-quality-custom-selector.css.map +1 -1
  29. package/lib/graph/metamodel/DSL_DataQuality_HashUtils.d.ts +3 -1
  30. package/lib/graph/metamodel/DSL_DataQuality_HashUtils.d.ts.map +1 -1
  31. package/lib/graph/metamodel/DSL_DataQuality_HashUtils.js +2 -0
  32. package/lib/graph/metamodel/DSL_DataQuality_HashUtils.js.map +1 -1
  33. package/lib/graph/metamodel/DSL_DataQuality_PureGraphPlugin.d.ts.map +1 -1
  34. package/lib/graph/metamodel/DSL_DataQuality_PureGraphPlugin.js +2 -1
  35. package/lib/graph/metamodel/DSL_DataQuality_PureGraphPlugin.js.map +1 -1
  36. package/lib/graph/metamodel/pure/packageableElements/data-quality/DataQualityValidationConfiguration.d.ts +35 -0
  37. package/lib/graph/metamodel/pure/packageableElements/data-quality/DataQualityValidationConfiguration.d.ts.map +1 -1
  38. package/lib/graph/metamodel/pure/packageableElements/data-quality/DataQualityValidationConfiguration.js +37 -0
  39. package/lib/graph/metamodel/pure/packageableElements/data-quality/DataQualityValidationConfiguration.js.map +1 -1
  40. package/lib/graph-manager/DSL_DataQuality_GraphManagerHelper.d.ts +2 -1
  41. package/lib/graph-manager/DSL_DataQuality_GraphManagerHelper.d.ts.map +1 -1
  42. package/lib/graph-manager/DSL_DataQuality_GraphManagerHelper.js +2 -1
  43. package/lib/graph-manager/DSL_DataQuality_GraphManagerHelper.js.map +1 -1
  44. package/lib/graph-manager/DSL_DataQuality_GraphModifierHelper.d.ts +7 -1
  45. package/lib/graph-manager/DSL_DataQuality_GraphModifierHelper.d.ts.map +1 -1
  46. package/lib/graph-manager/DSL_DataQuality_GraphModifierHelper.js +18 -0
  47. package/lib/graph-manager/DSL_DataQuality_GraphModifierHelper.js.map +1 -1
  48. package/lib/graph-manager/DSL_DataQuality_PureGraphManagerPlugin.d.ts.map +1 -1
  49. package/lib/graph-manager/DSL_DataQuality_PureGraphManagerPlugin.js +5 -2
  50. package/lib/graph-manager/DSL_DataQuality_PureGraphManagerPlugin.js.map +1 -1
  51. package/lib/graph-manager/action/changeDetection/DSL_DataQuality_ObserverHelper.d.ts +3 -1
  52. package/lib/graph-manager/action/changeDetection/DSL_DataQuality_ObserverHelper.d.ts.map +1 -1
  53. package/lib/graph-manager/action/changeDetection/DSL_DataQuality_ObserverHelper.js +28 -1
  54. package/lib/graph-manager/action/changeDetection/DSL_DataQuality_ObserverHelper.js.map +1 -1
  55. package/lib/graph-manager/protocol/pure/DSL_DataQuality_PureGraphManagerExtension.d.ts +4 -1
  56. package/lib/graph-manager/protocol/pure/DSL_DataQuality_PureGraphManagerExtension.d.ts.map +1 -1
  57. package/lib/graph-manager/protocol/pure/DSL_DataQuality_PureGraphManagerExtension.js.map +1 -1
  58. package/lib/graph-manager/protocol/pure/DSL_DataQuality_PureProtocolProcessorPlugin.d.ts.map +1 -1
  59. package/lib/graph-manager/protocol/pure/DSL_DataQuality_PureProtocolProcessorPlugin.js +33 -5
  60. package/lib/graph-manager/protocol/pure/DSL_DataQuality_PureProtocolProcessorPlugin.js.map +1 -1
  61. package/lib/graph-manager/protocol/pure/v1/V1_DSL_Data_Quality_PureGraphManagerExtension.d.ts +26 -2
  62. package/lib/graph-manager/protocol/pure/v1/V1_DSL_Data_Quality_PureGraphManagerExtension.d.ts.map +1 -1
  63. package/lib/graph-manager/protocol/pure/v1/V1_DSL_Data_Quality_PureGraphManagerExtension.js +107 -3
  64. package/lib/graph-manager/protocol/pure/v1/V1_DSL_Data_Quality_PureGraphManagerExtension.js.map +1 -1
  65. package/lib/graph-manager/protocol/pure/v1/V1_DataQualityValidationConfiguration.d.ts +19 -0
  66. package/lib/graph-manager/protocol/pure/v1/V1_DataQualityValidationConfiguration.d.ts.map +1 -1
  67. package/lib/graph-manager/protocol/pure/v1/V1_DataQualityValidationConfiguration.js +37 -0
  68. package/lib/graph-manager/protocol/pure/v1/V1_DataQualityValidationConfiguration.js.map +1 -1
  69. package/lib/graph-manager/protocol/pure/v1/transformation/V1_DSL_DataQuality_ValueSpecificationBuilderHelper.d.ts +2 -1
  70. package/lib/graph-manager/protocol/pure/v1/transformation/V1_DSL_DataQuality_ValueSpecificationBuilderHelper.d.ts.map +1 -1
  71. package/lib/graph-manager/protocol/pure/v1/transformation/V1_DSL_DataQuality_ValueSpecificationBuilderHelper.js +26 -3
  72. package/lib/graph-manager/protocol/pure/v1/transformation/V1_DSL_DataQuality_ValueSpecificationBuilderHelper.js.map +1 -1
  73. package/lib/graph-manager/protocol/pure/v1/transformation/V1_DSL_DataQuality_ValueSpecificationTransformer.d.ts +3 -2
  74. package/lib/graph-manager/protocol/pure/v1/transformation/V1_DSL_DataQuality_ValueSpecificationTransformer.d.ts.map +1 -1
  75. package/lib/graph-manager/protocol/pure/v1/transformation/V1_DSL_DataQuality_ValueSpecificationTransformer.js +29 -2
  76. package/lib/graph-manager/protocol/pure/v1/transformation/V1_DSL_DataQuality_ValueSpecificationTransformer.js.map +1 -1
  77. package/lib/graph-manager/protocol/pure/v1/transformation/pureProtocol/V1_DSL_DataQuality_ProtocolHelper.d.ts +4 -1
  78. package/lib/graph-manager/protocol/pure/v1/transformation/pureProtocol/V1_DSL_DataQuality_ProtocolHelper.d.ts.map +1 -1
  79. package/lib/graph-manager/protocol/pure/v1/transformation/pureProtocol/V1_DSL_DataQuality_ProtocolHelper.js +37 -2
  80. package/lib/graph-manager/protocol/pure/v1/transformation/pureProtocol/V1_DSL_DataQuality_ProtocolHelper.js.map +1 -1
  81. package/lib/index.css +2 -2
  82. package/lib/index.css.map +1 -1
  83. package/lib/package.json +1 -1
  84. package/package.json +11 -11
  85. package/src/components/DSL_DataQuality_LegendStudioApplicationPlugin.tsx +64 -12
  86. package/src/components/DataQualityCustomSelector.tsx +111 -6
  87. package/src/components/DataQualityRelationComparisonEditor.tsx +795 -0
  88. package/src/components/DataQualityRelationGridResult.tsx +1 -1
  89. package/src/components/DataQualityRelationLambdaGUIDataTypeHandlers.tsx +9 -87
  90. package/src/components/DataQualityRelationLambdaGUIValidationEditor.tsx +16 -6
  91. package/src/components/states/DataQualityRelationComparisonConfigurationState.ts +747 -0
  92. package/src/graph/metamodel/DSL_DataQuality_HashUtils.ts +2 -0
  93. package/src/graph/metamodel/DSL_DataQuality_PureGraphPlugin.ts +2 -0
  94. package/src/graph/metamodel/pure/packageableElements/data-quality/DataQualityValidationConfiguration.ts +66 -0
  95. package/src/graph-manager/DSL_DataQuality_GraphManagerHelper.ts +13 -0
  96. package/src/graph-manager/DSL_DataQuality_GraphModifierHelper.ts +57 -0
  97. package/src/graph-manager/DSL_DataQuality_PureGraphManagerPlugin.ts +8 -0
  98. package/src/graph-manager/action/changeDetection/DSL_DataQuality_ObserverHelper.ts +42 -0
  99. package/src/graph-manager/protocol/pure/DSL_DataQuality_PureGraphManagerExtension.ts +16 -0
  100. package/src/graph-manager/protocol/pure/DSL_DataQuality_PureProtocolProcessorPlugin.ts +65 -0
  101. package/src/graph-manager/protocol/pure/v1/V1_DSL_Data_Quality_PureGraphManagerExtension.ts +171 -1
  102. package/src/graph-manager/protocol/pure/v1/V1_DataQualityValidationConfiguration.ts +49 -0
  103. package/src/graph-manager/protocol/pure/v1/transformation/V1_DSL_DataQuality_ValueSpecificationBuilderHelper.ts +39 -0
  104. package/src/graph-manager/protocol/pure/v1/transformation/V1_DSL_DataQuality_ValueSpecificationTransformer.ts +50 -0
  105. package/src/graph-manager/protocol/pure/v1/transformation/pureProtocol/V1_DSL_DataQuality_ProtocolHelper.ts +71 -0
  106. package/style/_data-quality-relation-comparison-editor.scss +361 -0
  107. package/style/data-quality-custom-selector.scss +23 -0
  108. package/style/index.scss +74 -0
  109. package/tsconfig.json +2 -0
@@ -0,0 +1,747 @@
1
+ /**
2
+ * Copyright (c) 2026-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import {
18
+ action,
19
+ computed,
20
+ flow,
21
+ flowResult,
22
+ makeObservable,
23
+ observable,
24
+ } from 'mobx';
25
+ import {
26
+ type EditorStore,
27
+ ElementEditorState,
28
+ } from '@finos/legend-application-studio';
29
+ import {
30
+ type PackageableElement,
31
+ type ExecutionResult,
32
+ type RawLambda,
33
+ RawVariableExpression,
34
+ buildSourceInformationSourceId,
35
+ buildLambdaVariableExpressions,
36
+ observe_ValueSpecification,
37
+ VariableExpression,
38
+ GRAPH_MANAGER_EVENT,
39
+ ParserError,
40
+ RawLambda as RawLambdaCtor,
41
+ RelationTypeMetadata,
42
+ observe_RelationTypeMetadata,
43
+ } from '@finos/legend-graph';
44
+ import {
45
+ type GeneratorFn,
46
+ ActionState,
47
+ assertErrorThrown,
48
+ guaranteeType,
49
+ hashArray,
50
+ LogEvent,
51
+ StopWatch,
52
+ filterByType,
53
+ } from '@finos/legend-shared';
54
+ import {
55
+ buildExecutionParameterValues,
56
+ doesLambdaParameterStateContainFunctionValues,
57
+ ParameterInstanceValuesEditorState,
58
+ LambdaEditorState,
59
+ LambdaParametersState,
60
+ LambdaParameterState,
61
+ PARAMETER_SUBMIT_ACTION,
62
+ } from '@finos/legend-query-builder';
63
+ import {
64
+ type DataQualityRelationComparisonConfiguration,
65
+ type DataQualityRelationQueryLambda,
66
+ type ReconStrategy,
67
+ MD5HashStrategy,
68
+ } from '../../graph-manager/index.js';
69
+ import { DATA_QUALITY_HASH_STRUCTURE } from '../../graph/metamodel/DSL_DataQuality_HashUtils.js';
70
+ import { getDataQualityPureGraphManagerExtension } from '../../graph-manager/protocol/pure/DSL_DataQuality_PureGraphManagerExtension.js';
71
+
72
+ export type ComparisonSide = 'source' | 'target';
73
+
74
+ export enum RECONCILIATION_EXECUTION_TYPE {
75
+ RECONCILIATION = 'RECONCILIATION',
76
+ SOURCE_QUERY = 'SOURCE_QUERY',
77
+ TARGET_QUERY = 'TARGET_QUERY',
78
+ }
79
+
80
+ export const DEFAULT_LIMIT = 1000;
81
+
82
+ export class ComparisonLambdaEditorState extends LambdaEditorState {
83
+ readonly editorStore: EditorStore;
84
+ readonly queryLambda: DataQualityRelationQueryLambda;
85
+ readonly label: ComparisonSide;
86
+ readonly configurationState!: DataQualityRelationComparisonConfigurationState;
87
+
88
+ isConvertingFunctionBodyToString = false;
89
+
90
+ constructor(
91
+ configurationState: DataQualityRelationComparisonConfigurationState,
92
+ queryLambda: DataQualityRelationQueryLambda,
93
+ editorStore: EditorStore,
94
+ label: ComparisonSide,
95
+ ) {
96
+ super('', '');
97
+
98
+ makeObservable(this, {
99
+ isConvertingFunctionBodyToString: observable,
100
+ });
101
+
102
+ this.queryLambda = queryLambda;
103
+ this.editorStore = editorStore;
104
+ this.label = label;
105
+ this.configurationState = configurationState;
106
+ }
107
+
108
+ get lambdaId(): string {
109
+ return buildSourceInformationSourceId([`comparison_${this.label}`]);
110
+ }
111
+
112
+ *convertLambdaGrammarStringToObject(): GeneratorFn<void> {
113
+ if (this.lambdaString) {
114
+ try {
115
+ const lambda =
116
+ (yield this.editorStore.graphManagerState.graphManager.pureCodeToLambda(
117
+ this.fullLambdaString,
118
+ this.lambdaId,
119
+ )) as RawLambda;
120
+ this.setParserError(undefined);
121
+ const lambdaParameters =
122
+ (lambda.parameters as object[] | undefined) ?? [];
123
+ this.queryLambda.parameters = lambdaParameters
124
+ .map((param) =>
125
+ this.editorStore.graphManagerState.graphManager.buildRawValueSpecification(
126
+ param,
127
+ this.editorStore.graphManagerState.graph,
128
+ ),
129
+ )
130
+ .map((rawValueSpec) =>
131
+ guaranteeType(rawValueSpec, RawVariableExpression),
132
+ );
133
+ this.queryLambda.body = lambda.body;
134
+ // Refresh relation columns after a successful query update
135
+ yield flowResult(
136
+ this.configurationState.fetchColumnsForLambda(
137
+ this.queryLambda,
138
+ this.label,
139
+ ),
140
+ );
141
+ } catch (error) {
142
+ assertErrorThrown(error);
143
+ if (error instanceof ParserError) {
144
+ this.setParserError(error);
145
+ }
146
+ this.editorStore.applicationStore.logService.error(
147
+ LogEvent.create(GRAPH_MANAGER_EVENT.PARSING_FAILURE),
148
+ error,
149
+ );
150
+ }
151
+ } else {
152
+ this.clearErrors();
153
+ this.queryLambda.body = new RawLambdaCtor(undefined, undefined).body;
154
+ this.queryLambda.parameters = [];
155
+ }
156
+ }
157
+
158
+ *convertLambdaObjectToGrammarString(options?: {
159
+ pretty?: boolean | undefined;
160
+ preserveCompilationError?: boolean | undefined;
161
+ firstLoad?: boolean | undefined;
162
+ }): GeneratorFn<void> {
163
+ this.isConvertingFunctionBodyToString = true;
164
+ try {
165
+ const lambdas = new Map<string, RawLambda>();
166
+ const functionLambda = this.configurationState.buildRawLambda(
167
+ this.queryLambda,
168
+ );
169
+ lambdas.set(this.lambdaId, functionLambda);
170
+ const isolatedLambdas =
171
+ (yield this.editorStore.graphManagerState.graphManager.lambdasToPureCode(
172
+ lambdas,
173
+ options?.pretty,
174
+ )) as Map<string, string>;
175
+ const grammarText = isolatedLambdas.get(this.lambdaId);
176
+ this.setLambdaString(grammarText ?? '');
177
+ if (!options?.firstLoad) {
178
+ this.clearErrors({
179
+ preserveCompilationError: options?.preserveCompilationError,
180
+ });
181
+ }
182
+ this.isConvertingFunctionBodyToString = false;
183
+ } catch (error) {
184
+ assertErrorThrown(error);
185
+ this.editorStore.applicationStore.logService.error(
186
+ LogEvent.create(GRAPH_MANAGER_EVENT.PARSING_FAILURE),
187
+ error,
188
+ );
189
+ this.isConvertingFunctionBodyToString = false;
190
+ }
191
+ }
192
+
193
+ get hashCode(): string {
194
+ return hashArray([
195
+ DATA_QUALITY_HASH_STRUCTURE.DATA_QUALITY_RELATION_FUNCTION_DEFINITION,
196
+ this.queryLambda.body ? JSON.stringify(this.queryLambda.body) : '',
197
+ ]);
198
+ }
199
+ }
200
+
201
+ export class ComparisonParametersState extends LambdaParametersState {
202
+ readonly configurationState: DataQualityRelationComparisonConfigurationState;
203
+
204
+ constructor(
205
+ configurationState: DataQualityRelationComparisonConfigurationState,
206
+ ) {
207
+ super();
208
+ makeObservable(this, {
209
+ parameterValuesEditorState: observable,
210
+ parameterStates: observable,
211
+ addParameter: action,
212
+ removeParameter: action,
213
+ openModal: action,
214
+ build: action,
215
+ setParameters: action,
216
+ });
217
+ this.configurationState = configurationState;
218
+ }
219
+
220
+ openModal(lambda: RawLambda, onSubmit: () => Promise<void>): void {
221
+ this.parameterStates = this.build(lambda);
222
+ this.parameterValuesEditorState.open(
223
+ (): Promise<void> =>
224
+ onSubmit().catch(
225
+ this.configurationState.editorStore.applicationStore
226
+ .alertUnhandledError,
227
+ ),
228
+ PARAMETER_SUBMIT_ACTION.RUN,
229
+ );
230
+ }
231
+
232
+ build(lambda: RawLambda): LambdaParameterState[] {
233
+ const parameters = buildLambdaVariableExpressions(
234
+ lambda,
235
+ this.configurationState.editorStore.graphManagerState,
236
+ )
237
+ .map((parameter) =>
238
+ observe_ValueSpecification(
239
+ parameter,
240
+ this.configurationState.editorStore.changeDetectionState
241
+ .observerContext,
242
+ ),
243
+ )
244
+ .filter(filterByType(VariableExpression));
245
+ const existingStatesByName = new Map(
246
+ this.parameterStates.map((parameterState) => [
247
+ parameterState.variableName,
248
+ parameterState,
249
+ ]),
250
+ );
251
+ return parameters.map((variable) => {
252
+ const parameterState = new LambdaParameterState(
253
+ variable,
254
+ this.configurationState.editorStore.changeDetectionState.observerContext,
255
+ this.configurationState.editorStore.graphManagerState.graph,
256
+ );
257
+ const existingState = existingStatesByName.get(
258
+ parameterState.variableName,
259
+ );
260
+ if (existingState?.value) {
261
+ parameterState.setValue(existingState.value);
262
+ } else {
263
+ parameterState.mockParameterValue();
264
+ }
265
+ return parameterState;
266
+ });
267
+ }
268
+ }
269
+
270
+ export class DataQualityRelationComparisonConfigurationState extends ElementEditorState {
271
+ declare element: DataQualityRelationComparisonConfiguration;
272
+
273
+ sourceLambdaEditorState: ComparisonLambdaEditorState;
274
+ targetLambdaEditorState: ComparisonLambdaEditorState;
275
+
276
+ sourceColumnMetadata: RelationTypeMetadata = new RelationTypeMetadata();
277
+ targetColumnMetadata: RelationTypeMetadata = new RelationTypeMetadata();
278
+
279
+ lastSourceQueryHash: string | undefined = undefined;
280
+ lastTargetQueryHash: string | undefined = undefined;
281
+
282
+ // Column-fetch state
283
+ readonly fetchColumnsState = ActionState.create();
284
+ sourceColumnFetchError: string | undefined = undefined;
285
+ targetColumnFetchError: string | undefined = undefined;
286
+
287
+ // Execution state
288
+ currentExecutionType: RECONCILIATION_EXECUTION_TYPE | undefined = undefined;
289
+ lastExecutionType: RECONCILIATION_EXECUTION_TYPE | undefined = undefined;
290
+ executionResult?: ExecutionResult | undefined;
291
+ executionDuration?: number | undefined;
292
+ runPromise: Promise<ExecutionResult> | undefined = undefined;
293
+ limit = DEFAULT_LIMIT;
294
+ sourceParametersState: ComparisonParametersState;
295
+ targetParametersState: ComparisonParametersState;
296
+ comparisonParametersEditorState = new ParameterInstanceValuesEditorState();
297
+
298
+ constructor(editorStore: EditorStore, element: PackageableElement) {
299
+ super(editorStore, element);
300
+
301
+ this.element = element as DataQualityRelationComparisonConfiguration;
302
+
303
+ this.sourceLambdaEditorState = new ComparisonLambdaEditorState(
304
+ this,
305
+ this.element.source,
306
+ editorStore,
307
+ 'source',
308
+ );
309
+
310
+ this.targetLambdaEditorState = new ComparisonLambdaEditorState(
311
+ this,
312
+ this.element.target,
313
+ editorStore,
314
+ 'target',
315
+ );
316
+
317
+ this.sourceParametersState = new ComparisonParametersState(this);
318
+ this.targetParametersState = new ComparisonParametersState(this);
319
+
320
+ makeObservable(this, {
321
+ setKeys: action,
322
+ setColumnsToCompare: action,
323
+ setStrategy: action,
324
+ setSourceHashColumn: action,
325
+ setTargetHashColumn: action,
326
+ setAggregatedHash: action,
327
+ sourceColumnMetadata: observable,
328
+ targetColumnMetadata: observable,
329
+ lastSourceQueryHash: observable,
330
+ lastTargetQueryHash: observable,
331
+ sourceLambdaEditorState: observable,
332
+ targetLambdaEditorState: observable,
333
+ fetchColumnsForLambda: flow,
334
+ retryFetchColumns: flow,
335
+ sourceColumnFetchError: observable,
336
+ targetColumnFetchError: observable,
337
+ hasColumnFetchError: computed,
338
+ columnFetchError: computed,
339
+ hasNoOverlappingColumns: computed,
340
+ sourceColumnOptions: computed,
341
+ targetColumnOptions: computed,
342
+ combinedColumnOptions: computed,
343
+ // Execution observables
344
+ currentExecutionType: observable,
345
+ lastExecutionType: observable,
346
+ executionResult: observable,
347
+ executionDuration: observable,
348
+ runPromise: observable,
349
+ limit: observable,
350
+ isRunning: computed,
351
+ setExecutionResult: action,
352
+ setRunPromise: action,
353
+ setExecutionDuration: action,
354
+ setLimit: action,
355
+ handleRun: flow,
356
+ run: flow,
357
+ cancelRun: flow,
358
+ openComparisonParametersModal: action,
359
+ });
360
+ }
361
+
362
+ setKeys(keys: string[]): void {
363
+ this.element.keys = keys;
364
+ }
365
+
366
+ setColumnsToCompare(columns: string[]): void {
367
+ this.element.columnsToCompare = columns;
368
+ }
369
+
370
+ setStrategy(strategy: ReconStrategy): void {
371
+ this.element.strategy = strategy;
372
+ }
373
+
374
+ setSourceHashColumn(value: string | undefined): void {
375
+ guaranteeType(this.element.strategy, MD5HashStrategy).sourceHashColumn =
376
+ value;
377
+ }
378
+
379
+ setTargetHashColumn(value: string | undefined): void {
380
+ guaranteeType(this.element.strategy, MD5HashStrategy).targetHashColumn =
381
+ value;
382
+ }
383
+
384
+ setAggregatedHash(value: boolean | undefined): void {
385
+ guaranteeType(this.element.strategy, MD5HashStrategy).aggregatedHash =
386
+ value;
387
+ }
388
+
389
+ get sourceColumnOptions(): { value: string; label: string }[] {
390
+ return this.sourceColumnMetadata.columns.map((col) => ({
391
+ value: col.name,
392
+ label: col.name,
393
+ }));
394
+ }
395
+
396
+ get targetColumnOptions(): { value: string; label: string }[] {
397
+ return this.targetColumnMetadata.columns.map((col) => ({
398
+ value: col.name,
399
+ label: col.name,
400
+ }));
401
+ }
402
+
403
+ get combinedColumnOptions(): { value: string; label: string }[] {
404
+ return this.sourceColumnOptions.filter((srcOpt) =>
405
+ this.targetColumnOptions.some((tgtOpt) => tgtOpt.value === srcOpt.value),
406
+ );
407
+ }
408
+
409
+ get hasColumnFetchError(): boolean {
410
+ return (
411
+ this.sourceColumnFetchError !== undefined ||
412
+ this.targetColumnFetchError !== undefined
413
+ );
414
+ }
415
+
416
+ get columnFetchError(): string | undefined {
417
+ const errors = [
418
+ this.sourceColumnFetchError,
419
+ this.targetColumnFetchError,
420
+ ].filter(Boolean);
421
+ return errors.length > 0 ? errors.join('; ') : undefined;
422
+ }
423
+
424
+ get hasNoOverlappingColumns(): boolean {
425
+ return (
426
+ !this.fetchColumnsState.isInProgress &&
427
+ !this.hasColumnFetchError &&
428
+ this.sourceColumnOptions.length > 0 &&
429
+ this.targetColumnOptions.length > 0 &&
430
+ this.combinedColumnOptions.length === 0
431
+ );
432
+ }
433
+
434
+ get isRunning(): boolean {
435
+ return this.currentExecutionType !== undefined;
436
+ }
437
+
438
+ setExecutionResult(
439
+ executionResult: ExecutionResult | undefined,
440
+ type: RECONCILIATION_EXECUTION_TYPE,
441
+ ): void {
442
+ this.lastExecutionType = type;
443
+ this.executionResult = executionResult;
444
+ }
445
+
446
+ setRunPromise(promise: Promise<ExecutionResult> | undefined): void {
447
+ this.runPromise = promise;
448
+ }
449
+
450
+ setExecutionDuration(val: number | undefined): void {
451
+ this.executionDuration = val;
452
+ }
453
+
454
+ setLimit(val: number): void {
455
+ this.limit = Math.max(1, val);
456
+ }
457
+
458
+ private assertNoLetInjectionParameters(
459
+ type: RECONCILIATION_EXECUTION_TYPE,
460
+ ): void {
461
+ const unsupportedSourceParameters =
462
+ type !== RECONCILIATION_EXECUTION_TYPE.TARGET_QUERY
463
+ ? this.sourceParametersState.parameterStates.filter(
464
+ doesLambdaParameterStateContainFunctionValues,
465
+ )
466
+ : [];
467
+ const unsupportedTargetParameters =
468
+ type !== RECONCILIATION_EXECUTION_TYPE.SOURCE_QUERY
469
+ ? this.targetParametersState.parameterStates.filter(
470
+ doesLambdaParameterStateContainFunctionValues,
471
+ )
472
+ : [];
473
+
474
+ if (
475
+ unsupportedSourceParameters.length === 0 &&
476
+ unsupportedTargetParameters.length === 0
477
+ ) {
478
+ return;
479
+ }
480
+
481
+ const errors: string[] = [];
482
+ if (unsupportedSourceParameters.length > 0) {
483
+ errors.push(
484
+ `Source query parameters require function-value let injection (${unsupportedSourceParameters
485
+ .map((parameterState) => parameterState.variableName)
486
+ .join(', ')}), which reconciliation execution does not support.`,
487
+ );
488
+ }
489
+ if (unsupportedTargetParameters.length > 0) {
490
+ errors.push(
491
+ `Target query parameters require function-value let injection (${unsupportedTargetParameters
492
+ .map((parameterState) => parameterState.variableName)
493
+ .join(', ')}), which reconciliation execution does not support.`,
494
+ );
495
+ }
496
+
497
+ throw new Error(errors.join(' '));
498
+ }
499
+
500
+ buildRawLambda(queryLambda: DataQualityRelationQueryLambda): RawLambdaCtor {
501
+ const serializedParams = queryLambda.parameters.map((parameter) =>
502
+ this.editorStore.graphManagerState.graphManager.serializeRawValueSpecification(
503
+ parameter,
504
+ ),
505
+ );
506
+ return new RawLambdaCtor(serializedParams, queryLambda.body);
507
+ }
508
+
509
+ private buildSourceLambda(): RawLambdaCtor {
510
+ return this.buildRawLambda(this.element.source);
511
+ }
512
+
513
+ private buildTargetLambda(): RawLambdaCtor {
514
+ return this.buildRawLambda(this.element.target);
515
+ }
516
+
517
+ private get sourceHasParameters(): boolean {
518
+ const params = (this.buildSourceLambda().parameters ?? []) as object[];
519
+ return params.length > 0;
520
+ }
521
+
522
+ private get targetHasParameters(): boolean {
523
+ const params = (this.buildTargetLambda().parameters ?? []) as object[];
524
+ return params.length > 0;
525
+ }
526
+
527
+ openComparisonParametersModal(onSubmit: () => Promise<void>): void {
528
+ this.sourceParametersState.setParameters(
529
+ this.sourceParametersState.build(this.buildSourceLambda()),
530
+ );
531
+ this.targetParametersState.setParameters(
532
+ this.targetParametersState.build(this.buildTargetLambda()),
533
+ );
534
+ this.comparisonParametersEditorState.open(
535
+ (): Promise<void> =>
536
+ onSubmit().catch(this.editorStore.applicationStore.alertUnhandledError),
537
+ PARAMETER_SUBMIT_ACTION.RUN,
538
+ );
539
+ }
540
+
541
+ *handleRun(type: RECONCILIATION_EXECUTION_TYPE): GeneratorFn<void> {
542
+ if (this.isRunning) {
543
+ return;
544
+ }
545
+ const needsSourceParams =
546
+ this.sourceHasParameters &&
547
+ (type === RECONCILIATION_EXECUTION_TYPE.RECONCILIATION ||
548
+ type === RECONCILIATION_EXECUTION_TYPE.SOURCE_QUERY);
549
+ const needsTargetParams =
550
+ this.targetHasParameters &&
551
+ (type === RECONCILIATION_EXECUTION_TYPE.RECONCILIATION ||
552
+ type === RECONCILIATION_EXECUTION_TYPE.TARGET_QUERY);
553
+
554
+ if (needsSourceParams && needsTargetParams) {
555
+ this.openComparisonParametersModal(
556
+ (): Promise<void> => flowResult(this.run(type)),
557
+ );
558
+ } else if (needsSourceParams) {
559
+ this.sourceParametersState.openModal(
560
+ this.buildSourceLambda(),
561
+ (): Promise<void> => flowResult(this.run(type)),
562
+ );
563
+ } else if (needsTargetParams) {
564
+ this.targetParametersState.openModal(
565
+ this.buildTargetLambda(),
566
+ (): Promise<void> => flowResult(this.run(type)),
567
+ );
568
+ } else {
569
+ yield flowResult(this.run(type));
570
+ }
571
+ }
572
+
573
+ *run(type: RECONCILIATION_EXECUTION_TYPE): GeneratorFn<void> {
574
+ let promise: Promise<ExecutionResult> | undefined = undefined;
575
+ const stopWatch = new StopWatch();
576
+ try {
577
+ this.currentExecutionType = type;
578
+ const model = this.editorStore.graphManagerState.graph;
579
+ const extension = getDataQualityPureGraphManagerExtension(
580
+ this.editorStore.graphManagerState.graphManager,
581
+ );
582
+ const md5Strategy = guaranteeType(this.element.strategy, MD5HashStrategy);
583
+ this.assertNoLetInjectionParameters(type);
584
+ const sourceExecutionLambda = this.buildSourceLambda();
585
+ const targetExecutionLambda = this.buildTargetLambda();
586
+
587
+ const sourceParamValues = this.sourceHasParameters
588
+ ? buildExecutionParameterValues(
589
+ this.sourceParametersState.parameterStates,
590
+ this.editorStore.graphManagerState,
591
+ )
592
+ : [];
593
+ const targetParamValues = this.targetHasParameters
594
+ ? buildExecutionParameterValues(
595
+ this.targetParametersState.parameterStates,
596
+ this.editorStore.graphManagerState,
597
+ )
598
+ : [];
599
+
600
+ if (type === RECONCILIATION_EXECUTION_TYPE.RECONCILIATION) {
601
+ promise = extension.runReconciliation(model, {
602
+ source: sourceExecutionLambda,
603
+ target: targetExecutionLambda,
604
+ keys: this.element.keys,
605
+ colsForHash: this.element.columnsToCompare,
606
+ limit: this.limit,
607
+ aggregatedHash: md5Strategy.aggregatedHash,
608
+ sourceHashCol: md5Strategy.sourceHashColumn,
609
+ targetHashCol: md5Strategy.targetHashColumn,
610
+ // make sure we fetch all columns we compare so users can see the differences
611
+ includeColumnValues: true,
612
+ sourceLambdaParameterValues: sourceParamValues,
613
+ targetLambdaParameterValues: targetParamValues,
614
+ });
615
+ } else if (type === RECONCILIATION_EXECUTION_TYPE.SOURCE_QUERY) {
616
+ promise = extension.runReconciliationSourceQuery(model, {
617
+ source: sourceExecutionLambda,
618
+ target: targetExecutionLambda,
619
+ keys: this.element.keys,
620
+ colsForHash: this.element.columnsToCompare,
621
+ sourceLambdaParameterValues: sourceParamValues,
622
+ });
623
+ } else {
624
+ promise = extension.runReconciliationTargetQuery(model, {
625
+ source: sourceExecutionLambda,
626
+ target: targetExecutionLambda,
627
+ keys: this.element.keys,
628
+ colsForHash: this.element.columnsToCompare,
629
+ targetLambdaParameterValues: targetParamValues,
630
+ });
631
+ }
632
+
633
+ this.setRunPromise(promise);
634
+ const result = (yield promise) as ExecutionResult;
635
+
636
+ if (this.runPromise === promise) {
637
+ this.setExecutionResult(result, type);
638
+ this.setExecutionDuration(stopWatch.elapsed);
639
+ }
640
+ } catch (error) {
641
+ if (this.runPromise === promise) {
642
+ assertErrorThrown(error);
643
+ this.setExecutionResult(undefined, type);
644
+ this.editorStore.applicationStore.logService.error(
645
+ LogEvent.create(GRAPH_MANAGER_EVENT.EXECUTION_FAILURE),
646
+ error,
647
+ );
648
+ this.editorStore.applicationStore.notificationService.notifyError(
649
+ error,
650
+ );
651
+ }
652
+ } finally {
653
+ this.currentExecutionType = undefined;
654
+ }
655
+ }
656
+
657
+ *cancelRun(): GeneratorFn<void> {
658
+ this.currentExecutionType = undefined;
659
+ this.setRunPromise(undefined);
660
+ try {
661
+ yield this.editorStore.graphManagerState.graphManager.cancelUserExecutions(
662
+ true,
663
+ );
664
+ } catch (error) {
665
+ this.editorStore.applicationStore.logService.error(
666
+ LogEvent.create(GRAPH_MANAGER_EVENT.EXECUTION_FAILURE),
667
+ error,
668
+ );
669
+ }
670
+ }
671
+
672
+ *fetchColumnsForLambda(
673
+ queryLambda: DataQualityRelationQueryLambda,
674
+ side: ComparisonSide,
675
+ ): GeneratorFn<void> {
676
+ const { body } = queryLambda;
677
+ if (!body || (Array.isArray(body) && body.length === 0)) {
678
+ return;
679
+ }
680
+
681
+ const lambda = this.buildRawLambda(queryLambda);
682
+
683
+ const editorState =
684
+ side === 'source'
685
+ ? this.sourceLambdaEditorState
686
+ : this.targetLambdaEditorState;
687
+ const currentQueryHash = editorState.hashCode;
688
+ const lastHash =
689
+ side === 'source' ? this.lastSourceQueryHash : this.lastTargetQueryHash;
690
+
691
+ if (currentQueryHash === lastHash) {
692
+ return;
693
+ }
694
+
695
+ this.fetchColumnsState.inProgress();
696
+ try {
697
+ const metadata = observe_RelationTypeMetadata(
698
+ (yield this.editorStore.graphManagerState.graphManager.getLambdaRelationType(
699
+ lambda,
700
+ this.editorStore.graphManagerState.graph,
701
+ )) as RelationTypeMetadata,
702
+ );
703
+ if (side === 'source') {
704
+ this.sourceColumnMetadata = metadata;
705
+ this.lastSourceQueryHash = currentQueryHash;
706
+ this.sourceColumnFetchError = undefined;
707
+ } else {
708
+ this.targetColumnMetadata = metadata;
709
+ this.lastTargetQueryHash = currentQueryHash;
710
+ this.targetColumnFetchError = undefined;
711
+ }
712
+ } catch (error) {
713
+ assertErrorThrown(error);
714
+ // Update the hash even on failure so that reverting to a previously
715
+ // successful query will see a different hash and trigger a refetch.
716
+ if (side === 'source') {
717
+ this.lastSourceQueryHash = currentQueryHash;
718
+ this.sourceColumnFetchError = `Failed to fetch source relation columns: ${error.message}`;
719
+ } else {
720
+ this.lastTargetQueryHash = currentQueryHash;
721
+ this.targetColumnFetchError = `Failed to fetch target relation columns: ${error.message}`;
722
+ }
723
+ } finally {
724
+ this.fetchColumnsState.complete();
725
+ }
726
+ }
727
+
728
+ *retryFetchColumns(): GeneratorFn<void> {
729
+ // Reset hashes to force a refetch
730
+ this.lastSourceQueryHash = undefined;
731
+ this.lastTargetQueryHash = undefined;
732
+ this.sourceColumnFetchError = undefined;
733
+ this.targetColumnFetchError = undefined;
734
+ yield flowResult(this.fetchColumnsForLambda(this.element.source, 'source'));
735
+ yield flowResult(this.fetchColumnsForLambda(this.element.target, 'target'));
736
+ }
737
+
738
+ override reprocess(
739
+ newElement: PackageableElement,
740
+ editorStore: EditorStore,
741
+ ): ElementEditorState {
742
+ return new DataQualityRelationComparisonConfigurationState(
743
+ editorStore,
744
+ newElement,
745
+ );
746
+ }
747
+ }