UNPKG

@finos/legend-application-studio

Version:
468 lines 17.2 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 { DataElement, RelationalCSVData, DataElementReference, ExternalFormatData, ModelStoreData, ModelEmbeddedData, RelationElementsData, RelationRowTestData, observe_RelationRowTestData, observe_RelationElement, } from '@finos/legend-graph'; import { ContentType, guaranteeNonEmptyString, tryToFormatLosslessJSONString, UnsupportedOperationError, uuid, } from '@finos/legend-shared'; import { action, makeObservable, observable } from 'mobx'; import { dataElementReference_setDataElement, externalFormatData_setContentType, externalFormatData_setData, relationalData_addTable, relationalData_deleteData, relationalData_setTableValues, } from '../../../../graph-modifier/DSL_Data_GraphModifierHelper.js'; import { EmbeddedDataType } from '../../ExternalFormatState.js'; import { TEMPORARY__createRelationalDataFromCSV } from '../../../utils/TestableUtils.js'; export const createEmbeddedData = (type, editorStore) => { if (type === EmbeddedDataType.EXTERNAL_FORMAT_DATA) { const externalFormatData = new ExternalFormatData(); externalFormatData_setData(externalFormatData, ''); externalFormatData_setContentType(externalFormatData, guaranteeNonEmptyString(editorStore.graphState.graphGenerationState.externalFormatState .formatContentTypes[0])); return externalFormatData; } else if (type === EmbeddedDataType.RELATIONAL_CSV) { const relational = new RelationalCSVData(); return relational; } else if (type === EmbeddedDataType.RELATION_ELEMENTS_DATA) { const testData = new RelationElementsData(); return testData; } else if (type === EmbeddedDataType.MODEL_STORE_DATA) { const modelStoreData = new ModelStoreData(); return modelStoreData; } else { const extraEmbeddedDataCreator = editorStore.pluginManager .getApplicationPlugins() .flatMap((plugin) => plugin.getExtraEmbeddedDataCreators?.() ?? []); for (const creator of extraEmbeddedDataCreator) { const embeddedData = creator(type); if (embeddedData) { return embeddedData; } } throw new UnsupportedOperationError(`Can't create embedded data: no compatible creators available from plugins`, type); } }; export class EmbeddedDataState { editorStore; embeddedData; constructor(editorStore, embeddedData) { this.editorStore = editorStore; this.embeddedData = embeddedData; } } export class ExternalFormatDataState extends EmbeddedDataState { embeddedData; canEditContentType = true; constructor(editorStore, embeddedData) { super(editorStore, embeddedData); makeObservable(this, { format: action, canEditContentType: observable, }); this.embeddedData = embeddedData; } label() { return 'External Format Data'; } setCanEditoContentType(val) { this.canEditContentType = val; } get supportsFormatting() { return this.embeddedData.contentType === ContentType.APPLICATION_JSON; } format() { externalFormatData_setData(this.embeddedData, tryToFormatLosslessJSONString(this.embeddedData.data)); } } export class ModelDataState { uuid = uuid(); modelStoreDataState; modelData; constructor(modelData, modelStoreDataState) { this.modelStoreDataState = modelStoreDataState; this.modelData = modelData; } } export class ModelEmbeddedDataState extends ModelDataState { modelData; embeddedDataState; constructor(modelData, modelStoreDataState) { super(modelData, modelStoreDataState); this.modelData = modelData; this.embeddedDataState = buildEmbeddedDataEditorState(this.modelData.data, this.modelStoreDataState.editorStore); } } export class UnsupportedModelDataState extends ModelDataState { } export class ModelStoreDataState extends EmbeddedDataState { embeddedData; modelDataStates = []; hideClass = false; constructor(editorStore, embeddedData, hideClass) { super(editorStore, embeddedData); makeObservable(this, { hideClass: observable, modelDataStates: observable, buildStates: action, }); this.embeddedData = embeddedData; this.modelDataStates = this.buildStates(); this.hideClass = Boolean(hideClass); } label() { return 'Model Store Data'; } buildStates() { return (this.embeddedData.modelData?.map((modelData) => { if (modelData instanceof ModelEmbeddedData) { return new ModelEmbeddedDataState(modelData, this); } return new UnsupportedModelDataState(modelData, this); }) ?? []); } } export class RelationElementState { relationElement; constructor(relationElement) { makeObservable(this, { relationElement: observable, addColumn: action, removeColumn: action, updateColumn: action, addRow: action, removeRow: action, updateRow: action, clearAllData: action, importCSV: action, }); this.relationElement = relationElement; this.relationElement = observe_RelationElement(relationElement); } addColumn(name) { this.relationElement.columns.push(name); this.relationElement.rows.forEach((row) => { row.values.push(''); }); } removeColumn(index) { const columnToRemove = this.relationElement.columns[index]; if (columnToRemove) { this.relationElement.columns.splice(index, 1); this.relationElement.rows.forEach((row) => { row.values.splice(index, 1); }); } } updateColumn(index, name) { const oldName = this.relationElement.columns[index]; if (oldName && oldName !== name) { this.relationElement.columns[index] = name; } } addRow() { const row = new RelationRowTestData(); row.values = []; const newRow = observe_RelationRowTestData(row); this.relationElement.columns.forEach((col) => { newRow.values.push(''); }); this.relationElement.rows.push(newRow); } removeRow(index) { this.relationElement.rows.splice(index, 1); } updateRow(rowIndex, columnIndex, value) { if (this.relationElement.rows[rowIndex]) { this.relationElement.rows[rowIndex].values[columnIndex] = value; } } clearAllData() { this.relationElement.rows.splice(0); } exportJSON() { return JSON.stringify({ columns: this.relationElement.columns, data: this.relationElement.rows, }, null, 2); } exportSQL() { if (this.relationElement.columns.length === 0 || this.relationElement.rows.length === 0) { return ''; } const tableName = 'test_data'; const defaultDataType = 'VARCHAR(1000)'; const columnDefs = this.relationElement.columns .map((col) => `${col} ${defaultDataType}`) .join(', '); const createTable = `CREATE TABLE ${tableName} (${columnDefs});`; const insertStatements = this.relationElement.rows.map((row) => { const values = this.relationElement.columns .map((col, colIndex) => { const value = row.values[colIndex] ?? ''; if (value !== '') { return `'${value.replace(/'/g, "''")}'`; } return 'NULL'; }) .join(', '); return `INSERT INTO ${tableName} VALUES (${values});`; }); return [createTable, '', ...insertStatements].join('\n'); } exportCSV() { const headers = this.relationElement.columns.map((col) => col); const csvLines = [headers.join(',')]; this.relationElement.rows.forEach((row) => { const values = headers.map((header, headerIndex) => { const value = row.values[headerIndex] ?? ''; if (value.includes(',') || value.includes('"')) { return `"${value.replace(/"/g, '""')}"`; } return value; }); csvLines.push(values.join(',')); }); return csvLines.join('\n'); } parseCSVLine(line) { const result = []; let current = ''; let inQuotes = false; for (let i = 0; i < line.length; i++) { const char = line[i]; if (char === '"') { inQuotes = !inQuotes; } else if (char === ',' && !inQuotes) { result.push(current.trim()); current = ''; } else { current += char; } } result.push(current.trim()); return result; } importCSV(csvContent) { const lines = csvContent.trim().split('\n'); if (lines.length === 0) { return; } const firstLine = lines[0]; if (!firstLine) { return; } const headers = this.parseCSVLine(firstLine); this.relationElement.columns = headers; this.relationElement.rows = lines.slice(1).map((line) => { const values = this.parseCSVLine(line); const row = new RelationRowTestData(); row.values = []; headers.forEach((header, index) => { row.values[index] = values[index] ?? ''; }); return observe_RelationRowTestData(row); }); } } export class RelationElementsDataState extends EmbeddedDataState { embeddedData; showImportCSVModal = false; showNewRelationElementModal = false; activeRelationElement; relationElementStates; constructor(editorStore, embeddedData) { super(editorStore, embeddedData); makeObservable(this, { embeddedData: observable, showImportCSVModal: observable, showNewRelationElementModal: observable, activeRelationElement: observable, setActiveRelationElement: action, setShowImportCSVModal: action, setShowNewRelationElementModal: action, addRelationElement: action, }); this.embeddedData = embeddedData; this.relationElementStates = embeddedData.relationElements.map((relationElement) => new RelationElementState(relationElement)); this.activeRelationElement = this.relationElementStates[0]; } label() { return 'Relation Elements Test Data'; } setActiveRelationElement(val) { this.activeRelationElement = val; } addRelationElement(relationElement) { const newElementState = new RelationElementState(relationElement); this.relationElementStates.push(newElementState); this.embeddedData.relationElements.push(relationElement); this.setActiveRelationElement(newElementState); } setShowImportCSVModal(show) { this.showImportCSVModal = show; } setShowNewRelationElementModal(show) { this.showNewRelationElementModal = show; } } export class RelationalCSVDataTableState { editorStore; table; constructor(table, editorStore) { this.table = table; this.editorStore = editorStore; makeObservable(this, { table: observable, updateTableValues: action, }); } updateTableValues(val) { relationalData_setTableValues(this.table, val); } } export class RelationalCSVDataState extends EmbeddedDataState { embeddedData; selectedTable; showImportCSVModal = false; database; // showTableIdentifierModal = false; tableToEdit; constructor(editorStore, embeddedData) { super(editorStore, embeddedData); makeObservable(this, { selectedTable: observable, showTableIdentifierModal: observable, deleteTable: observable, showImportCSVModal: observable, database: observable, resetSelectedTable: action, changeSelectedTable: action, setDatabase: action, closeModal: action, openIdentifierModal: action, setShowImportCsvModal: action, closeCSVModal: action, importCSV: action, }); this.embeddedData = embeddedData; this.resetSelectedTable(); } setShowImportCsvModal(val) { this.showImportCSVModal = val; } setDatabase(val) { this.database = val; } openIdentifierModal(renameTable) { this.showTableIdentifierModal = true; this.tableToEdit = renameTable; } closeCSVModal() { this.showImportCSVModal = false; } closeModal() { this.showTableIdentifierModal = false; this.tableToEdit = undefined; } importCSV(val) { const generated = TEMPORARY__createRelationalDataFromCSV(val); generated.tables.forEach((t) => relationalData_addTable(this.embeddedData, t)); this.resetSelectedTable(); } resetSelectedTable() { const table = this.embeddedData.tables[0]; if (table) { this.selectedTable = new RelationalCSVDataTableState(table, this.editorStore); } else { this.selectedTable = undefined; } } deleteTable(val) { relationalData_deleteData(this.embeddedData, val); if (this.selectedTable?.table === val) { this.resetSelectedTable(); } } changeSelectedTable(val) { this.selectedTable = new RelationalCSVDataTableState(val, this.editorStore); } label() { return 'Relational Data'; } } export class UnsupportedDataState extends EmbeddedDataState { label() { return 'Unsupported embedded data'; } } export class DataElementReferenceState extends EmbeddedDataState { embeddedData; embeddedDataValueState; options; constructor(editorStore, embeddedData, options) { super(editorStore, embeddedData); this.embeddedData = embeddedData; this.options = options; this.embeddedDataValueState = this.buildValueState(); } label() { return 'Data Element Reference'; } setDataElement(dataElement) { dataElementReference_setDataElement(this.embeddedData, dataElement, this.editorStore.changeDetectionState.observerContext); this.embeddedDataValueState = this.buildValueState(); } buildValueState(options) { const packagableEl = this.embeddedData.dataElement.value; if (packagableEl instanceof DataElement) { return buildEmbeddedDataEditorState(packagableEl.data, this.editorStore, this.options); } return new UnsupportedDataState(this.editorStore, this.embeddedData); } } export function buildEmbeddedDataEditorState(_embeddedData, editorStore, options) { const embeddedData = _embeddedData; if (embeddedData instanceof ExternalFormatData) { return new ExternalFormatDataState(editorStore, embeddedData); } else if (embeddedData instanceof ModelStoreData) { return new ModelStoreDataState(editorStore, embeddedData, options?.hideSource); } else if (embeddedData instanceof RelationalCSVData) { return new RelationalCSVDataState(editorStore, embeddedData); } else if (embeddedData instanceof RelationElementsData) { return new RelationElementsDataState(editorStore, embeddedData); } else if (embeddedData instanceof DataElementReference) { return new DataElementReferenceState(editorStore, embeddedData, options); } else { const extraEmbeddedDataEditorStateBuilders = editorStore.pluginManager .getApplicationPlugins() .flatMap((plugin) => plugin.getExtraEmbeddedDataEditorStateBuilders?.() ?? []); for (const stateBuilder of extraEmbeddedDataEditorStateBuilders) { const state = stateBuilder(editorStore, embeddedData); if (state) { return state; } } return new UnsupportedDataState(editorStore, embeddedData); } } //# sourceMappingURL=EmbeddedDataState.js.map