ソースを参照

HUE-9207 [frontend] Extract shared ApiHelper logic to a separate module

Johan Ahlen 5 年 前
コミット
804f86568e

+ 1 - 0
apps/jobbrowser/src/jobbrowser/templates/job_browser.mako

@@ -2795,6 +2795,7 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
       };
 
       self.fetchJob = function () {
+        // TODO: Remove cancelActiveRequest from apiHelper when in webpack
         vm.apiHelper.cancelActiveRequest(lastFetchJobRequest);
         vm.apiHelper.cancelActiveRequest(lastUpdateJobRequest);
 

ファイルの差分が大きいため隠しています
+ 161 - 331
desktop/core/src/desktop/js/api/apiHelper.js


+ 165 - 0
desktop/core/src/desktop/js/api/apiUtils.js

@@ -0,0 +1,165 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import $ from 'jquery';
+import hueUtils from '../utils/hueUtils';
+
+/**
+ * @param {Object} [response]
+ * @param {number} [response.status]
+ * @returns {boolean} - True if actually an error
+ */
+export const successResponseIsError = response => {
+  return (
+    typeof response !== 'undefined' &&
+    (typeof response.traceback !== 'undefined' ||
+      (typeof response.status !== 'undefined' && response.status !== 0) ||
+      response.code === 503 ||
+      response.code === 500)
+  );
+};
+
+/**
+ * @param {Object} options
+ * @param {Function} [options.errorCallback]
+ * @param {boolean} [options.silenceErrors]
+ * @returns {Function}
+ */
+export const assistErrorCallback = options => {
+  return errorResponse => {
+    let errorMessage = 'Unknown error occurred';
+    if (typeof errorResponse !== 'undefined' && errorResponse !== null) {
+      if (typeof errorResponse.statusText !== 'undefined' && errorResponse.statusText === 'abort') {
+        return;
+      } else if (typeof errorResponse.responseText !== 'undefined') {
+        try {
+          const errorJs = JSON.parse(errorResponse.responseText);
+          if (typeof errorJs.message !== 'undefined') {
+            errorMessage = errorJs.message;
+          } else {
+            errorMessage = errorResponse.responseText;
+          }
+        } catch (err) {
+          errorMessage = errorResponse.responseText;
+        }
+      } else if (typeof errorResponse.message !== 'undefined' && errorResponse.message !== null) {
+        errorMessage = errorResponse.message;
+      } else if (
+        typeof errorResponse.statusText !== 'undefined' &&
+        errorResponse.statusText !== null
+      ) {
+        errorMessage = errorResponse.statusText;
+      } else if (
+        errorResponse.error !== 'undefined' &&
+        Object.prototype.toString.call(errorResponse.error) === '[object String]'
+      ) {
+        errorMessage = errorResponse.error;
+      } else if (Object.prototype.toString.call(errorResponse) === '[object String]') {
+        errorMessage = errorResponse;
+      }
+    }
+
+    if (!options || !options.silenceErrors) {
+      hueUtils.logError(errorResponse);
+      if (errorMessage && errorMessage.indexOf('AuthorizationException') === -1) {
+        $(document).trigger('error', errorMessage);
+      }
+    }
+
+    if (options && options.errorCallback) {
+      options.errorCallback(errorMessage);
+    }
+    return errorMessage;
+  };
+};
+
+/**
+ * @param {string} url
+ * @param {Object} [data]
+ * @param {Object} [options]
+ * @param {function} [options.successCallback]
+ * @param {function} [options.errorCallback]
+ * @param {boolean} [options.silenceErrors]
+ */
+export const simpleGet = (url, data, options) => {
+  if (!options) {
+    options = {};
+  }
+  return $.get(url, data, data => {
+    if (successResponseIsError(data)) {
+      assistErrorCallback(options)(data);
+    } else if (typeof options.successCallback !== 'undefined') {
+      options.successCallback(data);
+    }
+  }).fail(assistErrorCallback(options));
+};
+
+/**
+ * @param {string} url
+ * @param {Object} data
+ * @param {Object} [options]
+ * @param {function} [options.successCallback]
+ * @param {function} [options.errorCallback]
+ * @param {boolean} [options.silenceErrors]
+ * @param {string} [options.dataType] - Default: Intelligent Guess (xml, json, script, text, html)
+ *
+ * @return {Promise}
+ */
+export const simplePost = (url, data, options) => {
+  const deferred = $.Deferred();
+
+  const request = $.post({
+    url: url,
+    data: data,
+    dataType: options && options.dataType
+  })
+    .done(data => {
+      if (successResponseIsError(data)) {
+        deferred.reject(assistErrorCallback(options)(data));
+        return;
+      }
+      if (options && options.successCallback) {
+        options.successCallback(data);
+      }
+      deferred.resolve(data);
+    })
+    .fail(assistErrorCallback(options));
+
+  request.fail(data => {
+    deferred.reject(assistErrorCallback(options)(data));
+  });
+
+  const promise = deferred.promise();
+
+  promise.getReadyState = function() {
+    return request.readyState;
+  };
+
+  promise.abort = function() {
+    request.abort();
+  };
+
+  return promise;
+};
+
+export const cancelActiveRequest = request => {
+  if (typeof request !== 'undefined' && request !== null) {
+    const readyState = request.getReadyState ? request.getReadyState() : request.readyState;
+    if (readyState < 4) {
+      request.abort();
+    }
+  }
+};

+ 11 - 13
desktop/core/src/desktop/js/api/apiHelper.test.js → desktop/core/src/desktop/js/api/apiUtils.test.js

@@ -14,42 +14,40 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import apiHelper from './apiHelper';
-
-describe('apiHelper.js', () => {
-  const subject = apiHelper;
+import { successResponseIsError } from './apiUtils';
 
+describe('apiUtils.js', () => {
   describe('success response that is actually an error', () => {
     it('should not determine that a success response is an error response if status is 0', () => {
-      expect(subject.successResponseIsError({ status: 0 })).toBeFalsy();
+      expect(successResponseIsError({ status: 0 })).toBeFalsy();
     });
 
     it('should determine that a success response is an error response if status is 1', () => {
-      expect(subject.successResponseIsError({ status: 1 })).toBeTruthy();
+      expect(successResponseIsError({ status: 1 })).toBeTruthy();
     });
 
     it('should determine that a success response is an error response if status is -1', () => {
-      expect(subject.successResponseIsError({ status: -1 })).toBeTruthy();
+      expect(successResponseIsError({ status: -1 })).toBeTruthy();
     });
 
     it('should determine that a success response is an error response if status is -3', () => {
-      expect(subject.successResponseIsError({ status: -3 })).toBeTruthy();
+      expect(successResponseIsError({ status: -3 })).toBeTruthy();
     });
 
-    it('should determine that a success response is an error response if status is 500', () => {
-      expect(subject.successResponseIsError({ status: 500 })).toBeTruthy();
+    it('determine that a success response is an error response if status is 500', () => {
+      expect(successResponseIsError({ status: 500 })).toBeTruthy();
     });
 
     it('should determine that a success response is an error response if code is 500', () => {
-      expect(subject.successResponseIsError({ code: 500 })).toBeTruthy();
+      expect(successResponseIsError({ code: 500 })).toBeTruthy();
     });
 
     it('should determine that a success response is an error response if code is 503', () => {
-      expect(subject.successResponseIsError({ code: 503 })).toBeTruthy();
+      expect(successResponseIsError({ code: 503 })).toBeTruthy();
     });
 
     it('should determine that a success response is an error response if it contains traceback', () => {
-      expect(subject.successResponseIsError({ traceback: {} })).toBeTruthy();
+      expect(successResponseIsError({ traceback: {} })).toBeTruthy();
     });
   });
 });

+ 2 - 2
desktop/core/src/desktop/js/api/cancellablePromise.js

@@ -15,7 +15,7 @@
 // limitations under the License.
 
 import $ from 'jquery';
-import apiHelper from 'api/apiHelper';
+import { cancelActiveRequest } from './apiUtils';
 
 class CancellablePromise {
   constructor(deferred, request, otherCancellables) {
@@ -53,7 +53,7 @@ class CancellablePromise {
 
     self.cancelled = true;
     if (self.request) {
-      apiHelper.cancelActiveRequest(self.request);
+      cancelActiveRequest(self.request);
     }
 
     if (self.state && self.state() === 'pending' && self.deferred.reject) {

+ 8 - 7
desktop/core/src/desktop/js/apps/notebook/snippet.js

@@ -29,6 +29,7 @@ import Result from 'apps/notebook/result';
 import Session from 'apps/notebook/session';
 import sqlStatementsParser from 'parse/sqlStatementsParser';
 import { SHOW_EVENT as SHOW_GIST_MODAL_EVENT } from 'ko/components/ko.shareGistModal';
+import { cancelActiveRequest } from 'api/apiUtils';
 
 const NOTEBOOK_MAPPING = {
   ignore: [
@@ -370,7 +371,7 @@ class Snippet {
     let lastFetchQueriesRequest = null;
 
     self.fetchQueries = function() {
-      apiHelper.cancelActiveRequest(lastFetchQueriesRequest);
+      cancelActiveRequest(lastFetchQueriesRequest);
 
       const QUERIES_PER_PAGE = 50;
       lastQueriesPage = self.queriesCurrentPage();
@@ -1501,14 +1502,14 @@ class Snippet {
           return;
         }
 
-        apiHelper.cancelActiveRequest(lastComplexityRequest);
+        cancelActiveRequest(lastComplexityRequest);
 
         hueAnalytics.log('notebook', 'get_query_risk');
         clearActiveRisks();
 
         const changeSubscription = self.statement.subscribe(() => {
           changeSubscription.dispose();
-          apiHelper.cancelActiveRequest(lastComplexityRequest);
+          cancelActiveRequest(lastComplexityRequest);
         });
 
         const hash = self.statement().hashCode();
@@ -2032,7 +2033,7 @@ class Snippet {
     };
 
     self.queryCompatibility = function(targetPlatform) {
-      apiHelper.cancelActiveRequest(lastCompatibilityRequest);
+      cancelActiveRequest(lastCompatibilityRequest);
 
       hueAnalytics.log('notebook', 'compatibility');
       self.compatibilityCheckRunning(targetPlatform != self.type());
@@ -2483,13 +2484,13 @@ class Snippet {
     };
 
     self.clearActiveExecuteRequests = function() {
-      apiHelper.cancelActiveRequest(self.lastGetLogsRequest);
+      cancelActiveRequest(self.lastGetLogsRequest);
       if (self.getLogsTimeout !== null) {
         window.clearTimeout(self.getLogsTimeout);
         self.getLogsTimeout = null;
       }
 
-      apiHelper.cancelActiveRequest(self.lastCheckStatusRequest);
+      cancelActiveRequest(self.lastCheckStatusRequest);
       if (self.checkStatusTimeout !== null) {
         window.clearTimeout(self.checkStatusTimeout);
         self.checkStatusTimeout = null;
@@ -2497,7 +2498,7 @@ class Snippet {
     };
 
     self.getLogs = function() {
-      apiHelper.cancelActiveRequest(self.lastGetLogsRequest);
+      cancelActiveRequest(self.lastGetLogsRequest);
 
       self.lastGetLogsRequest = $.post(
         '/notebook/api/get_logs',

+ 2 - 1
desktop/core/src/desktop/js/apps/notebook2/components/ko.savedQueries.js

@@ -21,6 +21,7 @@ import DisposableComponent from 'ko/components/DisposableComponent';
 import I18n from 'utils/i18n';
 import { NAME as PAGINATOR_COMPONENT } from './ko.paginator';
 import apiHelper from 'api/apiHelper';
+import { cancelActiveRequest } from 'api/apiUtils';
 
 export const UPDATE_SAVED_QUERIES_EVENT = 'update.saved.queries';
 export const NAME = 'saved-queries';
@@ -156,7 +157,7 @@ class SavedQueries extends DisposableComponent {
   }
 
   async fetchQueries() {
-    apiHelper.cancelActiveRequest(this.lastFetchQueriesRequest);
+    cancelActiveRequest(this.lastFetchQueriesRequest);
 
     this.loading(true);
     this.hasErrors(false);

+ 2 - 2
desktop/core/src/desktop/js/apps/notebook2/execution/executor.test.js

@@ -16,9 +16,9 @@
 
 import $ from 'jquery';
 
-import ApiHelper from 'api/apiHelper';
 import { EXECUTION_STATUS } from './executable';
 import Executor from './executor';
+import * as ApiUtils from 'api/apiUtils';
 
 describe('executor.js', () => {
   /**
@@ -39,7 +39,7 @@ describe('executor.js', () => {
     const subject = createSubject('SELECT * FROM customers;');
 
     const simplePostDeferred = $.Deferred();
-    jest.spyOn(ApiHelper, 'simplePost').mockImplementation(url => {
+    jest.spyOn(ApiUtils, 'simplePost').mockImplementation(url => {
       expect(url).toEqual('/notebook/api/execute/impala');
       return simplePostDeferred;
     });

+ 5 - 4
desktop/core/src/desktop/js/apps/notebook2/execution/sessionManager.test.js

@@ -18,6 +18,7 @@ import $ from 'jquery';
 
 import ApiHelper from 'api/apiHelper';
 import sessionManager from './sessionManager';
+import * as ApiUtils from 'api/apiUtils';
 
 describe('sessionManager.js', () => {
   let spy;
@@ -104,7 +105,7 @@ describe('sessionManager.js', () => {
     expect(sessionManager.hasSession('impala')).toBeTruthy();
 
     // Close the session
-    const postSpy = jest.spyOn(ApiHelper, 'simplePost').mockImplementation((url, data, options) => {
+    const postSpy = jest.spyOn(ApiUtils, 'simplePost').mockImplementation((url, data, options) => {
       expect(JSON.parse(data.session).session_id).toEqual(session.session_id);
       expect(options.silenceErrors).toBeTruthy();
       expect(url).toEqual('/notebook/api/close_session');
@@ -114,7 +115,7 @@ describe('sessionManager.js', () => {
 
     expect(sessionManager.hasSession('impala')).toBeFalsy();
     expect(ApiHelper.createSession).toHaveBeenCalledTimes(1);
-    expect(ApiHelper.simplePost).toHaveBeenCalledTimes(1);
+    expect(ApiUtils.simplePost).toHaveBeenCalledTimes(1);
     postSpy.mockClear();
   });
 
@@ -129,7 +130,7 @@ describe('sessionManager.js', () => {
 
     // Restart the session
     const postSpy = jest
-      .spyOn(ApiHelper, 'simplePost')
+      .spyOn(ApiUtils, 'simplePost')
       .mockReturnValue(new $.Deferred().resolve().promise());
     session = await sessionManager.restartSession(session);
 
@@ -137,7 +138,7 @@ describe('sessionManager.js', () => {
     expect(sessionManager.hasSession('impala')).toBeTruthy();
 
     expect(ApiHelper.createSession).toHaveBeenCalledTimes(2);
-    expect(ApiHelper.simplePost).toHaveBeenCalledTimes(1);
+    expect(ApiUtils.simplePost).toHaveBeenCalledTimes(1);
     postSpy.mockClear();
   });
 });

+ 4 - 3
desktop/core/src/desktop/js/apps/notebook2/execution/sqlExecutable.test.js

@@ -20,6 +20,7 @@ import ApiHelper from 'api/apiHelper';
 import SqlExecutable from './sqlExecutable';
 import { EXECUTION_STATUS } from './executable';
 import sessionManager from './sessionManager';
+import * as ApiUtils from 'api/apiUtils';
 
 describe('sqlExecutable.js', () => {
   afterEach(() => {
@@ -70,7 +71,7 @@ describe('sqlExecutable.js', () => {
     const subject = createSubject('SELECT * FROM customers');
 
     const simplePostDeferred = $.Deferred();
-    jest.spyOn(ApiHelper, 'simplePost').mockImplementation(url => {
+    jest.spyOn(ApiUtils, 'simplePost').mockImplementation(url => {
       expect(url).toEqual('/notebook/api/execute/impala');
       return simplePostDeferred;
     });
@@ -100,7 +101,7 @@ describe('sqlExecutable.js', () => {
         })
     );
 
-    jest.spyOn(ApiHelper, 'simplePost').mockImplementation(url => {
+    jest.spyOn(ApiUtils, 'simplePost').mockImplementation(url => {
       expect(url).toEqual('/notebook/api/execute/impala');
       return simplePostDeferred;
     });
@@ -123,7 +124,7 @@ describe('sqlExecutable.js', () => {
 
     const simplePostExeuteDeferred = $.Deferred();
     const simplePostCancelDeferred = $.Deferred();
-    jest.spyOn(ApiHelper, 'simplePost').mockImplementation(url => {
+    jest.spyOn(ApiUtils, 'simplePost').mockImplementation(url => {
       if (url === '/notebook/api/execute/impala') {
         return simplePostExeuteDeferred;
       } else if (url === '/notebook/api/cancel_statement') {

+ 4 - 3
desktop/core/src/desktop/js/apps/notebook2/snippet.js

@@ -47,6 +47,7 @@ import {
 import { EXECUTE_ACTIVE_EXECUTABLE_EVENT } from 'apps/notebook2/components/ko.executableActions';
 import { UPDATE_HISTORY_EVENT } from 'apps/notebook2/components/ko.queryHistory';
 import { GET_KNOWN_CONFIG_EVENT } from 'utils/hueConfig';
+import { cancelActiveRequest } from 'api/apiUtils';
 
 // TODO: Remove for ENABLE_NOTEBOOK_2. Temporary here for debug
 window.SqlExecutable = SqlExecutable;
@@ -892,14 +893,14 @@ export default class Snippet {
           return;
         }
 
-        apiHelper.cancelActiveRequest(lastComplexityRequest);
+        cancelActiveRequest(lastComplexityRequest);
 
         hueAnalytics.log('notebook', 'get_query_risk');
         clearActiveRisks();
 
         const changeSubscription = this.statement.subscribe(() => {
           changeSubscription.dispose();
-          apiHelper.cancelActiveRequest(lastComplexityRequest);
+          cancelActiveRequest(lastComplexityRequest);
         });
 
         const hash = this.statement().hashCode();
@@ -1344,7 +1345,7 @@ export default class Snippet {
   }
 
   async queryCompatibility(targetPlatform) {
-    apiHelper.cancelActiveRequest(this.lastCompatibilityRequest);
+    cancelActiveRequest(this.lastCompatibilityRequest);
 
     hueAnalytics.log('notebook', 'compatibility');
     this.compatibilityCheckRunning(targetPlatform !== this.dialect());

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

@@ -26,6 +26,7 @@ import I18n from 'utils/i18n';
 import sqlUtils from 'sql/sqlUtils';
 import { SqlSetOptions, SqlFunctions } from 'sql/sqlFunctions';
 import { DIALECT } from 'apps/notebook2/snippet';
+import { cancelActiveRequest } from 'api/apiUtils';
 
 const normalizedColors = HueColors.getNormalizedColors();
 
@@ -417,7 +418,7 @@ class AutocompleteResults {
     const self = this;
 
     while (self.lastKnownRequests.length) {
-      apiHelper.cancelActiveRequest(self.lastKnownRequests.pop());
+      cancelActiveRequest(self.lastKnownRequests.pop());
     }
 
     while (self.cancellablePromises.length) {

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません