@finos/legend-application-studio
Version:
Legend Studio application core
875 lines (828 loc) • 32.6 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 {
action,
computed,
flow,
flowResult,
makeObservable,
observable,
} from 'mobx';
import { GRAPH_EDITOR_MODE } from './EditorConfig.js';
import {
type GeneratorFn,
type PlainObject,
LogEvent,
UnsupportedOperationError,
assertErrorThrown,
assertTrue,
isNonNullable,
NetworkClientError,
guaranteeNonNullable,
StopWatch,
filterByType,
ActionState,
} from '@finos/legend-shared';
import type { EditorStore } from './EditorStore.js';
import { ElementEditorState } from './editor-state/element-editor-state/ElementEditorState.js';
import { GraphGenerationState } from './editor-state/GraphGenerationState.js';
import { MODEL_IMPORT_NATIVE_INPUT_TYPE } from './editor-state/ModelImporterState.js';
import type { DSL_LegendStudioApplicationPlugin_Extension } from '../LegendStudioApplicationPlugin.js';
import { type Entity, EntitiesWithOrigin } from '@finos/legend-storage';
import {
type EntityChange,
type ProjectDependency,
ProjectConfiguration,
applyEntityChanges,
} from '@finos/legend-server-sdlc';
import {
type ProjectDependencyGraphReport,
ProjectVersionEntities,
ProjectDependencyCoordinates,
RawProjectDependencyReport,
buildDependencyReport,
} from '@finos/legend-server-depot';
import {
type EngineError,
type PackageableElement,
type CompilationWarning,
type PureModel,
type FunctionActivatorConfiguration,
type RelationalDatabaseTypeConfiguration,
GRAPH_MANAGER_EVENT,
Package,
Profile,
PrimitiveType,
Enumeration,
Class,
Association,
Mapping,
ConcreteFunctionDefinition,
Service,
FlatData,
PackageableConnection,
PackageableRuntime,
FileGenerationSpecification,
GenerationSpecification,
Measure,
Unit,
Database,
SectionIndex,
DependencyGraphBuilderError,
GraphDataDeserializationError,
DataElement,
createGraphBuilderReport,
ExecutionEnvironmentInstance,
SnowflakeApp,
GraphEntities,
HostedService,
} from '@finos/legend-graph';
import { CONFIGURATION_EDITOR_TAB } from './editor-state/project-configuration-editor-state/ProjectConfigurationEditorState.js';
import { PACKAGEABLE_ELEMENT_TYPE } from './utils/ModelClassifierUtils.js';
import { LEGEND_STUDIO_APP_EVENT } from '../../__lib__/LegendStudioEvent.js';
import { LEGEND_STUDIO_SETTING_KEY } from '../../__lib__/LegendStudioSetting.js';
import { LegendStudioTelemetryHelper } from '../../__lib__/LegendStudioTelemetryHelper.js';
export enum GraphBuilderStatus {
SUCCEEDED = 'SUCCEEDED',
FAILED = 'FAILED',
REDIRECTED_TO_TEXT_MODE = 'REDIRECTED_TO_TEXT_MODE',
}
export enum GraphCompilationOutcome {
SKIPPED = 'SKIPPED',
SUCCEEDED = 'SUCCEEDED',
FAILED = 'FAILED',
}
export interface GraphBuilderResult {
status: GraphBuilderStatus;
error?: Error;
}
export type Problem = CompilationWarning | EngineError;
export class EditorGraphState {
readonly editorStore: EditorStore;
readonly graphGenerationState: GraphGenerationState;
isInitializingGraph = false;
isRunningGlobalCompile = false;
isRunningGlobalGenerate = false;
isApplicationLeavingGraphEditMode = false;
isUpdatingGraph = false; // critical synchronous update to refresh the graph
isUpdatingApplication = false; // including graph update and async operations such as change detection
functionActivatorConfigurations: FunctionActivatorConfiguration[] = [];
relationalDatabseTypeConfigurations:
| RelationalDatabaseTypeConfiguration[]
| undefined;
warnings: CompilationWarning[] = [];
error: EngineError | undefined;
compilationResultEntities: Entity[] = [];
enableStrictMode: boolean;
mostRecentCompilationGraphHash: string | undefined = undefined;
mostRecentCompilationOutcome: GraphCompilationOutcome | undefined = undefined;
constructor(editorStore: EditorStore) {
makeObservable(this, {
isInitializingGraph: observable,
isRunningGlobalCompile: observable,
isRunningGlobalGenerate: observable,
isApplicationLeavingGraphEditMode: observable,
isUpdatingGraph: observable,
isUpdatingApplication: observable,
functionActivatorConfigurations: observable,
mostRecentCompilationGraphHash: observable,
mostRecentCompilationOutcome: observable,
warnings: observable,
error: observable,
enableStrictMode: observable,
relationalDatabseTypeConfigurations: observable,
problems: computed,
areProblemsStale: computed,
isApplicationUpdateOperationIsRunning: computed,
clearProblems: action,
setEnableStrictMode: action,
setMostRecentCompilationGraphHash: action,
fetchAvailableRelationalDatabseTypeConfigurations: flow,
fetchAvailableFunctionActivatorConfigurations: flow,
buildGraph: flow,
loadEntityChangesToGraph: flow,
updateGenerationGraphAndApplication: flow,
rebuildDependencies: flow,
buildGraphForLazyText: flow,
});
this.editorStore = editorStore;
this.graphGenerationState = new GraphGenerationState(this.editorStore);
this.enableStrictMode =
this.editorStore.applicationStore.settingService.getBooleanValue(
LEGEND_STUDIO_SETTING_KEY.EDITOR_STRICT_MODE,
) ?? false;
}
get problems(): Problem[] {
return [this.error, ...this.warnings].filter(isNonNullable);
}
/**
* This function is temporary. There is no good way to detect if a problem not coming from
* the main graph at the moment. In text mode, we can rely on the fact that the source information
* has line 0 column 0. But this is not the case for form mode, so this is just temporary
* to help with text-mode.
*/
TEMPORARY__removeDependencyProblems(
problems: Problem[] | CompilationWarning[],
): Problem[] | CompilationWarning[] {
return problems.filter((problem) => {
if (problem.sourceInformation) {
return !(
problem.sourceInformation.startLine === 0 &&
problem.sourceInformation.startColumn === 0 &&
problem.sourceInformation.endLine === 0 &&
problem.sourceInformation.endColumn === 0
);
}
return true;
});
}
setMostRecentCompilationGraphHash(val: string | undefined): void {
this.mostRecentCompilationGraphHash = val;
}
setMostRecentCompilationOutcome(
val: GraphCompilationOutcome | undefined,
): void {
this.mostRecentCompilationOutcome = val;
}
get areProblemsStale(): boolean {
return (
this.mostRecentCompilationGraphHash !==
this.editorStore.graphEditorMode.getCurrentGraphHash()
);
}
get isApplicationUpdateOperationIsRunning(): boolean {
return (
this.isRunningGlobalCompile ||
this.isRunningGlobalGenerate ||
this.isApplicationLeavingGraphEditMode ||
this.isUpdatingApplication ||
this.isInitializingGraph
);
}
checkIfApplicationUpdateOperationIsRunning(): boolean {
if (this.isRunningGlobalGenerate) {
this.editorStore.applicationStore.notificationService.notifyWarning(
'Please wait for model generation to complete',
);
return true;
}
if (this.isRunningGlobalCompile) {
this.editorStore.applicationStore.notificationService.notifyWarning(
'Please wait for graph compilation to complete',
);
return true;
}
if (this.isApplicationLeavingGraphEditMode) {
this.editorStore.applicationStore.notificationService.notifyWarning(
'Please wait for editor to leave edit mode completely',
);
return true;
}
if (this.isUpdatingApplication) {
this.editorStore.applicationStore.notificationService.notifyWarning(
'Please wait for editor state to rebuild',
);
return true;
}
if (this.isInitializingGraph) {
this.editorStore.applicationStore.notificationService.notifyWarning(
'Please wait for editor initialization to complete',
);
return true;
}
return false;
}
clearProblems(): void {
this.error = undefined;
this.editorStore.tabManagerState.tabs
.filter(filterByType(ElementEditorState))
.forEach((editorState) => editorState.clearCompilationError());
this.mostRecentCompilationGraphHash = undefined;
this.warnings = [];
}
setEnableStrictMode(val: boolean): void {
this.enableStrictMode = val;
}
*fetchAvailableFunctionActivatorConfigurations(): GeneratorFn<void> {
try {
this.functionActivatorConfigurations =
(yield this.editorStore.graphManagerState.graphManager.getAvailableFunctionActivatorConfigurations(
this.editorStore.graphManagerState.coreModel,
this.editorStore.graphManagerState.systemModel,
)) as FunctionActivatorConfiguration[];
} catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.logService.error(
LogEvent.create(LEGEND_STUDIO_APP_EVENT.GENERIC_FAILURE),
error,
);
this.editorStore.applicationStore.notificationService.notifyError(error);
}
}
*fetchAvailableRelationalDatabseTypeConfigurations(): GeneratorFn<void> {
try {
this.relationalDatabseTypeConfigurations =
(yield this.editorStore.graphManagerState.graphManager.getAvailableRelationalDatabaseTypeConfigurations()) as
| RelationalDatabaseTypeConfiguration[]
| undefined;
} catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.logService.error(
LogEvent.create(LEGEND_STUDIO_APP_EVENT.GENERIC_FAILURE),
error,
);
this.editorStore.applicationStore.notificationService.notifyError(error);
}
}
findRelationalDatabaseTypeConfiguration(
type: string,
): RelationalDatabaseTypeConfiguration | undefined {
return this.relationalDatabseTypeConfigurations?.find(
(aFlow) => aFlow.type === type,
);
}
*buildGraph(entities: Entity[]): GeneratorFn<GraphBuilderResult> {
try {
this.isInitializingGraph = true;
const stopWatch = new StopWatch();
// reset
this.editorStore.graphManagerState.resetGraph();
// fetch and build dependencies
stopWatch.record();
const dependencyManager =
this.editorStore.graphManagerState.graphManager.createDependencyManager();
this.editorStore.graphManagerState.graph.dependencyManager =
dependencyManager;
this.editorStore.graphManagerState.dependenciesBuildState.setMessage(
`Fetching dependencies...`,
);
const dependencyEntitiesIndex = (yield flowResult(
this.getIndexedDependencyEntities(),
)) as Map<string, EntitiesWithOrigin>;
stopWatch.record(GRAPH_MANAGER_EVENT.FETCH_GRAPH_DEPENDENCIES__SUCCESS);
const dependency_buildReport = createGraphBuilderReport();
yield this.editorStore.graphManagerState.graphManager.buildDependencies(
this.editorStore.graphManagerState.coreModel,
this.editorStore.graphManagerState.systemModel,
dependencyManager,
dependencyEntitiesIndex,
this.editorStore.graphManagerState.dependenciesBuildState,
{},
dependency_buildReport,
);
dependency_buildReport.timings[
GRAPH_MANAGER_EVENT.FETCH_GRAPH_DEPENDENCIES__SUCCESS
] = stopWatch.getRecord(
GRAPH_MANAGER_EVENT.FETCH_GRAPH_DEPENDENCIES__SUCCESS,
);
// build graph
const graph_buildReport = createGraphBuilderReport();
yield this.editorStore.graphManagerState.graphManager.buildGraph(
this.editorStore.graphManagerState.graph,
entities,
this.editorStore.graphManagerState.graphBuildState,
{
TEMPORARY__preserveSectionIndex:
this.editorStore.applicationStore.config.options
.TEMPORARY__preserveSectionIndex,
strict: this.enableStrictMode,
},
graph_buildReport,
);
// build generations
const generation_buildReport = createGraphBuilderReport();
yield this.editorStore.graphManagerState.graphManager.buildGenerations(
this.editorStore.graphManagerState.graph,
this.graphGenerationState.generatedEntities,
this.editorStore.graphManagerState.generationsBuildState,
{},
generation_buildReport,
);
// report
stopWatch.record(GRAPH_MANAGER_EVENT.INITIALIZE_GRAPH__SUCCESS);
const graphBuilderReportData = {
timings:
this.editorStore.applicationStore.timeService.finalizeTimingsRecord(
stopWatch,
),
dependencies: dependency_buildReport,
dependenciesCount:
this.editorStore.graphManagerState.graph.dependencyManager
.numberOfDependencies,
graph: graph_buildReport,
generations: generation_buildReport,
generationsCount: this.graphGenerationState.generatedEntities.size,
};
LegendStudioTelemetryHelper.logEvent_GraphInitializationSucceeded(
this.editorStore.applicationStore.telemetryService,
graphBuilderReportData,
);
this.editorStore.applicationStore.logService.info(
LogEvent.create(GRAPH_MANAGER_EVENT.INITIALIZE_GRAPH__SUCCESS),
graphBuilderReportData,
);
// add generation specification if model generation elements exists in graph and no generation specification
yield flowResult(
this.graphGenerationState.possiblyAddMissingGenerationSpecifications(),
);
return {
status: GraphBuilderStatus.SUCCEEDED,
};
} catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.logService.error(
LogEvent.create(GRAPH_MANAGER_EVENT.GRAPH_BUILDER_FAILURE),
error,
);
if (error instanceof DependencyGraphBuilderError) {
this.editorStore.graphManagerState.graphBuildState.fail();
// no recovery if dependency models cannot be built, this makes assumption that all dependencies models are compiled successfully
// TODO: we might want to handle this more gracefully when we can show people the dependency model element in the future
this.editorStore.applicationStore.notificationService.notifyError(
`Can't initialize dependency models. Error: ${error.message}`,
);
const projectConfigurationEditorState =
this.editorStore.projectConfigurationEditorState;
projectConfigurationEditorState.setSelectedTab(
CONFIGURATION_EDITOR_TAB.PROJECT_DEPENDENCIES,
);
this.editorStore.tabManagerState.openTab(
projectConfigurationEditorState,
);
} else if (error instanceof GraphDataDeserializationError) {
// if something goes wrong with de-serialization, redirect to model importer to fix
this.redirectToModelImporterForDebugging(error);
} else if (error instanceof NetworkClientError) {
this.editorStore.graphManagerState.graphBuildState.fail();
this.editorStore.applicationStore.notificationService.notifyWarning(
`Can't build graph. Error: ${error.message}`,
);
} else {
// TODO: we should split this into 2 notifications when we support multiple notifications
this.editorStore.applicationStore.notificationService.notifyError(
`Can't build graph. Redirected to text mode for debugging. Error: ${error.message}`,
);
this.editorStore.applicationStore.logService.error(
LogEvent.create(GRAPH_MANAGER_EVENT.GRAPH_BUILDER_FAILURE),
error,
);
try {
yield flowResult(
this.editorStore.switchModes(GRAPH_EDITOR_MODE.GRAMMAR_TEXT, {
isGraphBuildFailure: true,
}),
);
} catch (error2) {
assertErrorThrown(error2);
this.editorStore.applicationStore.logService.error(
LogEvent.create(GRAPH_MANAGER_EVENT.GRAPH_BUILDER_FAILURE),
error2,
);
if (error2 instanceof NetworkClientError) {
// in case the server cannot even transform the JSON due to corrupted protocol, we can redirect to model importer
this.redirectToModelImporterForDebugging(error2);
return {
status: GraphBuilderStatus.FAILED,
error: error2,
};
}
if (error2 instanceof Error) {
return {
status: GraphBuilderStatus.FAILED,
error: error2,
};
}
}
return {
status: GraphBuilderStatus.REDIRECTED_TO_TEXT_MODE,
error,
};
}
return {
status: GraphBuilderStatus.FAILED,
error,
};
} finally {
this.isInitializingGraph = false;
}
}
*buildGraphForLazyText(): GeneratorFn<void> {
this.isInitializingGraph = true;
const stopWatch = new StopWatch();
// reset
this.editorStore.graphManagerState.resetGraph();
// fetch and build dependencies
stopWatch.record();
const dependencyManager =
this.editorStore.graphManagerState.graphManager.createDependencyManager();
this.editorStore.graphManagerState.graph.dependencyManager =
dependencyManager;
this.editorStore.graphManagerState.dependenciesBuildState.setMessage(
`Fetching dependencies...`,
);
const dependencyEntitiesIndex = (yield flowResult(
this.getIndexedDependencyEntities(),
)) as Map<string, EntitiesWithOrigin>;
stopWatch.record(GRAPH_MANAGER_EVENT.FETCH_GRAPH_DEPENDENCIES__SUCCESS);
dependencyManager.initialize(dependencyEntitiesIndex);
// set dependency manager graph origin to entities
if (dependencyManager.origin === undefined) {
dependencyManager.setOrigin(
new GraphEntities(
Array.from(dependencyEntitiesIndex.values())
.map((e) => e.entities)
.flat(),
),
);
}
this.isInitializingGraph = false;
this.editorStore.graphManagerState.dependenciesBuildState.sync(
ActionState.create().pass(),
);
}
private redirectToModelImporterForDebugging(error: Error): void {
if (this.editorStore.isInConflictResolutionMode) {
this.editorStore.applicationStore.alertService.setBlockingAlert({
message: `Can't de-serialize graph model from entities`,
prompt: `Please refresh the application and abort conflict resolution`,
});
return;
}
this.editorStore.applicationStore.notificationService.notifyWarning(
`Can't de-serialize graph model from entities. Redirected to model importer for debugging. Error: ${error.message}`,
);
const nativeImporterState =
this.editorStore.modelImporterState.setNativeImportType(
MODEL_IMPORT_NATIVE_INPUT_TYPE.ENTITIES,
);
// Making an async call
nativeImporterState.loadCurrentProjectEntities();
this.editorStore.tabManagerState.openTab(
this.editorStore.modelImporterState,
);
}
/**
* Loads entity changes to graph and updates application.
*/
*loadEntityChangesToGraph(
changes: EntityChange[],
baseEntities: Entity[] | undefined,
): GeneratorFn<void> {
try {
assertTrue(
this.editorStore.graphEditorMode.mode === GRAPH_EDITOR_MODE.FORM,
`Can't apply entity changes: operation only supported in form mode`,
);
const entities =
baseEntities ??
this.editorStore.graphManagerState.graph.allOwnElements.map((element) =>
this.editorStore.graphManagerState.graphManager.elementToEntity(
element,
),
);
const modifiedEntities = applyEntityChanges(entities, changes);
yield flowResult(
this.editorStore.graphEditorMode.updateGraphAndApplication(
modifiedEntities,
),
);
} catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.notificationService.notifyError(
`Can't load entity changes: ${error.message}`,
);
}
}
/**
* NOTE: this can post memory-leak issue if we start having immutable elements referencing current graph elements:
* e.g. subclass analytics on the immutable class, etc.
*
* @risk memory-leak
*/
*rebuildDependencies(newGraph: PureModel): GeneratorFn<void> {
if (
this.editorStore.graphManagerState.dependenciesBuildState.hasSucceeded
) {
newGraph.dependencyManager =
this.editorStore.graphManagerState.graph.dependencyManager;
} else {
this.editorStore.projectConfigurationEditorState.setProjectConfiguration(
ProjectConfiguration.serialization.fromJson(
(yield this.editorStore.sdlcServerClient.getConfiguration(
this.editorStore.sdlcState.activeProject.projectId,
this.editorStore.sdlcState.activeWorkspace,
)) as PlainObject<ProjectConfiguration>,
),
);
const dependencyManager =
this.editorStore.graphManagerState.graphManager.createDependencyManager();
newGraph.dependencyManager = dependencyManager;
const dependenciesBuildState = ActionState.create();
yield this.editorStore.graphManagerState.graphManager.buildDependencies(
this.editorStore.graphManagerState.coreModel,
this.editorStore.graphManagerState.systemModel,
dependencyManager,
(yield flowResult(this.getIndexedDependencyEntities())) as Map<
string,
EntitiesWithOrigin
>,
dependenciesBuildState,
);
// NOTE: here we don't want to modify the current graph build state directly
// instead, we quietly run this in the background and then sync it with the current build state
this.editorStore.graphManagerState.dependenciesBuildState.sync(
dependenciesBuildState,
);
}
}
/**
* Used to update generation model and generation graph using the generated entities
* does not alter the main or dependency model
*/
*updateGenerationGraphAndApplication(): GeneratorFn<void> {
assertTrue(
this.editorStore.graphManagerState.graphBuildState.hasSucceeded &&
this.editorStore.graphManagerState.dependenciesBuildState.hasSucceeded,
'Both main model and dependencies must be processed to built generation graph',
);
this.isUpdatingApplication = true;
try {
this.editorStore.tabManagerState.cacheAndClose({ cacheGeneration: true });
yield flowResult(
this.editorStore.graphManagerState.graph.generationModel.dispose(),
);
// we reset the generation model
this.editorStore.graphManagerState.graph.generationModel =
this.editorStore.graphManagerState.graphManager.createGenerationModel();
yield this.editorStore.graphManagerState.graphManager.buildGenerations(
this.editorStore.graphManagerState.graph,
this.graphGenerationState.generatedEntities,
this.editorStore.graphManagerState.generationsBuildState,
);
this.editorStore.explorerTreeState.reprocess();
this.editorStore.tabManagerState.recoverTabs();
} catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.logService.error(
LogEvent.create(GRAPH_MANAGER_EVENT.GRAPH_BUILDER_FAILURE),
error,
);
this.editorStore.applicationStore.notificationService.notifyError(
`Can't build graph: ${error.message}`,
);
} finally {
this.isUpdatingApplication = false;
}
}
async getIndexedDependencyEntities(): Promise<
Map<string, EntitiesWithOrigin>
> {
const dependencyEntitiesIndex = new Map<string, EntitiesWithOrigin>();
const currentConfiguration =
this.editorStore.projectConfigurationEditorState
.currentProjectConfiguration;
try {
if (currentConfiguration.projectDependencies.length) {
const dependencyCoordinates =
await this.buildProjectDependencyCoordinates(
currentConfiguration.projectDependencies,
);
// NOTE: if A@v1 is transitive dependencies of 2 or more
// direct dependencies, metadata server will take care of deduplication
const dependencyEntitiesJson =
await this.editorStore.depotServerClient.collectDependencyEntities(
dependencyCoordinates.map((e) =>
ProjectDependencyCoordinates.serialization.toJson(e),
),
true,
true,
);
const dependencyEntities = dependencyEntitiesJson.map((e) =>
ProjectVersionEntities.serialization.fromJson(e),
);
const dependencyProjects = new Map<string, Set<string>>();
dependencyEntities.forEach((dependencyInfo) => {
const projectId = dependencyInfo.id;
// There are a few validations that must be done:
// 1. Unlike above, if in the depdendency graph, we have both A@v1 and A@v2
// then we need to throw. Both SDLC and metadata server should handle this
// validation, but haven't, so for now, we can do that in Studio.
// 2. Same as the previous case, but for version-to-version transformation
// This is a special case that needs handling, right now, SDLC does auto
// healing, by scanning all the path and convert them into versioned path
// e.g. model::someClass -> project1::v1_0_0::model::someClass
// But this is a rare and advanced use-case which we will not attempt to handle now.
if (dependencyProjects.has(projectId)) {
dependencyProjects.get(projectId)?.add(dependencyInfo.versionId);
} else {
dependencyProjects.set(
dependencyInfo.id,
new Set<string>([dependencyInfo.versionId]),
);
}
dependencyEntitiesIndex.set(
dependencyInfo.id,
new EntitiesWithOrigin(
dependencyInfo.groupId,
dependencyInfo.artifactId,
dependencyInfo.versionId,
dependencyInfo.entities,
),
);
});
const hasConflicts = Array.from(dependencyProjects.entries()).find(
([k, v]) => v.size > 1,
);
if (hasConflicts) {
let dependencyInfo: ProjectDependencyGraphReport | undefined;
try {
const dependencyTree =
await this.editorStore.depotServerClient.analyzeDependencyTree(
dependencyCoordinates.map((e) =>
ProjectDependencyCoordinates.serialization.toJson(e),
),
);
const rawReport =
RawProjectDependencyReport.serialization.fromJson(dependencyTree);
dependencyInfo = buildDependencyReport(rawReport);
} catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.logService.error(
LogEvent.create(LEGEND_STUDIO_APP_EVENT.DEPOT_MANAGER_FAILURE),
error,
);
}
const startErrorMessage =
'Depending on multiple versions of a project is not supported. Found conflicts:\n';
if (dependencyInfo?.conflicts.length) {
const conflictingProjects = dependencyInfo.conflicts
.map(
(c) =>
`project: ${c.groupId}:${c.artifactId}\nversions:[${c.versions
.map((v) => v.versionId)
.join(',')}]`,
)
.join('\n');
throw new UnsupportedOperationError(
startErrorMessage + conflictingProjects,
);
} else {
const conflictMessages = Array.from(dependencyProjects.entries())
.filter(([, v]) => v.size > 1)
.map(
([k, v]) =>
`project: ${k}\n versions: ${Array.from(v.values()).join(
',',
)}`,
)
.join('\n\n');
throw new UnsupportedOperationError(
startErrorMessage + conflictMessages,
);
}
}
}
} catch (error) {
assertErrorThrown(error);
const message = `Can't acquire dependency entitites. Error: ${error.message}`;
this.editorStore.applicationStore.logService.error(
LogEvent.create(GRAPH_MANAGER_EVENT.GRAPH_BUILDER_FAILURE),
message,
);
this.editorStore.applicationStore.notificationService.notifyError(error);
throw new DependencyGraphBuilderError(error);
}
return dependencyEntitiesIndex;
}
async buildProjectDependencyCoordinates(
projectDependencies: ProjectDependency[],
): Promise<ProjectDependencyCoordinates[]> {
return Promise.all(
projectDependencies.map(async (dep) =>
Promise.resolve(
new ProjectDependencyCoordinates(
guaranteeNonNullable(dep.groupId),
guaranteeNonNullable(dep.artifactId),
dep.versionId,
),
),
),
);
}
// -------------------------------------------------- UTILITIES -----------------------------------------------------
getPackageableElementType(element: PackageableElement): string {
if (element instanceof PrimitiveType) {
return PACKAGEABLE_ELEMENT_TYPE.PRIMITIVE;
} else if (element instanceof Package) {
return PACKAGEABLE_ELEMENT_TYPE.PACKAGE;
} else if (element instanceof Class) {
return PACKAGEABLE_ELEMENT_TYPE.CLASS;
} else if (element instanceof Association) {
return PACKAGEABLE_ELEMENT_TYPE.ASSOCIATION;
} else if (element instanceof Enumeration) {
return PACKAGEABLE_ELEMENT_TYPE.ENUMERATION;
} else if (element instanceof Measure) {
return PACKAGEABLE_ELEMENT_TYPE.MEASURE;
} else if (element instanceof Unit) {
return PACKAGEABLE_ELEMENT_TYPE.UNIT;
} else if (element instanceof Profile) {
return PACKAGEABLE_ELEMENT_TYPE.PROFILE;
} else if (element instanceof ConcreteFunctionDefinition) {
return PACKAGEABLE_ELEMENT_TYPE.FUNCTION;
} else if (element instanceof FlatData) {
return PACKAGEABLE_ELEMENT_TYPE.FLAT_DATA_STORE;
} else if (element instanceof Database) {
return PACKAGEABLE_ELEMENT_TYPE.DATABASE;
} else if (element instanceof Mapping) {
return PACKAGEABLE_ELEMENT_TYPE.MAPPING;
} else if (element instanceof Service) {
return PACKAGEABLE_ELEMENT_TYPE.SERVICE;
} else if (element instanceof PackageableConnection) {
return PACKAGEABLE_ELEMENT_TYPE.CONNECTION;
} else if (element instanceof PackageableRuntime) {
return PACKAGEABLE_ELEMENT_TYPE.RUNTIME;
} else if (element instanceof FileGenerationSpecification) {
return PACKAGEABLE_ELEMENT_TYPE.FILE_GENERATION;
} else if (element instanceof GenerationSpecification) {
return PACKAGEABLE_ELEMENT_TYPE.GENERATION_SPECIFICATION;
} else if (element instanceof SectionIndex) {
return PACKAGEABLE_ELEMENT_TYPE.SECTION_INDEX;
} else if (element instanceof DataElement) {
return PACKAGEABLE_ELEMENT_TYPE.DATA;
} else if (element instanceof ExecutionEnvironmentInstance) {
return PACKAGEABLE_ELEMENT_TYPE.EXECUTION_ENVIRONMENT;
} else if (element instanceof SnowflakeApp) {
return PACKAGEABLE_ELEMENT_TYPE.SNOWFLAKE_APP;
} else if (element instanceof HostedService) {
return PACKAGEABLE_ELEMENT_TYPE.HOSTED_SERVICE;
}
const extraElementTypeLabelGetters = this.editorStore.pluginManager
.getApplicationPlugins()
.flatMap(
(plugin) =>
(
plugin as DSL_LegendStudioApplicationPlugin_Extension
).getExtraElementClassifiers?.() ?? [],
);
for (const labelGetter of extraElementTypeLabelGetters) {
const label = labelGetter(element);
if (label) {
return label;
}
}
return PACKAGEABLE_ELEMENT_TYPE.INTERNAL__UnknownElement;
}
}