Browse Source

[catalog] Move navigator meta fetch function to Axios

Johan Ahlen 5 năm trước cách đây
mục cha
commit
6ee1f411b0

+ 0 - 58
desktop/core/src/desktop/js/api/apiHelper.js

@@ -1788,64 +1788,6 @@ class ApiHelper {
     return new CancellableJqPromise(deferred, undefined, cancellablePromises);
   }
 
-  /**
-   * Fetches a navigator entity for the given source and path
-   *
-   * @param {Object} options
-   * @param {boolean} [options.silenceErrors]
-   *
-   * @param {boolean} [options.isView] - Default false
-   * @param {string[]} options.path
-   *
-   * @return {CancellableJqPromise}
-   */
-  fetchNavigatorMetadata(options) {
-    const deferred = $.Deferred();
-    let url = URLS.NAV_API.FIND_ENTITY;
-
-    if (options.path.length === 1) {
-      url += '?type=database&name=' + options.path[0];
-    } else if (options.path.length === 2) {
-      url +=
-        (options.isView ? '?type=view' : '?type=table') +
-        '&database=' +
-        options.path[0] +
-        '&name=' +
-        options.path[1];
-    } else if (options.path.length === 3) {
-      url +=
-        '?type=field&database=' +
-        options.path[0] +
-        '&table=' +
-        options.path[1] +
-        '&name=' +
-        options.path[2];
-    } else {
-      return new CancellableJqPromise($.Deferred().reject());
-    }
-
-    const request = simplePost(
-      url,
-      {
-        notebook: {},
-        snippet: ko.mapping.toJSON({
-          type: 'nav'
-        })
-      },
-      {
-        silenceErrors: options.silenceErrors,
-        successCallback: data => {
-          data = data.entity || data;
-          data.hueTimestamp = Date.now();
-          deferred.resolve(data);
-        },
-        errorCallback: deferred.reject
-      }
-    );
-
-    return new CancellableJqPromise(deferred, request);
-  }
-
   /**
    * Updates Navigator properties and custom metadata for the given entity
    *

+ 14 - 17
desktop/core/src/desktop/js/api/utils.ts

@@ -20,11 +20,17 @@ import qs from 'qs';
 import { CancellablePromise } from './cancellablePromise';
 import hueUtils from 'utils/hueUtils';
 
-export const successResponseIsError = (responseData?: {
-  traceback?: string;
-  status?: number;
+export interface DefaultApiResponse {
+  status: number;
   code?: number;
-}): boolean => {
+  error?: string | unknown;
+  message?: string;
+  responseText?: string;
+  statusText?: string;
+  traceback?: string;
+}
+
+export const successResponseIsError = (responseData?: DefaultApiResponse): boolean => {
   return (
     typeof responseData !== 'undefined' &&
     (typeof responseData.traceback !== 'undefined' ||
@@ -36,16 +42,7 @@ export const successResponseIsError = (responseData?: {
 
 const UNKNOWN_ERROR_MESSAGE = 'Unknown error occurred';
 
-export const extractErrorMessage = (
-  errorResponse?:
-    | {
-        statusText?: string;
-        responseText?: string;
-        message?: string;
-        error?: string | unknown;
-      }
-    | string
-): string => {
+export const extractErrorMessage = (errorResponse?: DefaultApiResponse | string): string => {
   if (!errorResponse) {
     return UNKNOWN_ERROR_MESSAGE;
   }
@@ -84,7 +81,7 @@ export const post = <T, U = unknown>(
     ignoreSuccessErrors?: boolean;
     transformResponse?: AxiosTransformer;
     handleResponse?: (
-      response: T
+      response: T & DefaultApiResponse
     ) => { valid: boolean; reason?: unknown; adjustedResponse?: T } | undefined;
   }
 ): CancellablePromise<T> =>
@@ -98,7 +95,7 @@ export const post = <T, U = unknown>(
       }
     };
 
-    const handleErrorResponse = (response: AxiosResponse<T>): void => {
+    const handleErrorResponse = (response: AxiosResponse<DefaultApiResponse>): void => {
       const errorMessage = extractErrorMessage(response.data);
       reject(errorMessage);
       notifyError(errorMessage, response.data);
@@ -108,7 +105,7 @@ export const post = <T, U = unknown>(
     let completed = false;
 
     axios
-      .post<T>(url, qs.stringify(data), {
+      .post<T & DefaultApiResponse>(url, qs.stringify(data), {
         cancelToken: cancelTokenSource.token,
         transformResponse: options && options.transformResponse
       })

+ 24 - 42
desktop/core/src/desktop/js/catalog/DataCatalogEntry.ts

@@ -15,7 +15,7 @@
 // limitations under the License.
 
 import { Cancellable, CancellablePromise } from 'api/cancellablePromise';
-import { fetchSourceMeta } from 'catalog/api';
+import { fetchNavigatorMetadata, fetchSourceMetadata } from 'catalog/api';
 import MultiTableEntry, { TopAggs, TopFilters, TopJoins } from 'catalog/MultiTableEntry';
 import { getOptimizer } from './optimizer/optimizer';
 import * as ko from 'knockout';
@@ -226,31 +226,27 @@ const reloadSourceMeta = (
 ): CancellablePromise<SourceMeta> => {
   entry.sourceMetaPromise = new CancellablePromise<SourceMeta>(
     async (resolve, reject, onCancel) => {
+      onCancel(() => {
+        entry.sourceMetaPromise = undefined;
+      });
+
       if (entry.dataCatalog.invalidatePromise) {
         try {
           await entry.dataCatalog.invalidatePromise;
         } catch (err) {}
       }
 
-      entry.sourceMetaPromise = fetchSourceMeta({
-        ...options,
-        entry
-      });
-
-      onCancel(() => {
-        if (entry.sourceMetaPromise) {
-          entry.sourceMetaPromise.cancel();
-          entry.sourceMetaPromise = undefined;
-        }
-      });
-
       try {
-        entry.sourceMeta = await entry.sourceMetaPromise;
+        entry.sourceMeta = await fetchSourceMetadata({
+          ...options,
+          entry
+        });
         resolve(entry.sourceMeta);
-        entry.saveLater();
       } catch (err) {
         reject(err);
+        return;
       }
+      entry.saveLater();
     }
   );
   return entry.sourceMetaPromise;
@@ -261,39 +257,25 @@ const reloadSourceMeta = (
  */
 const reloadNavigatorMeta = (
   entry: DataCatalogEntry,
-  apiOptions?: { silenceErrors?: boolean }
+  options?: { silenceErrors?: boolean }
 ): CancellablePromise<NavigatorMeta> => {
   if (entry.canHaveNavigatorMetadata()) {
     entry.navigatorMetaPromise = new CancellablePromise<NavigatorMeta>(
-      (resolve, reject, onCancel) => {
-        const fetchPromise = fetchAndSave(
-          (<(options: FetchOptions) => CancellableJqPromise<NavigatorMeta>>(
-            (<unknown>apiHelper.fetchNavigatorMetadata)
-          )).bind(apiHelper),
-          val => {
-            entry.navigatorMeta = val;
-          },
-          entry,
-          apiOptions
-        );
-
+      async (resolve, reject, onCancel) => {
         onCancel(() => {
-          fetchPromise.cancel();
           entry.navigatorMetaPromise = undefined;
         });
-
-        fetchPromise
-          .then(navigatorMeta => {
-            if (!navigatorMeta) {
-              reject();
-            } else {
-              if (entry.commentObservable) {
-                entry.commentObservable(entry.getResolvedComment());
-              }
-              resolve(navigatorMeta);
-            }
-          })
-          .fail(reject);
+        try {
+          entry.navigatorMeta = await fetchNavigatorMetadata({ ...options, entry });
+          resolve(entry.navigatorMeta);
+          if (entry.commentObservable) {
+            entry.commentObservable(entry.getResolvedComment());
+          }
+        } catch (err) {
+          reject(err);
+          return;
+        }
+        entry.saveLater();
       }
     );
   } else {

+ 59 - 11
desktop/core/src/desktop/js/catalog/api.ts

@@ -15,20 +15,20 @@
 // limitations under the License.
 
 import { CancellablePromise } from 'api/cancellablePromise';
-import { extractErrorMessage, post, successResponseIsError } from 'api/utils';
-import DataCatalogEntry, { SourceMeta } from 'catalog/DataCatalogEntry';
+import { DefaultApiResponse, extractErrorMessage, post, successResponseIsError } from 'api/utils';
+import DataCatalogEntry, { NavigatorMeta, SourceMeta } from 'catalog/DataCatalogEntry';
 
-const AUTOCOMPLETE_API_PREFIX = '/notebook/api/autocomplete/';
+const AUTOCOMPLETE_URL_PREFIX = '/notebook/api/autocomplete/';
+const FIND_ENTITY_URL = '/metadata/api/catalog/find_entity';
 
-export const fetchSourceMeta = (options: {
+export const fetchSourceMetadata = (options: {
   entry: DataCatalogEntry;
   silenceErrors?: boolean;
-}): CancellablePromise<SourceMeta> => {
-  const url =
-    AUTOCOMPLETE_API_PREFIX + options.entry.path.join('/') + (options.entry.path.length ? '/' : '');
-
-  return post<SourceMeta & { status: number; code?: number; error?: string; message?: string }>(
-    url,
+}): CancellablePromise<SourceMeta> =>
+  post<SourceMeta>(
+    `${AUTOCOMPLETE_URL_PREFIX}${options.entry.path.join('/')}${
+      options.entry.path.length ? '/' : ''
+    }`,
     {
       notebook: {},
       snippet: JSON.stringify({
@@ -40,7 +40,7 @@ export const fetchSourceMeta = (options: {
     {
       ...options,
       handleResponse: response => {
-        const message = response.error || response.message || '';
+        const message = <string>response.error || response.message || '';
         const adjustedResponse = response;
         adjustedResponse.notFound =
           response.status === 0 &&
@@ -51,6 +51,54 @@ export const fetchSourceMeta = (options: {
 
         const valid = adjustedResponse.notFound || !successResponseIsError(response);
 
+        if (!valid) {
+          return { valid, reason: extractErrorMessage(response) };
+        }
+        return { valid, adjustedResponse };
+      }
+    }
+  );
+
+export const fetchNavigatorMetadata = (options: {
+  entry: DataCatalogEntry;
+  silenceErrors?: boolean;
+}): CancellablePromise<NavigatorMeta> => {
+  const params = new URLSearchParams();
+
+  if (options.entry.path.length === 1) {
+    params.append('type', 'database');
+  } else if (options.entry.path.length === 2) {
+    params.append('type', options.entry.isView() ? 'view' : 'table');
+    params.append('database', options.entry.path[0]);
+  } else if (options.entry.path.length === 3) {
+    params.append('type', 'field');
+    params.append('database', options.entry.path[1]);
+    params.append('table', options.entry.path[1]);
+  } else {
+    return CancellablePromise.reject();
+  }
+  params.append('name', options.entry.path[options.entry.path.length - 1]);
+
+  return post<NavigatorMeta>(
+    `${FIND_ENTITY_URL}?${params}`,
+    {
+      notebook: {},
+      snippet: JSON.stringify({
+        type: options.entry.getConnector().id,
+        source: 'data'
+      }),
+      cluster: (options.entry.compute && JSON.stringify(options.entry.compute)) || '""'
+    },
+    {
+      ...options,
+      handleResponse: (
+        response: (NavigatorMeta | { entity: NavigatorMeta }) & DefaultApiResponse
+      ) => {
+        const adjustedResponse = (<{ entity: NavigatorMeta }>response).entity || response;
+        adjustedResponse.hueTimestamp = Date.now();
+
+        const valid = !successResponseIsError(response);
+
         if (!valid) {
           return { valid, reason: extractErrorMessage(response) };
         }

+ 3 - 1
desktop/core/src/desktop/js/ko/components/ko.pollingCatalogEntriesList.test.js

@@ -23,7 +23,9 @@ describe('ko.pollingCatalogEntriesList.js', () => {
   const setup = koSetup();
 
   it('should render component', async () => {
-    jest.spyOn(CatalogApi, 'fetchSourceMeta').mockImplementation(() => CancellablePromise.reject());
+    jest
+      .spyOn(CatalogApi, 'fetchSourceMetadata')
+      .mockImplementation(() => CancellablePromise.reject());
 
     const element = await setup.renderComponent(NAME, {
       sourceType: 'impala',

+ 1 - 1
desktop/core/src/desktop/js/sql/autocompleteResults.test.js

@@ -33,7 +33,7 @@ describe('AutocompleteResults.js', () => {
     status: 500
   };
 
-  jest.spyOn(CatalogApi, 'fetchSourceMeta').mockImplementation(options => {
+  jest.spyOn(CatalogApi, 'fetchSourceMetadata').mockImplementation(options => {
     if (Math.random() < 0.5) {
       return CancellablePromise.reject(failResponse);
     }