@finos/legend-application-studio
Version:
Legend Studio application core
724 lines • 33.7 kB
JavaScript
/**
* 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 { assertErrorThrown, LogEvent, addUniqueEntry, guaranteeNonNullable, isNonNullable, filterByType, ActionState, at, guaranteeType, assertNonEmptyString, assertTrue, UnsupportedOperationError, } from '@finos/legend-shared';
import { observable, action, makeObservable, flow, flowResult, computed, } from 'mobx';
import { LEGEND_STUDIO_APP_EVENT } from '../../../../../__lib__/LegendStudioEvent.js';
import { TDSExecutionResult, getColumn, PrimitiveType, PRIMITIVE_TYPE, TDSRow, Table, RelationalDatabaseConnection, DatabaseBuilderInput, DatabasePattern, TargetDatabase, Column, Database, resolvePackagePathAndElementName, getSchema, getNullableSchema, getNullableTable, isStubbed_PackageableElement, isValidFullPath, PackageableElementExplicitReference, getTable, Mapping, EngineRuntime, StoreConnections, IdentifiedConnection, getOrCreateGraphPackage, extractElementNameFromPath, extractPackagePathFromPath, } from '@finos/legend-graph';
import { GraphEditFormModeState } from '../../../GraphEditFormModeState.js';
import { connection_setStore } from '../../../../graph-modifier/DSL_Mapping_GraphModifierHelper.js';
import { getTDSColumnDerivedProperyFromType } from '@finos/legend-query-builder';
import { getPrimitiveTypeFromRelationalType } from '../../../utils/MockDataUtils.js';
const GENERATED_PACKAGE = 'generated';
const TDS_LIMIT = 1000;
const buildTableToTDSQueryGrammar = (table) => {
const tableName = table.name;
const schemaName = table.schema.name;
const db = table.schema._OWNER.path;
return `|${db}->tableReference(
'${schemaName}',
'${tableName}'
)->tableToTDS()->take(${TDS_LIMIT})`;
};
const buildTableToTDSQueryNonNumericWithColumnGrammar = (column) => {
const table = guaranteeType(column.owner, Table);
const tableName = table.name;
const colName = column.name;
const schemaName = table.schema.name;
const db = table.schema._OWNER.path;
const PREVIEW_COLUMN_NAME = 'Count Value';
const columnGetter = getTDSColumnDerivedProperyFromType(getPrimitiveTypeFromRelationalType(column.type) ?? PrimitiveType.STRING);
return `|${db}->tableReference(
'${schemaName}',
'${tableName}'
)->tableToTDS()->restrict(
['${colName}']
)->groupBy(
['${colName}'],
'${PREVIEW_COLUMN_NAME}'->agg(
row|$row.${columnGetter}('${colName}'),
y|$y->count()
)
)->sort(
[
desc('${colName}'),
asc('${PREVIEW_COLUMN_NAME}')
]
)->take(${TDS_LIMIT})`;
};
const buildTableToTDSQueryNumericWithColumnGrammar = (column) => {
const table = guaranteeType(column.owner, Table);
const tableName = table.name;
const colName = column.name;
const schemaName = table.schema.name;
const db = table.schema._OWNER.path;
const columnGetter = getTDSColumnDerivedProperyFromType(getPrimitiveTypeFromRelationalType(column.type) ?? PrimitiveType.STRING);
return `|${db}->tableReference(
'${schemaName}',
'${tableName}'
)->tableToTDS()->restrict(
['${colName}']
)->groupBy(
[],
[
'Count'->agg(
row|$row.${columnGetter}('${colName}'),
x|$x->count()
),
'Distinct Count'->agg(
row|$row.${columnGetter}('${colName}'),
x|$x->distinct()->count()
),
'Sum'->agg(
row|$row.${columnGetter}('${colName}'),
x|$x->sum()
),
'Min'->agg(
row|$row.${columnGetter}('${colName}'),
x|$x->min()
),
'Max'->agg(
row|$row.${columnGetter}('${colName}'),
x|$x->max()
),
'Average'->agg(
row|$row.${columnGetter}('${colName}'),
x|$x->average()
),
'Std Dev (Population)'->agg(
row|$row.${columnGetter}('${colName}'),
x|$x->stdDevPopulation()
),
'Std Dev (Sample)'->agg(
row|$row.${columnGetter}('${colName}'),
x|$x->stdDevSample()
)
]
)`;
};
const buildTableToTDSQueryColumnQuery = (column) => {
const type = getPrimitiveTypeFromRelationalType(column.type) ?? PrimitiveType.STRING;
const numerics = [
PRIMITIVE_TYPE.NUMBER,
PRIMITIVE_TYPE.INTEGER,
PRIMITIVE_TYPE.DECIMAL,
PRIMITIVE_TYPE.FLOAT,
];
if (numerics.includes(type.path)) {
return [buildTableToTDSQueryNumericWithColumnGrammar(column), true];
}
return [buildTableToTDSQueryNonNumericWithColumnGrammar(column), false];
};
// 1. mapping
// 2. connection
// 3. runtime
const buildTDSModel = (graph, connection, db) => {
// mapping
const mappingName = 'EmptyMapping';
const _mapping = new Mapping(mappingName);
graph.addElement(_mapping, GENERATED_PACKAGE);
const engineRuntime = new EngineRuntime();
engineRuntime.mappings = [
PackageableElementExplicitReference.create(_mapping),
];
const _storeConnection = new StoreConnections(PackageableElementExplicitReference.create(db));
// copy over new connection
const newconnection = new RelationalDatabaseConnection(PackageableElementExplicitReference.create(db), connection.type, connection.datasourceSpecification, connection.authenticationStrategy);
newconnection.localMode = connection.localMode;
newconnection.timeZone = connection.timeZone;
_storeConnection.storeConnections = [
new IdentifiedConnection('connection1', newconnection),
];
engineRuntime.connections = [_storeConnection];
return {
runtime: engineRuntime,
mapping: _mapping,
};
};
export class DatabaseSchemaExplorerTreeNodeData {
isOpen;
id;
label;
parentId;
childrenIds;
isChecked = false;
constructor(id, label, parentId) {
makeObservable(this, {
isChecked: observable,
setChecked: action,
});
this.id = id;
this.label = label;
this.parentId = parentId;
}
setChecked(val) {
this.isChecked = val;
}
}
export class DatabaseSchemaExplorerTreeSchemaNodeData extends DatabaseSchemaExplorerTreeNodeData {
schema;
constructor(id, schema) {
super(id, schema.name, undefined);
this.schema = schema;
}
}
export class DatabaseSchemaExplorerTreeTableNodeData extends DatabaseSchemaExplorerTreeNodeData {
parentId;
owner;
table;
constructor(id, parentId, owner, table) {
super(id, table.name, parentId);
this.parentId = parentId;
this.owner = owner;
this.table = table;
}
}
export class DatabaseSchemaExplorerTreeTabularFunctionNodeData extends DatabaseSchemaExplorerTreeNodeData {
parentId;
owner;
tabularFunction;
constructor(id, parentId, owner, tabularFunction) {
super(id, tabularFunction.name, parentId);
this.parentId = parentId;
this.owner = owner;
this.tabularFunction = tabularFunction;
}
}
export class DatabaseSchemaExplorerTreeColumnNodeData extends DatabaseSchemaExplorerTreeNodeData {
parentId;
owner;
column;
constructor(id, parentId, owner, column) {
super(id, column.name, parentId);
this.parentId = parentId;
this.owner = owner;
this.column = column;
}
}
export const DEFAULT_DATABASE_PATH = 'store::MyDatabase';
export class DatabaseSchemaExplorerState {
editorStore;
connection;
database;
targetDatabasePath;
makeTargetDatabasePathEditable;
isGeneratingDatabase = false;
isUpdatingDatabase = false;
treeData;
previewer;
previewDataState = ActionState.create();
constructor(editorStore, connection) {
makeObservable(this, {
isGeneratingDatabase: observable,
isUpdatingDatabase: observable,
database: observable,
treeData: observable,
targetDatabasePath: observable,
previewer: observable,
previewDataState: observable,
makeTargetDatabasePathEditable: observable,
isCreatingNewDatabase: computed,
resolveDatabasePackageAndName: computed,
setTreeData: action,
setTargetDatabasePath: action,
setMakeTargetDatabasePathEditable: action,
onNodeSelect: flow,
fetchDatabaseMetadata: flow,
fetchSchemaMetadata: flow,
fetchTableMetadata: flow,
fetchTabularFunctionMetadata: flow,
generateDatabase: flow,
updateDatabase: flow,
updateDatabaseAndGraph: flow,
previewData: flow,
});
this.connection = connection;
this.database = guaranteeType(connection.store?.value, Database);
this.editorStore = editorStore;
this.targetDatabasePath = DEFAULT_DATABASE_PATH;
}
get isCreatingNewDatabase() {
if (!this.connection.store) {
return false;
}
return isStubbed_PackageableElement(this.connection.store.value);
}
setMakeTargetDatabasePathEditable(val) {
this.makeTargetDatabasePathEditable = val;
}
get resolveDatabasePackageAndName() {
if (!this.isCreatingNewDatabase && !this.makeTargetDatabasePathEditable) {
return [
guaranteeNonNullable(this.database.package).path,
this.database.name,
];
}
assertNonEmptyString(this.targetDatabasePath, 'Must specify database path');
assertTrue(isValidFullPath(this.targetDatabasePath), 'Invalid database path');
return resolvePackagePathAndElementName(this.targetDatabasePath, this.targetDatabasePath);
}
setTargetDatabasePath(val) {
this.targetDatabasePath = val;
}
setTreeData(builderTreeData) {
this.treeData = builderTreeData;
}
*onNodeSelect(node, treeData) {
if (node instanceof DatabaseSchemaExplorerTreeSchemaNodeData &&
!node.childrenIds) {
yield flowResult(this.fetchSchemaMetadata(node, treeData));
}
else if (node instanceof DatabaseSchemaExplorerTreeTableNodeData &&
!node.childrenIds) {
yield flowResult(this.fetchTableMetadata(node, treeData));
}
else if (node instanceof DatabaseSchemaExplorerTreeTabularFunctionNodeData &&
!node.childrenIds) {
yield flowResult(this.fetchTabularFunctionMetadata(node, treeData));
}
node.isOpen = !node.isOpen;
this.setTreeData({ ...treeData });
}
getChildNodes(node, treeData) {
return node.childrenIds
?.map((childNode) => treeData.nodes.get(childNode))
.filter(isNonNullable);
}
toggleCheckedNode(node, treeData) {
node.setChecked(!node.isChecked);
if (node instanceof DatabaseSchemaExplorerTreeSchemaNodeData) {
this.getChildNodes(node, treeData)?.forEach((childNode) => {
childNode.setChecked(node.isChecked);
});
}
else if (node instanceof DatabaseSchemaExplorerTreeTableNodeData ||
node instanceof DatabaseSchemaExplorerTreeTabularFunctionNodeData) {
if (node.parentId) {
const parent = treeData.nodes.get(node.parentId);
if (parent &&
this.getChildNodes(parent, treeData)?.every((e) => e.isChecked === node.isChecked)) {
parent.setChecked(node.isChecked);
}
}
}
// TODO: support toggling check for columns
this.setTreeData({ ...treeData });
}
*fetchDatabaseMetadata() {
try {
this.isGeneratingDatabase = true;
const databaseBuilderInput = new DatabaseBuilderInput(this.connection);
const [packagePath, name] = this.resolveDatabasePackageAndName;
databaseBuilderInput.targetDatabase = new TargetDatabase(packagePath, name);
databaseBuilderInput.config.maxTables = undefined;
databaseBuilderInput.config.enrichTables = false;
databaseBuilderInput.config.enrichTableFunctions = false;
databaseBuilderInput.config.patterns = [
new DatabasePattern(undefined, undefined, undefined),
];
const database = (yield this.buildIntermediateDatabase(databaseBuilderInput));
const rootIds = [];
const nodes = new Map();
database.schemas
.toSorted((schemaA, schemaB) => schemaA.name.localeCompare(schemaB.name))
.forEach((schema) => {
const schemaId = schema.name;
rootIds.push(schemaId);
const schemaNode = new DatabaseSchemaExplorerTreeSchemaNodeData(schemaId, schema);
nodes.set(schemaId, schemaNode);
schemaNode.setChecked(Boolean(this.database.schemas.find((cSchema) => cSchema.name === schema.name)));
});
const treeData = { rootIds, nodes, database };
this.setTreeData(treeData);
}
catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.DATABASE_BUILDER_FAILURE), error);
this.editorStore.applicationStore.notificationService.notifyError(error);
}
finally {
this.isGeneratingDatabase = false;
}
}
*fetchSchemaMetadata(schemaNode, treeData) {
try {
this.isGeneratingDatabase = true;
const schema = schemaNode.schema;
const databaseBuilderInput = new DatabaseBuilderInput(this.connection);
const [packagePath, name] = this.resolveDatabasePackageAndName;
databaseBuilderInput.targetDatabase = new TargetDatabase(packagePath, name);
databaseBuilderInput.config.maxTables = undefined;
databaseBuilderInput.config.enrichTables = true;
databaseBuilderInput.config.enrichTableFunctions = true;
databaseBuilderInput.config.patterns = [
new DatabasePattern(schema.name, undefined, undefined),
];
const database = (yield this.buildIntermediateDatabase(databaseBuilderInput));
const tables = getSchema(database, schema.name).tables;
const tabularFunctions = getSchema(database, schema.name).tabularFunctions;
const childrenIds = schemaNode.childrenIds ?? [];
schema.tables = tables;
schema.tabularFunctions = tabularFunctions;
tables
.toSorted((tableA, tableB) => tableA.name.localeCompare(tableB.name))
.forEach((table) => {
table.schema = schema;
const tableId = `${schema.name}.${table.name}`;
const tableNode = new DatabaseSchemaExplorerTreeTableNodeData(tableId, schemaNode.id, schema, table);
treeData.nodes.set(tableId, tableNode);
addUniqueEntry(childrenIds, tableId);
const matchingSchema = getNullableSchema(this.database, schema.name);
tableNode.setChecked(Boolean(matchingSchema
? getNullableTable(matchingSchema, table.name)
: undefined));
});
tabularFunctions
.toSorted((tabularFunctionA, tabularFunctionB) => tabularFunctionA.name.localeCompare(tabularFunctionB.name))
.forEach((tabularFunction) => {
tabularFunction.schema = schema;
const tabularFunctionId = `${schema.name}.${tabularFunction.name}`;
const tabularFunctionNode = new DatabaseSchemaExplorerTreeTabularFunctionNodeData(tabularFunctionId, schemaNode.id, schema, tabularFunction);
treeData.nodes.set(tabularFunctionId, tabularFunctionNode);
addUniqueEntry(childrenIds, tabularFunctionId);
const matchingSchema = getNullableSchema(this.database, schema.name);
tabularFunctionNode.setChecked(Boolean(matchingSchema
? getNullableTable(matchingSchema, tabularFunction.name)
: undefined));
});
schemaNode.childrenIds = childrenIds;
this.setTreeData({ ...treeData });
}
catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.DATABASE_BUILDER_FAILURE), error);
this.editorStore.applicationStore.notificationService.notifyError(error);
}
finally {
this.isGeneratingDatabase = false;
}
}
*fetchTableMetadata(tableNode, treeData) {
try {
this.isGeneratingDatabase = true;
const databaseBuilderInput = new DatabaseBuilderInput(this.connection);
const [packagePath, name] = this.resolveDatabasePackageAndName;
databaseBuilderInput.targetDatabase = new TargetDatabase(packagePath, name);
const table = tableNode.table;
const config = databaseBuilderInput.config;
config.maxTables = undefined;
config.enrichTables = true;
config.enrichTableFunctions = true;
config.enrichColumns = true;
config.enrichPrimaryKeys = true;
config.patterns = [
new DatabasePattern(table.schema.name, table.name, undefined),
];
const database = (yield this.buildIntermediateDatabase(databaseBuilderInput));
const enrichedTable = database.schemas
.find((s) => table.schema.name === s.name)
?.tables.find((t) => t.name === table.name);
if (enrichedTable) {
table.primaryKey = enrichedTable.primaryKey;
const columns = enrichedTable.columns.filter(filterByType(Column));
tableNode.table.columns = columns;
tableNode.childrenIds?.forEach((childId) => treeData.nodes.delete(childId));
tableNode.childrenIds = undefined;
const childrenIds = [];
const tableId = tableNode.id;
columns
.toSorted((colA, colB) => colA.name.localeCompare(colB.name))
.forEach((column) => {
const columnId = `${tableId}.${column.name}`;
const columnNode = new DatabaseSchemaExplorerTreeColumnNodeData(columnId, tableId, table, column);
column.owner = tableNode.table;
treeData.nodes.set(columnId, columnNode);
addUniqueEntry(childrenIds, columnId);
});
tableNode.childrenIds = childrenIds;
}
}
catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.DATABASE_BUILDER_FAILURE), error);
this.editorStore.applicationStore.notificationService.notifyError(error);
}
finally {
this.isGeneratingDatabase = false;
}
}
*fetchTabularFunctionMetadata(tabularFunctionNode, treeData) {
try {
this.isGeneratingDatabase = true;
const databaseBuilderInput = new DatabaseBuilderInput(this.connection);
const [packagePath, name] = this.resolveDatabasePackageAndName;
databaseBuilderInput.targetDatabase = new TargetDatabase(packagePath, name);
const tabularFunction = tabularFunctionNode.tabularFunction;
const config = databaseBuilderInput.config;
config.maxTables = undefined;
config.enrichTables = false;
config.enrichTableFunctions = true;
config.enrichColumns = true;
config.enrichPrimaryKeys = true;
config.patterns = [
new DatabasePattern(tabularFunction.schema.name, undefined, tabularFunction.name),
];
const database = (yield this.buildIntermediateDatabase(databaseBuilderInput));
const enrichedTabularFunction = database.schemas
.find((s) => tabularFunction.schema.name === s.name)
?.tabularFunctions.find((t) => t.name === tabularFunction.name);
if (enrichedTabularFunction) {
const columns = enrichedTabularFunction.columns.filter(filterByType(Column));
tabularFunctionNode.tabularFunction.columns = columns;
tabularFunctionNode.childrenIds?.forEach((childId) => treeData.nodes.delete(childId));
tabularFunctionNode.childrenIds = undefined;
const childrenIds = [];
const tabularFunctionId = tabularFunctionNode.id;
columns
.toSorted((colA, colB) => colA.name.localeCompare(colB.name))
.forEach((column) => {
const columnId = `${tabularFunctionId}.${column.name}`;
const columnNode = new DatabaseSchemaExplorerTreeColumnNodeData(columnId, tabularFunctionId, tabularFunction, column);
column.owner = tabularFunctionNode.tabularFunction;
treeData.nodes.set(columnId, columnNode);
addUniqueEntry(childrenIds, columnId);
});
tabularFunctionNode.childrenIds = childrenIds;
}
}
catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.DATABASE_BUILDER_FAILURE), error);
this.editorStore.applicationStore.notificationService.notifyError(error);
}
finally {
this.isGeneratingDatabase = false;
}
}
async buildIntermediateDatabase(databaseBuilderInput) {
const entities = await this.editorStore.graphManagerState.graphManager.buildDatabase(databaseBuilderInput);
const graph = this.editorStore.graphManagerState.createNewGraph();
await this.editorStore.graphManagerState.graphManager.buildGraph(graph, entities, ActionState.create());
return at(graph.ownDatabases, 0, 'Expected one database to be generated from input');
}
*previewData(node) {
try {
this.previewer = undefined;
this.previewDataState.inProgress();
let column;
let nodeInstance;
if (node instanceof DatabaseSchemaExplorerTreeTableNodeData) {
nodeInstance = node.table;
}
else if (node instanceof DatabaseSchemaExplorerTreeTabularFunctionNodeData) {
nodeInstance = node.tabularFunction;
}
else if (node instanceof DatabaseSchemaExplorerTreeColumnNodeData) {
nodeInstance = guaranteeType(node.column.owner, Table);
column = node.column;
}
else {
throw new UnsupportedOperationError('Preview data only supported for column, table and tabular function');
}
const schemaName = nodeInstance.schema.name;
const tableName = nodeInstance.name;
const dummyPackage = 'generation';
const dummyName = 'myDB';
const dummyDbPath = `${dummyPackage}::${dummyName}`;
const databaseBuilderInput = new DatabaseBuilderInput(this.connection);
databaseBuilderInput.targetDatabase = new TargetDatabase(dummyPackage, dummyName);
const config = databaseBuilderInput.config;
config.maxTables = undefined;
config.enrichColumns = true;
config.enrichPrimaryKeys = true;
if (node instanceof DatabaseSchemaExplorerTreeTableNodeData) {
config.enrichTables = true;
config.enrichTableFunctions = false;
config.patterns.push(new DatabasePattern(schemaName, node.table.name, undefined));
}
else if (node instanceof DatabaseSchemaExplorerTreeTabularFunctionNodeData) {
config.enrichTables = false;
config.enrichTableFunctions = true;
config.patterns.push(new DatabasePattern(schemaName, undefined, node.tabularFunction.name));
}
const entities = (yield this.editorStore.graphManagerState.graphManager.buildDatabase(databaseBuilderInput));
assertTrue(entities.length === 1);
const dbEntity = guaranteeNonNullable(entities[0]);
const emptyGraph = this.editorStore.graphManagerState.createNewGraph();
(yield this.editorStore.graphManagerState.graphManager.buildGraph(emptyGraph, [dbEntity], ActionState.create()));
const generatedDb = emptyGraph.getDatabase(dummyDbPath);
const resolvedTable = getTable(getSchema(generatedDb, schemaName), tableName);
let queryGrammar;
let resolveResult = false;
if (column) {
const resolvedColumn = getColumn(resolvedTable, column.name);
const grammarResult = buildTableToTDSQueryColumnQuery(resolvedColumn);
queryGrammar = grammarResult[0];
resolveResult = grammarResult[1];
}
else {
queryGrammar = buildTableToTDSQueryGrammar(resolvedTable);
}
const rawLambda = (yield this.editorStore.graphManagerState.graphManager.pureCodeToLambda(queryGrammar, 'QUERY'));
const { mapping, runtime } = buildTDSModel(emptyGraph, this.connection, generatedDb);
const execPlan = (yield this.editorStore.graphManagerState.graphManager.runQuery(rawLambda, mapping, runtime, emptyGraph)).executionResult;
let tdsResult = guaranteeType(execPlan, TDSExecutionResult, 'Execution from `tabletoTDS` expected to be TDS');
if (resolveResult) {
const newResult = new TDSExecutionResult();
newResult.result.columns = ['Aggregation', 'Value'];
newResult.result.rows = tdsResult.result.columns.map((col, idx) => {
const _row = new TDSRow();
_row.values = [
col,
guaranteeNonNullable(guaranteeNonNullable(tdsResult.result.rows[0]).values[idx]),
];
return _row;
});
tdsResult = newResult;
}
this.previewer = tdsResult;
}
catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.notificationService.notifyError(`Unable to preview data: ${error.message}`);
}
finally {
this.previewDataState.complete();
}
}
*generateDatabase() {
try {
this.isGeneratingDatabase = true;
const treeData = guaranteeNonNullable(this.treeData);
const databaseBuilderInput = new DatabaseBuilderInput(this.connection);
const [packagePath, name] = this.resolveDatabasePackageAndName;
databaseBuilderInput.targetDatabase = new TargetDatabase(packagePath, name);
const config = databaseBuilderInput.config;
config.maxTables = undefined;
config.enrichColumns = true;
config.enrichPrimaryKeys = true;
config.enrichTables = false;
config.enrichTableFunctions = false;
treeData.rootIds
.map((e) => treeData.nodes.get(e))
.filter(isNonNullable)
.forEach((schemaNode) => {
if (schemaNode instanceof DatabaseSchemaExplorerTreeSchemaNodeData) {
const nodes = this.getChildNodes(schemaNode, treeData);
const allChecked = nodes?.every((t) => t.isChecked === true);
if (allChecked ||
(schemaNode.isChecked && !schemaNode.childrenIds)) {
config.enrichTables = true;
config.enrichTableFunctions = true;
config.patterns.push(new DatabasePattern(schemaNode.schema.name, undefined, undefined));
}
else {
nodes?.forEach((t) => {
if (t instanceof DatabaseSchemaExplorerTreeTableNodeData &&
t.isChecked) {
config.enrichTables = true;
config.patterns.push(new DatabasePattern(schemaNode.schema.name, t.table.name, undefined));
}
else if (t instanceof
DatabaseSchemaExplorerTreeTabularFunctionNodeData &&
t.isChecked) {
config.enrichTableFunctions = true;
config.patterns.push(new DatabasePattern(schemaNode.schema.name, undefined, t.tabularFunction.name));
}
});
}
}
});
const entities = (yield this.editorStore.graphManagerState.graphManager.buildDatabase(databaseBuilderInput));
return at(entities, 0, 'Expected a database to be generated');
}
finally {
this.isGeneratingDatabase = false;
}
}
// this method just updates database
*updateDatabase(forceRename) {
this.isUpdatingDatabase = true;
const graph = this.editorStore.graphManagerState.createNewGraph();
(yield this.editorStore.graphManagerState.graphManager.buildGraph(graph, [(yield flowResult(this.generateDatabase()))], ActionState.create()));
const database = at(graph.ownDatabases, 0, 'Expected one database to be generated from input');
// remove undefined schemas
const schemas = Array.from(guaranteeNonNullable(this.treeData).nodes.values())
.map((schemaNode) => {
if (schemaNode instanceof DatabaseSchemaExplorerTreeSchemaNodeData) {
return schemaNode.schema;
}
return undefined;
})
.filter(isNonNullable);
// update this.database packge and name
if (forceRename || this.database.name === '' || !this.database.package) {
this.database.package = getOrCreateGraphPackage(graph, extractPackagePathFromPath(this.targetDatabasePath), undefined);
this.database.name = extractElementNameFromPath(this.targetDatabasePath);
}
// update schemas
this.database.schemas = this.database.schemas.filter((schema) => {
if (schemas.find((item) => item.name === schema.name) &&
!database.schemas.find((s) => s.name === schema.name)) {
return false;
}
return true;
});
// update existing schemas
database.schemas.forEach((schema) => {
schema._OWNER = this.database;
const currentSchemaIndex = this.database.schemas.findIndex((item) => item.name === schema.name);
if (currentSchemaIndex !== -1) {
this.database.schemas[currentSchemaIndex] = schema;
}
else {
this.database.schemas.push(schema);
}
});
this.isUpdatingDatabase = false;
return database;
}
// this method updates database and add database to the graph
*updateDatabaseAndGraph() {
if (!this.treeData) {
return;
}
try {
const createDatabase = this.isCreatingNewDatabase &&
!this.editorStore.graphManagerState.graph.databases.includes(this.database);
this.isUpdatingDatabase = true;
const database = (yield flowResult(this.updateDatabase()));
if (createDatabase) {
connection_setStore(this.connection, PackageableElementExplicitReference.create(database));
const packagePath = guaranteeNonNullable(database.package?.name, 'Database package is missing');
yield flowResult(this.editorStore.graphEditorMode.addElement(database, packagePath, false));
}
this.editorStore.applicationStore.notificationService.notifySuccess(`Database successfully updated`);
yield flowResult(this.editorStore
.getGraphEditorMode(GraphEditFormModeState)
.globalCompile({
message: `Can't compile graph after editing database. Redirecting you to text mode`,
}));
}
catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.DATABASE_BUILDER_FAILURE), error);
this.editorStore.applicationStore.notificationService.notifyError(error);
}
finally {
this.isUpdatingDatabase = false;
}
}
}
//# sourceMappingURL=DatabaseBuilderState.js.map