@finos/legend-studio
Version:
398 lines (368 loc) • 12.3 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 {
computed,
action,
makeObservable,
observable,
flow,
flowResult,
} from 'mobx';
import {
type GeneratorFn,
readFileAsText,
assertErrorThrown,
LogEvent,
addUniqueEntry,
deepEqual,
guaranteeType,
isEmpty,
ActionState,
deleteEntry,
} from '@finos/legend-shared';
import {
type GenerationProperty,
type PackageableElement,
type ExternalFormatDescription,
CompilationError,
ConfigurationProperty,
GenerationPropertyItemType,
ExternalFormatSchema as Schema,
SchemaSet,
} from '@finos/legend-graph';
import type { Entity } from '@finos/legend-model-storage';
import { type EntityChange, EntityChangeType } from '@finos/legend-server-sdlc';
import type { EditorStore } from '../../../EditorStore.js';
import { ElementEditorState } from '../ElementEditorState.js';
import { LEGEND_STUDIO_APP_EVENT } from '../../../LegendStudioAppEvent.js';
import { configurationProperty_setValue } from '../../../graphModifier/DSLGeneration_GraphModifierHelper.js';
export enum SCHEMA_SET_TAB_TYPE {
GENERAL = 'GENERAL',
MODEL_GENERATION = 'MODEL_GENERATION',
}
export class SchemaSetModelGenerationState {
isGenerating = false;
isImportingGeneratedElements = false;
schemaSetEditorState: SchemaSetEditorState;
configurationProperties: ConfigurationProperty[] = [];
generationValue = '';
constructor(schemaSetEditorState: SchemaSetEditorState) {
this.schemaSetEditorState = schemaSetEditorState;
makeObservable(this, {
configurationProperties: observable,
isGenerating: observable,
generationValue: observable,
setConfigurationProperty: action,
setGenerationValue: action,
generateModel: flow,
importGrammar: flow,
});
}
setConfigurationProperty(val: ConfigurationProperty[]): void {
this.configurationProperties = val;
}
get description(): ExternalFormatDescription {
return this.schemaSetEditorState.editorStore.graphState.graphGenerationState.externalFormatState.getTypeDescription(
this.schemaSetEditorState.schemaSet.format,
);
}
get modelGenerationProperties(): GenerationProperty[] {
return this.description.modelGenerationProperties;
}
getConfigValue(name: string): unknown | undefined {
return this.getConfig(name)?.value;
}
getConfig(name: string): ConfigurationProperty | undefined {
return this.configurationProperties.find(
(property) => name === property.name,
);
}
setGenerationValue(val: string): void {
this.generationValue = val;
}
updateGenerationParameters(
generationProperty: GenerationProperty,
newValue: unknown,
): void {
if (generationProperty.type === GenerationPropertyItemType.MAP) {
if (
!newValue ||
isEmpty(newValue) ||
deepEqual(newValue, generationProperty.defaultValue)
) {
this.configurationProperties = this.configurationProperties.filter(
(e) => e.name !== generationProperty.name,
);
} else {
const configProperty = this.getConfig(generationProperty.name);
if (configProperty) {
configurationProperty_setValue(configProperty, {
...(newValue as object),
});
} else {
const newItem = new ConfigurationProperty(
generationProperty.name,
newValue,
);
addUniqueEntry(this.configurationProperties, newItem);
}
}
} else {
const configProperty = this.getConfig(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,
);
addUniqueEntry(this.configurationProperties, newItem);
}
} else {
this.configurationProperties = this.configurationProperties.filter(
(e) => e.name !== generationProperty.name,
);
}
}
}
*generateModel(): GeneratorFn<void> {
this.isGenerating = true;
try {
const SCHEMA_SET_PROPERTY_NAME = 'sourceSchemaSet';
const SCHEMA_FORMAT_PROPERTY_NAME = 'format';
const properties = [...this.configurationProperties];
if (!properties.find((e) => e.name === SCHEMA_SET_PROPERTY_NAME)) {
const genProperty = new ConfigurationProperty(
SCHEMA_SET_PROPERTY_NAME,
this.schemaSetEditorState.schemaSet.path,
);
properties.push(genProperty);
}
if (!properties.find((e) => e.name === SCHEMA_FORMAT_PROPERTY_NAME)) {
const genProperty = new ConfigurationProperty(
SCHEMA_FORMAT_PROPERTY_NAME,
this.schemaSetEditorState.schemaSet.format,
);
properties.push(genProperty);
}
const val =
(yield this.schemaSetEditorState.editorStore.graphManagerState.graphManager.generateModelFromExternalFormat(
properties,
this.schemaSetEditorState.editorStore.graphManagerState.graph,
)) as string;
this.setGenerationValue(val);
} catch (error) {
assertErrorThrown(error);
this.schemaSetEditorState.editorStore.applicationStore.log.error(
LogEvent.create(LEGEND_STUDIO_APP_EVENT.EXTERNAL_FORMAT_FAILURE),
error,
);
this.schemaSetEditorState.editorStore.applicationStore.notifyError(error);
} finally {
this.isGenerating = false;
}
}
*importGrammar(): GeneratorFn<void> {
try {
this.isImportingGeneratedElements = true;
const entities =
(yield this.schemaSetEditorState.editorStore.graphManagerState.graphManager.pureCodeToEntities(
this.generationValue,
)) as Entity[];
const newEntities: EntityChange[] = entities.map((e) => ({
type: EntityChangeType.CREATE,
entityPath: e.path,
content: e.content,
}));
yield flowResult(
this.schemaSetEditorState.editorStore.graphState.loadEntityChangesToGraph(
newEntities,
undefined,
),
);
this.schemaSetEditorState.editorStore.applicationStore.notifySuccess(
'Generated elements imported into project',
);
} catch (error) {
assertErrorThrown(error);
this.schemaSetEditorState.editorStore.applicationStore.log.error(
LogEvent.create(LEGEND_STUDIO_APP_EVENT.EXTERNAL_FORMAT_FAILURE),
error,
);
this.schemaSetEditorState.editorStore.applicationStore.notifyError(error);
} finally {
this.isImportingGeneratedElements = false;
}
}
}
export class ImportSchemaContentState {
editorStore: EditorStore;
schemaSetEditorState: SchemaSetEditorState;
loadingSchemaContentState = ActionState.create();
files: File[] | undefined;
importSchemaModal = false;
constructor(
schemaSetEditorState: SchemaSetEditorState,
editorStore: EditorStore,
) {
this.editorStore = editorStore;
this.schemaSetEditorState = schemaSetEditorState;
makeObservable(this, {
schemaSetEditorState: false,
editorStore: false,
files: observable,
importSchemaModal: observable,
loadingSchemaContentState: observable,
removeFile: action,
closeModal: action,
importSchemas: flow,
});
}
setImportSchemaModal(val: boolean): void {
this.importSchemaModal = val;
}
setFiles(files: File[] | undefined): void {
this.files = files;
}
removeFile(file: File): void {
if (this.files) {
deleteEntry(this.files, file);
}
}
closeModal(): void {
this.files = undefined;
this.setImportSchemaModal(false);
}
*importSchemas(files: File[]): GeneratorFn<void> {
try {
this.loadingSchemaContentState.inProgress();
const schemaSet = this.schemaSetEditorState.schemaSet;
yield Promise.all(
files.map((file) =>
readFileAsText(file).then((content) => {
const _schema = new Schema();
_schema.location = file.name;
_schema.content = content;
// https://stackoverflow.com/questions/4250364
_schema.id = file.name.replace(/\.[^/.]+$/, '');
schemaSet.schemas.push(_schema);
}),
),
);
if (schemaSet.schemas.length) {
this.schemaSetEditorState.setCurrentSchema(schemaSet.schemas[0]);
}
this.loadingSchemaContentState.complete();
this.closeModal();
} catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.notifyError(
`Can't load patch: Error: ${error.message}`,
);
this.loadingSchemaContentState.fail();
}
}
}
export class SchemaSetEditorState extends ElementEditorState {
currentSchema?: Schema | undefined;
selectedTab = SCHEMA_SET_TAB_TYPE.GENERAL;
schemaValidationError?: CompilationError;
importSchemaContentState: ImportSchemaContentState;
schemaSetModelGenerationState: SchemaSetModelGenerationState;
constructor(editorStore: EditorStore, element: PackageableElement) {
super(editorStore, element);
makeObservable(this, {
schemaValidationError: observable,
currentSchema: observable,
selectedTab: observable,
schemaSet: computed,
setSelectedTab: action,
setCurrentSchema: action,
reprocess: action,
setSchemaValidationerror: action,
validateSchema: flow,
});
if (this.schemaSet.schemas.length !== 0) {
this.currentSchema =
this.schemaSet.schemas[this.schemaSet.schemas.length - 1];
}
this.schemaSetModelGenerationState = new SchemaSetModelGenerationState(
this,
);
this.importSchemaContentState = new ImportSchemaContentState(
this,
this.editorStore,
);
}
get schemaSet(): SchemaSet {
return guaranteeType(
this.element,
SchemaSet,
'Element inside schema set element editor state must be a SchemaSet',
);
}
setCurrentSchema(value: Schema | undefined): void {
this.currentSchema = value;
}
setSelectedTab(tab: SCHEMA_SET_TAB_TYPE): void {
this.selectedTab = tab;
}
setSchemaValidationerror(error: CompilationError): void {
this.schemaValidationError = error;
}
reprocess(
newElement: PackageableElement,
editorStore: EditorStore,
): ElementEditorState {
const schemaSetEditorState = new SchemaSetEditorState(
editorStore,
newElement,
);
return schemaSetEditorState;
}
*validateSchema(currentSchema: Schema): GeneratorFn<void> {
try {
const _schemaSet = new SchemaSet(this.schemaSet.name);
_schemaSet.format = this.schemaSet.format;
_schemaSet.schemas = [currentSchema];
const newGraph = this.editorStore.graphManagerState.createEmptyGraph();
newGraph.addElement(_schemaSet, this.schemaSet.package?.path);
yield this.editorStore.graphManagerState.graphManager.compileGraph(
newGraph,
{
keepSourceInformation: true,
},
);
} catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.log.error(
LogEvent.create(LEGEND_STUDIO_APP_EVENT.EXTERNAL_FORMAT_FAILURE),
error,
);
if (error instanceof CompilationError) {
this.setSchemaValidationerror(error);
}
}
}
}