Skip to content

Commit

Permalink
studio: support import execute input
Browse files Browse the repository at this point in the history
  • Loading branch information
akphi committed Feb 14, 2025
1 parent ff1b3b9 commit 02b16fc
Show file tree
Hide file tree
Showing 9 changed files with 281 additions and 6 deletions.
6 changes: 6 additions & 0 deletions .changeset/nine-shirts-call.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@finos/legend-application-data-cube': patch
'@finos/legend-application-studio': patch
'@finos/legend-data-cube': patch
'@finos/legend-graph': patch
---
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
MODEL_IMPORT_NATIVE_INPUT_TYPE,
NativeModelImporterEditorState,
ExternalFormatModelImporterState,
ExecuteInputDebugModelImporterEditorState,
} from '../../../stores/editor/editor-state/ModelImporterState.js';
import { prettyCONSTName } from '@finos/legend-shared';
import {
Expand Down Expand Up @@ -220,6 +221,33 @@ export const ModelImporter = observer(() => {
externalFormatState={modelImportEditorState}
/>
);
} else if (
modelImportEditorState instanceof
ExecuteInputDebugModelImporterEditorState
) {
return (
<PanelContent className="model-loader__editor">
<div className="model-loader__debugger__function-path">
<div className="model-loader__debugger__function-path__label">
Debug Function:
</div>
<input
className="panel__content__form__section__input"
value={modelImportEditorState.functionPath}
onChange={(event) =>
modelImportEditorState.setFunctionPath(event.target.value)
}
/>
</div>
<div className="model-loader__debugger__execute-input">
<CodeEditor
language={CODE_EDITOR_LANGUAGE.JSON}
inputValue={modelImportEditorState.executeInput}
updateInput={(val) => modelImportEditorState.setExecuteInput(val)}
/>
</div>
</PanelContent>
);
}
return null;
};
Expand Down Expand Up @@ -248,6 +276,14 @@ export const ModelImporter = observer(() => {
{prettyCONSTName(inputType)}
</MenuContentItem>
))}
<MenuContentItem
className="model-loader__header__configs__type-option__group__option"
onClick={() =>
modelImporterState.setExecuteInputDebugModelImporter()
}
>
{`Execute Input (DEBUG)`}
</MenuContentItem>
</div>
</div>
{Boolean(externalFormatDescriptions.length) && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,16 @@ import {
} from '@finos/legend-shared';
import { LEGEND_STUDIO_APP_EVENT } from '../../../__lib__/LegendStudioEvent.js';
import type { EditorStore } from '../EditorStore.js';
import type { Entity } from '@finos/legend-storage';
import { generateGAVCoordinates, type Entity } from '@finos/legend-storage';
import { DEFAULT_TAB_SIZE } from '@finos/legend-application';
import type {
ModelImporterExtensionConfiguration,
LegendStudioApplicationPlugin,
} from '../../LegendStudioApplicationPlugin.js';
import {
type ExternalFormatDescription,
GraphEntities,
LegendSDLC,
type PureModel,
SchemaSet,
observe_SchemaSet,
Expand All @@ -51,6 +53,10 @@ import {
externalFormat_schemaSet_setSchemas,
} from '../../graph-modifier/DSL_ExternalFormat_GraphModifierHelper.js';
import { InnerSchemaSetEditorState } from './element-editor-state/external-format/DSL_ExternalFormat_SchemaSetEditorState.js';
import {
ProjectDependency,
UpdateProjectConfigurationCommand,
} from '@finos/legend-server-sdlc';

export enum MODEL_IMPORT_NATIVE_INPUT_TYPE {
ENTITIES = 'ENTITIES',
Expand Down Expand Up @@ -279,6 +285,120 @@ export class NativeModelImporterEditorState extends ModelImporterEditorState {
}
}

export class ExecuteInputDebugModelImporterEditorState extends ModelImporterEditorState {
executeInput = '{}';
functionPath = 'test::Debugger';
readonly loadState = ActionState.create();

constructor(modelImporterState: ModelImporterState) {
super(modelImporterState);

makeObservable(this, {
executeInput: observable,
setExecuteInput: action,

functionPath: observable,
setFunctionPath: action,
});
}

get label(): string {
return `Execute Input [DEBUG]`;
}

get allowHardReplace(): boolean {
return false;
}

get isLoadingDisabled(): boolean {
return this.loadState.isInProgress;
}

setExecuteInput(val: string): void {
this.executeInput = val;
}

setFunctionPath(val: string): void {
this.functionPath = val;
}

async loadModel(): Promise<void> {
this.loadModelActionState.inProgress();
try {
this.editorStore.applicationStore.alertService.setBlockingAlert({
message: 'Loading model...',
prompt: 'Please do not close the application',
showLoading: true,
});
const result =
await this.editorStore.graphManagerState.graphManager.analyzeExecuteInput(
JSON.parse(this.executeInput),
this.functionPath,
);
let entities: Entity[] = [];
let dependencies: ProjectDependency[] = [];
if (result.origin instanceof GraphEntities) {
entities = [...result.origin.entities, ...result.entities];
} else if (result.origin instanceof LegendSDLC) {
entities = [...result.entities];
dependencies = [
new ProjectDependency(
generateGAVCoordinates(
result.origin.groupId,
result.origin.artifactId,
undefined,
),
result.origin.versionId,
),
];
}
const message = `loading entities from ${
this.editorStore.applicationStore.config.appName
} [${this.modelImporterState.replace ? `potentially affected ` : ''} ${
entities.length
} entities]`;
await this.editorStore.sdlcServerClient.updateEntities(
this.editorStore.sdlcState.activeProject.projectId,
this.editorStore.sdlcState.activeWorkspace,
{ replace: this.modelImporterState.replace, entities, message },
);

const currentProjectConfiguration =
this.editorStore.projectConfigurationEditorState.originalConfig;
const updateProjectConfigurationCommand =
new UpdateProjectConfigurationCommand(
currentProjectConfiguration.groupId,
currentProjectConfiguration.artifactId,
currentProjectConfiguration.projectStructureVersion,
`update project configuration from ${this.editorStore.applicationStore.config.appName}`,
);
updateProjectConfigurationCommand.projectDependenciesToAdd = dependencies;
updateProjectConfigurationCommand.projectDependenciesToRemove =
currentProjectConfiguration.projectDependencies;
await flowResult(
this.editorStore.projectConfigurationEditorState.updateProjectConfiguration(
updateProjectConfigurationCommand,
),
);
this.editorStore.applicationStore.navigationService.navigator.reload({
ignoreBlocking: true,
});
} catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.logService.error(
LogEvent.create(LEGEND_STUDIO_APP_EVENT.MODEL_LOADER_FAILURE),
error,
);
this.editorStore.applicationStore.notificationService.notifyError(error);
} finally {
this.loadModelActionState.complete();
this.editorStore.applicationStore.alertService.setBlockingAlert(
undefined,
);
}
}
}

export abstract class ExtensionModelImportRendererState {
importerState: ModelImporterState;

Expand Down Expand Up @@ -541,4 +661,13 @@ export class ModelImporterState extends EditorState {
return modelImporterEditorState;
}
}

setExecuteInputDebugModelImporter() {
const executeInputDebugModelImporterEditorState =
this.modelImportEditorState instanceof
ExecuteInputDebugModelImporterEditorState
? this.modelImportEditorState
: new ExecuteInputDebugModelImporterEditorState(this);
this.setImportEditorState(executeInputDebugModelImporterEditorState);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -200,5 +200,28 @@
&__editor {
overflow-y: hidden;
position: relative;
background: var(--color-dark-grey-50);
}

&__debugger__function-path {
@include flexVCenter;

border-bottom: 0.1rem solid var(--color-dark-grey-250);
height: 4rem;
padding: 0.5rem;
}

&__debugger__function-path__label {
@include flexVCenter;

flex-shrink: 0;
padding: 0 1rem;
font-size: 1.2rem;
color: var(--color-light-grey-400);
margin-right: 0.5rem;
}

&__debugger__execute-input {
height: calc(100% - 4rem);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

import { observer } from 'mobx-react-lite';
import { DataCubeIcon, useDropdownMenu } from '@finos/legend-art';
import { DataCubeIcon } from '@finos/legend-art';
import {
type ColDef,
type ColDefField,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,14 @@ export abstract class AbstractPureGraphManager {

abstract serializeExecutionNode(executionNode: ExecutionNode): object;

abstract analyzeExecuteInput(
data: PlainObject,
functionPath: string,
): Promise<{
origin: GraphDataOrigin;
entities: Entity[];
}>;

// ------------------------------------------- Query -------------------------------------------

abstract searchQueries(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,11 @@ import {
getNullableIDFromTestable,
getNullableTestable,
} from '../../../helpers/DSL_Data_GraphManagerHelper.js';
import { pruneSourceInformation } from '../../../../graph/MetaModelUtils.js';
import {
extractElementNameFromPath,
extractPackagePathFromPath,
pruneSourceInformation,
} from '../../../../graph/MetaModelUtils.js';
import {
V1_buildModelCoverageAnalysisResult,
V1_MappingModelCoverageAnalysisInput,
Expand Down Expand Up @@ -347,6 +351,11 @@ import type {
PersistentDataCube,
} from '../../../action/query/PersistentDataCube.js';
import { V1_QueryParameterValue } from './engine/query/V1_Query.js';
import { V1_Multiplicity } from './model/packageableElements/domain/V1_Multiplicity.js';
import {
V1_buildFunctionSignature,
V1_createGenericTypeWithElementPath,
} from './helpers/V1_DomainHelper.js';

class V1_PureModelContextDataIndex {
elements: V1_PackageableElement[] = [];
Expand Down Expand Up @@ -3044,6 +3053,51 @@ export class V1_PureGraphManager extends AbstractPureGraphManager {
return this.engine.cancelUserExecutions(broadcastToCluster);
}

override async analyzeExecuteInput(
data: PlainObject,
functionPath: string,
): Promise<{ origin: GraphDataOrigin; entities: Entity[] }> {
const executeInput = V1_ExecuteInput.serialization.fromJson(data);
let origin: GraphDataOrigin | undefined;
if (executeInput.model instanceof V1_PureModelContextData) {
origin = new GraphEntities(
this.pureModelContextDataToEntities(executeInput.model),
);
} else if (
executeInput.model instanceof V1_PureModelContextPointer &&
executeInput.model.sdlcInfo instanceof V1_LegendSDLC
) {
origin = new LegendSDLC(
executeInput.model.sdlcInfo.groupId,
executeInput.model.sdlcInfo.artifactId,
executeInput.model.sdlcInfo.version,
);
}

// TODO: we dont support runtime, mapping, parameter values,
// we assume the function does not take parameters and use ->from() to
// set execution context
const func = new V1_ConcreteFunctionDefinition();
func.name = extractElementNameFromPath(functionPath);
func.package = extractPackagePathFromPath(functionPath) ?? 'test';
func.body = executeInput.function.body as object[];
func.returnMultiplicity = V1_Multiplicity.ZERO_ONE;
func.returnGenericType = V1_createGenericTypeWithElementPath(
CORE_PURE_PATH.ANY,
);
func.name = V1_buildFunctionSignature(func);

// TODO: we can also add a new Text element with the content of
// the execute input and instruction to go to debugger function
if (!origin) {
throw new Error(`Can't analyze execute input: no origin found`);
}
return {
origin,
entities: [this.elementProtocolToEntity(func)],
};
}

// --------------------------------------------- Query ---------------------------------------------

async searchQueries(
Expand Down
Loading

0 comments on commit 02b16fc

Please sign in to comment.