@finos/legend-studio
Version:
351 lines (329 loc) • 12.1 kB
text/typescript
/**
* 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 { observable, action, computed, makeObservable } from 'mobx';
import {
InstanceSetImplementationState,
PropertyMappingState,
} from '../MappingElementState.js';
import {
type GeneratorFn,
assertErrorThrown,
LogEvent,
IllegalStateError,
isNonNullable,
UnsupportedOperationError,
} from '@finos/legend-shared';
import type { EditorStore } from '../../../../EditorStore.js';
import { MappingElementDecorator } from '../MappingElementDecorator.js';
import { MAPPING_ELEMENT_TYPE } from '../MappingEditorState.js';
import {
type PropertyMapping,
type RelationalInstanceSetImplementation,
type RawRelationalOperationElement,
type CompilationError,
type SourceInformation,
RelationalPropertyMapping,
ParserError,
GRAPH_MANAGER_EVENT,
EmbeddedRelationalInstanceSetImplementation,
buildSourceInformationSourceId,
isStubbed_RawRelationalOperationElement,
stub_RawRelationalOperationElement,
} from '@finos/legend-graph';
export class RelationalPropertyMappingState extends PropertyMappingState {
editorStore: EditorStore;
declare instanceSetImplementationState: RelationalInstanceSetImplementationState;
declare propertyMapping:
| RelationalPropertyMapping
| EmbeddedRelationalInstanceSetImplementation;
constructor(
editorStore: EditorStore,
instanceSetImplementationState: RootRelationalInstanceSetImplementationState,
propertyMapping: RelationalPropertyMapping,
) {
super(instanceSetImplementationState, propertyMapping, '', '');
this.propertyMapping = propertyMapping;
this.editorStore = editorStore;
}
// NOTE: `operationId` is properly the more appropriate term to use, but we are just following what we
// do for other property mapping for consistency
get lambdaId(): string {
// NOTE: Added the index here just in case but the order needs to be checked carefully as bugs may result from inaccurate orderings
return buildSourceInformationSourceId(
[
this.propertyMapping._OWNER._PARENT.path,
MAPPING_ELEMENT_TYPE.CLASS,
this.propertyMapping._OWNER.id.value,
this.propertyMapping.property.value.name,
this.propertyMapping.targetSetImplementation?.value.id.value,
this.uuid, // in case of duplications
].filter(isNonNullable),
);
}
*convertLambdaGrammarStringToObject(): GeneratorFn<void> {
const stubOperation = stub_RawRelationalOperationElement();
if (this.lambdaString) {
try {
const operation =
(yield this.editorStore.graphManagerState.graphManager.pureCodeToRelationalOperationElement(
this.fullLambdaString,
this.lambdaId,
)) as RawRelationalOperationElement;
this.setParserError(undefined);
if (this.propertyMapping instanceof RelationalPropertyMapping) {
this.propertyMapping.relationalOperation = operation;
}
} catch (error) {
assertErrorThrown(error);
if (error instanceof ParserError) {
this.setParserError(error);
}
this.editorStore.applicationStore.log.error(
LogEvent.create(GRAPH_MANAGER_EVENT.PARSING_FAILURE),
error,
);
}
} else {
this.clearErrors();
if (this.propertyMapping instanceof RelationalPropertyMapping) {
this.propertyMapping.relationalOperation = stubOperation;
}
}
}
*convertLambdaObjectToGrammarString(pretty: boolean): GeneratorFn<void> {
if (this.propertyMapping instanceof RelationalPropertyMapping) {
if (!isStubbed_RawRelationalOperationElement(this.propertyMapping)) {
try {
const operations = new Map<string, RawRelationalOperationElement>();
operations.set(
this.lambdaId,
this.propertyMapping.relationalOperation,
);
const operationsInText =
(yield this.editorStore.graphManagerState.graphManager.relationalOperationElementToPureCode(
operations,
)) as Map<string, string>;
const grammarText = operationsInText.get(this.lambdaId);
this.setLambdaString(
grammarText !== undefined
? this.extractLambdaString(grammarText)
: '',
);
this.clearErrors();
} catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.log.error(
LogEvent.create(GRAPH_MANAGER_EVENT.PARSING_FAILURE),
error,
);
}
} else {
this.clearErrors();
this.setLambdaString('');
}
}
}
}
export abstract class RelationalInstanceSetImplementationState extends InstanceSetImplementationState {}
export class EmbeddedRelationalInstanceSetImplementationState
extends RelationalInstanceSetImplementationState
implements RelationalPropertyMappingState
{
declare instanceSetImplementationState: RelationalInstanceSetImplementationState;
declare mappingElement: EmbeddedRelationalInstanceSetImplementation;
declare propertyMapping: EmbeddedRelationalInstanceSetImplementation;
constructor(
editorStore: EditorStore,
instanceSetImplementationState: RelationalInstanceSetImplementationState,
setImplementation: EmbeddedRelationalInstanceSetImplementation,
) {
super(editorStore, setImplementation);
this.instanceSetImplementationState = instanceSetImplementationState;
this.mappingElement = setImplementation;
this.propertyMapping = setImplementation;
this.propertyMappingStates = this.getPropertyMappingStates(
setImplementation.propertyMappings,
);
}
get lambdaId(): string {
throw new IllegalStateError(
`Can't build lambda ID for embedded relational instance set implementation state`,
);
}
getPropertyMappingStates(
propertyMappings: PropertyMapping[],
): RelationalPropertyMappingState[] {
// TODO
return [];
}
// dummy lambda editor states needed because embedded flat-data should be seen as `PropertMappingState`
lambdaPrefix = '';
lambdaString = '';
parserError?: ParserError | undefined;
compilationError?: CompilationError | undefined;
decorate(): void {
return;
}
*convertPropertyMappingTransformObjects(): GeneratorFn<void> {
throw new UnsupportedOperationError();
}
setLambdaString(val: string): void {
return;
}
setParserError(error: ParserError | undefined): void {
return;
}
setCompilationError(error: CompilationError | undefined): void {
// TODO
return;
}
get fullLambdaString(): string {
throw new UnsupportedOperationError();
}
processSourceInformation(
sourceInformation: SourceInformation,
): SourceInformation {
throw new UnsupportedOperationError();
}
extractLambdaString(fullLambdaString: string): string {
throw new UnsupportedOperationError();
}
clearErrors(): void {
// TODO
return;
}
*convertLambdaGrammarStringToObject(): GeneratorFn<void> {
throw new UnsupportedOperationError();
}
*convertLambdaObjectToGrammarString(pretty: boolean): GeneratorFn<void> {
throw new UnsupportedOperationError();
}
}
export class RootRelationalInstanceSetImplementationState extends RelationalInstanceSetImplementationState {
declare mappingElement: RelationalInstanceSetImplementation;
declare propertyMappingStates: RelationalPropertyMappingState[];
isConvertingTransformLambdaObjects = false;
constructor(
editorStore: EditorStore,
setImplementation: RelationalInstanceSetImplementation,
) {
super(editorStore, setImplementation);
makeObservable(this, {
isConvertingTransformLambdaObjects: observable,
hasParserError: computed,
setPropertyMappingStates: action,
});
this.mappingElement = setImplementation;
this.propertyMappingStates = this.getPropertyMappingStates(
setImplementation.propertyMappings,
);
}
getPropertyMappingStates(
propertyMappings: PropertyMapping[],
): RelationalPropertyMappingState[] {
return propertyMappings
.map((pm) => {
if (pm instanceof RelationalPropertyMapping) {
return new RelationalPropertyMappingState(this.editorStore, this, pm);
} else if (pm instanceof EmbeddedRelationalInstanceSetImplementation) {
return new EmbeddedRelationalInstanceSetImplementationState(
this.editorStore,
this,
pm,
);
}
return undefined;
})
.filter(isNonNullable);
}
get hasParserError(): boolean {
return this.propertyMappingStates.some(
(propertyMappingState) => propertyMappingState.parserError,
);
}
setPropertyMappingStates(
propertyMappingState: RelationalPropertyMappingState[],
): void {
this.propertyMappingStates = propertyMappingState;
}
/**
* When we decorate, we might lose the error (parser/compiler) on each of the property mapping state
* so here we make sure that we reuse existing state and only add new decorated ones
*/
decorate(): void {
this.mappingElement.accept_SetImplementationVisitor(
new MappingElementDecorator(this.editorStore),
);
const newPropertyMappingStates: RelationalPropertyMappingState[] = [];
const propertyMappingstatesAfterDecoration = this.getPropertyMappingStates(
this.mappingElement.propertyMappings,
);
propertyMappingstatesAfterDecoration.forEach((propertyMappingState) => {
const existingPropertyMappingState = this.propertyMappingStates.find(
(p) => p.propertyMapping === propertyMappingState.propertyMapping,
);
newPropertyMappingStates.push(
existingPropertyMappingState ?? propertyMappingState,
);
});
this.setPropertyMappingStates(newPropertyMappingStates);
}
*convertPropertyMappingTransformObjects(): GeneratorFn<void> {
const operations = new Map<string, RawRelationalOperationElement>();
const propertyMappingStates = new Map<
string,
RelationalPropertyMappingState
>();
this.propertyMappingStates.forEach((pmState) => {
if (
pmState.propertyMapping instanceof RelationalPropertyMapping &&
!isStubbed_RawRelationalOperationElement(
pmState.propertyMapping.relationalOperation,
)
) {
operations.set(
pmState.lambdaId,
pmState.propertyMapping.relationalOperation,
);
propertyMappingStates.set(pmState.lambdaId, pmState);
}
// we don't have to do anything for embedded. they don't have a transform and do not require converting back and form.
});
if (operations.size) {
this.isConvertingTransformLambdaObjects = true;
try {
const operationsInText =
(yield this.editorStore.graphManagerState.graphManager.relationalOperationElementToPureCode(
operations,
)) as Map<string, string>;
operationsInText.forEach((grammarText, key) => {
const relationalPropertyMappingState = propertyMappingStates.get(key);
relationalPropertyMappingState?.setLambdaString(
relationalPropertyMappingState.extractLambdaString(grammarText),
);
});
} catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.log.error(
LogEvent.create(GRAPH_MANAGER_EVENT.PARSING_FAILURE),
error,
);
} finally {
this.isConvertingTransformLambdaObjects = false;
}
}
}
}