diff --git a/src/Frontend/src/components/audit/auditClient.ts b/src/Frontend/src/components/audit/auditClient.ts new file mode 100644 index 0000000000..9b7a33973d --- /dev/null +++ b/src/Frontend/src/components/audit/auditClient.ts @@ -0,0 +1,32 @@ +import Message, { MessageStatus } from "@/resources/Message"; +import type { SortInfo } from "@/components/SortInfo"; +import type { DateRange } from "@/types/date"; +import serviceControlClient from "@/components/serviceControlClient"; + +export interface AuditQuery { + endpointName?: string; + dateRange?: DateRange; + messageFilterString?: string; + itemsPerPage?: number; + sort?: SortInfo; +} + +class AuditClient { + public async getMessages(query: AuditQuery): Promise<[Response, Message[]]> { + const [fromDate, toDate] = query.dateRange ?? []; + const from = fromDate?.toISOString() ?? ""; + const to = toDate?.toISOString() ?? ""; + return await serviceControlClient.fetchTypedFromServiceControl( + `messages2/?endpoint_name=${query.endpointName ?? ""}&from=${from}&to=${to}&q=${query.messageFilterString ?? ""}&page_size=${query.itemsPerPage ?? 100}&sort=${query.sort?.property ?? "time_sent"}&direction=${query.sort?.isAscending ? "asc" : "desc"}` + ); + } + + public async hasSuccessfulMessages(): Promise { + // Fetch the latest 10 messages and check if any are successful + // ideally we would want to filter successful messages server-side, but the API doesn't currently support that + const [, data] = await serviceControlClient.fetchTypedFromServiceControl(`messages2/?page_size=10&sort=time_sent&direction=desc`); + return data?.some((msg) => msg.status === MessageStatus.Successful) ?? false; + } +} + +export default new AuditClient(); diff --git a/src/Frontend/src/components/platformcapabilities/capabilities/AuditingCapability.ts b/src/Frontend/src/components/platformcapabilities/capabilities/AuditingCapability.ts index c98b47bed5..dffcff81af 100644 --- a/src/Frontend/src/components/platformcapabilities/capabilities/AuditingCapability.ts +++ b/src/Frontend/src/components/platformcapabilities/capabilities/AuditingCapability.ts @@ -5,7 +5,7 @@ import useIsAllMessagesSupported, { minimumSCVersionForAllMessages } from "@/com import { storeToRefs } from "pinia"; import { type CapabilityComposable, type CapabilityStatusToStringMap, useCapabilityBase } from "./BaseCapability"; import useRemoteInstancesAutoRefresh from "@/composables/useRemoteInstancesAutoRefresh"; -import useAuditStoreAutoRefresh from "@/composables/useAuditStoreAutoRefresh"; +import usePlatformCapabilitiesRefresh from "@/composables/usePlatformCapabilitiesRefresh"; import { RemoteInstanceStatus, RemoteInstanceType, type RemoteInstance } from "@/resources/RemoteInstance"; import routeLinks from "@/router/routeLinks"; @@ -110,8 +110,8 @@ export function useAuditingCapability(): CapabilityComposable { // This gives us the hasSuccessfulMessages flag which indicates if any successful messages exist. // Uses auto-refresh (minimal) to periodically check for at least 1 successful message (every 5 seconds) - const { store: auditStore } = useAuditStoreAutoRefresh(); - const { hasSuccessfulMessages } = storeToRefs(auditStore); + const { store: platformCapabilitiesStore } = usePlatformCapabilitiesRefresh(); + const { hasSuccessfulMessages } = storeToRefs(platformCapabilitiesStore); // This tells us if the "All Messages" feature is supported by checking the SC version const isAllMessagesSupported = useIsAllMessagesSupported(); diff --git a/src/Frontend/src/components/platformcapabilities/capabilities/MonitoringCapability.ts b/src/Frontend/src/components/platformcapabilities/capabilities/MonitoringCapability.ts index 197abe6fbe..170ee459e0 100644 --- a/src/Frontend/src/components/platformcapabilities/capabilities/MonitoringCapability.ts +++ b/src/Frontend/src/components/platformcapabilities/capabilities/MonitoringCapability.ts @@ -3,10 +3,10 @@ import { StatusIndicator } from "@/components/platformcapabilities/types"; import { CapabilityStatus } from "@/components/platformcapabilities/constants"; import { storeToRefs } from "pinia"; import { useConnectionsAndStatsStore } from "@/stores/ConnectionsAndStatsStore"; -import useMonitoringStoreAutoRefresh from "@/composables/useMonitoringStoreAutoRefresh"; import { type CapabilityComposable, type CapabilityStatusToStringMap, useCapabilityBase } from "./BaseCapability"; import monitoringClient from "@/components/monitoring/monitoringClient"; import { useEnvironmentAndVersionsStore } from "@/stores/EnvironmentAndVersionsStore"; +import usePlatformCapabilitiesRefresh from "@/composables/usePlatformCapabilitiesRefresh"; import routeLinks from "@/router/routeLinks"; const MonitoringDescriptions: CapabilityStatusToStringMap = { @@ -46,8 +46,8 @@ export function useMonitoringCapability(): CapabilityComposable { // this tells us if there are any endpoints sending data // Uses auto-refresh to periodically check for monitored endpoints (every 5 seconds) - const { store: monitoringStore } = useMonitoringStoreAutoRefresh(); - const { hasMonitoredEndpoints } = storeToRefs(monitoringStore); + const { store: platformCapabilitiesStore } = usePlatformCapabilitiesRefresh(); + const { hasMonitoredEndpoints } = storeToRefs(platformCapabilitiesStore); // this tells us the connection state to the monitoring instance // this is auto refreshed in the ConnectionsAndStatsStore (every 5 seconds) diff --git a/src/Frontend/src/composables/useAuditStoreAutoRefresh.ts b/src/Frontend/src/composables/useAuditStoreAutoRefresh.ts deleted file mode 100644 index cc6e390fc6..0000000000 --- a/src/Frontend/src/composables/useAuditStoreAutoRefresh.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { useAuditStore } from "@/stores/AuditStore"; -import { useStoreAutoRefresh } from "./useAutoRefresh"; - -// Override the refresh method to use checkForSuccessfulMessages, which is more lightweight -const useAuditStoreWithRefresh = () => { - const store = useAuditStore(); - return Object.assign(store, { refresh: store.checkForSuccessfulMessages }); -}; - -export default useStoreAutoRefresh("auditStoreSuccessfulMessages", useAuditStoreWithRefresh, 5000).autoRefresh; diff --git a/src/Frontend/src/composables/useMonitoringStoreAutoRefresh.ts b/src/Frontend/src/composables/useMonitoringStoreAutoRefresh.ts deleted file mode 100644 index 1dd7cef963..0000000000 --- a/src/Frontend/src/composables/useMonitoringStoreAutoRefresh.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { useMonitoringStore } from "@/stores/MonitoringStore"; -import { useStoreAutoRefresh } from "./useAutoRefresh"; - -// Override the refresh method to use checkForMonitoredEndpoints, which is more lightweight -const useMonitoringStoreWithRefresh = () => { - const store = useMonitoringStore(); - return Object.assign(store, { refresh: store.checkForMonitoredEndpoints }); -}; - -export default useStoreAutoRefresh("monitoringStoreMonitoredEndpoints", useMonitoringStoreWithRefresh, 5000).autoRefresh; diff --git a/src/Frontend/src/composables/usePlatformCapabilitiesRefresh.ts b/src/Frontend/src/composables/usePlatformCapabilitiesRefresh.ts new file mode 100644 index 0000000000..fe48c647e8 --- /dev/null +++ b/src/Frontend/src/composables/usePlatformCapabilitiesRefresh.ts @@ -0,0 +1,4 @@ +import { usePlatformCapabilitiesStore } from "@/stores/PlatformCapabilitiesStore"; +import { useStoreAutoRefresh } from "./useAutoRefresh"; + +export default useStoreAutoRefresh("platformCapabilities", usePlatformCapabilitiesStore, 5000).autoRefresh; diff --git a/src/Frontend/src/stores/AuditStore.ts b/src/Frontend/src/stores/AuditStore.ts index 1dba6a24d2..e4aaae8973 100644 --- a/src/Frontend/src/stores/AuditStore.ts +++ b/src/Frontend/src/stores/AuditStore.ts @@ -1,10 +1,11 @@ import { acceptHMRUpdate, defineStore } from "pinia"; import { ref } from "vue"; import type { SortInfo } from "@/components/SortInfo"; -import Message, { MessageStatus } from "@/resources/Message"; +import Message from "@/resources/Message"; import { EndpointsView } from "@/resources/EndpointView"; import type { DateRange } from "@/types/date"; import serviceControlClient from "@/components/serviceControlClient"; +import auditClient from "@/components/audit/auditClient"; export enum FieldNames { TimeSent = "time_sent", @@ -27,19 +28,6 @@ export const useAuditStore = defineStore("AuditStore", () => { const selectedEndpointName = ref(""); const endpoints = ref([]); - const hasSuccessfulMessages = ref(false); - - async function checkForSuccessfulMessages() { - try { - // Fetch the latest 10 messages and check if any are successful - // ideally we would want to filter successful messages server-side, but the API doesn't currently support that - const [, data] = await serviceControlClient.fetchTypedFromServiceControl(`messages2/?page_size=10&sort=time_sent&direction=desc`); - hasSuccessfulMessages.value = data?.some((msg) => msg.status === MessageStatus.Successful) ?? false; - } catch { - hasSuccessfulMessages.value = false; - } - } - async function loadEndpoints() { try { const [, data] = await serviceControlClient.fetchTypedFromServiceControl(`endpoints`); @@ -52,12 +40,13 @@ export const useAuditStore = defineStore("AuditStore", () => { async function refresh() { try { - const [fromDate, toDate] = dateRange.value; - const from = fromDate?.toISOString() ?? ""; - const to = toDate?.toISOString() ?? ""; - const [response, data] = await serviceControlClient.fetchTypedFromServiceControl( - `messages2/?endpoint_name=${selectedEndpointName.value}&from=${from}&to=${to}&q=${messageFilterString.value}&page_size=${itemsPerPage.value}&sort=${sortByInstances.value.property}&direction=${sortByInstances.value.isAscending ? "asc" : "desc"}` - ); + const [response, data] = await auditClient.getMessages({ + endpointName: selectedEndpointName.value, + dateRange: dateRange.value, + messageFilterString: messageFilterString.value, + itemsPerPage: itemsPerPage.value, + sort: sortByInstances.value, + }); totalCount.value = parseInt(response.headers.get("total-count") ?? "0"); messages.value = data; } catch (e) { @@ -69,10 +58,8 @@ export const useAuditStore = defineStore("AuditStore", () => { return { refresh, loadEndpoints, - checkForSuccessfulMessages, sortBy: sortByInstances, messages, - hasSuccessfulMessages, messageFilterString, selectedEndpointName, itemsPerPage, diff --git a/src/Frontend/src/stores/MonitoringStore.ts b/src/Frontend/src/stores/MonitoringStore.ts index 17248f5bd5..1519bd03dd 100644 --- a/src/Frontend/src/stores/MonitoringStore.ts +++ b/src/Frontend/src/stores/MonitoringStore.ts @@ -29,7 +29,6 @@ export const useMonitoringStore = defineStore("MonitoringStore", () => { const endpointList = ref([]); const filterString = ref(""); - const hasMonitoredEndpoints = ref(false); const endpointListCount = computed(() => endpointList.value.length); const endpointListIsEmpty = computed(() => endpointListCount.value === 0); const endpointListIsGrouped = computed(() => grouping.value.selectedGrouping !== 0); @@ -70,20 +69,6 @@ export const useMonitoringStore = defineStore("MonitoringStore", () => { } } - async function checkForMonitoredEndpoints() { - try { - if (!monitoringClient.isMonitoringEnabled || connectionStore.monitoringConnectionState.unableToConnect) { - hasMonitoredEndpoints.value = false; - return; - } - // Minimal query: just need to check if any endpoints exist - const data = await monitoringClient.getMonitoredEndpoints(1); - hasMonitoredEndpoints.value = (data?.length ?? 0) > 0; - } catch { - hasMonitoredEndpoints.value = false; - } - } - async function getAllMonitoredEndpoints() { let endpoints: Endpoint[] = []; if (monitoringClient.isMonitoringEnabled) { @@ -217,7 +202,6 @@ export const useMonitoringStore = defineStore("MonitoringStore", () => { grouping, filterString, sortBy, - hasMonitoredEndpoints, //getters endpointListIsEmpty, @@ -228,7 +212,6 @@ export const useMonitoringStore = defineStore("MonitoringStore", () => { updateSelectedGrouping, updateEndpointList, updateFilterString, - checkForMonitoredEndpoints, }; }); diff --git a/src/Frontend/src/stores/PlatformCapabilitiesStore.ts b/src/Frontend/src/stores/PlatformCapabilitiesStore.ts index 5b46081921..b7a275c12b 100644 --- a/src/Frontend/src/stores/PlatformCapabilitiesStore.ts +++ b/src/Frontend/src/stores/PlatformCapabilitiesStore.ts @@ -1,6 +1,9 @@ import { acceptHMRUpdate, defineStore } from "pinia"; import { ref, watch } from "vue"; import serviceControlClient from "@/components/serviceControlClient"; +import auditClient from "@/components/audit/auditClient"; +import monitoringClient from "@/components/monitoring/monitoringClient"; +import useConnectionsAndStatsAutoRefresh from "@/composables/useConnectionsAndStatsAutoRefresh"; const STORAGE_KEY_PREFIX = "servicepulse-capabilities-vis"; @@ -16,7 +19,7 @@ function getStorageKey(): string { return STORAGE_KEY_PREFIX; } -interface PlatformCapabilitiesVisibility { +export interface PlatformCapabilitiesVisibility { showSection: boolean; showAuditingCard: boolean; showMonitoringCard: boolean; @@ -52,7 +55,36 @@ function saveVisibility(visibility: PlatformCapabilitiesVisibility): void { } export const usePlatformCapabilitiesStore = defineStore("PlatformCapabilitiesStore", () => { + const { store: connectionStore } = useConnectionsAndStatsAutoRefresh(); const visibility = ref(loadVisibility()); + const hasSuccessfulMessages = ref(false); + const hasMonitoredEndpoints = ref(false); + + async function checkForSuccessfulMessages() { + try { + hasSuccessfulMessages.value = await auditClient.hasSuccessfulMessages(); + } catch { + hasSuccessfulMessages.value = false; + } + } + + async function checkForMonitoredEndpoints() { + try { + if (!monitoringClient.isMonitoringEnabled || connectionStore.monitoringConnectionState.unableToConnect) { + hasMonitoredEndpoints.value = false; + return; + } + // Minimal query: just need to check if any endpoints exist + const data = await monitoringClient.getMonitoredEndpoints(1); + hasMonitoredEndpoints.value = (data?.length ?? 0) > 0; + } catch { + hasMonitoredEndpoints.value = false; + } + } + + async function refresh() { + await Promise.all([checkForSuccessfulMessages(), checkForMonitoredEndpoints()]); + } // Watch for changes and persist to localStorage watch( @@ -93,6 +125,9 @@ export const usePlatformCapabilitiesStore = defineStore("PlatformCapabilitiesSto toggleMonitoringCard, toggleErrorCard, showAll, + hasSuccessfulMessages, + hasMonitoredEndpoints, + refresh, }; });