From 4737f511b4a0f50c0f870ed70b4f22afaab36ae5 Mon Sep 17 00:00:00 2001 From: rick Date: Mon, 16 Jun 2025 14:10:59 +0800 Subject: [PATCH] feat: remeber the last query info on the data-manager page --- console/atest-ui/.editorconfig | 15 + console/atest-ui/src/views/DataManager.vue | 535 +++++++++++---------- console/atest-ui/src/views/cache.ts | 211 ++++---- 3 files changed, 412 insertions(+), 349 deletions(-) create mode 100644 console/atest-ui/.editorconfig diff --git a/console/atest-ui/.editorconfig b/console/atest-ui/.editorconfig new file mode 100644 index 00000000..1918605e --- /dev/null +++ b/console/atest-ui/.editorconfig @@ -0,0 +1,15 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 + +# 4 space indentation +[*.{py,proto,go,js,ts,json,vue}] +indent_style = space +indent_size = 4 diff --git a/console/atest-ui/src/views/DataManager.vue b/console/atest-ui/src/views/DataManager.vue index 7ba86e3b..f008c101 100644 --- a/console/atest-ui/src/views/DataManager.vue +++ b/console/atest-ui/src/views/DataManager.vue @@ -4,6 +4,7 @@ import { API } from './net' import type { QueryObject } from './net' import type { Store } from './store' import type { Pair } from './types' +import { GetDataManagerPreference, SetDataManagerPreference } from './cache' import { ElMessage } from 'element-plus' import { Codemirror } from 'vue-codemirror' import HistoryInput from '../components/HistoryInput.vue' @@ -14,8 +15,8 @@ const stores: Ref = ref([]) const kind = ref('') const store = ref('') const query = ref({ - offset: 0, - limit: 10 + offset: 0, + limit: 10 } as QueryObject) const currentTable = ref('') const sqlQuery = ref('') @@ -33,329 +34,339 @@ const largeContent = ref('') const largeContentDialogVisible = ref(false) interface TreeItem { - label: string + label: string } const tablesTree = ref([] as TreeItem[]) -watch(store, (s) => { - kind.value = '' - stores.value.forEach((e: Store) => { - if (e.name === s) { - kind.value = e.kind.name - return - } - }) - - switch (kind.value) { - case 'atest-store-elasticsearch': - case 'atest-store-etcd': - case 'atest-store-redis': - sqlQuery.value = '*' - complexEditor.value = false - break - default: - complexEditor.value = true - queryDataMeta.value.currentDatabase = '' - sqlQuery.value = '' +const storeChangedEvent = (s: string) => { + kind.value = '' + SetDataManagerPreference('currentStore', s) + stores.value.forEach((e: Store) => { + if (e.name === s) { + kind.value = e.kind.name + return } + }) - executeQuery() -}) + switch (kind.value) { + case 'atest-store-elasticsearch': + case 'atest-store-etcd': + case 'atest-store-redis': + sqlQuery.value = '*' + complexEditor.value = false + break + default: + complexEditor.value = true + } + + queryDataMeta.value.currentDatabase = GetDataManagerPreference().currentDatabase + executeQuery() +} +watch(store, storeChangedEvent) interface QueryDataMeta { - databases: string[] - tables: string[] - currentDatabase: string - duration: string - labels: Pair[] + databases: string[] + tables: string[] + currentDatabase: string + duration: string + labels: Pair[] } interface QueryData { - items: any[] - data: any[] - label: string - meta: QueryDataMeta + items: any[] + data: any[] + label: string + meta: QueryDataMeta } const queryDataFromTable = (data: QueryData) => { - sqlQuery.value = `@selectTableLImit100_${data.label}` - currentTable.value = data.label - executeQuery() + sqlQuery.value = `@selectTableLImit100_${data.label}` + currentTable.value = data.label + executeQuery() } const describeTable = (data: QueryData) => { - switch (kind.value) { - case 'atest-store-cassandra': - sqlQuery.value = `@describeTable_${queryDataMeta.value.currentDatabase}:${data.label}` - break - break - default: - sqlQuery.value = `@describeTable_${data.label}` - } - executeQuery() + switch (kind.value) { + case 'atest-store-cassandra': + sqlQuery.value = `@describeTable_${queryDataMeta.value.currentDatabase}:${data.label}` + break + default: + sqlQuery.value = `@describeTable_${data.label}` + } + executeQuery() } const queryTables = () => { - executeQuery() + executeQuery() } watch(kind, (k) => { - switch (k) { - case 'atest-store-orm': - case 'atest-store-cassandra': - case 'atest-store-iotdb': - queryTip.value = 'Enter SQL query' - executeQuery() - break; - case 'atest-store-etcd': - case 'atest-store-redis': - queryTip.value = 'Enter key' - break; - case 'atest-store-elasticsearch': - queryTip.value = 'field:value OR field:other' - break; - } + switch (k) { + case 'atest-store-orm': + case 'atest-store-cassandra': + case 'atest-store-iotdb': + queryTip.value = 'Enter SQL query' + executeQuery() + break; + case 'atest-store-etcd': + case 'atest-store-redis': + queryTip.value = 'Enter key' + break; + case 'atest-store-elasticsearch': + queryTip.value = 'field:value OR field:other' + break; + } +}) +watch(sqlQuery, (sql: string) => { + SetDataManagerPreference('query', sql) }) API.GetStores((data) => { - stores.value = data.data + stores.value = data.data + + sqlQuery.value = GetDataManagerPreference().query + store.value = GetDataManagerPreference().currentStore + if (store.value) { + storeChangedEvent(store.value) + } }, (e) => { - ElMessage({ - showClose: true, - message: e.message, - type: 'error' - }); + ElMessage({ + showClose: true, + message: e.message, + type: 'error' + }); }, () => { - loadingStores.value = false + loadingStores.value = false }) const ormDataHandler = (data: QueryData) => { - const result = [] as any[] - const cols = new Set() + const result = [] as any[] + const cols = new Set() - data.items.forEach(e => { - const obj = {} - e.data.forEach((item: Pair) => { - obj[item.key] = item.value - cols.add(item.key) - }) - result.push(obj) + data.items.forEach(e => { + const obj = {} + e.data.forEach((item: Pair) => { + obj[item.key] = item.value + cols.add(item.key) }) + result.push(obj) + }) - columns.value = [] as string[] - data.meta.labels = data.meta.labels.filter((item) => { - if (item.key === '_native_sql') { - sqlQuery.value = item.value - return false - } - if (item.key === '_columns') { - columns.value = JSON.parse(item.value) - } - return !item.key.startsWith('_') - }) - - queryDataMeta.value = data.meta - queryResult.value = result - queryResultAsJSON.value = JSON.stringify(result, null, 2) - if (columns.value.length == 0) { - columns.value = Array.from(cols).sort((a, b) => { - if (a === 'id') return -1; - if (b === 'id') return 1; - return a.localeCompare(b); - }) + columns.value = [] as string[] + data.meta.labels = data.meta.labels.filter((item) => { + if (item.key === '_native_sql') { + sqlQuery.value = item.value + return false } + if (item.key === '_columns') { + columns.value = JSON.parse(item.value) + } + return !item.key.startsWith('_') + }) + + SetDataManagerPreference('currentDatabase', data.meta.currentDatabase) + queryDataMeta.value = data.meta + queryResult.value = result + queryResultAsJSON.value = JSON.stringify(result, null, 2) + if (columns.value.length == 0) { + columns.value = Array.from(cols).sort((a, b) => { + if (a === 'id') return -1; + if (b === 'id') return 1; + return a.localeCompare(b); + }) + } - tablesTree.value = [] - queryDataMeta.value.tables.forEach((i) => { - tablesTree.value.push({ - label: i, - }) + tablesTree.value = [] + queryDataMeta.value.tables.forEach((i) => { + tablesTree.value.push({ + label: i, }) + }) } const keyValueDataHandler = (data: QueryData) => { - queryResult.value = [] - columns.value = ['key', 'value'] - data.data.forEach(e => { - queryResult.value.push({ - key: e.key, - value: e.value - }) + queryResult.value = [] + columns.value = ['key', 'value'] + data.data.forEach(e => { + queryResult.value.push({ + key: e.key, + value: e.value }) + }) } const executeQuery = async () => { - if (sqlQuery.value === '') { - switch (kind.value) { - case 'atest-store-elasticsearch': - case 'atest-store-etcd': - case 'atest-store-redis': - sqlQuery.value = '*' - break - default: - sqlQuery.value = '' + if (sqlQuery.value === '') { + switch (kind.value) { + case 'atest-store-elasticsearch': + case 'atest-store-etcd': + case 'atest-store-redis': + if (sqlQuery.value === '') { + sqlQuery.value = '*' } + break } - return executeWithQuery(sqlQuery.value) + } + return executeWithQuery(sqlQuery.value) } const executeWithQuery = async (sql: string) => { - let success = false - query.value.store = store.value - query.value.key = queryDataMeta.value.currentDatabase - query.value.sql = sql + let success = false + query.value.store = store.value + query.value.key = queryDataMeta.value.currentDatabase + query.value.sql = sql - try { - const data = await API.DataQueryAsync(query.value); - switch (kind.value) { - case 'atest-store-orm': - case 'atest-store-cassandra': - case 'atest-store-iotdb': - case 'atest-store-opengemini': - case 'atest-store-elasticsearch': - ormDataHandler(data) - success = true - break; - case 'atest-store-etcd': - case 'atest-store-redis': - keyValueDataHandler(data) - break; - default: - ElMessage({ - showClose: true, - message: 'Unsupported store kind', - type: 'error' - }); - } - } catch (e: any) { + try { + const data = await API.DataQueryAsync(query.value); + switch (kind.value) { + case 'atest-store-orm': + case 'atest-store-cassandra': + case 'atest-store-iotdb': + case 'atest-store-opengemini': + case 'atest-store-elasticsearch': + ormDataHandler(data) + success = true + break; + case 'atest-store-etcd': + case 'atest-store-redis': + keyValueDataHandler(data) + break; + default: ElMessage({ - showClose: true, - message: e.message, - type: 'error' + showClose: true, + message: 'Unsupported store kind', + type: 'error' }); } - return success + } catch (e: any) { + ElMessage({ + showClose: true, + message: e.message, + type: 'error' + }); + } + return success } const nextPage = () => { - query.value.offset = Number(query.value.limit) + Number(query.value.offset) - executeQuery() + query.value.offset = Number(query.value.limit) + Number(query.value.offset) + executeQuery() } const overflowChange = () => { - showOverflowTooltip.value = !showOverflowTooltip.value + showOverflowTooltip.value = !showOverflowTooltip.value } const tryShowPrettyJSON = (row: any, column: any, cell: HTMLTableCellElement, event: Event) => { - const cellValue = row[column.rawColumnKey] - const obj = JSON.parse(cellValue) - if (obj) { - largeContent.value = JSON.stringify(obj, null, 2) - } + const cellValue = row[column.rawColumnKey] + const obj = JSON.parse(cellValue) + if (obj) { + largeContent.value = JSON.stringify(obj, null, 2) + } } watch(largeContent, (e) => { - largeContentDialogVisible.value = e !== '' + largeContentDialogVisible.value = e !== '' }) diff --git a/console/atest-ui/src/views/cache.ts b/console/atest-ui/src/views/cache.ts index 03d01678..e21e7df1 100644 --- a/console/atest-ui/src/views/cache.ts +++ b/console/atest-ui/src/views/cache.ts @@ -14,146 +14,183 @@ See the License for the specific language governing permissions and limitations under the License. */ export interface TestCaseResponse { - output: string - body: {}, - statusCode: number + output: string + body: {}, + statusCode: number } export interface Store { - name: string - readOnly: boolean + name: string + readOnly: boolean } export interface Stores { - current: string - items: Store[] + current: string + items: Store[] } export interface Preference { - darkTheme: boolean - requestActiveTab: string, - responseActiveTab: string + darkTheme: boolean + requestActiveTab: string, + responseActiveTab: string } export function GetTestCaseResponseCache(id: string) { - const val = sessionStorage.getItem(id) - if (val && val !== '') { - return JSON.parse(val) - } else { - return {} as TestCaseResponse - } + const val = sessionStorage.getItem(id) + if (val && val !== '') { + return JSON.parse(val) + } else { + return {} as TestCaseResponse + } } export function SetTestCaseResponseCache(id: string, resp: TestCaseResponse) { - sessionStorage.setItem(id, JSON.stringify(resp)) + sessionStorage.setItem(id, JSON.stringify(resp)) } const lastTestCaseLocationKey = "api-testing-case-location" export function GetLastTestCaseLocation() { - const val = localStorage.getItem(lastTestCaseLocationKey) - if (val && val !== '') { - return JSON.parse(val) - } else { - return {} - } + const val = localStorage.getItem(lastTestCaseLocationKey) + if (val && val !== '') { + return JSON.parse(val) + } else { + return {} + } } export function SetLastTestCaseLocation(suite: string, testcase: string) { - localStorage.setItem(lastTestCaseLocationKey, JSON.stringify({ - suite: suite, - testcase: testcase - })) - return + localStorage.setItem(lastTestCaseLocationKey, JSON.stringify({ + suite: suite, + testcase: testcase + })) + return } const preferenceKey = "api-testing-preference" export function GetPreference() { - const val = localStorage.getItem(preferenceKey) - if (val && val !== '') { - return JSON.parse(val) - } else { - const navLanguage = navigator.language != null ? navigator.language : 'zh-CN'; - return { - darkTheme: false, - language: navLanguage, - requestActiveTab: "body", - responseActiveTab: "body" - } as Preference - } + const val = localStorage.getItem(preferenceKey) + if (val && val !== '') { + return JSON.parse(val) + } else { + const navLanguage = navigator.language != null ? navigator.language : 'zh-CN'; + return { + darkTheme: false, + language: navLanguage, + requestActiveTab: "body", + responseActiveTab: "body" + } as Preference + } } export function SetPreference(preference: Preference) { - localStorage.setItem(preferenceKey, JSON.stringify(preference)) - return + localStorage.setItem(preferenceKey, JSON.stringify(preference)) + return } export function WithRequestActiveTab(tab: string) { - const preference = GetPreference() - preference.requestActiveTab = tab - SetPreference(preference) + const preference = GetPreference() + preference.requestActiveTab = tab + SetPreference(preference) } function WithResponseActiveTab(tab: string) { - const preference = GetPreference() - preference.responseActiveTab = tab - SetPreference(preference) + const preference = GetPreference() + preference.responseActiveTab = tab + SetPreference(preference) } function WithDarkTheme(darkTheme: boolean) { - const preference = GetPreference() - preference.darkTheme = darkTheme - SetPreference(preference) + const preference = GetPreference() + preference.darkTheme = darkTheme + SetPreference(preference) } function WithLocale(locale: string) { - const preference = GetPreference() - preference.language = locale - SetPreference(preference) + const preference = GetPreference() + preference.language = locale + SetPreference(preference) } const storeKey = "stores" function GetCurrentStore() { - const val = sessionStorage.getItem(storeKey) - if (val && val !== '') { - const stores = JSON.parse(val) - for (let i = 0; i < stores.items.length; i++) { - if (stores.items[i].name === stores.current) { - return stores.items[i] - } - } + const val = sessionStorage.getItem(storeKey) + if (val && val !== '') { + const stores = JSON.parse(val) + for (let i = 0; i < stores.items.length; i++) { + if (stores.items[i].name === stores.current) { + return stores.items[i] + } } - return {} + } + return {} } function SetCurrentStore(name: string) { - const val = sessionStorage.getItem(storeKey) - if (val && val !== '') { - const stores = JSON.parse(val) - stores.current = name - SetStores(stores) - } + const val = sessionStorage.getItem(storeKey) + if (val && val !== '') { + const stores = JSON.parse(val) + stores.current = name + SetStores(stores) + } } function SetStores(stores: Stores | Store[]) { - if ('current' in stores) { - sessionStorage.setItem(storeKey, JSON.stringify(stores)) - } else { - sessionStorage.setItem(storeKey, JSON.stringify({ - items: stores - })) - } - return + if ('current' in stores) { + sessionStorage.setItem(storeKey, JSON.stringify(stores)) + } else { + sessionStorage.setItem(storeKey, JSON.stringify({ + items: stores + })) + } + return +} + +interface DataManagerPreference { + currentStore: string + currentDatabase: string + query: string +} + +const DataManagerPreferenceKey = "data-manager-preference" +export function GetDataManagerPreference(): DataManagerPreference { + const val = localStorage.getItem(DataManagerPreferenceKey) + if (val && val !== '') { + return JSON.parse(val) + } else { + return { + currentStore: '' + } as DataManagerPreference + } +} + +export function SetDataManagerPreference(field: string, value: string) { + const preference = GetDataManagerPreference() + switch (field) { + case 'currentStore': + preference.currentStore = value + break; + case 'currentDatabase': + preference.currentDatabase = value + break; + case 'query': + preference.query = value + break; + default: + return + } + localStorage.setItem(DataManagerPreferenceKey, JSON.stringify(preference)) + return } export const Cache = { - GetTestCaseResponseCache, - SetTestCaseResponseCache, - GetLastTestCaseLocation, - SetLastTestCaseLocation, - GetPreference, - WithRequestActiveTab, - WithResponseActiveTab, - WithDarkTheme, - WithLocale, - GetCurrentStore, SetStores, SetCurrentStore + GetTestCaseResponseCache, + SetTestCaseResponseCache, + GetLastTestCaseLocation, + SetLastTestCaseLocation, + GetPreference, + WithRequestActiveTab, + WithResponseActiveTab, + WithDarkTheme, + WithLocale, + GetCurrentStore, SetStores, SetCurrentStore }