diff --git a/app/components/analysis/AnalysesTable.vue b/app/components/analysis/AnalysesTable.vue index e87ced7..5236be1 100644 --- a/app/components/analysis/AnalysesTable.vue +++ b/app/components/analysis/AnalysesTable.vue @@ -7,7 +7,7 @@ import { getAnalysisNodes } from "~/composables/useAPIFetch"; import { formatDataRow } from "~/utils/format-data-row"; import { showCacheWarningToast, - showConnectionErrorToast + showConnectionErrorToast, } from "~/composables/connectionErrorToast"; import { FilterMatchMode } from "@primevue/core/api"; import SearchBar from "~/components/table/SearchBar.vue"; @@ -15,7 +15,7 @@ import AnalysisControlButtons from "./AnalysisControlButtons.vue"; import { getApprovalStatusSeverity, getBuildStatusSeverity, - getExecutionStatusSeverity + getExecutionStatusSeverity, } from "~/utils/status-tag-severity"; import { type AnalysisNode, @@ -24,7 +24,7 @@ import { type PodProgressResponse, PodStatus, type Project, - type Route + type Route, } from "~/services/Api"; import { ApprovalStatus } from "~/types/node"; import ContainerCounter from "~/components/analysis/ContainerCounter.vue"; @@ -40,12 +40,12 @@ const { nodeType, requireDataStore: datastoreRequired } = useDatastoreRequirement(); const datastoreBadgeSeverity = computed(() => - datastoreRequired.value ? "danger" : "secondary" + datastoreRequired.value ? "danger" : "secondary", ); const datastoreBadgeTooltip = computed(() => datastoreRequired.value ? "Data store missing!" - : "Data store missing, but not required" + : "Data store missing, but not required", ); const analysesMap = ref>(new Map()); @@ -61,11 +61,11 @@ const filters = ref(); // Cache const analysisCache = useState( "analysisCache", - () => undefined + () => undefined, ); const projectCache = useState( "projectCache", - () => undefined + () => undefined, ); const podOrcUnreacheable = ref(false); @@ -81,9 +81,7 @@ const approvalStatuses = Object.values(ApprovalStatus); const processStatuses = Object.values(ProcessStatus); const podStatuses = Object.values(PodStatus); -// Stable sort function for distribution status column — maps to binary value so -// rows group by badge (executed = '1', everything else = '0') rather than sorting -// alphabetically across the many possible status strings. +// Stable sort function for distribution status column maps to binary value const distributionStatusSortFn = (row: ModifiedAnalysisNode) => row.analysis?.distribution_status === "executed" ? "1" : "0"; @@ -95,8 +93,8 @@ async function getProjects() { method: "GET", query: { sort: "-updated_at", - fields: "id,name" - } + fields: "id,name", + }, }) .catch(() => undefined)) as Project[]; } @@ -104,7 +102,7 @@ async function getProjects() { async function getKongRoutes() { const kongRoutesResp = (await useNuxtApp() .$hubApi("/kong/project", { - method: "GET" + method: "GET", }) .catch(() => undefined)) as ListRoutes; if (kongRoutesResp && kongRoutesResp.data) { @@ -141,53 +139,30 @@ async function parseProjects() { } } -// /** -// * Changes to "executing" to "running" and "executed" to "finished" which is simpler language. -// * @param status -// */ -// function useCommonLanguage( -// status: PodStatus, -// ): PodStatus | "running" | "finished" { -// if (status === PodStatus.Executing) { -// return PodStatus.Running; -// } else if (status === PodStatus.Executed) { -// return PodStatus.Finished; -// } else { -// return status; -// } -// } - async function getExecutionStatusesFromPodOrc(): Promise< PodProgressResponse | undefined > { const podOrcResponse = (await useNuxtApp() .$hubApi("/po/status", { - method: "GET" + method: "GET", }) .catch(() => { - showConnectionErrorToast(toast, { - severity: "warn", - summary: "Missing PO Status Update", - detail: - "Unable to retrieve pod statuses from the PO, relying on information from the Hub", - life: 3000 - }); + if (!podOrcUnreacheable.value) { + podOrcUnreacheable.value = true; + showConnectionErrorToast(toast, { + severity: "warn", + summary: "Missing PO Status Update", + detail: + "Unable to retrieve pod statuses from the PO, relying on information from the Hub", + life: 3000, + }); + } + return undefined; })) as PodProgressResponse; podOrcUnreacheable.value = !podOrcResponse; return podOrcResponse; } -async function checkForUpdatesFromPodOrc() { - if (!podOrcUnreacheable.value) { - const newStatuses = await getExecutionStatusesFromPodOrc(); - if (newStatuses) { - for (const [analysisId, progressData] of Object.entries(newStatuses)) { - updateAnalysisRun(analysisId, progressData); - } - } - } -} - function setProgress(analysis: ModifiedAnalysisNode): ModifiedAnalysisNode { // For testing: Math.round(Math.random() * 100); analysis.execution_progress = analysis.execution_progress @@ -198,10 +173,7 @@ function setProgress(analysis: ModifiedAnalysisNode): ModifiedAnalysisNode { if (currentRunStatus) { if (currentRunStatus === PodStatus.Failed) { analysis.execution_progress = 0; - } else if ( - currentRunStatus === PodStatus.Executed || - currentRunStatus === PodStatus.Finished // deprecated - ) { + } else if (currentRunStatus === PodStatus.Executed) { analysis.execution_progress = 100; } } @@ -222,13 +194,13 @@ function determineProgressBarColor(progress: number) { } return { - "--p-progressbar-value-background": color + "--p-progressbar-value-background": color, }; } function parseAnalysis( analysisEntry: ModifiedAnalysisNode, - executionStatuses: PodProgressResponse | undefined + executionStatuses: PodProgressResponse | undefined, ): ModifiedAnalysisNode { const projId = analysisEntry.analysis?.project_id; const analysisId = analysisEntry.analysis_id; @@ -236,14 +208,9 @@ function parseAnalysis( analysisEntry.project_name = projMap.has(projId) ? projMap.get(projId) : ""; analysisEntry.datastore = kongRoutes.value.has(projId); } - // If PodOrc status update returns undefined -> use hub info since it's all we have - // If status from PodOrc -> use it - // If no run status reported by PodOrc, and it's not failed/finished -> set to null (wrong hub info) - // REMEMBER: PO sends running/finished instead of executing/executed const acceptableHubStatuses: Array = [ PodStatus.Failed, PodStatus.Executed, - PodStatus.Finished // Deprecated but still returned by PO ]; if (executionStatuses && analysisId in executionStatuses) { const podStatus = executionStatuses[analysisId]!; @@ -260,9 +227,10 @@ function parseAnalysis( async function compileAnalysisTable( respStatus: string, - respData: AnalysisNode[] | undefined + respData: AnalysisNode[] | undefined, + silent = false, ) { - tableLoading.value = true; + if (!silent) tableLoading.value = true; await parseProjects(); await getKongRoutes(); const parsedAnalyses = new Map(); @@ -281,13 +249,13 @@ async function compileAnalysisTable( const formattedAnalyses = formatDataRow( analysisData, ["created_at", "updated_at"], - expandRowEntries + expandRowEntries, ) as ModifiedAnalysisNode[]; if (formattedAnalyses && projMap.size > 0) { formattedAnalyses.forEach((analysisEntry: ModifiedAnalysisNode) => { parsedAnalyses.set( analysisEntry.analysis_id, - parseAnalysis(analysisEntry, currentExecutionStatuses) + parseAnalysis(analysisEntry, currentExecutionStatuses), ); }); analysesMap.value = parsedAnalyses; @@ -295,17 +263,27 @@ async function compileAnalysisTable( tableLoading.value = false; } -let pollIntervalId: ReturnType | undefined; +let tableRefreshIntervalId: ReturnType | undefined; +let isPolling = false; + +async function pollTableData() { + if (isPolling) return; + isPolling = true; + try { + await refresh(); + await compileAnalysisTable(status.value, analysisNodeResp.value, true); + } finally { + isPolling = false; + } +} onMounted(() => { compileAnalysisTable(status.value, analysisNodeResp.value); - pollIntervalId = setInterval(checkForUpdatesFromPodOrc, 15000); // Poll PO every 15 seconds + tableRefreshIntervalId = setInterval(pollTableData, 15000); }); onUnmounted(() => { - if (pollIntervalId) { - clearInterval(pollIntervalId); - } + if (tableRefreshIntervalId) clearInterval(tableRefreshIntervalId); }); async function onTableRefresh() { @@ -334,11 +312,11 @@ async function getNextPage() { query: { page: { offset: currentOffset, - limit: queryLimit + limit: queryLimit, }, include: "analysis,node", - sort: "-updated_at" - } + sort: "-updated_at", + }, }) .catch(() => undefined)) as AnalysisNode[]; if (nextSetResults.length > 0) { @@ -359,7 +337,7 @@ const defaultFilters = { global: { value: undefined, matchMode: FilterMatchMode.CONTAINS }, approval_status: { value: undefined, matchMode: FilterMatchMode.EQUALS }, "analysis.build_status": { value: undefined, matchMode: FilterMatchMode.IN }, - execution_status: { value: undefined, matchMode: FilterMatchMode.IN } + execution_status: { value: undefined, matchMode: FilterMatchMode.IN }, }; filters.value = defaultFilters; @@ -367,7 +345,7 @@ function resetFilters() { const clearedFilters = {}; for (const filterKey in defaultFilters) { clearedFilters[filterKey] = { - ...defaultFilters[filterKey] + ...defaultFilters[filterKey], }; clearedFilters[filterKey].value = undefined; } @@ -380,7 +358,7 @@ const updateFilters = (filterText: string) => { function updateAnalysisRun( analysisId: string, - newStatusData: AnalysisStatus | undefined + newStatusData: AnalysisStatus | undefined, ) { if (analysesMap.value.has(analysisId)) { const analysisToUpdate = analysesMap.value.get(analysisId)!; // Tell typescript we are sure there is a value @@ -402,7 +380,7 @@ function updateExecutionStatusFilter(filterText: string) { if (currentExecutionStatusFilters.includes(filterText)) { // If filter already there, then remove it const filteredStatuses = currentExecutionStatusFilters.filter( - (item) => item !== filterText + (item) => item !== filterText, ); if (filteredStatuses.length == 0) { // If empty array after filtering then set to null @@ -425,7 +403,7 @@ const showDataStoreNavToast = () => { "Unable to find an associated data store, click the button below " + "to create a data store for the project of this analysis", group: "datastoreToastLink", - life: 10000 + life: 10000, }); }; @@ -659,15 +637,15 @@ const onCloseNavToast = () => { class="distribution-badge" >
@@ -749,17 +727,17 @@ const onCloseNavToast = () => { diff --git a/app/components/analysis/AnalysisControlButtons.vue b/app/components/analysis/AnalysisControlButtons.vue index bb10eda..5a95992 100644 --- a/app/components/analysis/AnalysisControlButtons.vue +++ b/app/components/analysis/AnalysisControlButtons.vue @@ -440,6 +440,7 @@ async function onDeleteAnalysis() { :to="{ name: 'analyses-id', params: { id: props.analysisId }, + query: { nodeId: props.analysisNodeId }, }" target="_blank" > diff --git a/app/components/analysis/logs/AnalysisLogCardContent.vue b/app/components/analysis/logs/AnalysisLogCardContent.vue index faad238..89f5097 100644 --- a/app/components/analysis/logs/AnalysisLogCardContent.vue +++ b/app/components/analysis/logs/AnalysisLogCardContent.vue @@ -1,6 +1,6 @@