@finos/legend-application-studio
Version:
Legend Studio application core
267 lines (250 loc) • 8.65 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 {
type ExternalFormatDescription,
type GenerationProperty,
type SchemaSet,
GenerationOutput,
ConfigurationProperty,
ModelUnit,
observe_ModelUnit,
GenerationPropertyItemType,
PackageableElementExplicitReference,
} from '@finos/legend-graph';
import {
type GeneratorFn,
uuid,
assertErrorThrown,
LogEvent,
isEmpty,
deepEqual,
AssertionError,
} from '@finos/legend-shared';
import { action, flow, flowResult, makeObservable, observable } from 'mobx';
import type { EditorStore } from '../../EditorStore.js';
import { LEGEND_STUDIO_APP_EVENT } from '../../../../__lib__/LegendStudioEvent.js';
import { GENERATION_FILE_ROOT_NAME } from '../../utils/FileSystemTreeUtils.js';
import {
configurationProperty_addConfigurationProperty,
configurationProperty_setValue,
} from '../../../graph-modifier/DSL_Generation_GraphModifierHelper.js';
import { GeneratedFileStructureState } from '../FileGenerationState.js';
import { ElementEditorState } from './ElementEditorState.js';
export class ExternalFormatConfigurationSpecification {
configurationProperties: ConfigurationProperty[] = [];
modelUnit: ModelUnit;
constructor() {
makeObservable(this, {
configurationProperties: observable,
});
this.modelUnit = observe_ModelUnit(new ModelUnit());
}
}
const getNullableFileGenerationConfig = (
config: ExternalFormatConfigurationSpecification,
name: string,
): ConfigurationProperty | undefined =>
config.configurationProperties.find((property) => name === property.name);
export class XTGenerationState extends GeneratedFileStructureState {
readonly elementXTSchemaGenerationState: ElementXTSchemaGenerationState;
readonly configSpecification: ExternalFormatConfigurationSpecification;
constructor(
elementXTSchemaGenerationState: ElementXTSchemaGenerationState,
fileGeneration: ExternalFormatConfigurationSpecification,
) {
super(
GENERATION_FILE_ROOT_NAME,
elementXTSchemaGenerationState.editorStore,
);
makeObservable(this, {
configSpecification: observable,
resetGenerator: action,
updateFileGenerationParameters: action,
generate: flow,
});
this.elementXTSchemaGenerationState = elementXTSchemaGenerationState;
this.configSpecification = fileGeneration;
}
get rootFolder(): string {
return '';
}
get generationParentId(): undefined {
return undefined;
}
resetGenerator(): void {
this.configSpecification.configurationProperties = [];
}
*generate(): GeneratorFn<void> {
this.generatingAction.inProgress();
try {
const modelUnit = this.configSpecification.modelUnit;
const properties = [...this.configSpecification.configurationProperties];
this.addInferredConfigurationProperties(properties);
const result =
(yield this.editorStore.graphManagerState.graphManager.generateSchemaFromExternalFormatConfig(
modelUnit,
undefined,
properties,
this.editorStore.graphManagerState.graph,
)) as SchemaSet[];
const output = result
.map((schemaSet) =>
schemaSet.schemas.map((e) => {
const o = new GenerationOutput();
o.content = e.content;
o.fileName = e.location ?? '';
o.format = schemaSet.format;
return o;
}),
)
.flat();
this.processGenerationResult(output);
} catch (error) {
assertErrorThrown(error);
this.fileSystemState.selectedNode = undefined;
this.processGenerationResult([]);
this.editorStore.applicationStore.logService.error(
LogEvent.create(LEGEND_STUDIO_APP_EVENT.GENERATION_FAILURE),
error,
);
this.editorStore.applicationStore.notificationService.notifyError(error);
} finally {
this.generatingAction.complete();
}
}
addInferredConfigurationProperties(
properties: ConfigurationProperty[],
): void {
const SCHEMA_FORMAT_PROPERTY_NAME = 'format';
if (!properties.find((e) => e.name === SCHEMA_FORMAT_PROPERTY_NAME)) {
const genProperty = new ConfigurationProperty(
SCHEMA_FORMAT_PROPERTY_NAME,
this.elementXTSchemaGenerationState.description.name,
);
properties.push(genProperty);
}
this.elementXTSchemaGenerationState.description.schemaGenerationProperties
.filter(
(property) =>
property.required &&
property.defaultValue &&
!properties.find((pv) => pv.name === property.name),
)
.forEach((toAdd) => {
const value = new ConfigurationProperty(toAdd.name, toAdd.defaultValue);
configurationProperty_addConfigurationProperty(properties, value);
});
}
updateFileGenerationParameters(
config: ExternalFormatConfigurationSpecification,
generationProperty: GenerationProperty,
newValue: unknown,
): void {
if (generationProperty.type === GenerationPropertyItemType.MAP) {
if (
!newValue ||
isEmpty(newValue) ||
deepEqual(newValue, generationProperty.defaultValue)
) {
config.configurationProperties = config.configurationProperties.filter(
(e) => e.name !== generationProperty.name,
);
} else {
const configProperty = getNullableFileGenerationConfig(
config,
generationProperty.name,
);
if (configProperty) {
configurationProperty_setValue(configProperty, {
...(newValue as object),
});
} else {
const newItem = new ConfigurationProperty(
generationProperty.name,
newValue,
);
configurationProperty_addConfigurationProperty(
config.configurationProperties,
newItem,
);
}
}
} else {
const configProperty = getNullableFileGenerationConfig(
config,
generationProperty.name,
);
let useDefaultValue = generationProperty.defaultValue === newValue;
if (generationProperty.type === GenerationPropertyItemType.BOOLEAN) {
useDefaultValue =
(generationProperty.defaultValue === 'true') ===
(newValue as boolean);
}
const newConfigValue = useDefaultValue ? undefined : newValue;
if (newConfigValue !== undefined) {
if (configProperty) {
configurationProperty_setValue(configProperty, newConfigValue);
} else {
const newItem = new ConfigurationProperty(
generationProperty.name,
newConfigValue,
);
configurationProperty_addConfigurationProperty(
config.configurationProperties,
newItem,
);
}
} else {
config.configurationProperties = config.configurationProperties.filter(
(e) => e.name !== generationProperty.name,
);
}
}
}
}
export class ElementXTSchemaGenerationState {
readonly uuid = uuid();
editorStore: EditorStore;
description: ExternalFormatDescription;
xtGenerationState: XTGenerationState;
constructor(
editorStore: EditorStore,
description: ExternalFormatDescription,
) {
makeObservable(this, {
xtGenerationState: observable,
regenerate: flow,
});
this.editorStore = editorStore;
this.description = description;
this.xtGenerationState = new XTGenerationState(
this,
new ExternalFormatConfigurationSpecification(),
);
}
*regenerate(): GeneratorFn<void> {
const currentState = this.editorStore.tabManagerState.currentTab;
if (currentState instanceof ElementEditorState) {
this.xtGenerationState.configSpecification.modelUnit.packageableElementIncludes =
[PackageableElementExplicitReference.create(currentState.element)];
yield flowResult(this.xtGenerationState.generate());
} else {
throw new AssertionError(
'Generation state must have at least an element editor opened',
);
}
}
}