UNPKG

@finos/legend-application-studio

Version:
1,016 lines (944 loc) 30.6 kB
/** * Copyright (c) 2020-present, Goldman Sachs * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { type MappingEditorState, getMappingElementSource, getMappingElementTarget, generateMappingTestName, } from './MappingEditorState.js'; import type { EditorStore } from '../../../EditorStore.js'; import { observable, action, flow, computed, makeObservable, flowResult, } from 'mobx'; import { type GeneratorFn, assertErrorThrown, LogEvent, guaranteeNonNullable, assertTrue, IllegalStateError, UnsupportedOperationError, uuid, tryToMinifyJSONString, toGrammarString, isValidJSONString, createUrlStringFromData, stringifyLosslessJSON, guaranteeType, ContentType, generateEnumerableNameFromToken, tryToFormatLosslessJSONString, StopWatch, } from '@finos/legend-shared'; import { createMockDataForMappingElementSource } from '../../../utils/MockDataUtils.js'; import { type DEPRECATED__InputData, type Mapping, type Connection, type SetImplementation, type Table, type View, type RawLambda, type RawExecutionPlan, type EmbeddedData, type TestAssertion, type ExecutionResultWithMetadata, DEFAULT_TEST_ASSERTION_PREFIX, DEFAULT_TEST_PREFIX, EqualToJson, ServiceTest, LAMBDA_PIPE, GRAPH_MANAGER_EVENT, Class, DEPRECATED__ObjectInputData, ObjectInputType, DEPRECATED__ExpectedOutputMappingTestAssert, IdentifiedConnection, EngineRuntime, JsonModelConnection, FlatDataConnection, FlatDataInputData, Service, PureSingleExecution, RootFlatDataRecordType, PackageableElementExplicitReference, DatabaseType, RelationalDatabaseConnection, LocalH2DatasourceSpecification, DefaultH2AuthenticationStrategy, RelationalInputData, RelationalInputType, OperationSetImplementation, buildSourceInformationSourceId, TableAlias, stub_RawLambda, isStubbed_RawLambda, generateIdentifiedConnectionId, ServiceTestSuite, TestData, ConnectionTestData, DEFAULT_TEST_SUITE_PREFIX, DEPRECATED__MappingTest, ModelStore, reportGraphAnalytics, } from '@finos/legend-graph'; import { ActionAlertActionType, ActionAlertType, DEFAULT_TAB_SIZE, } from '@finos/legend-application'; import { objectInputData_setData, runtime_addIdentifiedConnection, runtime_addMapping, } from '../../../../graph-modifier/DSL_Mapping_GraphModifierHelper.js'; import { flatData_setData } from '../../../../graph-modifier/STO_FlatData_GraphModifierHelper.js'; import { service_addTestSuite, service_initNewService, service_setExecution, } from '../../../../graph-modifier/DSL_Service_GraphModifierHelper.js'; import { localH2DatasourceSpecification_setTestDataSetupCsv, localH2DatasourceSpecification_setTestDataSetupSqls, relationalInputData_setData, } from '../../../../graph-modifier/STO_Relational_GraphModifierHelper.js'; import { createEmptyEqualToJsonAssertion, createBareExternalFormat, } from '../../../utils/TestableUtils.js'; import { SERIALIZATION_FORMAT } from '../service/testable/ServiceTestEditorState.js'; import { LambdaEditorState, QueryBuilderTelemetryHelper, QUERY_BUILDER_EVENT, ExecutionPlanState, } from '@finos/legend-query-builder'; import { MappingEditorTabState } from './MappingTabManagerState.js'; export class MappingExecutionQueryState extends LambdaEditorState { editorStore: EditorStore; isInitializingLambda = false; query: RawLambda; constructor(editorStore: EditorStore, query: RawLambda) { super('', LAMBDA_PIPE); makeObservable(this, { query: observable, isInitializingLambda: observable, setIsInitializingLambda: action, updateLamba: flow, }); this.editorStore = editorStore; this.query = query; } get lambdaId(): string { return buildSourceInformationSourceId([this.uuid]); } setIsInitializingLambda(val: boolean): void { this.isInitializingLambda = val; } *updateLamba(val: RawLambda): GeneratorFn<void> { this.query = val; yield flowResult(this.convertLambdaObjectToGrammarString({ pretty: true })); } *convertLambdaObjectToGrammarString(options?: { pretty?: boolean | undefined; }): GeneratorFn<void> { if (!isStubbed_RawLambda(this.query)) { try { const lambdas = new Map<string, RawLambda>(); lambdas.set(this.lambdaId, this.query); const isolatedLambdas = (yield this.editorStore.graphManagerState.graphManager.lambdasToPureCode( lambdas, options?.pretty, )) as Map<string, string>; const grammarText = isolatedLambdas.get(this.lambdaId); this.setLambdaString( grammarText !== undefined ? this.extractLambdaString(grammarText) : '', ); this.clearErrors(); } catch (error) { assertErrorThrown(error); this.editorStore.applicationStore.logService.error( LogEvent.create(GRAPH_MANAGER_EVENT.PARSING_FAILURE), error, ); } } else { this.clearErrors(); this.setLambdaString(''); } } // NOTE: since we don't allow edition in text mode, we don't need to implement this *convertLambdaGrammarStringToObject(): GeneratorFn<void> { throw new UnsupportedOperationError(); } } abstract class MappingExecutionInputDataState { readonly uuid = uuid(); editorStore: EditorStore; mapping: Mapping; inputData?: DEPRECATED__InputData | undefined; constructor( editorStore: EditorStore, mapping: Mapping, inputData: DEPRECATED__InputData | undefined, ) { this.editorStore = editorStore; this.mapping = mapping; this.inputData = inputData; } abstract get isValid(): boolean; abstract get runtime(): EngineRuntime; createEmbeddedData(): EmbeddedData | undefined { return undefined; } createAssertion(executionResult: string): TestAssertion | undefined { return undefined; } abstract buildInputDataForTest(): DEPRECATED__InputData; } export const createRuntimeForExecution = ( mapping: Mapping, connection: Connection, editorStore: EditorStore, ): EngineRuntime => { const runtime = new EngineRuntime(); runtime_addMapping( runtime, PackageableElementExplicitReference.create(mapping), ); runtime_addIdentifiedConnection( runtime, new IdentifiedConnection( generateIdentifiedConnectionId(runtime), connection, ), editorStore.changeDetectionState.observerContext, ); return runtime; }; export class MappingExecutionEmptyInputDataState extends MappingExecutionInputDataState { get isValid(): boolean { return false; } get runtime(): EngineRuntime { throw new IllegalStateError( 'Mapping execution runtime information is not specified', ); } buildInputDataForTest(): DEPRECATED__InputData { throw new IllegalStateError( 'Mapping execution runtime information is not specified', ); } } // TODO?: handle XML export class MappingExecutionObjectInputDataState extends MappingExecutionInputDataState { declare inputData: DEPRECATED__ObjectInputData; constructor(editorStore: EditorStore, mapping: Mapping, _class: Class) { super( editorStore, mapping, new DEPRECATED__ObjectInputData( PackageableElementExplicitReference.create( guaranteeNonNullable(_class), ), ObjectInputType.JSON, tryToMinifyJSONString('{}'), ), ); makeObservable(this, { isValid: computed, }); } get isValid(): boolean { return isValidJSONString(this.inputData.data); } get runtime(): EngineRuntime { assertTrue( this.isValid, 'Model-to-model mapping execution test data is not a valid JSON string', ); const engineConfig = this.editorStore.graphManagerState.graphManager.TEMPORARY__getEngineConfig(); return createRuntimeForExecution( this.mapping, new JsonModelConnection( PackageableElementExplicitReference.create(ModelStore.INSTANCE), PackageableElementExplicitReference.create( guaranteeNonNullable(this.inputData.sourceClass.value), ), createUrlStringFromData( tryToMinifyJSONString(this.inputData.data), ContentType.APPLICATION_JSON, engineConfig.useBase64ForAdhocConnectionDataUrls, ), ), this.editorStore, ); } override createEmbeddedData(): EmbeddedData | undefined { const embeddedData = createBareExternalFormat(); embeddedData.data = tryToFormatLosslessJSONString( tryToMinifyJSONString(this.inputData.data), ); return embeddedData; } override createAssertion(executionResult: string): TestAssertion | undefined { const jsonAssertion = new EqualToJson(); jsonAssertion.id = generateEnumerableNameFromToken( [], DEFAULT_TEST_ASSERTION_PREFIX, ); const expected = createBareExternalFormat(); expected.data = toGrammarString(executionResult); jsonAssertion.expected = expected; return jsonAssertion; } buildInputDataForTest(): DEPRECATED__InputData { return new DEPRECATED__ObjectInputData( PackageableElementExplicitReference.create( guaranteeNonNullable(this.inputData.sourceClass.value), ), this.inputData.inputType, tryToMinifyJSONString(this.inputData.data), ); } } export class MappingExecutionFlatDataInputDataState extends MappingExecutionInputDataState { declare inputData: FlatDataInputData; constructor( editorStore: EditorStore, mapping: Mapping, rootFlatDataRecordType: RootFlatDataRecordType, ) { super( editorStore, mapping, new FlatDataInputData( PackageableElementExplicitReference.create( guaranteeNonNullable(rootFlatDataRecordType._OWNER._OWNER), ), '', ), ); makeObservable(this, { isValid: computed, }); } get isValid(): boolean { return true; } get runtime(): EngineRuntime { const engineConfig = this.editorStore.graphManagerState.graphManager.TEMPORARY__getEngineConfig(); return createRuntimeForExecution( this.mapping, new FlatDataConnection( PackageableElementExplicitReference.create( guaranteeNonNullable(this.inputData.sourceFlatData.value), ), createUrlStringFromData( this.inputData.data, ContentType.TEXT_PLAIN, engineConfig.useBase64ForAdhocConnectionDataUrls, ), ), this.editorStore, ); } buildInputDataForTest(): DEPRECATED__InputData { return new FlatDataInputData( PackageableElementExplicitReference.create( guaranteeNonNullable(this.inputData.sourceFlatData.value), ), this.inputData.data, ); } } export class MappingExecutionRelationalInputDataState extends MappingExecutionInputDataState { declare inputData: RelationalInputData; constructor( editorStore: EditorStore, mapping: Mapping, tableOrView: Table | View, ) { super( editorStore, mapping, new RelationalInputData( PackageableElementExplicitReference.create( guaranteeNonNullable(tableOrView.schema._OWNER), ), '', RelationalInputType.SQL, ), ); makeObservable(this, { isValid: computed, }); } get isValid(): boolean { return true; } get runtime(): EngineRuntime { const datasourceSpecification = new LocalH2DatasourceSpecification(); switch (this.inputData.inputType) { case RelationalInputType.SQL: localH2DatasourceSpecification_setTestDataSetupSqls( datasourceSpecification, // NOTE: this is a gross simplification of handling the input for relational input data [this.inputData.data], ); break; case RelationalInputType.CSV: localH2DatasourceSpecification_setTestDataSetupCsv( datasourceSpecification, this.inputData.data, ); break; default: throw new UnsupportedOperationError(`Invalid input data type`); } return createRuntimeForExecution( this.mapping, new RelationalDatabaseConnection( PackageableElementExplicitReference.create( guaranteeNonNullable(this.inputData.database.value), ), DatabaseType.H2, datasourceSpecification, new DefaultH2AuthenticationStrategy(), ), this.editorStore, ); } buildInputDataForTest(): DEPRECATED__InputData { return new RelationalInputData( PackageableElementExplicitReference.create( guaranteeNonNullable(this.inputData.database.value), ), this.inputData.data, this.inputData.inputType, ); } } export class MappingExecutionState extends MappingEditorTabState { readonly editorStore: EditorStore; readonly mappingEditorState: MappingEditorState; name: string; queryState: MappingExecutionQueryState; inputDataState: MappingExecutionInputDataState; showServicePathModal = false; executionResultText?: string | undefined; // NOTE: stored as lossless JSON text isExecuting = false; isGeneratingPlan = false; executionPlanState: ExecutionPlanState; planGenerationDebugText?: string | undefined; executionRunPromise: Promise<ExecutionResultWithMetadata> | undefined = undefined; constructor( editorStore: EditorStore, mappingEditorState: MappingEditorState, name: string, ) { super(); makeObservable(this, { name: observable, queryState: observable, inputDataState: observable, showServicePathModal: observable, executionPlanState: observable, isExecuting: observable, isGeneratingPlan: observable, planGenerationDebugText: observable, executionRunPromise: observable, setExecutionRunPromise: action, setQueryState: action, setInputDataState: action, setExecutionResultText: action, setShowServicePathModal: action, setPlanGenerationDebugText: action, setInputDataStateBasedOnSource: action, reset: action, promoteToTest: flow, promoteToService: flow, cancelExecution: flow, executeMapping: flow, generatePlan: flow, buildQueryWithClassMapping: flow, }); this.editorStore = editorStore; this.mappingEditorState = mappingEditorState; this.name = name; this.queryState = new MappingExecutionQueryState( editorStore, stub_RawLambda(), ); this.inputDataState = new MappingExecutionEmptyInputDataState( editorStore, mappingEditorState.mapping, undefined, ); this.executionPlanState = new ExecutionPlanState( this.editorStore.applicationStore, this.editorStore.graphManagerState, ); } get label(): string { return this.name; } setExecutionRunPromise( promise: Promise<ExecutionResultWithMetadata> | undefined, ): void { this.executionRunPromise = promise; } setQueryState(val: MappingExecutionQueryState): void { this.queryState = val; } setInputDataState(val: MappingExecutionInputDataState): void { this.inputDataState = val; } setExecutionResultText(val: string | undefined): void { this.executionResultText = val; } setShowServicePathModal(val: boolean): void { this.showServicePathModal = val; } setPlanGenerationDebugText(val: string | undefined): void { this.planGenerationDebugText = val; } reset(): void { this.queryState = new MappingExecutionQueryState( this.editorStore, stub_RawLambda(), ); this.inputDataState = new MappingExecutionEmptyInputDataState( this.editorStore, this.mappingEditorState.mapping, undefined, ); this.setExecutionResultText(undefined); } setInputDataStateBasedOnSource( source: unknown, populateWithMockData: boolean, ): void { if (source instanceof Class) { const newRuntimeState = new MappingExecutionObjectInputDataState( this.editorStore, this.mappingEditorState.mapping, source, ); if (populateWithMockData) { objectInputData_setData( newRuntimeState.inputData, createMockDataForMappingElementSource(source, this.editorStore), ); } this.setInputDataState(newRuntimeState); } else if (source instanceof RootFlatDataRecordType) { const newRuntimeState = new MappingExecutionFlatDataInputDataState( this.editorStore, this.mappingEditorState.mapping, source, ); if (populateWithMockData) { flatData_setData( newRuntimeState.inputData, createMockDataForMappingElementSource(source, this.editorStore), ); } this.setInputDataState(newRuntimeState); } else if (source instanceof TableAlias) { const newRuntimeState = new MappingExecutionRelationalInputDataState( this.editorStore, this.mappingEditorState.mapping, source.relation.value, ); if (populateWithMockData) { relationalInputData_setData( newRuntimeState.inputData, createMockDataForMappingElementSource(source, this.editorStore), ); } this.setInputDataState(newRuntimeState); } else if (source === undefined) { this.setInputDataState( new MappingExecutionEmptyInputDataState( this.editorStore, this.mappingEditorState.mapping, undefined, ), ); } else { this.editorStore.applicationStore.notificationService.notifyWarning( new UnsupportedOperationError( `Can't build input data for the specified source`, source, ), ); } } *promoteToTest(): GeneratorFn<void> { try { const query = this.queryState.query; if ( !isStubbed_RawLambda(this.queryState.query) && this.inputDataState.isValid && this.inputDataState.inputData && this.executionResultText ) { const inputData = this.inputDataState.buildInputDataForTest(); const assert = new DEPRECATED__ExpectedOutputMappingTestAssert( toGrammarString(this.executionResultText), ); const mappingTest = new DEPRECATED__MappingTest( generateMappingTestName(this.mappingEditorState.mapping), query, [inputData], assert, ); yield flowResult(this.mappingEditorState.addTest(mappingTest)); this.mappingEditorState.closeTab(this); // after promoting to test, remove the execution state } } catch (error) { assertErrorThrown(error); this.editorStore.applicationStore.logService.error( LogEvent.create(GRAPH_MANAGER_EVENT.EXECUTION_FAILURE), error, ); this.editorStore.applicationStore.notificationService.notifyError(error); } } *promoteToService( packagePath: string, serviceName: string, ): GeneratorFn<void> { try { const query = this.queryState.query; if ( !isStubbed_RawLambda(this.queryState.query) && this.inputDataState.isValid && this.executionResultText ) { if ( this.inputDataState instanceof MappingExecutionObjectInputDataState ) { const service = new Service(serviceName); const engineRuntime = this.inputDataState.runtime; service_initNewService(service); const pureSingleExecution = new PureSingleExecution( query, service, PackageableElementExplicitReference.create( this.mappingEditorState.mapping, ), engineRuntime, ); service_setExecution( service, pureSingleExecution, this.editorStore.changeDetectionState.observerContext, ); const suite = new ServiceTestSuite(); suite.id = generateEnumerableNameFromToken( [], DEFAULT_TEST_SUITE_PREFIX, ); suite.testData = new TestData(); const embeddedData = this.inputDataState.createEmbeddedData(); const connection = engineRuntime.connections[0]?.storeConnections[0]; if (embeddedData && connection) { const connectionTestData = new ConnectionTestData(); connectionTestData.connectionId = connection.id; connectionTestData.testData = embeddedData; suite.testData.connectionsTestData = [connectionTestData]; } const test = new ServiceTest(); test.serializationFormat = SERIALIZATION_FORMAT.PURE; test.id = generateEnumerableNameFromToken([], DEFAULT_TEST_PREFIX); test.__parent = suite; suite.tests = [test]; const assertion = this.inputDataState.createAssertion(this.executionResultText) ?? createEmptyEqualToJsonAssertion(test); test.assertions = [assertion]; assertion.parentTest = test; service_addTestSuite( service, suite, this.editorStore.changeDetectionState.observerContext, ); yield flowResult( this.editorStore.graphEditorMode.addElement( service, packagePath, true, ), ); } else { throw new UnsupportedOperationError( `Can't build service from input data state`, this.inputDataState, ); } } } catch (error) { assertErrorThrown(error); this.editorStore.applicationStore.logService.error( LogEvent.create(GRAPH_MANAGER_EVENT.EXECUTION_FAILURE), error, ); this.editorStore.applicationStore.notificationService.notifyError(error); } } *cancelExecution(): GeneratorFn<void> { this.isExecuting = false; this.setExecutionRunPromise(undefined); try { yield this.editorStore.graphManagerState.graphManager.cancelUserExecutions( true, ); } catch (error) { // don't notify users about success or failure this.editorStore.applicationStore.logService.error( LogEvent.create(GRAPH_MANAGER_EVENT.EXECUTION_FAILURE), error, ); } } *executeMapping(): GeneratorFn<void> { let promise; try { const query = this.queryState.query; const runtime = this.inputDataState.runtime; if ( !isStubbed_RawLambda(this.queryState.query) && this.inputDataState.isValid && !this.isExecuting ) { this.isExecuting = true; QueryBuilderTelemetryHelper.logEvent_QueryRunLaunched( this.editorStore.applicationStore.telemetryService, ); const stopWatch = new StopWatch(); const report = reportGraphAnalytics( this.editorStore.graphManagerState.graph, ); promise = this.editorStore.graphManagerState.graphManager.runQuery( query, this.mappingEditorState.mapping, runtime, this.editorStore.graphManagerState.graph, { useLosslessParse: true, }, report, ); this.setExecutionRunPromise(promise); const result = (yield promise) as ExecutionResultWithMetadata; if (this.executionRunPromise === promise) { this.setExecutionResultText( stringifyLosslessJSON( result.executionResult, undefined, DEFAULT_TAB_SIZE, ), ); // report report.timings = this.editorStore.applicationStore.timeService.finalizeTimingsRecord( stopWatch, report.timings, ); QueryBuilderTelemetryHelper.logEvent_QueryRunSucceeded( this.editorStore.applicationStore.telemetryService, report, ); } } } catch (error) { if (this.executionRunPromise === promise) { assertErrorThrown(error); this.editorStore.applicationStore.logService.error( LogEvent.create(GRAPH_MANAGER_EVENT.EXECUTION_FAILURE), error, ); this.editorStore.applicationStore.notificationService.notifyError( error, ); this.setExecutionResultText(''); } } finally { this.isExecuting = false; } } *generatePlan(debug: boolean): GeneratorFn<void> { try { const query = this.queryState.query; const runtime = this.inputDataState.runtime; if ( !isStubbed_RawLambda(this.queryState.query) && this.inputDataState.isValid && !this.isGeneratingPlan ) { this.isGeneratingPlan = true; let rawPlan: RawExecutionPlan; const stopWatch = new StopWatch(); const report = reportGraphAnalytics( this.editorStore.graphManagerState.graph, ); if (debug) { QueryBuilderTelemetryHelper.logEvent_ExecutionPlanDebugLaunched( this.editorStore.applicationStore.telemetryService, ); const debugResult = (yield this.editorStore.graphManagerState.graphManager.debugExecutionPlanGeneration( query, this.mappingEditorState.mapping, runtime, this.editorStore.graphManagerState.graph, report, )) as { plan: RawExecutionPlan; debug: string }; rawPlan = debugResult.plan; this.executionPlanState.setDebugText(debugResult.debug); } else { QueryBuilderTelemetryHelper.logEvent_ExecutionPlanGenerationLaunched( this.editorStore.applicationStore.telemetryService, ); rawPlan = (yield this.editorStore.graphManagerState.graphManager.generateExecutionPlan( query, this.mappingEditorState.mapping, runtime, this.editorStore.graphManagerState.graph, report, )) as object; } stopWatch.record(); try { this.executionPlanState.setRawPlan(rawPlan); const plan = this.editorStore.graphManagerState.graphManager.buildExecutionPlan( rawPlan, this.editorStore.graphManagerState.graph, ); this.executionPlanState.initialize(plan); } catch { // do nothing } stopWatch.record(QUERY_BUILDER_EVENT.BUILD_EXECUTION_PLAN__SUCCESS); // report report.timings = this.editorStore.applicationStore.timeService.finalizeTimingsRecord( stopWatch, report.timings, ); if (debug) { QueryBuilderTelemetryHelper.logEvent_ExecutionPlanDebugSucceeded( this.editorStore.applicationStore.telemetryService, report, ); } else { QueryBuilderTelemetryHelper.logEvent_ExecutionPlanGenerationSucceeded( this.editorStore.applicationStore.telemetryService, report, ); } } } catch (error) { assertErrorThrown(error); this.editorStore.applicationStore.logService.error( LogEvent.create(GRAPH_MANAGER_EVENT.EXECUTION_FAILURE), error, ); this.editorStore.applicationStore.notificationService.notifyError(error); } finally { this.isGeneratingPlan = false; } } *buildQueryWithClassMapping( setImplementation: SetImplementation | undefined, ): GeneratorFn<void> { // do all the necessary updates this.setExecutionResultText(undefined); yield flowResult( this.queryState.updateLamba( setImplementation ? this.editorStore.graphManagerState.graphManager.createGetAllRawLambda( guaranteeType(getMappingElementTarget(setImplementation), Class), ) : stub_RawLambda(), ), ); // Attempt to generate data for input data panel as we pick the class mapping: // - If the source panel is empty right now, automatically try to generate input data: // - We generate based on the class mapping, if it's concrete // - If the class mapping is operation, output a warning message // - If the source panel is non-empty (show modal), show an option to keep current input data if (setImplementation) { if (this.inputDataState instanceof MappingExecutionEmptyInputDataState) { if (setImplementation instanceof OperationSetImplementation) { this.editorStore.applicationStore.notificationService.notifyWarning( `Can't auto-generate input data for operation class mapping. Please pick a concrete class mapping instead`, ); } else { this.setInputDataStateBasedOnSource( getMappingElementSource( setImplementation, this.editorStore.pluginManager.getApplicationPlugins(), ), true, ); } } else { this.editorStore.applicationStore.alertService.setActionAlertInfo({ message: 'Mapping execution input data is already set', prompt: 'Do you want to regenerate the input data?', type: ActionAlertType.CAUTION, actions: [ { label: 'Regenerate', type: ActionAlertActionType.PROCEED_WITH_CAUTION, handler: (): void => this.setInputDataStateBasedOnSource( getMappingElementSource( setImplementation, this.editorStore.pluginManager.getApplicationPlugins(), ), true, ), }, { label: 'Keep my input data', type: ActionAlertActionType.PROCEED, default: true, }, ], }); } } } }