ソースを参照

[frontend] Switch to axios for fetching nav related calls

This allows for a custom base url for catalog related api calls and drops the jquery dependency from the dataCatalog
Johan Åhlén 4 年 前
コミット
e55c876bcb

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

@@ -1226,45 +1226,6 @@ class ApiHelper {
     return deferred.resolve().promise();
   }
 
-  updateSourceMetadata(options) {
-    let url;
-    const data = {
-      source_type: options.sourceType
-    };
-    if (options.path.length === 1) {
-      url = '/metastore/databases/' + options.path[0] + '/alter';
-      data.properties = ko.mapping.toJSON(options.properties);
-    } else if (options.path.length === 2) {
-      url = '/metastore/table/' + options.path[0] + '/' + options.path[1] + '/alter';
-      if (options.properties) {
-        if (options.properties.comment) {
-          data.comment = options.properties.comment;
-        }
-        if (options.properties.name) {
-          data.new_table_name = options.properties.name;
-        }
-      }
-    } else if (options.path.length > 2) {
-      url = '/metastore/table/' + options.path[0] + '/' + options.path[1] + '/alter_column';
-      data.column = options.path.slice(2).join('.');
-      if (options.properties) {
-        if (options.properties.comment) {
-          data.comment = options.properties.comment;
-        }
-        if (options.properties.name) {
-          data.new_column_name = options.properties.name;
-        }
-        if (options.properties.type) {
-          data.new_column_type = options.properties.name;
-        }
-        if (options.properties.partitions) {
-          data.partition_spec = ko.mapping.toJSON(options.properties.partitions);
-        }
-      }
-    }
-    return simplePost(url, data, options);
-  }
-
   /**
    * Fetches the partitions for the given path
    *
@@ -1500,73 +1461,6 @@ class ApiHelper {
     });
   }
 
-  /**
-   * Updates Navigator properties and custom metadata for the given entity
-   *
-   * @param {Object} options
-   * @param {string} options.identity - The identifier for the Navigator entity to update
-   * @param {Object} [options.properties]
-   * @param {Object.<string|string>|undefined} [options.modifiedCustomMetadata]
-   * @param {string[]|undefined} [options.deletedCustomMetadataKeys]
-   * @param {boolean} [options.silenceErrors]
-   *
-   * @return {JQueryPromise}
-   */
-  updateNavigatorProperties(options) {
-    const data = { id: ko.mapping.toJSON(options.identity) };
-
-    if (options.properties) {
-      data.properties = ko.mapping.toJSON(options.properties);
-    }
-    if (options.modifiedCustomMetadata) {
-      data.modifiedCustomMetadata = ko.mapping.toJSON(options.modifiedCustomMetadata);
-    }
-    if (options.deletedCustomMetadataKeys) {
-      data.deletedCustomMetadataKeys = ko.mapping.toJSON(options.deletedCustomMetadataKeys);
-    }
-    return simplePost(URLS.NAV_API.UPDATE_PROPERTIES, data, options);
-  }
-
-  /**
-   * Lists all available navigator tags
-   *
-   * @param {Object} options
-   * @param {boolean|undefined} [options.silenceErrors]
-   *
-   * @return {CancellableJqPromise}
-   */
-  fetchAllNavigatorTags(options) {
-    const deferred = $.Deferred();
-
-    const request = simplePost(URLS.NAV_API.LIST_TAGS, undefined, {
-      silenceErrors: options.silenceErrors,
-      successCallback: data => {
-        if (data && data.tags) {
-          deferred.resolve(data.tags);
-        } else {
-          deferred.resolve({});
-        }
-      },
-      errorCallback: deferred.reject
-    });
-
-    return new CancellableJqPromise(deferred, request);
-  }
-
-  addNavTags(entityId, tags) {
-    return simplePost(URLS.NAV_API.ADD_TAGS, {
-      id: ko.mapping.toJSON(entityId),
-      tags: ko.mapping.toJSON(tags)
-    });
-  }
-
-  deleteNavTags(entityId, tags) {
-    return simplePost(URLS.NAV_API.DELETE_TAGS, {
-      id: ko.mapping.toJSON(entityId),
-      tags: ko.mapping.toJSON(tags)
-    });
-  }
-
   /**
    * @param {Object} options
    * @param {boolean} [options.silenceErrors]
@@ -1727,27 +1621,6 @@ class ApiHelper {
     return new CancellableJqPromise(deferred, request);
   }
 
-  searchEntities(options) {
-    const deferred = $.Deferred();
-
-    const request = simplePost(
-      URLS.SEARCH_API,
-      {
-        query_s: ko.mapping.toJSON(options.query),
-        limit: options.limit || 100,
-        raw_query: !!options.rawQuery,
-        sources: options.sources ? ko.mapping.toJSON(options.sources) : '["sql"]'
-      },
-      {
-        silenceErrors: options.silenceErrors,
-        successCallback: deferred.resolve,
-        errorCallback: deferred.reject
-      }
-    );
-
-    return new CancellableJqPromise(deferred, request);
-  }
-
   /**
    *
    * @param {Object} options

+ 45 - 43
desktop/core/src/desktop/js/catalog/DataCatalogEntry.ts

@@ -14,18 +14,23 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import * as ko from 'knockout';
+
 import { Cancellable, CancellablePromise } from 'api/cancellablePromise';
 import {
+  addNavTags,
+  deleteNavTags,
   fetchDescribe,
   fetchNavigatorMetadata,
   fetchPartitions,
   fetchSample,
-  fetchSourceMetadata
+  fetchSourceMetadata,
+  searchEntities,
+  updateNavigatorProperties,
+  updateSourceMetadata
 } from 'catalog/api';
 import MultiTableEntry, { TopAggs, TopFilters, TopJoins } from 'catalog/MultiTableEntry';
-import * as ko from 'knockout';
 
-import apiHelper from 'api/apiHelper';
 import { applyCancellable, forceSilencedErrors } from 'catalog/catalogUtils';
 import { Compute, Connector, Namespace } from 'config/types';
 import { hueWindow } from 'types/types';
@@ -764,17 +769,17 @@ export default class DataCatalogEntry {
             });
           };
 
-          const searchPromise = apiHelper.searchEntities({
-            query: query,
+          const searchPromise = searchEntities({
+            query,
             rawQuery: true,
             limit: children.length,
-            silenceErrors: options && options.silenceErrors
+            silenceErrors: options?.silenceErrors
           });
 
           cancellablePromises.push(searchPromise);
 
           searchPromise
-            .done((result: { entities: NavigatorMeta[] }) => {
+            .then(result => {
               if (result && result.entities) {
                 const childEntryIndex: { [name: string]: DataCatalogEntry } = {};
                 children.forEach(childEntry => {
@@ -798,7 +803,8 @@ export default class DataCatalogEntry {
                 });
               }
             })
-            .always(() => {
+            .catch(() => resolve([]))
+            .finally(() => {
               rejectUnknown();
               resolve(children);
             });
@@ -1076,13 +1082,12 @@ export default class DataCatalogEntry {
     }
 
     return new Promise<NavigatorMeta>((resolve, reject) => {
-      apiHelper
-        .updateNavigatorProperties({
-          identity: navigatorMeta.identity,
-          modifiedCustomMetadata: modifiedCustomMetadata,
-          deletedCustomMetadataKeys: deletedCustomMetadataKeys
-        })
-        .done(entity => {
+      updateNavigatorProperties({
+        identity: navigatorMeta.identity,
+        modifiedCustomMetadata,
+        deletedCustomMetadataKeys
+      })
+        .then(entity => {
           if (entity) {
             this.navigatorMeta = entity;
             this.navigatorMetaPromise = CancellablePromise.resolve(entity);
@@ -1091,7 +1096,8 @@ export default class DataCatalogEntry {
           } else {
             reject();
           }
-        });
+        })
+        .catch(reject);
     });
   }
 
@@ -1109,14 +1115,13 @@ export default class DataCatalogEntry {
       }
 
       return new Promise<string>((resolve, reject) => {
-        apiHelper
-          .updateNavigatorProperties({
-            identity: navigatorMeta.identity,
-            properties: {
-              description: comment
-            }
-          })
-          .done(async entity => {
+        updateNavigatorProperties({
+          identity: navigatorMeta.identity,
+          properties: {
+            description: comment
+          }
+        })
+          .then(async entity => {
             if (entity) {
               this.navigatorMeta = entity;
               this.navigatorMetaPromise = CancellablePromise.resolve(entity);
@@ -1131,20 +1136,19 @@ export default class DataCatalogEntry {
               })
               .catch(reject);
           })
-          .fail(reject);
+          .catch(reject);
       });
     }
 
     return new Promise((resolve, reject) => {
-      apiHelper
-        .updateSourceMetadata({
-          sourceType: this.getConnector().id,
-          path: this.path,
-          properties: {
-            comment: comment
-          }
-        })
-        .done(async () => {
+      updateSourceMetadata({
+        entry: this,
+        properties: {
+          comment: comment
+        },
+        silenceErrors: options?.silenceErrors
+      })
+        .then(async () => {
           try {
             await this.reloadSourceMeta(options);
             const comment = await this.getComment(options);
@@ -1156,7 +1160,7 @@ export default class DataCatalogEntry {
             reject(err);
           }
         })
-        .fail(reject);
+        .catch(reject);
     });
   }
 
@@ -1174,9 +1178,8 @@ export default class DataCatalogEntry {
     const navigatorMeta = await this.getNavigatorMeta(apiOptions);
 
     return new Promise((resolve, reject) => {
-      apiHelper
-        .addNavTags(navigatorMeta.identity, tags)
-        .done(entity => {
+      addNavTags(navigatorMeta.identity, tags)
+        .then(entity => {
           if (entity) {
             this.navigatorMeta = entity;
             this.navigatorMetaPromise = CancellablePromise.resolve(entity);
@@ -1186,7 +1189,7 @@ export default class DataCatalogEntry {
             reject();
           }
         })
-        .fail(reject);
+        .catch(reject);
     });
   }
 
@@ -1204,9 +1207,8 @@ export default class DataCatalogEntry {
     const navigatorMeta = await this.getNavigatorMeta(apiOptions);
 
     return new Promise((resolve, reject) => {
-      apiHelper
-        .deleteNavTags(navigatorMeta.identity, tags)
-        .done(entity => {
+      deleteNavTags(navigatorMeta.identity, tags)
+        .then(entity => {
           if (entity) {
             this.navigatorMeta = entity;
             this.navigatorMetaPromise = CancellablePromise.resolve(entity);
@@ -1216,7 +1218,7 @@ export default class DataCatalogEntry {
             reject();
           }
         })
-        .fail(reject);
+        .catch(reject);
     });
   }
 

+ 5 - 6
desktop/core/src/desktop/js/catalog/GeneralDataCatalog.ts

@@ -16,8 +16,8 @@
 
 import localforage from 'localforage';
 
-import apiHelper from 'api/apiHelper';
 import { hueWindow } from 'types/types';
+import { fetchAllNavigatorTags } from './api';
 import { DataCatalog } from './dataCatalog';
 
 export interface Tags {
@@ -64,10 +64,9 @@ export default class GeneralDataCatalog {
 
     this.allNavigatorTagsPromise = new Promise((resolve, reject) => {
       const reloadAllTags = () => {
-        apiHelper
-          .fetchAllNavigatorTags({
-            silenceErrors: options && options.silenceErrors
-          })
+        fetchAllNavigatorTags({
+          silenceErrors: options && options.silenceErrors
+        })
           .then(allTags => {
             resolve(allTags);
             if (ttl.default && ttl.default > 0) {
@@ -78,7 +77,7 @@ export default class GeneralDataCatalog {
               });
             }
           })
-          .fail(reject);
+          .catch(reject);
       };
 
       if (

+ 2 - 3
desktop/core/src/desktop/js/catalog/MultiTableEntry.ts

@@ -14,8 +14,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import { noop } from 'lodash';
-
 import DataCatalogEntry from 'catalog/DataCatalogEntry';
 import { PopularityOptions, SqlAnalyzer } from './analyzer/types';
 import { CatalogGetOptions, DataCatalog, TimestampedData } from './dataCatalog';
@@ -24,6 +22,7 @@ import { applyCancellable } from 'catalog/catalogUtils';
 import { UdfDetails } from 'sql/reference/types';
 import { Connector } from 'config/types';
 import { hueWindow } from 'types/types';
+import noop from 'utils/timing/noop';
 
 export interface TopJoinValue {
   totalTableCount: number;
@@ -203,7 +202,7 @@ class MultiTableEntry {
     if (ttl && ttl.default && ttl.default > 0) {
       window.clearTimeout(this.saveTimeout);
       this.saveTimeout = window.setTimeout(() => {
-        this.save();
+        this.save().catch();
       }, 1000);
     }
   }

+ 140 - 2
desktop/core/src/desktop/js/catalog/api.ts

@@ -14,6 +14,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import { Tags } from './GeneralDataCatalog';
 import { Cancellable, CancellablePromise } from 'api/cancellablePromise';
 import {
   DefaultApiResponse,
@@ -32,10 +33,10 @@ import DataCatalogEntry, {
   SampleMeta,
   SourceMeta
 } from 'catalog/DataCatalogEntry';
+import { Cluster, Compute, Connector, Namespace } from 'config/types';
 import { hueWindow } from 'types/types';
 import sleep from 'utils/timing/sleep';
 import UUID from 'utils/string/UUID';
-import { Cluster, Compute, Connector, Namespace } from '../config/types';
 
 interface AnalyzeResponse {
   status: number;
@@ -57,15 +58,20 @@ interface SampleFetchOptions extends SharedFetchOptions {
   sampleCount?: number;
 }
 
+const ADD_TAGS_URL = '/metadata/api/catalog/add_tags';
+const AUTOCOMPLETE_URL_PREFIX = '/notebook/api/autocomplete/';
 const AUTOCOMPLETE_URL_PREFIX = '/api/editor/autocomplete/';
-
 const CANCEL_STATEMENT_URL = '/notebook/api/cancel_statement';
 const CHECK_STATUS_URL = '/notebook/api/check_status';
+const DELETE_TAGS_URL = '/metadata/api/catalog/delete_tags';
 const DESCRIBE_URL = '/notebook/api/describe/';
 const FETCH_RESULT_DATA_URL = '/notebook/api/fetch_result_data';
 const FIND_ENTITY_URL = '/metadata/api/catalog/find_entity';
+const LIST_TAGS_URL = '/metadata/api/catalog/list_tags';
 const METASTORE_TABLE_URL_PREFIX = '/metastore/table/';
 const SAMPLE_URL_PREFIX = '/notebook/api/sample/';
+const SEARCH_URL = '/desktop/api/search/entities';
+const UPDATE_PROPERTIES_URL = '/metadata/api/catalog/update_properties';
 
 const getEntryUrlPath = (entry: DataCatalogEntry) =>
   entry.path.join('/') + (entry.path.length ? '/' : '');
@@ -248,6 +254,20 @@ export const fetchNavigatorMetadata = ({
   );
 };
 
+export const fetchAllNavigatorTags = ({
+  silenceErrors
+}: Pick<SharedFetchOptions, 'silenceErrors'>): CancellablePromise<Tags> =>
+  post<Tags>(LIST_TAGS_URL, undefined, {
+    silenceErrors,
+    handleSuccess: (response: { tags?: Tags } & DefaultApiResponse, resolve, reject) => {
+      if (successResponseIsError(response)) {
+        reject(extractErrorMessage(response));
+      } else {
+        resolve(response.tags || {});
+      }
+    }
+  });
+
 export const fetchPartitions = ({
   entry,
   silenceErrors
@@ -554,3 +574,121 @@ export const fetchSourceMetadata = ({
       }
     }
   );
+
+interface SearchOptions {
+  limit?: number;
+  query: string;
+  rawQuery?: boolean;
+  silenceErrors?: boolean;
+  sources?: string[];
+}
+
+type SearchResponse = { entities?: NavigatorMeta[] } | undefined;
+
+export const searchEntities = ({
+  limit,
+  query,
+  rawQuery,
+  silenceErrors,
+  sources
+}: SearchOptions): CancellablePromise<SearchResponse> =>
+  post<SearchResponse>(
+    SEARCH_URL,
+    {
+      query_s: JSON.stringify(query),
+      limit: limit || 100,
+      raw_query: !!rawQuery,
+      sources: (sources && JSON.stringify(sources)) || '["sql"]'
+    },
+    { silenceErrors }
+  );
+
+interface UpdateNavigatorPropertiesOptions {
+  deletedCustomMetadataKeys?: string[];
+  identity: string;
+  modifiedCustomMetadata?: Record<string, string>;
+  properties?: unknown;
+  silenceErrors?: boolean;
+}
+
+export const updateNavigatorProperties = ({
+  deletedCustomMetadataKeys,
+  identity,
+  modifiedCustomMetadata,
+  properties,
+  silenceErrors
+}: UpdateNavigatorPropertiesOptions): CancellablePromise<NavigatorMeta> => {
+  const data: Record<string, string> = { id: JSON.stringify(identity) };
+
+  if (properties) {
+    data.properties = JSON.stringify(properties);
+  }
+  if (modifiedCustomMetadata) {
+    data.modifiedCustomMetadata = JSON.stringify(modifiedCustomMetadata);
+  }
+  if (deletedCustomMetadataKeys) {
+    data.deletedCustomMetadataKeys = JSON.stringify(deletedCustomMetadataKeys);
+  }
+  return post<NavigatorMeta>(UPDATE_PROPERTIES_URL, data, { silenceErrors });
+};
+
+interface UpdateSourceMetadataOptions extends SharedFetchOptions {
+  properties: Record<string, string>;
+}
+
+export const updateSourceMetadata = ({
+  entry,
+  properties,
+  silenceErrors
+}: UpdateSourceMetadataOptions): CancellablePromise<void> => {
+  let url;
+  const data: Record<string, string> = {
+    source_type: entry.getConnector().id
+  };
+  if (entry.path.length === 1) {
+    url = `/metastore/databases/${entry.path[0]}/alter`;
+    data.properties = JSON.stringify(properties);
+  } else if (entry.path.length === 2) {
+    url = `/metastore/table/${entry.path[0]}/${entry.path[1]}/alter`;
+    if (properties?.name) {
+      data.new_table_name = properties.name;
+    }
+  } else if (entry.path.length > 2) {
+    url = `/metastore/table/${entry.path[0]}/${entry.path[1]}/alter_column`;
+    data.column = entry.path.slice(2).join('.');
+    if (properties?.name) {
+      data.new_column_name = properties.name;
+    }
+    if (properties?.type) {
+      data.new_column_type = properties.type;
+    }
+    if (properties?.partitions) {
+      data.partition_spec = JSON.stringify(properties.partitions);
+    }
+  }
+
+  if (properties?.comment) {
+    data.comment = properties.comment;
+  }
+
+  if (!url) {
+    return CancellablePromise.reject();
+  }
+
+  return post<void>(url, data, { silenceErrors });
+};
+
+export const addNavTags = (entityId: string, tags: string[]): CancellablePromise<NavigatorMeta> =>
+  post<NavigatorMeta>(ADD_TAGS_URL, {
+    id: JSON.stringify(entityId),
+    tags: JSON.stringify(tags)
+  });
+
+export const deleteNavTags = (
+  entityId: string,
+  tags: string[]
+): CancellablePromise<NavigatorMeta> =>
+  post<NavigatorMeta>(DELETE_TAGS_URL, {
+    id: JSON.stringify(entityId),
+    tags: JSON.stringify(tags)
+  });