From 4b92c092a9a7dbde86a1da9ba9192d5b49dcce99 Mon Sep 17 00:00:00 2001 From: ktx-vaidehi <134508096+ktx-vaidehi@users.noreply.github.com> Date: Tue, 30 Apr 2024 22:41:56 +0530 Subject: [PATCH] feat: Query Cancellation (#3207) Co-authored-by: oasisk Co-authored-by: Hengfei Yang --- src/handler/http/request/status/mod.rs | 2 + src/infra/src/errors/mod.rs | 4 +- .../assets/images/common/management_query.svg | 17 + web/src/components/icons/ManagementIcon.vue | 58 +++ web/src/components/icons/SlackIcon.vue | 22 +- web/src/components/queries/QueryList.vue | 165 ++++++ .../components/queries/RunningQueriesList.vue | 482 ++++++++++++++++++ web/src/components/settings/General.vue | 8 +- web/src/components/settings/index.vue | 54 +- web/src/composables/shared/router.ts | 23 +- .../composables/shared/useManagementRoutes.ts | 50 ++ web/src/composables/useIsMetaOrg.ts | 15 + web/src/composables/useLogs.ts | 5 + web/src/enterprise/composables/router.ts | 7 +- web/src/layouts/MainLayout.vue | 4 +- web/src/locales/languages/en.json | 23 +- web/src/services/search.ts | 8 + 17 files changed, 889 insertions(+), 58 deletions(-) create mode 100644 web/src/assets/images/common/management_query.svg create mode 100644 web/src/components/icons/ManagementIcon.vue create mode 100644 web/src/components/queries/QueryList.vue create mode 100644 web/src/components/queries/RunningQueriesList.vue create mode 100644 web/src/composables/shared/useManagementRoutes.ts create mode 100644 web/src/composables/useIsMetaOrg.ts diff --git a/src/handler/http/request/status/mod.rs b/src/handler/http/request/status/mod.rs index 6dffc03031..26b51dac7b 100644 --- a/src/handler/http/request/status/mod.rs +++ b/src/handler/http/request/status/mod.rs @@ -94,6 +94,7 @@ struct ConfigResponse<'a> { rum: Rum, custom_logo_img: Option, custom_hide_menus: String, + meta_org: String, } #[derive(Serialize)] @@ -234,6 +235,7 @@ pub async fn zo_config() -> Result { api_version: CONFIG.rum.api_version.to_string(), insecure_http: CONFIG.rum.insecure_http, }, + meta_org: CONFIG.common.usage_org.to_string(), })) } diff --git a/src/infra/src/errors/mod.rs b/src/infra/src/errors/mod.rs index 9808cc231f..8fb27e2f46 100644 --- a/src/infra/src/errors/mod.rs +++ b/src/infra/src/errors/mod.rs @@ -146,7 +146,9 @@ impl ErrorCodes { format!("Search field has no compatible data type: {field}") } ErrorCodes::SearchSQLExecuteError(_) => "Search SQL execute error".to_string(), - ErrorCodes::SearchCancelQuery(_) => "Search query cancelled".to_string(), + ErrorCodes::SearchCancelQuery(_) => { + "Search query was cancelled by the administrator".to_string() + } } } diff --git a/web/src/assets/images/common/management_query.svg b/web/src/assets/images/common/management_query.svg new file mode 100644 index 0000000000..49010cb506 --- /dev/null +++ b/web/src/assets/images/common/management_query.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/web/src/components/icons/ManagementIcon.vue b/web/src/components/icons/ManagementIcon.vue new file mode 100644 index 0000000000..e2dc4ef67c --- /dev/null +++ b/web/src/components/icons/ManagementIcon.vue @@ -0,0 +1,58 @@ + + + diff --git a/web/src/components/icons/SlackIcon.vue b/web/src/components/icons/SlackIcon.vue index a9bf55a768..4f2a2b47a6 100644 --- a/web/src/components/icons/SlackIcon.vue +++ b/web/src/components/icons/SlackIcon.vue @@ -1,15 +1,21 @@ \ No newline at end of file + name: "SlackIcon", +}); + diff --git a/web/src/components/queries/QueryList.vue b/web/src/components/queries/QueryList.vue new file mode 100644 index 0000000000..5484b42837 --- /dev/null +++ b/web/src/components/queries/QueryList.vue @@ -0,0 +1,165 @@ + + + + + diff --git a/web/src/components/queries/RunningQueriesList.vue b/web/src/components/queries/RunningQueriesList.vue new file mode 100644 index 0000000000..3e98f78de6 --- /dev/null +++ b/web/src/components/queries/RunningQueriesList.vue @@ -0,0 +1,482 @@ + + + + + + + diff --git a/web/src/components/settings/General.vue b/web/src/components/settings/General.vue index 973ad108a0..85f672ceaa 100644 --- a/web/src/components/settings/General.vue +++ b/web/src/components/settings/General.vue @@ -106,9 +106,9 @@ along with this program. If not, see .
- {{ +
{{ t("settings.customLogoText") - }}
{{ store.state.zoConfig.custom_logo_text || "No Text Available" }} .
- {{ t("settings.customLogoTitle") }} {{ t("settings.customLogoTitle") }}

+ { + if (router.currentRoute.value.name === "settings") { + if (isMetaOrg.value && config.isEnterprise === "true") { + settingsTab.value = "queryManagement"; + router.push({ + path: "/settings/query_management", + query: { + org_identifier: store.state.selectedOrganization.identifier, + }, + }); + } else { + settingsTab.value = "general"; + router.push({ + path: "/settings/general", + query: { + org_identifier: store.state.selectedOrganization.identifier, + }, + }); + } + } + }; onBeforeMount(() => { - if (router.currentRoute.value.name == "settings") { - settingsTab.value = "general"; - router.push({ path: "/settings/general" }); - } + handleSettingsRouting(); }); - // render general settings component onActivated(() => { - settingsTab.value = "general"; - router.push({ path: "/settings/general" }); + handleSettingsRouting(); }); onUpdated(() => { - if (router.currentRoute.value.name === "settings") { - settingsTab.value = "general"; - router.push({ - name: "general", - query: { - org_identifier: store.state.selectedOrganization.identifier, - }, - }); - return; - } + handleSettingsRouting(); }); return { @@ -111,6 +128,7 @@ export default defineComponent({ settingsTab, splitterModel: ref(200), outlinedSettings, + isMetaOrg, }; }, }); diff --git a/web/src/composables/shared/router.ts b/web/src/composables/shared/router.ts index a7e3170352..aa61198ec0 100644 --- a/web/src/composables/shared/router.ts +++ b/web/src/composables/shared/router.ts @@ -62,6 +62,7 @@ import { routeGuard } from "@/utils/zincutils"; import useIngestionRoutes from "./useIngestionRoutes"; import useIamRoutes from "./useIamRoutes"; import config from "@/aws-exports"; +import useManagementRoutes from "./useManagementRoutes"; const useRoutes = () => { const parentRoutes: never[] = []; @@ -202,27 +203,7 @@ const useRoutes = () => { routeGuard(to, from, next); }, }, - { - path: "settings", - name: "settings", - component: Settings, - meta: { - keepAlive: true, - }, - beforeEnter(to: any, from: any, next: any) { - routeGuard(to, from, next); - }, - children: [ - { - path: "general", - name: "general", - component: () => import("@/components/settings/General.vue"), - beforeEnter(to: any, from: any, next: any) { - routeGuard(to, from, next); - }, - }, - ], - }, + ...useManagementRoutes(), { path: "functions", name: "functions", diff --git a/web/src/composables/shared/useManagementRoutes.ts b/web/src/composables/shared/useManagementRoutes.ts new file mode 100644 index 0000000000..007e624314 --- /dev/null +++ b/web/src/composables/shared/useManagementRoutes.ts @@ -0,0 +1,50 @@ +import config from "@/aws-exports"; +import { routeGuard } from "@/utils/zincutils"; +const Settings = () => import("@/components/settings/index.vue"); + +const useManagementRoutes = () => { + const routes: any = [ + { + path: "settings", + name: "settings", + component: Settings, + meta: { + keepAlive: true, + }, + beforeEnter(to: any, from: any, next: any) { + routeGuard(to, from, next); + }, + children: [ + { + path: "general", + name: "general", + component: () => import("@/components/settings/General.vue"), + beforeEnter(to: any, from: any, next: any) { + routeGuard(to, from, next); + }, + }, + ], + }, + ]; + if (config.isEnterprise == "true") { + routes[0].children.push( + ...[ + { + path: "query_management", + name: "query_management", + component: () => + import("@/components/queries/RunningQueriesList.vue"), + meta: { + keepAlive: true, + }, + beforeEnter(to: any, from: any, next: any) { + routeGuard(to, from, next); + }, + }, + ] + ); + } + return routes; +}; + +export default useManagementRoutes; diff --git a/web/src/composables/useIsMetaOrg.ts b/web/src/composables/useIsMetaOrg.ts new file mode 100644 index 0000000000..dae63162c4 --- /dev/null +++ b/web/src/composables/useIsMetaOrg.ts @@ -0,0 +1,15 @@ +import { computed } from "vue"; +import { useStore } from "vuex"; + +export default function useIsMetaOrg() { + + const store = useStore(); + + const isMetaOrg = computed(() => { + return store.state.selectedOrganization.label === store.state.zoConfig.meta_org; + }); + + return { + isMetaOrg, + }; +} diff --git a/web/src/composables/useLogs.ts b/web/src/composables/useLogs.ts index bc90da7c8b..f982f4bcb4 100644 --- a/web/src/composables/useLogs.ts +++ b/web/src/composables/useLogs.ts @@ -1416,6 +1416,11 @@ const useLogs = () => { if (customMessage != "") { searchObj.data.errorMsg = t(customMessage); } + + if (err?.response?.data?.code == 429) { + notificationMsg.value = err.response.data.message; + searchObj.data.errorMsg = err.response.data.message; + } reject(false); }); }); diff --git a/web/src/enterprise/composables/router.ts b/web/src/enterprise/composables/router.ts index e77452677e..c5003fa74e 100644 --- a/web/src/enterprise/composables/router.ts +++ b/web/src/enterprise/composables/router.ts @@ -72,7 +72,12 @@ const useEnvRoutes = () => { path: "/cb", name: "callback", component: LoginCallback, - }, + }, + { + path: "/web/cb", + name: "callback", + component: LoginCallback, + }, ]; const homeChildRoutes = [ diff --git a/web/src/layouts/MainLayout.vue b/web/src/layouts/MainLayout.vue index e883ffe34c..78dd58afb5 100644 --- a/web/src/layouts/MainLayout.vue +++ b/web/src/layouts/MainLayout.vue @@ -353,6 +353,7 @@ import { outlinedDescription, } from "@quasar/extras/material-icons-outlined"; import SlackIcon from "@/components/icons/SlackIcon.vue"; +import ManagementIcon from "@/components/icons/ManagementIcon.vue"; import organizations from "@/services/organizations"; import useStreams from "@/composables/useStreams"; import { openobserveRum } from "@openobserve/browser-rum"; @@ -389,6 +390,7 @@ export default defineComponent({ "q-select": QSelect, ThemeSwitcher, SlackIcon, + ManagementIcon, }, methods: { navigateToDocs() { @@ -532,7 +534,7 @@ export default defineComponent({ }, { title: t("menu.settings"), - icon: outlinedSettings, + iconComponent: markRaw(ManagementIcon), link: "/settings/", name: "settings", }, diff --git a/web/src/locales/languages/en.json b/web/src/locales/languages/en.json index 583087b9c4..a85fc8d43b 100644 --- a/web/src/locales/languages/en.json +++ b/web/src/locales/languages/en.json @@ -51,7 +51,8 @@ "actions": "Actions", "type": "Type", "search": "Search", - "expand": "Expand" + "expand": "Expand", + "date": "Date" }, "search": { "selectIndex": "Select Stream First", @@ -134,7 +135,7 @@ "docs": "Docs", "billings": "Billing", "rum": "Real User Monitoring", - "settings": "Settings", + "settings": "Management", "iam": "IAM", "report": "Reports" }, @@ -788,7 +789,7 @@ "addToDashboard": "Add to dashboard" }, "settings": { - "header": "Settings", + "header": "Management", "userKeyLabel": "User Key", "rumKeyLabel": "RUM Key", "newUserKeyBtnLbl": "Add User Key", @@ -800,7 +801,8 @@ "updateuserKey": "Update User Key", "id": "Id", "update": "Update", - "generalLabel": "General", + "generalLabel": "Settings", + "queryManagement": "Query Management", "apikeyLabel": "API Keys", "generalPageTitle": "General Settings", "scrapintervalLabel": "Scrape Interval (In Seconds) *", @@ -828,5 +830,18 @@ "description": "Description", "cron": "Cron Job", "cronExpression": "Cron expression" + }, + "queries": { + "runningQueries": "Running Queries", + "search": "Search", + "sessionId": "Session ID", + "initiatedAt": "Initiated At", + "query": "Query", + "queryList": "Query Details", + "duration": "Exec. Duration", + "status": "Status", + "cancelQuery": "Cancel Query", + "queryRange": "Query Range", + "refreshQuery": "Refresh Query" } } diff --git a/web/src/services/search.ts b/web/src/services/search.ts index 9772717196..7dbd0b84d2 100644 --- a/web/src/services/search.ts +++ b/web/src/services/search.ts @@ -128,6 +128,14 @@ const search = { const url = `/api/${org_identifier}/_search_partition?type=${page_type}`; return http().post(url, query); }, + get_running_queries: () => { + const url = `/api/query_manager/status`; + return http().get(url); + }, + delete_running_query: (traceID: string) => { + const url = `/api/query_manager/${traceID}`; + return http().delete(url); + }, }; export default search;