UNPKG

@finos/legend-studio

Version:
398 lines (368 loc) 12.3 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 { 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); } } } }