浏览代码

[frontend] Use api/utils for loading Impala data

sreenaths 2 年之前
父节点
当前提交
922e9578f5

+ 8 - 1
desktop/core/src/desktop/js/api/utils.ts

@@ -36,12 +36,14 @@ export interface DefaultApiResponse {
   responseText?: string;
   statusText?: string;
   traceback?: string;
+  content?: string;
 }
 
 export interface ApiFetchOptions<T, E = string> extends AxiosRequestConfig {
   silenceErrors?: boolean;
   ignoreSuccessErrors?: boolean;
   transformResponse?: AxiosTransformer;
+  qsEncodeData?: boolean;
   handleSuccess?: (
     response: T & DefaultApiResponse,
     resolve: (val: T) => void,
@@ -118,6 +120,9 @@ export const extractErrorMessage = (
     } catch (err) {}
     return defaultResponse.responseText;
   }
+  if (defaultResponse.message && defaultResponse.content) {
+    return `${defaultResponse.message}\n${defaultResponse.content}`;
+  }
   if (errorResponse.message) {
     return errorResponse.message;
   }
@@ -187,8 +192,10 @@ export const post = <T, U = unknown>(
     const { cancelToken, cancel } = getCancelToken();
     let completed = false;
 
+    const encodeData = options?.qsEncodeData == undefined || options?.qsEncodeData;
+
     axiosInstance
-      .post<T & DefaultApiResponse>(url, qs.stringify(data), {
+      .post<T & DefaultApiResponse>(url, encodeData ? qs.stringify(data) : data, {
         cancelToken,
         ...options
       })

+ 8 - 40
desktop/core/src/desktop/js/apps/jobBrowser/commons/api-utils/api.test.ts

@@ -16,50 +16,18 @@
  * limitations under the License.
  */
 
-import api from './api';
 import { ApiError } from './api';
-import { AxiosAdapter, AxiosPromise, AxiosRequestConfig } from 'axios';
-
-const TEST_URL = 'http://test-url';
-
-const TEST_MSG = 'Test msg';
-const TEST_DETAILS = 'Test details';
 
 describe('api.ts', () => {
-  it('Interceptor should throw ApiError if response contain invalid status, even if HTTP status is 200', async () => {
-    // eslint-disable-next-line @typescript-eslint/no-explicit-any
-    api.defaults.adapter = <AxiosAdapter>((config: AxiosRequestConfig): AxiosPromise<any> => {
-      // eslint-disable-next-line @typescript-eslint/no-explicit-any
-      return <AxiosPromise<any>>new Promise((resolve: any) => {
-        const response = {
-          data: {
-            status: -1,
-            message: TEST_MSG,
-            content: TEST_DETAILS
-          },
-          status: 200,
-          statusText: '',
-          headers: [],
-          config
-        };
-        resolve(response);
-      });
-    });
-
-    let response!: ApiError;
-
-    try {
-      await api.get(TEST_URL);
-    } catch (e) {
-      response = e;
-    }
+  it('ApiError instantiation', async () => {
+    expect(ApiError).toBeTruthy();
 
-    expect(response).toBeTruthy();
-    expect(response.message).toBe(TEST_MSG);
-    expect(response.details).toBe(TEST_DETAILS);
-  });
+    let apiError = new ApiError('error message 1');
+    expect(apiError.message).toEqual('error message 1');
+    expect(apiError.details).toEqual(undefined);
 
-  it('ApiError', async () => {
-    expect(ApiError).toBeTruthy();
+    apiError = new ApiError('error message 2\nerror details\nerror details');
+    expect(apiError.message).toEqual('error message 2');
+    expect(apiError.details).toEqual('error details\nerror details');
   });
 });

+ 11 - 17
desktop/core/src/desktop/js/apps/jobBrowser/commons/api-utils/api.ts

@@ -16,26 +16,20 @@
  * limitations under the License.
  */
 
-import axios, { AxiosInstance, AxiosResponse } from 'axios';
+export class ApiError extends Error {
+  details: string | undefined;
 
-const api: AxiosInstance = axios.create();
-export default api;
+  constructor(message: string) {
+    let firstLine = message;
+    let details: string | undefined = undefined;
 
-api.interceptors.response.use(
-  (response: AxiosResponse): AxiosResponse => {
-    if (response.data.status === -1) {
-      throw new ApiError(response);
+    const firstLineEnd = message.indexOf('\n');
+    if (firstLineEnd != -1) {
+      firstLine = message.substring(0, firstLineEnd);
+      details = message.substring(firstLineEnd + 1);
     }
-    return response;
-  },
-  error => Promise.reject(error)
-);
-
-export class ApiError extends Error {
-  details!: string;
 
-  constructor(response: AxiosResponse) {
-    super(response.data.message);
-    this.details = response.data.content;
+    super(firstLine);
+    this.details = details;
   }
 }

+ 3 - 3
desktop/core/src/desktop/js/apps/jobBrowser/components/impalaQueries/ImpalaQueries.vue

@@ -151,7 +151,7 @@
           this.searchMeta = searchResponse.meta;
           this.queries = searchResponse.queries;
         } catch (error) {
-          this.error = error as ApiError;
+          this.error = new ApiError(String(error));
         }
         this.loading = false;
       },
@@ -164,7 +164,7 @@
           const fetchPromises = queriesToDiff.map(query => loadQuery(query.queryId));
           this.queriesToDiff = await Promise.all(fetchPromises);
         } catch (error) {
-          this.error = error as ApiError;
+          this.error = new ApiError(String(error));
         }
         this.loading = false;
       },
@@ -174,7 +174,7 @@
         try {
           this.selectedQuery = await loadQuery(query.queryId);
         } catch (error) {
-          this.error = error as ApiError;
+          this.error = new ApiError(String(error));
         }
         this.loading = false;
       }

+ 9 - 12
desktop/core/src/desktop/js/apps/jobBrowser/components/impalaQueries/api/query.ts

@@ -16,28 +16,25 @@
  * limitations under the License.
  */
 
-import api from '../../../commons/api-utils/api';
-import { AxiosResponse } from 'axios';
-
 import { ImpalaQuery, ImpalaQueryProfile } from '../index.d';
 import { SearchRequest, SearchResponse } from '../../../commons/api-utils/search';
+import { get, post } from 'api/utils';
 
 const QUERIES_URL = '/jobbrowser/query-store/api/impala/queries';
 
-export const searchQueries = async <Q>(options: SearchRequest): Promise<SearchResponse<Q>> => {
-  const response = await api.post<SearchRequest, AxiosResponse<SearchResponse<Q>>>(
-    QUERIES_URL,
-    options
-  );
-  return response.data;
+export const searchQueries = async <Q>(data: SearchRequest): Promise<SearchResponse<Q>> => {
+  const response = await post<SearchResponse<Q>>(QUERIES_URL, data, {
+    qsEncodeData: false
+  });
+  return response;
 };
 
 type QueryData = { query: ImpalaQuery; profile: ImpalaQueryProfile };
 export const loadQuery = async (queryId: string): Promise<ImpalaQuery> => {
   const url = `${QUERIES_URL}/${encodeURIComponent(queryId)}`;
-  const response = await api.get<ImpalaQuery, AxiosResponse<QueryData>>(url);
+  const response = await get<QueryData>(url);
   return {
-    ...response.data.query,
-    profile: response.data.profile
+    ...response.query,
+    profile: response.profile
   };
 };

+ 1 - 3
desktop/core/src/desktop/js/components/InlineAlert.vue

@@ -35,9 +35,7 @@
       <a v-if="details" class="more-less-button" @click="showDetails = false">
         {{ I18n('Less details') }}
       </a>
-      <pre class="inline-alert-details">
-        {{ details }}
-      </pre>
+      <pre class="inline-alert-details">{{ details }}</pre>
     </div>
     <a v-else-if="details" class="more-less-button" @click="showDetails = true">
       {{ I18n('More details') }}