@finos/legend-application-studio
Version:
Legend Studio application core
380 lines • 17.3 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 { observable, action, flowResult, makeObservable, flow } from 'mobx';
import { LEGEND_STUDIO_APP_EVENT } from '../../../__lib__/LegendStudioEvent.js';
import { uuid, assertErrorThrown, LogEvent, ActionState, filterByType, } from '@finos/legend-shared';
import { WorkflowJob, Workflow } from '@finos/legend-server-sdlc';
export class WorkflowExplorerTreeNodeData {
isSelected;
isOpen;
id;
label;
childrenIds;
constructor(id, label) {
this.id = id;
this.label = label;
}
}
const getWorkflowNodeId = (workflowId) => `workflow_${workflowId}`;
const getJobId = (jobId) => `job_${jobId}`;
export class WorkflowTreeNodeData extends WorkflowExplorerTreeNodeData {
workflow;
constructor(workflow) {
super(getWorkflowNodeId(workflow.id), workflow.id);
this.workflow = workflow;
}
}
export class WorkflowJobTreeNodeData extends WorkflowExplorerTreeNodeData {
workflowJob;
constructor(workflowJob) {
super(getJobId(workflowJob.id), workflowJob.name);
this.workflowJob = workflowJob;
}
}
const addWorkflowJobNodeToTree = (workflowJob, workflowNode, treeData) => {
const node = new WorkflowJobTreeNodeData(workflowJob);
if (workflowNode.childrenIds) {
workflowNode.childrenIds.push(node.id);
}
else {
workflowNode.childrenIds = [node.id];
}
treeData.nodes.set(node.id, node);
return node;
};
const updateWorkflowJobData = (workflowJobs, workflowId, treeData) => {
const workflowNode = treeData.nodes.get(getWorkflowNodeId(workflowId));
if (workflowNode instanceof WorkflowTreeNodeData) {
workflowNode.childrenIds?.forEach((id) => treeData.nodes.delete(id));
workflowNode.childrenIds = [];
workflowJobs.forEach((job) => addWorkflowJobNodeToTree(job, workflowNode, treeData));
}
};
export class WorkflowLogState {
editorStore;
workflowManagerState;
fetchJobLogState = ActionState.create();
job;
logs;
constructor(editorStore, workflowManagerState, job, logs) {
makeObservable(this, {
job: observable,
logs: observable,
setLogs: action,
setJob: action,
closeModal: action,
refreshJobLogs: flow,
viewJobLogs: flow,
});
this.editorStore = editorStore;
this.workflowManagerState = workflowManagerState;
this.job = job;
this.logs = logs ?? '';
}
setLogs(val) {
this.logs = val;
}
setJob(val) {
this.job = val;
}
closeModal() {
this.setJob(undefined);
this.setLogs('');
}
*refreshJobLogs(workflowJob) {
try {
this.fetchJobLogState.inProgress();
const job = (yield flowResult(this.workflowManagerState.getJob(workflowJob)));
this.setJob(job);
const logs = (yield flowResult(this.workflowManagerState.getJobLogs(workflowJob)));
this.setLogs(logs);
this.fetchJobLogState.pass();
}
catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE), error);
this.editorStore.applicationStore.notificationService.notifyError(error);
this.fetchJobLogState.fail();
}
}
*viewJobLogs(workflowJob) {
try {
this.setJob(workflowJob);
this.fetchJobLogState.inProgress();
const logs = (yield flowResult(this.workflowManagerState.getJobLogs(workflowJob)));
this.setLogs(logs);
this.fetchJobLogState.pass();
}
catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE), error);
this.editorStore.applicationStore.notificationService.notifyError(error);
this.fetchJobLogState.fail();
}
}
}
export class WorkflowState {
uuid = uuid();
editorStore;
workflowManagerState;
treeData;
isExecutingWorkflowRequest = false;
constructor(editorStore, workflowManagerState, workflow, jobs) {
makeObservable(this, {
treeData: observable.ref,
isExecutingWorkflowRequest: observable,
setWorkflowTreeData: action,
fetchAllWorkspaceWorkJobs: flow,
onTreeNodeSelect: flow,
cancelJob: flow,
refreshWorkflow: flow,
retryJob: flow,
runManualJob: flow,
});
this.editorStore = editorStore;
this.workflowManagerState = workflowManagerState;
this.treeData = this.buildTreeData(workflow, jobs);
}
setWorkflowTreeData(val) {
this.treeData = val;
}
buildTreeData(workflow, jobs) {
const rootIds = [];
const nodes = new Map();
const treeData = { rootIds, nodes };
const workflowNode = new WorkflowTreeNodeData(workflow);
treeData.rootIds.push(workflowNode.id);
treeData.nodes.set(workflowNode.id, workflowNode);
if (jobs) {
workflowNode.isOpen = true;
updateWorkflowJobData(jobs, workflow.id, treeData);
}
return treeData;
}
*fetchAllWorkspaceWorkJobs(workflowId, treeData) {
try {
this.isExecutingWorkflowRequest = true;
const workflowJobs = (yield flowResult(this.workflowManagerState.getJobs(workflowId))).sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
updateWorkflowJobData(workflowJobs, workflowId, treeData);
this.setWorkflowTreeData({ ...treeData });
}
catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE), error);
this.editorStore.applicationStore.notificationService.notifyError(error);
}
finally {
this.isExecutingWorkflowRequest = false;
}
}
*onTreeNodeSelect(node, treeData) {
if (node instanceof WorkflowTreeNodeData) {
if (!node.childrenIds) {
yield flowResult(this.fetchAllWorkspaceWorkJobs(node.workflow.id, treeData));
}
node.isOpen = !node.isOpen;
}
this.setWorkflowTreeData({ ...treeData });
}
*cancelJob(workflowJob, treeData) {
try {
this.isExecutingWorkflowRequest = true;
this.workflowManagerState.cancelJob(workflowJob);
yield flowResult(this.refreshWorkflow(workflowJob.workflowId, treeData));
}
catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE), error);
this.editorStore.applicationStore.notificationService.notifyError(error);
}
finally {
this.isExecutingWorkflowRequest = false;
}
}
*refreshWorkflow(workflowId, treeData) {
const node = treeData.nodes.get(getWorkflowNodeId(workflowId));
if (node instanceof WorkflowTreeNodeData) {
const workflow = (yield flowResult(this.workflowManagerState.getWorkflow(workflowId)));
node.workflow = workflow;
}
yield flowResult(this.fetchAllWorkspaceWorkJobs(workflowId, treeData));
}
*retryJob(workflowJob, treeData) {
try {
this.isExecutingWorkflowRequest = true;
yield flowResult(this.workflowManagerState.retryJob(workflowJob));
yield flowResult(this.refreshWorkflow(workflowJob.workflowId, treeData));
}
catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE), error);
this.editorStore.applicationStore.notificationService.notifyError(error);
}
finally {
this.isExecutingWorkflowRequest = false;
}
}
*runManualJob(workflowJob, treeData) {
try {
this.isExecutingWorkflowRequest = true;
yield flowResult(this.workflowManagerState.runManualJob(workflowJob));
yield flowResult(this.refreshWorkflow(workflowJob.workflowId, treeData));
}
catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE), error);
this.editorStore.applicationStore.notificationService.notifyError(error);
}
finally {
this.isExecutingWorkflowRequest = false;
}
}
}
export class WorkflowManagerState {
editorStore;
sdlcState;
fetchWorkflowsState = ActionState.create();
logState;
workflowStates = [];
constructor(editorStore, sdlcState) {
makeObservable(this, {
logState: observable,
workflowStates: observable,
fetchAllWorkflows: flow,
getWorkflows: flow,
getWorkflow: flow,
getJobs: flow,
getJob: flow,
retryJob: flow,
cancelJob: flow,
runManualJob: flow,
getJobLogs: flow,
});
this.editorStore = editorStore;
this.sdlcState = sdlcState;
this.logState = new WorkflowLogState(this.editorStore, this, undefined, undefined);
}
*fetchAllWorkflows() {
try {
this.fetchWorkflowsState.inProgress();
// NOTE: this network call can take a while, so we might consider limiting the number of workflows to 10 or so
const workflows = (yield flowResult(this.getWorkflows()));
const openWorkflowIds = this.workflowStates
.map((workflowState) => Array.from(workflowState.treeData.nodes.values()))
.flat()
.filter(filterByType(WorkflowTreeNodeData))
.filter((node) => node.isOpen)
.map((node) => node.workflow.id);
const jobsIndex = new Map();
yield Promise.all(workflows
.filter((workflow) => openWorkflowIds.includes(workflow.id))
// NOTE: this network call can take a while, so we might consider limiting the number of workflows to 10 or so
.map((workflow) => flowResult(this.getJobs(workflow.id)).then((jobs) => jobsIndex.set(workflow.id, jobs.toSorted((a, b) => a.createdAt.getTime() - b.createdAt.getTime())))));
this.workflowStates = workflows.map((workflow) => new WorkflowState(this.editorStore, this, workflow, jobsIndex.get(workflow.id)));
this.fetchWorkflowsState.pass();
}
catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE), error);
this.editorStore.applicationStore.notificationService.notifyError(error);
this.fetchWorkflowsState.fail();
}
}
}
export class WorkspaceWorkflowManagerState extends WorkflowManagerState {
*getJobs(workflowId) {
return (yield this.editorStore.sdlcServerClient.getWorkflowJobs(this.sdlcState.activeProject.projectId, this.sdlcState.activeWorkspace, workflowId, undefined, undefined, undefined)).map((v) => WorkflowJob.serialization.fromJson(v));
}
*getJob(job) {
return (yield this.editorStore.sdlcServerClient.getWorkflowJob(this.sdlcState.activeProject.projectId, this.sdlcState.activeWorkspace, job)).map((v) => WorkflowJob.serialization.fromJson(v));
}
*getWorkflows() {
return (yield this.editorStore.sdlcServerClient.getWorkflows(this.sdlcState.activeProject.projectId, this.sdlcState.activeWorkspace, undefined, undefined, undefined)).map((v) => Workflow.serialization.fromJson(v));
}
*getWorkflow(workflowId) {
return Workflow.serialization.fromJson((yield this.editorStore.sdlcServerClient.getWorkflow(this.sdlcState.activeProject.projectId, this.sdlcState.activeWorkspace, workflowId)));
}
*retryJob(workflowJob) {
(yield this.editorStore.sdlcServerClient.retryWorkflowJob(this.sdlcState.activeProject.projectId, this.sdlcState.activeWorkspace, workflowJob));
}
*runManualJob(workflowJob) {
(yield this.editorStore.sdlcServerClient.runManualWorkflowJob(this.sdlcState.activeProject.projectId, this.sdlcState.activeWorkspace, workflowJob));
}
*cancelJob(workflowJob) {
(yield this.editorStore.sdlcServerClient.cancelWorkflowJob(this.sdlcState.activeProject.projectId, this.sdlcState.activeWorkspace, workflowJob));
}
*getJobLogs(workflowJob) {
return (yield this.editorStore.sdlcServerClient.getWorkflowJobLogs(this.sdlcState.activeProject.projectId, this.sdlcState.activeWorkspace, workflowJob));
}
}
export class ProjectVersionWorkflowManagerState extends WorkflowManagerState {
version;
constructor(editorStore, sdlcState, version) {
super(editorStore, sdlcState);
this.version = version;
}
*getJobs(workflowId) {
return (yield this.editorStore.sdlcServerClient.getWorkflowJobsByVersion(this.sdlcState.activeProject.projectId, this.version.id.id, workflowId, undefined, undefined, undefined)).map((v) => WorkflowJob.serialization.fromJson(v));
}
*getJob(job) {
return (yield this.editorStore.sdlcServerClient.getWorkflowJobByVersion(this.sdlcState.activeProject.projectId, this.version.id.id, job)).map((v) => WorkflowJob.serialization.fromJson(v));
}
*getWorkflows() {
return (yield this.editorStore.sdlcServerClient.getWorkflowsByVersion(this.sdlcState.activeProject.projectId, this.version.id.id, undefined, undefined, undefined)).map((v) => Workflow.serialization.fromJson(v));
}
*getWorkflow(workflowId) {
return Workflow.serialization.fromJson((yield this.editorStore.sdlcServerClient.getWorkflowByVersion(this.sdlcState.activeProject.projectId, this.version.id.id, workflowId)));
}
*retryJob(workflowJob) {
(yield this.editorStore.sdlcServerClient.retryWorkflowJobByVersion(this.sdlcState.activeProject.projectId, this.version.id.id, workflowJob));
}
*runManualJob(workflowJob) {
(yield this.editorStore.sdlcServerClient.runManualWorkflowJobByVersion(this.sdlcState.activeProject.projectId, this.version.id.id, workflowJob));
}
*cancelJob(workflowJob) {
(yield this.editorStore.sdlcServerClient.cancelWorkflowJobByVersion(this.sdlcState.activeProject.projectId, this.version.id.id, workflowJob));
}
*getJobLogs(workflowJob) {
return (yield this.editorStore.sdlcServerClient.getWorkflowJobLogsByVersion(this.sdlcState.activeProject.projectId, this.version.id.id, workflowJob));
}
}
export class ProjectWorkflowManagerState extends WorkflowManagerState {
*getJobs(workflowId) {
return (yield this.editorStore.sdlcServerClient.getWorkflowJobs(this.sdlcState.activeProject.projectId, undefined, workflowId, undefined, undefined, undefined)).map((v) => WorkflowJob.serialization.fromJson(v));
}
*getJob(job) {
return (yield this.editorStore.sdlcServerClient.getWorkflowJob(this.sdlcState.activeProject.projectId, undefined, job)).map((v) => WorkflowJob.serialization.fromJson(v));
}
*getWorkflows() {
return (yield this.editorStore.sdlcServerClient.getWorkflows(this.sdlcState.activeProject.projectId, undefined, undefined, undefined, undefined)).map((v) => Workflow.serialization.fromJson(v));
}
*getWorkflow(workflowId) {
return Workflow.serialization.fromJson((yield this.editorStore.sdlcServerClient.getWorkflow(this.sdlcState.activeProject.projectId, undefined, workflowId)));
}
*retryJob(workflowJob) {
(yield this.editorStore.sdlcServerClient.retryWorkflowJob(this.sdlcState.activeProject.projectId, undefined, workflowJob));
}
*runManualJob(workflowJob) {
(yield this.editorStore.sdlcServerClient.runManualWorkflowJob(this.sdlcState.activeProject.projectId, undefined, workflowJob));
}
*cancelJob(workflowJob) {
(yield this.editorStore.sdlcServerClient.cancelWorkflowJob(this.sdlcState.activeProject.projectId, undefined, workflowJob));
}
*getJobLogs(workflowJob) {
return (yield this.editorStore.sdlcServerClient.getWorkflowJobLogs(this.sdlcState.activeProject.projectId, undefined, workflowJob));
}
}
//# sourceMappingURL=WorkflowManagerState.js.map