Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 64 additions & 29 deletions app/components/analysis/AnalysisControlButtons.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { ProcessStatus } from "~/types/analysis";
import {
type PodProgressResponse,
PodStatus,
type StatusOnlyResponse,
type StatusOnlyResponse
} from "~/services/Api";

type ToastSeverity = "success" | "info" | "warn" | "error" | undefined;
Expand All @@ -22,41 +22,73 @@ const props = defineProps({
analysisBuildStatus: [String, null],
analysisExecutionStatus: {
type: [String, null],
required: true,
required: true
},
analysisDistributionStatus: {
type: [String, null],
required: true,
required: true
},
analysisNodeId: {
type: String,
required: true,
required: true
},
analysisId: {
type: String,
required: true,
required: true
},
projectId: {
type: String,
required: true,
required: true
},
nodeId: {
type: String,
required: true,
required: true
},
datastore: {
type: Boolean,
required: true,
required: true
},
requireDatastore: {
type: Boolean,
required: true,
},
required: true
}
});

const emit = defineEmits(["updateAnalysisRow", "missingDataStore"]);
const toast = useToast();
const loading = ref(false);
// Needed in order to distinguish in-flight request from stored
const loadingRestoredFromStorage = ref(false);

const loadingStorageKey = computed(
() => `analysis-start-loading-${props.analysisId}`
);

onMounted(() => {
if (
localStorage.getItem(loadingStorageKey.value) === "true" &&
playButtonActiveStates.includes(props.analysisExecutionStatus)
) {
loading.value = true;
loadingRestoredFromStorage.value = true;
} else {
localStorage.removeItem(loadingStorageKey.value);
}
});

watch(
() => props.analysisExecutionStatus,
(newStatus) => {
if (
loadingRestoredFromStorage.value &&
!playButtonActiveStates.includes(newStatus)
) {
localStorage.removeItem(loadingStorageKey.value);
loading.value = false;
loadingRestoredFromStorage.value = false;
}
}
);

// API Constants
const NOT_FOUND_STATUS = 404;
Expand All @@ -68,14 +100,14 @@ const rerunButtonActiveStates: Array<string | null | undefined> = [
PodStatus.Executed,
PodStatus.Finished, // Deprecated
PodStatus.Stopped,
PodStatus.Stopping,
PodStatus.Stopping
];
const stopButtonActiveStates: Array<string | null | undefined> = [
PodStatus.Executing,
PodStatus.Running, // Deprecated
PodStatus.Starting,
PodStatus.Started,
PodStatus.Stopping,
PodStatus.Stopping
];
const deleteButtonActiveStates: Array<string | null | undefined> = [
PodStatus.Failed,
Expand All @@ -84,29 +116,29 @@ const deleteButtonActiveStates: Array<string | null | undefined> = [
PodStatus.Executing,
PodStatus.Running, // Deprecated
PodStatus.Starting,
PodStatus.Started,
PodStatus.Started
];

function getButtonStatuses(podStatus: string | null | undefined) {
return {
playActive: playButtonActiveStates.includes(podStatus),
rerunActive: rerunButtonActiveStates.includes(podStatus),
stopActive: stopButtonActiveStates.includes(podStatus),
deleteActive: deleteButtonActiveStates.includes(podStatus),
deleteActive: deleteButtonActiveStates.includes(podStatus)
};
}

const buttonStatuses = computed<ButtonStates>(
() => getButtonStatuses(props.analysisExecutionStatus), // Changes buttons when new status update comes in
() => getButtonStatuses(props.analysisExecutionStatus) // Changes buttons when new status update comes in
);

function updatePodStatus(
podStatus: string | null | undefined,
progressUpdate?: number | null | undefined,
progressUpdate?: number | null | undefined
) {
const analysisUpdate = {
status: podStatus,
progress: progressUpdate,
progress: progressUpdate
};
emit("updateAnalysisRow", props.analysisId, analysisUpdate);
}
Expand All @@ -116,22 +148,22 @@ const showToast = (severity: ToastSeverity, summary: string, msg: string) => {
severity: severity,
summary: summary,
detail: msg,
life: 5000,
life: 5000
});
};

const showStatusUnknownToast = () => {
showToast(
"warn",
"Status unknown",
"Pod was not found, but the stop command was still issued",
"Pod was not found, but the stop command was still issued"
);
};

async function checkPodStatus(): Promise<boolean> {
const podStatus: PodProgressResponse = (await useNuxtApp()
.$hubApi(`/po/status/${props.analysisId}`, {
method: "GET",
method: "GET"
})
.catch(() => null)) as PodProgressResponse; // Set the response to undefined if an error occurs

Expand All @@ -144,7 +176,7 @@ async function checkPodStatus(): Promise<boolean> {
showToast(
"warn",
"Analysis already running",
"The analysis is already running on this node, the controls have been updated",
"The analysis is already running on this node, the controls have been updated"
);
updatePodStatus(currentPodStatus, currentPodProgress); // Grab the first status update
return true;
Expand All @@ -160,6 +192,8 @@ async function onRerunAnalysis() {

async function onStartAnalysis() {
loading.value = true;
loadingRestoredFromStorage.value = false;
localStorage.setItem(loadingStorageKey.value, "true");
const originalExecutionStatus = props.analysisExecutionStatus;
updatePodStatus(PodStatus.Starting);
let analysisProps = new FormData();
Expand All @@ -170,7 +204,7 @@ async function onStartAnalysis() {
let startPodResp: StatusOnlyResponse = (await useNuxtApp()
.$hubApi("/analysis/initialize", {
method: "POST",
body: analysisProps,
body: analysisProps
})
.catch((e) => {
updatePodStatus(null);
Expand All @@ -181,7 +215,7 @@ async function onStartAnalysis() {
"info",
"Analysis submitted",
"The PodOrc did not respond in time, but the request was sent to start the analysis. " +
"The timeout was likely due to a large image being pulled.",
"The timeout was likely due to a large image being pulled."
);
} else if (e.status === NOT_FOUND_STATUS) {
emit("missingDataStore");
Expand All @@ -206,6 +240,7 @@ async function onStartAnalysis() {
updatePodStatus(originalExecutionStatus);
}

localStorage.removeItem(loadingStorageKey.value);
loading.value = false;
}

Expand All @@ -216,13 +251,13 @@ async function onStopAnalysis() {

const stopResp: StatusOnlyResponse = (await useNuxtApp()
.$hubApi(`/po/stop/${props.analysisId}`, {
method: "PUT",
method: "PUT"
})
.catch(() => {
showToast(
"error",
"Stop failure",
"Failed to stop the analysis container",
"Failed to stop the analysis container"
);
})) as StatusOnlyResponse;

Expand All @@ -232,7 +267,7 @@ async function onStopAnalysis() {
showToast(
"success",
"Stop success",
"Successfully stopped the container",
"Successfully stopped the container"
);
} else {
// No pod statuses returned from PO for analysis
Expand All @@ -252,13 +287,13 @@ async function onDeleteAnalysis() {

const deleteResp: StatusOnlyResponse = (await useNuxtApp()
.$hubApi(`/analysis/terminate/${props.analysisId}`, {
method: "DELETE",
method: "DELETE"
})
.catch(() => {
showToast(
"error",
"Terminate request failure",
"Failed to terminate the analysis",
"Failed to terminate the analysis"
);
})) as StatusOnlyResponse;

Expand All @@ -268,7 +303,7 @@ async function onDeleteAnalysis() {
showToast(
"success",
"Delete success",
"Successfully removed the container",
"Successfully removed the container"
);
} else {
showStatusUnknownToast();
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"eslint": "~9.26.0",
"eslint-config-prettier": "^9.1.2",
"eslint-plugin-vue": "^9.33.0",
"happy-dom": "^20.0.8",
"happy-dom": "^20.8.9",
"msw": "^2.11.6",
"typescript-eslint": "^8.46.2",
"vite-tsconfig-paths": "^5.1.4",
Expand Down
Loading
Loading