Skip to content
Open
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
5 changes: 4 additions & 1 deletion src/Frontend/src/components/BackendChecksNotifications.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import useConnectionsAndStatsAutoRefresh from "@/composables/useConnectionsAndSt
import useEnvironmentAndVersionsAutoRefresh from "@/composables/useEnvironmentAndVersionsAutoRefresh";
import { useServiceControlStore } from "@/stores/ServiceControlStore";
import { storeToRefs } from "pinia";
import { useMonitoringStore } from "@/stores/MonitoringStore";

const router = useRouter();

Expand All @@ -18,7 +19,9 @@ const monitoringConnectionState = connectionStore.monitoringConnectionState;
const { store: environmentStore } = useEnvironmentAndVersionsAutoRefresh();
const environment = environmentStore.environment;
const serviceControlStore = useServiceControlStore();
const { monitoringUrl, serviceControlUrl, isMonitoringDisabled } = storeToRefs(serviceControlStore);
const monitoringStore = useMonitoringStore();
const { serviceControlUrl } = storeToRefs(serviceControlStore);
const { monitoringUrl, isMonitoringDisabled } = storeToRefs(monitoringStore);

const primaryConnectionFailure = computed(() => connectionState.unableToConnect);
const monitoringConnectionFailure = computed(() => monitoringConnectionState.unableToConnect);
Expand Down
5 changes: 4 additions & 1 deletion src/Frontend/src/components/PageFooter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useServiceControlStore } from "@/stores/ServiceControlStore";
import { storeToRefs } from "pinia";
import { useConfigurationStore } from "@/stores/ConfigurationStore";
import { useLicenseStore } from "@/stores/LicenseStore";
import { useMonitoringStore } from "@/stores/MonitoringStore";

const { store: connectionStore } = useConnectionsAndStatsAutoRefresh();
const connectionState = connectionStore.connectionState;
Expand All @@ -18,7 +19,9 @@ const { store: environmentAndVersionsStore } = useEnvironmentAndVersionsAutoRefr
const newVersions = environmentAndVersionsStore.newVersions;
const environment = environmentAndVersionsStore.environment;
const serviceControlStore = useServiceControlStore();
const { serviceControlUrl, monitoringUrl } = storeToRefs(serviceControlStore);
const monitoringStore = useMonitoringStore();
const { serviceControlUrl } = storeToRefs(serviceControlStore);
const { monitoringUrl } = storeToRefs(monitoringStore);
const licenseStore = useLicenseStore();
const { licenseStatus, license } = licenseStore;

Expand Down
6 changes: 3 additions & 3 deletions src/Frontend/src/components/PageHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import DashboardMenuItem from "@/components/dashboard/DashboardMenuItem.vue";
import FeedbackButton from "@/components/FeedbackButton.vue";
import ThroughputMenuItem from "@/views/throughputreport/ThroughputMenuItem.vue";
import AuditMenuItem from "./audit/AuditMenuItem.vue";
import { useServiceControlStore } from "@/stores/ServiceControlStore";
import { useMonitoringStore } from "@/stores/MonitoringStore";
import { storeToRefs } from "pinia";

const serviceControlStore = useServiceControlStore();
const { isMonitoringEnabled } = storeToRefs(serviceControlStore);
const monitoringStore = useMonitoringStore();
const { isMonitoringEnabled } = storeToRefs(monitoringStore);
// prettier-ignore
const menuItems = computed(
() => [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import CodeEditor from "@/components/CodeEditor.vue";
import { useServiceControlStore } from "@/stores/ServiceControlStore";
import { storeToRefs } from "pinia";
import LoadingSpinner from "../LoadingSpinner.vue";
import { useMonitoringStore } from "@/stores/MonitoringStore";

interface ServiceControlInstanceConnection {
settings: { [key: string]: object };
Expand All @@ -19,7 +20,9 @@ interface MetricsConnectionDetails {
}

const serviceControlStore = useServiceControlStore();
const { serviceControlUrl, monitoringUrl } = storeToRefs(serviceControlStore);
const monitoringStore = useMonitoringStore();
const { serviceControlUrl } = storeToRefs(serviceControlStore);
const { monitoringUrl } = storeToRefs(monitoringStore);

const loading = ref(true);
const showCodeOnlyTab = ref(true);
Expand Down Expand Up @@ -102,7 +105,7 @@ async function getServiceControlConnection() {

async function getMonitoringConnection() {
try {
const [, data] = await serviceControlStore.fetchTypedFromMonitoring<{ Metrics: MetricsConnectionDetails }>("connection");
const [, data] = await monitoringStore.fetchTypedFromMonitoring<{ Metrics: MetricsConnectionDetails }>("connection");
return { ...data, errors: [] };
} catch {
return { Metrics: null, errors: [`Error SC Monitoring instance at ${monitoringUrl.value}connection`] };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,19 @@ import FAIcon from "@/components/FAIcon.vue";
import useConnectionsAndStatsAutoRefresh from "@/composables/useConnectionsAndStatsAutoRefresh";
import { useServiceControlStore } from "@/stores/ServiceControlStore";
import { storeToRefs } from "pinia";
import { useMonitoringStore } from "@/stores/MonitoringStore";

const { store: connectionStore } = useConnectionsAndStatsAutoRefresh();
const connectionState = connectionStore.connectionState;
const monitoringConnectionState = connectionStore.monitoringConnectionState;

const serviceControlStore = useServiceControlStore();
const monitoringStore = useMonitoringStore();
serviceControlStore.refresh();
monitoringStore.refresh();
const localServiceControlUrl = ref(serviceControlStore.serviceControlUrl);
const localMonitoringUrl = ref(serviceControlStore.monitoringUrl);
const { isMonitoringDisabled } = storeToRefs(serviceControlStore);
const localMonitoringUrl = ref(monitoringStore.monitoringUrl);
const { isMonitoringDisabled } = storeToRefs(monitoringStore);

const testingServiceControl = ref(false);
const serviceControlValid = ref<boolean | null>(null);
Expand Down
12 changes: 6 additions & 6 deletions src/Frontend/src/components/monitoring/EndpointDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { useMonitoringHistoryPeriodStore } from "@/stores/MonitoringHistoryPerio
import routeLinks from "@/router/routeLinks";
import FAIcon from "@/components/FAIcon.vue";
import { faEnvelope } from "@fortawesome/free-solid-svg-icons";
import { useServiceControlStore } from "@/stores/ServiceControlStore";
import { useMonitoringStore } from "@/stores/MonitoringStore";

const { store: connectionStore } = useConnectionsAndStatsAutoRefresh();
const monitoringConnectionState = connectionStore.monitoringConnectionState;
Expand All @@ -30,13 +30,13 @@ const router = useRouter();
const endpointName = route.params.endpointName.toString();
let refreshInterval: number;

const monitoringStore = useMonitoringEndpointDetailsStore();
const monitoringEndpointDetailsStore = useMonitoringEndpointDetailsStore();
const monitoringHistoryPeriodStore = useMonitoringHistoryPeriodStore();
const serviceControlStore = useServiceControlStore();
const { isMonitoringDisabled } = storeToRefs(serviceControlStore);
const monitoringStore = useMonitoringStore();
const { isMonitoringDisabled } = storeToRefs(monitoringStore);

const { historyPeriod } = storeToRefs(monitoringHistoryPeriodStore);
const { negativeCriticalTimeIsPresent, endpointDetails: endpoint } = storeToRefs(monitoringStore);
const { negativeCriticalTimeIsPresent, endpointDetails: endpoint } = storeToRefs(monitoringEndpointDetailsStore);

watch(historyPeriod, (newValue) => {
changeRefreshInterval(newValue.refreshIntervalVal);
Expand All @@ -57,7 +57,7 @@ const activeTab = computed({
});

async function getEndpointDetails() {
await monitoringStore.getEndpointDetails(endpointName);
await monitoringEndpointDetailsStore.getEndpointDetails(endpointName);
}

function changeRefreshInterval(milliseconds: number) {
Expand Down
12 changes: 6 additions & 6 deletions src/Frontend/src/components/monitoring/EndpointInstances.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,19 @@ import ColumnHeader from "@/components/ColumnHeader.vue";
import { CriticalTime, InstanceName, ProcessingTime, ScheduledRetries, Throughput } from "@/resources/MonitoringResources";
import FAIcon from "@/components/FAIcon.vue";
import { faEnvelope, faTrash } from "@fortawesome/free-solid-svg-icons";
import { useServiceControlStore } from "@/stores/ServiceControlStore";
import { useMonitoringStore } from "@/stores/MonitoringStore";

const isRemovingEndpointEnabled = ref<boolean>(false);
const router = useRouter();

const monitoringStore = useMonitoringEndpointDetailsStore();
const { endpointDetails: endpoint, endpointName } = storeToRefs(monitoringStore);
const monitoringEndpointDetailsStore = useMonitoringEndpointDetailsStore();
const { endpointDetails: endpoint, endpointName } = storeToRefs(monitoringEndpointDetailsStore);

const serviceControlStore = useServiceControlStore();
const monitoringStore = useMonitoringStore();

async function removeEndpoint(endpointName: string, instance: ExtendedEndpointInstance) {
try {
await serviceControlStore.deleteFromMonitoring("monitored-instance/" + endpointName + "/" + instance.id);
await monitoringStore.deleteFromMonitoring("monitored-instance/" + endpointName + "/" + instance.id);
endpoint.value.instances.splice(endpoint.value.instances.indexOf(instance), 1);
if (endpoint.value.instances.length === 0) {
router.push(routeLinks.monitoring.root);
Expand All @@ -37,7 +37,7 @@ async function removeEndpoint(endpointName: string, instance: ExtendedEndpointIn

async function getIsRemovingEndpointEnabled() {
try {
const response = await serviceControlStore.optionsFromMonitoring();
const response = await monitoringStore.optionsFromMonitoring();
if (response) {
const headers = response.headers;
const allow = headers.get("Allow");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<script setup lang="ts">
import routeLinks from "@/router/routeLinks";
import { useServiceControlStore } from "@/stores/ServiceControlStore";
import { useMonitoringStore } from "@/stores/MonitoringStore";
import { storeToRefs } from "pinia";

const store = useServiceControlStore();
const store = useMonitoringStore();
const { monitoringUrl } = storeToRefs(store);
</script>

Expand Down
6 changes: 4 additions & 2 deletions src/Frontend/src/stores/ConnectionsAndStatsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import { FailedMessage, FailedMessageStatus } from "@/resources/FailedMessage";
import { ConnectionState } from "@/resources/ConnectionState";
import { useCounter } from "@vueuse/core";
import { useServiceControlStore } from "./ServiceControlStore";
import { useMonitoringStore } from "./MonitoringStore";

export const useConnectionsAndStatsStore = defineStore("ConnectionsAndStatsStore", () => {
const serviceControlStore = useServiceControlStore();
const { isMonitoringEnabled } = storeToRefs(serviceControlStore);
const monitoringStore = useMonitoringStore();
const { isMonitoringEnabled } = storeToRefs(monitoringStore);

const failedMessageCount = ref(0);
const archivedMessageCount = ref(0);
Expand Down Expand Up @@ -61,7 +63,7 @@ export const useConnectionsAndStatsStore = defineStore("ConnectionsAndStatsStore

function getDisconnectedEndpointsCount() {
return fetchAndSetConnectionState(
() => serviceControlStore.fetchTypedFromMonitoring<number>("monitored-endpoints/disconnected"),
() => monitoringStore.fetchTypedFromMonitoring<number>("monitored-endpoints/disconnected"),
monitoringConnectionState,
(_, data) => {
return data;
Expand Down
4 changes: 3 additions & 1 deletion src/Frontend/src/stores/EnvironmentAndVersionsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import { useMemoize } from "@vueuse/core";
import { acceptHMRUpdate, defineStore } from "pinia";
import { computed, reactive } from "vue";
import { useServiceControlStore } from "./ServiceControlStore";
import { useMonitoringStore } from "./MonitoringStore";

export const useEnvironmentAndVersionsStore = defineStore("EnvironmentAndVersionsStore", () => {
const serviceControlStore = useServiceControlStore();
const monitoringStore = useMonitoringStore();

const environment = reactive({
monitoring_version: "",
Expand Down Expand Up @@ -95,7 +97,7 @@ export const useEnvironmentAndVersionsStore = defineStore("EnvironmentAndVersion

async function setMonitoringVersion() {
try {
const [response] = await serviceControlStore.fetchTypedFromMonitoring("");
const [response] = await monitoringStore.fetchTypedFromMonitoring("");
if (response) {
environment.monitoring_version = response.headers.get("X-Particular-Version") ?? "";
}
Expand Down
8 changes: 4 additions & 4 deletions src/Frontend/src/stores/MonitoringEndpointDetailsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,22 @@ import type GroupOperation from "@/resources/GroupOperation";
import { emptyEndpointDetails } from "@/components/monitoring/endpoints";
import { useMemoize } from "@vueuse/core";
import useConnectionsAndStatsAutoRefresh from "@/composables/useConnectionsAndStatsAutoRefresh";
import { useServiceControlStore } from "./ServiceControlStore";
import { useMonitoringStore } from "./MonitoringStore";

export const useMonitoringEndpointDetailsStore = defineStore("MonitoringEndpointDetailsStore", () => {
const historyPeriodStore = useMonitoringHistoryPeriodStore();
const { store: connectionStore } = useConnectionsAndStatsAutoRefresh();
const serviceControlStore = useServiceControlStore();
const monitoringStore = useMonitoringStore();
const messageGroupClient = createMessageGroupClient();

const getMemoisedEndpointDetails = useMemoize((endpointName: string, historyPeriod = 1) => {
const data = ref<EndpointDetails | EndpointDetailsError | null>(null);
return {
data,
refresh: async () => {
if (serviceControlStore.isMonitoringEnabled) {
if (monitoringStore.isMonitoringEnabled) {
try {
const [, details] = await serviceControlStore.fetchTypedFromMonitoring<EndpointDetails>(`${`monitored-endpoints`}/${endpointName}?history=${historyPeriod}`);
const [, details] = await monitoringStore.fetchTypedFromMonitoring<EndpointDetails>(`${`monitored-endpoints`}/${endpointName}?history=${historyPeriod}`);
data.value = details!;
} catch (error) {
console.error(error);
Expand Down
74 changes: 71 additions & 3 deletions src/Frontend/src/stores/MonitoringStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,21 @@ import { useMonitoringHistoryPeriodStore } from "./MonitoringHistoryPeriodStore"
import type { EndpointGroup, Endpoint, GroupedEndpoint } from "@/resources/MonitoringEndpoint";
import type { SortInfo } from "@/components/SortInfo";
import useConnectionsAndStatsAutoRefresh from "@/composables/useConnectionsAndStatsAutoRefresh";
import { useServiceControlStore } from "./ServiceControlStore";
import GroupOperation from "@/resources/GroupOperation";
import { getParameter, getParams } from "./environment";
import { useServiceControlStore } from "./ServiceControlStore";

export const useMonitoringStore = defineStore("MonitoringStore", () => {
const historyPeriodStore = useMonitoringHistoryPeriodStore();

const route = useRoute();
const router = useRouter();
const { store: connectionStore } = useConnectionsAndStatsAutoRefresh();
//TODO: if/when a recoverabilityStore is created, replace this
const serviceControlStore = useServiceControlStore();

//STORE STATE CONSTANTS
const monitoringUrl = ref<string | null>();
const grouping = ref({
groupedEndpoints: [] as EndpointGroup[],
groupSegments: 0,
Expand All @@ -36,12 +39,39 @@ export const useMonitoringStore = defineStore("MonitoringStore", () => {
const endpointListIsGrouped = computed<boolean>(() => grouping.value.selectedGrouping !== 0);
const getEndpointList = computed<Endpoint[]>(() => (filterString.value ? endpointList.value.filter((endpoint) => endpoint.name.toLowerCase().includes(filterString.value.toLowerCase())) : endpointList.value));

const isMonitoringDisabled = computed(() => monitoringUrl.value == null || monitoringUrl.value === "" || monitoringUrl.value === "!");
const isMonitoringEnabled = computed(() => !isMonitoringDisabled.value);

watch(sortBy, async () => await updateEndpointList(), { deep: true });
watch(filterString, async (newValue) => {
await updateFilterString(newValue);
});

//STORE ACTIONS
function getMonitoringUrl() {
if (!monitoringUrl.value) refresh();
return monitoringUrl.value;
}

function refresh() {
const params = getParams();
const mu = getParameter(params, "mu");

if (mu) {
monitoringUrl.value = mu.value;
window.localStorage.setItem("mu", monitoringUrl.value);
console.debug(`Monitoring Url found in QS and stored in local storage: ${monitoringUrl.value}`);
} else if (window.localStorage.getItem("mu")) {
monitoringUrl.value = window.localStorage.getItem("mu");
console.debug(`Monitoring Url, not in QS, found in local storage: ${monitoringUrl.value}`);
} else if (window.defaultConfig && window.defaultConfig.monitoring_urls && window.defaultConfig.monitoring_urls.length) {
monitoringUrl.value = window.defaultConfig.monitoring_urls[0];
console.debug(`setting Monitoring Url to its default value: ${window.defaultConfig.monitoring_urls[0]}`);
} else {
console.warn("Monitoring Url is not defined.");
}
}

async function updateFilterString(filter: string | null = null) {
filterString.value = filter ?? route.query.filter?.toString() ?? "";

Expand Down Expand Up @@ -73,9 +103,9 @@ export const useMonitoringStore = defineStore("MonitoringStore", () => {

async function getAllMonitoredEndpoints() {
let endpoints: Endpoint[] = [];
if (serviceControlStore.isMonitoringEnabled) {
if (isMonitoringEnabled.value) {
try {
const [, data] = await serviceControlStore.fetchTypedFromMonitoring<Endpoint[]>(`monitored-endpoints?history=${historyPeriodStore.historyPeriod.pVal}`);
const [, data] = await fetchTypedFromMonitoring<Endpoint[]>(`monitored-endpoints?history=${historyPeriodStore.historyPeriod.pVal}`);
endpoints = data ?? [];
const [, exceptionGroups] = await serviceControlStore.fetchTypedFromServiceControl<GroupOperation[]>(`recoverability/groups/Endpoint Name`);

Expand Down Expand Up @@ -200,24 +230,62 @@ export const useMonitoringStore = defineStore("MonitoringStore", () => {
}
}

async function fetchTypedFromMonitoring<T>(suffix: string): Promise<[Response?, T?]> {
if (!monitoringUrl.value) refresh();

if (isMonitoringDisabled.value) {
return [];
}

const response = await fetch(`${getMonitoringUrl()}${suffix}`);
const data = await response.json();

return [response, data];
}

async function deleteFromMonitoring(suffix: string) {
const requestOptions = {
method: "DELETE",
};
return await fetch(`${getMonitoringUrl()}${suffix}`, requestOptions);
}

async function optionsFromMonitoring() {
if (isMonitoringDisabled.value) {
return Promise.resolve(null);
}

const requestOptions = {
method: "OPTIONS",
};
return await fetch(getMonitoringUrl() ?? "", requestOptions);
}

return {
//state
monitoringUrl,
grouping,
endpointList,
disconnectedEndpointCount,
filterString,
sortBy,

//getters
isMonitoringDisabled,
isMonitoringEnabled,
Comment on lines +274 to +275
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need two?

Copy link
Contributor Author

@PhilBastian PhilBastian Oct 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

¯_(ツ)_/¯. I guess it makes it more readable in the usage sites if it's a positive or negative based usage

endpointListCount,
endpointListIsEmpty,
endpointListIsGrouped,
getEndpointList,

//actions
refresh,
updateSelectedGrouping,
updateEndpointList,
updateFilterString,
fetchTypedFromMonitoring,
deleteFromMonitoring,
optionsFromMonitoring,
Comment on lines +286 to +288
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these be typed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what you're referring to

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example, instead of await monitoringStore.fetchTypedFromMonitoring<EndpointDetails>(${monitored-endpoints}/${endpointName}?history=${historyPeriod}); we would call monitoringStore.getHistory(endpointName, historyPeriod)
Or instead of monitoringStore.fetchTypedFromMonitoring<number>("monitored-endpoints/disconnected"), we would call monitoringStore.disconnected()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we applied the same logic to the servicecontrolstore, we'd have ~100 different functions exposed from the store.
It's an idea worth exploring, but not as part of this refactor IMO

};
});

Expand Down
Loading