Convert gr-auth to class and use new API for auth check
auth-check API is available since: https://gerrit-review.googlesource.com/c/gerrit/+/185990 Change-Id: Icd5e0183ee42e746c32bfd7929af9796ab752627
This commit is contained in:
		@@ -23,6 +23,9 @@ limitations under the License.
 | 
				
			|||||||
<link rel="import" href="../../shared/gr-alert/gr-alert.html">
 | 
					<link rel="import" href="../../shared/gr-alert/gr-alert.html">
 | 
				
			||||||
<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
 | 
					<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
 | 
				
			||||||
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
 | 
					<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
 | 
				
			||||||
 | 
					<!-- Import to get Gerrit interface -->
 | 
				
			||||||
 | 
					<!-- TODO(taoalpha): decouple gr-gerrit from gr-js-api-interface -->
 | 
				
			||||||
 | 
					<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<dom-module id="gr-error-manager">
 | 
					<dom-module id="gr-error-manager">
 | 
				
			||||||
  <template>
 | 
					  <template>
 | 
				
			||||||
@@ -33,6 +36,13 @@ limitations under the License.
 | 
				
			|||||||
          confirm-label="Dismiss"
 | 
					          confirm-label="Dismiss"
 | 
				
			||||||
          confirm-on-enter></gr-error-dialog>
 | 
					          confirm-on-enter></gr-error-dialog>
 | 
				
			||||||
    </gr-overlay>
 | 
					    </gr-overlay>
 | 
				
			||||||
 | 
					    <gr-overlay
 | 
				
			||||||
 | 
					      id="noInteractionOverlay"
 | 
				
			||||||
 | 
					      with-backdrop
 | 
				
			||||||
 | 
					      always-on-top
 | 
				
			||||||
 | 
					      no-cancel-on-esc-key
 | 
				
			||||||
 | 
					      no-cancel-on-outside-click>
 | 
				
			||||||
 | 
					    </gr-overlay>
 | 
				
			||||||
    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 | 
					    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
 | 
				
			||||||
    <gr-reporting id="reporting"></gr-reporting>
 | 
					    <gr-reporting id="reporting"></gr-reporting>
 | 
				
			||||||
  </template>
 | 
					  </template>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -64,15 +64,29 @@
 | 
				
			|||||||
      };
 | 
					      };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					      super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      /** @type {!Gerrit.Auth} */
 | 
				
			||||||
 | 
					      this._authService = Gerrit.Auth;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      /** @type {?Function} */
 | 
				
			||||||
 | 
					      this._authErrorHandlerDeregistrationHook;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    attached() {
 | 
					    attached() {
 | 
				
			||||||
      super.attached();
 | 
					      super.attached();
 | 
				
			||||||
      this.listen(document, 'server-error', '_handleServerError');
 | 
					      this.listen(document, 'server-error', '_handleServerError');
 | 
				
			||||||
      this.listen(document, 'network-error', '_handleNetworkError');
 | 
					      this.listen(document, 'network-error', '_handleNetworkError');
 | 
				
			||||||
      this.listen(document, 'auth-error', '_handleAuthError');
 | 
					 | 
				
			||||||
      this.listen(document, 'show-alert', '_handleShowAlert');
 | 
					      this.listen(document, 'show-alert', '_handleShowAlert');
 | 
				
			||||||
      this.listen(document, 'show-error', '_handleShowErrorDialog');
 | 
					      this.listen(document, 'show-error', '_handleShowErrorDialog');
 | 
				
			||||||
      this.listen(document, 'visibilitychange', '_handleVisibilityChange');
 | 
					      this.listen(document, 'visibilitychange', '_handleVisibilityChange');
 | 
				
			||||||
      this.listen(document, 'show-auth-required', '_handleAuthRequired');
 | 
					      this.listen(document, 'show-auth-required', '_handleAuthRequired');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      this._authErrorHandlerDeregistrationHook = Gerrit.on('auth-error',
 | 
				
			||||||
 | 
					          event => {
 | 
				
			||||||
 | 
					            this._handleAuthError(event.message, event.action);
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    detached() {
 | 
					    detached() {
 | 
				
			||||||
@@ -80,10 +94,11 @@
 | 
				
			|||||||
      this._clearHideAlertHandle();
 | 
					      this._clearHideAlertHandle();
 | 
				
			||||||
      this.unlisten(document, 'server-error', '_handleServerError');
 | 
					      this.unlisten(document, 'server-error', '_handleServerError');
 | 
				
			||||||
      this.unlisten(document, 'network-error', '_handleNetworkError');
 | 
					      this.unlisten(document, 'network-error', '_handleNetworkError');
 | 
				
			||||||
      this.unlisten(document, 'auth-error', '_handleAuthError');
 | 
					 | 
				
			||||||
      this.unlisten(document, 'show-auth-required', '_handleAuthRequired');
 | 
					      this.unlisten(document, 'show-auth-required', '_handleAuthRequired');
 | 
				
			||||||
      this.unlisten(document, 'visibilitychange', '_handleVisibilityChange');
 | 
					      this.unlisten(document, 'visibilitychange', '_handleVisibilityChange');
 | 
				
			||||||
      this.unlisten(document, 'show-error', '_handleShowErrorDialog');
 | 
					      this.unlisten(document, 'show-error', '_handleShowErrorDialog');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      this._authErrorHandlerDeregistrationHook();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _shouldSuppressError(msg) {
 | 
					    _shouldSuppressError(msg) {
 | 
				
			||||||
@@ -95,32 +110,41 @@
 | 
				
			|||||||
          'Log in is required to perform that action.', 'Log in.');
 | 
					          'Log in is required to perform that action.', 'Log in.');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _handleAuthError() {
 | 
					    _handleAuthError(msg, action) {
 | 
				
			||||||
      this._showAuthErrorAlert('Auth error', 'Refresh credentials.');
 | 
					      this.$.noInteractionOverlay.open().then(() => {
 | 
				
			||||||
 | 
					        this._showAuthErrorAlert(msg, action);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _handleServerError(e) {
 | 
					    _handleServerError(e) {
 | 
				
			||||||
      const {request, response} = e.detail;
 | 
					      const {request, response} = e.detail;
 | 
				
			||||||
      Promise.all([response.text(), this._getLoggedIn()])
 | 
					      response.text().then(errorText => {
 | 
				
			||||||
          .then(([errorText, loggedIn]) => {
 | 
					        const url = request && (request.anonymizedUrl || request.url);
 | 
				
			||||||
            const url = request && (request.anonymizedUrl || request.url);
 | 
					        const {status, statusText} = response;
 | 
				
			||||||
            const {status, statusText} = response;
 | 
					        if (response.status === 403
 | 
				
			||||||
            if (response.status === 403 &&
 | 
					                && !this._authService.isAuthed
 | 
				
			||||||
                loggedIn &&
 | 
					                && errorText === AUTHENTICATION_REQUIRED) {
 | 
				
			||||||
                errorText === AUTHENTICATION_REQUIRED) {
 | 
					          // if not authed previously, this is trying to access auth required APIs
 | 
				
			||||||
              // The app was logged at one point and is now getting auth errors.
 | 
					          // show auth required alert
 | 
				
			||||||
              // This indicates the auth token is no longer valid.
 | 
					          this._handleAuthRequired();
 | 
				
			||||||
              this._handleAuthError();
 | 
					        } else if (response.status === 403
 | 
				
			||||||
            } else if (!this._shouldSuppressError(errorText)) {
 | 
					                && this._authService.isAuthed
 | 
				
			||||||
              this._showErrorDialog(this._constructServerErrorMsg({
 | 
					                && errorText === AUTHENTICATION_REQUIRED) {
 | 
				
			||||||
                status,
 | 
					          // The app was logged at one point and is now getting auth errors.
 | 
				
			||||||
                statusText,
 | 
					          // This indicates the auth token may no longer valid.
 | 
				
			||||||
                errorText,
 | 
					          // Re-check on auth
 | 
				
			||||||
                url,
 | 
					          this._authService.clearCache();
 | 
				
			||||||
              }));
 | 
					          this.$.restAPI.getLoggedIn();
 | 
				
			||||||
            }
 | 
					        } else if (!this._shouldSuppressError(errorText)) {
 | 
				
			||||||
            console.error(errorText);
 | 
					          this._showErrorDialog(this._constructServerErrorMsg({
 | 
				
			||||||
          });
 | 
					            status,
 | 
				
			||||||
 | 
					            statusText,
 | 
				
			||||||
 | 
					            errorText,
 | 
				
			||||||
 | 
					            url,
 | 
				
			||||||
 | 
					          }));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        console.log(`server error: ${errorText}`);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _constructServerErrorMsg({errorText, status, statusText, url}) {
 | 
					    _constructServerErrorMsg({errorText, status, statusText, url}) {
 | 
				
			||||||
@@ -142,10 +166,6 @@
 | 
				
			|||||||
      console.error(e.detail.error.message);
 | 
					      console.error(e.detail.error.message);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _getLoggedIn() {
 | 
					 | 
				
			||||||
      return this.$.restAPI.getLoggedIn();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * @param {string} text
 | 
					     * @param {string} text
 | 
				
			||||||
     * @param {?string=} opt_actionText
 | 
					     * @param {?string=} opt_actionText
 | 
				
			||||||
@@ -222,7 +242,11 @@
 | 
				
			|||||||
          this.knownAccountId !== undefined &&
 | 
					          this.knownAccountId !== undefined &&
 | 
				
			||||||
          timeSinceLastCheck > STALE_CREDENTIAL_THRESHOLD_MS) {
 | 
					          timeSinceLastCheck > STALE_CREDENTIAL_THRESHOLD_MS) {
 | 
				
			||||||
        this._lastCredentialCheck = Date.now();
 | 
					        this._lastCredentialCheck = Date.now();
 | 
				
			||||||
        this.$.restAPI.checkCredentials();
 | 
					
 | 
				
			||||||
 | 
					        // check auth status in case:
 | 
				
			||||||
 | 
					        // - user signed out
 | 
				
			||||||
 | 
					        // - user switched account
 | 
				
			||||||
 | 
					        this._checkSignedIn();
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -232,22 +256,36 @@
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _checkSignedIn() {
 | 
					    _checkSignedIn() {
 | 
				
			||||||
      this.$.restAPI.checkCredentials().then(account => {
 | 
					      this._lastCredentialCheck = Date.now();
 | 
				
			||||||
        const isLoggedIn = !!account;
 | 
					 | 
				
			||||||
        this._lastCredentialCheck = Date.now();
 | 
					 | 
				
			||||||
        if (this._refreshingCredentials) {
 | 
					 | 
				
			||||||
          if (isLoggedIn) {
 | 
					 | 
				
			||||||
            // If the credentials were refreshed but the account is different
 | 
					 | 
				
			||||||
            // then reload the page completely.
 | 
					 | 
				
			||||||
            if (account._account_id !== this.knownAccountId) {
 | 
					 | 
				
			||||||
              this._reloadPage();
 | 
					 | 
				
			||||||
              return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this._handleCredentialRefreshed();
 | 
					      // force to refetch account info
 | 
				
			||||||
          } else {
 | 
					      this.$.restAPI.invalidateAccountsCache();
 | 
				
			||||||
            this._requestCheckLoggedIn();
 | 
					      this._authService.clearCache();
 | 
				
			||||||
          }
 | 
					
 | 
				
			||||||
 | 
					      this.$.restAPI.getLoggedIn().then(isLoggedIn => {
 | 
				
			||||||
 | 
					        // do nothing if its refreshing
 | 
				
			||||||
 | 
					        if (!this._refreshingCredentials) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!isLoggedIn) {
 | 
				
			||||||
 | 
					          // check later
 | 
				
			||||||
 | 
					          // 1. guest mode
 | 
				
			||||||
 | 
					          // 2. or signed out
 | 
				
			||||||
 | 
					          // in case #2, auth-error is taken care of separately
 | 
				
			||||||
 | 
					          this._requestCheckLoggedIn();
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          // check account
 | 
				
			||||||
 | 
					          this.$.restAPI.getAccount().then(account => {
 | 
				
			||||||
 | 
					            if (this._refreshingCredentials) {
 | 
				
			||||||
 | 
					              // If the credentials were refreshed but the account is different
 | 
				
			||||||
 | 
					              // then reload the page completely.
 | 
				
			||||||
 | 
					              if (account._account_id !== this.knownAccountId) {
 | 
				
			||||||
 | 
					                this._reloadPage();
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              this._handleCredentialRefreshed();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -277,6 +315,10 @@
 | 
				
			|||||||
      this._refreshingCredentials = false;
 | 
					      this._refreshingCredentials = false;
 | 
				
			||||||
      this._hideAlert();
 | 
					      this._hideAlert();
 | 
				
			||||||
      this._showAlert('Credentials refreshed.');
 | 
					      this._showAlert('Credentials refreshed.');
 | 
				
			||||||
 | 
					      this.$.noInteractionOverlay.close();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Clear the cache for auth
 | 
				
			||||||
 | 
					      this._authService.clearCache();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _handleWindowFocus() {
 | 
					    _handleWindowFocus() {
 | 
				
			||||||
@@ -299,4 +341,4 @@
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  customElements.define(GrErrorManager.is, GrErrorManager);
 | 
					  customElements.define(GrErrorManager.is, GrErrorManager);
 | 
				
			||||||
})();
 | 
					})();
 | 
				
			||||||
@@ -23,10 +23,10 @@ limitations under the License.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
 | 
					<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
 | 
				
			||||||
<script src="/bower_components/web-component-tester/browser.js"></script>
 | 
					<script src="/bower_components/web-component-tester/browser.js"></script>
 | 
				
			||||||
<link rel="import" href="../../../test/common-test-setup.html"/>
 | 
					<link rel="import" href="../../../test/common-test-setup.html" />
 | 
				
			||||||
<link rel="import" href="gr-error-manager.html">
 | 
					<link rel="import" href="gr-error-manager.html">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script>void(0);</script>
 | 
					<script>void (0);</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<test-fixture id="basic">
 | 
					<test-fixture id="basic">
 | 
				
			||||||
  <template>
 | 
					  <template>
 | 
				
			||||||
@@ -41,285 +41,324 @@ limitations under the License.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    setup(() => {
 | 
					    setup(() => {
 | 
				
			||||||
      sandbox = sinon.sandbox.create();
 | 
					      sandbox = sinon.sandbox.create();
 | 
				
			||||||
      stub('gr-rest-api-interface', {
 | 
					 | 
				
			||||||
        getLoggedIn() { return Promise.resolve(true); },
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
      element = fixture('basic');
 | 
					 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    teardown(() => {
 | 
					    teardown(() => {
 | 
				
			||||||
      sandbox.restore();
 | 
					      sandbox.restore();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    test('does not show auth error on 403 by default', done => {
 | 
					    suite('when authed', () => {
 | 
				
			||||||
      const showAuthErrorStub = sandbox.stub(element, '_showAuthErrorAlert');
 | 
					      setup(() => {
 | 
				
			||||||
      const responseText = Promise.resolve('server says no.');
 | 
					        sandbox.stub(window, 'fetch')
 | 
				
			||||||
      element.fire('server-error',
 | 
					            .returns(Promise.resolve({ok: true, status: 204}));
 | 
				
			||||||
          {response: {status: 403, text() { return responseText; }}}
 | 
					        element = fixture('basic');
 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
      Promise.all([
 | 
					 | 
				
			||||||
        element.$.restAPI.getLoggedIn.lastCall.returnValue,
 | 
					 | 
				
			||||||
        responseText,
 | 
					 | 
				
			||||||
      ]).then(() => {
 | 
					 | 
				
			||||||
        assert.isFalse(showAuthErrorStub.calledOnce);
 | 
					 | 
				
			||||||
        done();
 | 
					 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    test('shows auth error on 403 and Authentication required', done => {
 | 
					      test('does not show auth error on 403 by default', done => {
 | 
				
			||||||
      const showAuthErrorStub = sandbox.stub(element, '_showAuthErrorAlert');
 | 
					        const showAuthErrorStub = sandbox.stub(element, '_showAuthErrorAlert');
 | 
				
			||||||
      const responseText = Promise.resolve('Authentication required\n');
 | 
					        const responseText = Promise.resolve('server says no.');
 | 
				
			||||||
      element.fire('server-error',
 | 
					        element.fire('server-error',
 | 
				
			||||||
          {response: {status: 403, text() { return responseText; }}}
 | 
					            {response: {status: 403, text() { return responseText; }}}
 | 
				
			||||||
      );
 | 
					        );
 | 
				
			||||||
      Promise.all([
 | 
					 | 
				
			||||||
        element.$.restAPI.getLoggedIn.lastCall.returnValue,
 | 
					 | 
				
			||||||
        responseText,
 | 
					 | 
				
			||||||
      ]).then(() => {
 | 
					 | 
				
			||||||
        assert.isTrue(showAuthErrorStub.calledOnce);
 | 
					 | 
				
			||||||
        done();
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('show logged in error', () => {
 | 
					 | 
				
			||||||
      sandbox.stub(element, '_showAuthErrorAlert');
 | 
					 | 
				
			||||||
      element.fire('show-auth-required');
 | 
					 | 
				
			||||||
      assert.isTrue(element._showAuthErrorAlert.calledWithExactly(
 | 
					 | 
				
			||||||
          'Log in is required to perform that action.', 'Log in.'));
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('show normal Error', done => {
 | 
					 | 
				
			||||||
      const showErrorStub = sandbox.stub(element, '_showErrorDialog');
 | 
					 | 
				
			||||||
      const textSpy = sandbox.spy(() => { return Promise.resolve('ZOMG'); });
 | 
					 | 
				
			||||||
      element.fire('server-error', {response: {status: 500, text: textSpy}});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      assert.isTrue(textSpy.called);
 | 
					 | 
				
			||||||
      Promise.all([
 | 
					 | 
				
			||||||
        element.$.restAPI.getLoggedIn.lastCall.returnValue,
 | 
					 | 
				
			||||||
        textSpy.lastCall.returnValue,
 | 
					 | 
				
			||||||
      ]).then(() => {
 | 
					 | 
				
			||||||
        assert.isTrue(showErrorStub.calledOnce);
 | 
					 | 
				
			||||||
        assert.isTrue(showErrorStub.lastCall.calledWithExactly(
 | 
					 | 
				
			||||||
            'Error 500: ZOMG'));
 | 
					 | 
				
			||||||
        done();
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('_constructServerErrorMsg', () => {
 | 
					 | 
				
			||||||
      const errorText = 'change conflicts';
 | 
					 | 
				
			||||||
      const status = 409;
 | 
					 | 
				
			||||||
      const statusText = 'Conflict';
 | 
					 | 
				
			||||||
      const url = '/my/test/url';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      assert.equal(element._constructServerErrorMsg({status}),
 | 
					 | 
				
			||||||
          'Error 409');
 | 
					 | 
				
			||||||
      assert.equal(element._constructServerErrorMsg({status, url}),
 | 
					 | 
				
			||||||
          'Error 409: \nEndpoint: /my/test/url');
 | 
					 | 
				
			||||||
      assert.equal(element._constructServerErrorMsg({status, statusText, url}),
 | 
					 | 
				
			||||||
          'Error 409 (Conflict): \nEndpoint: /my/test/url');
 | 
					 | 
				
			||||||
      assert.equal(element._constructServerErrorMsg({
 | 
					 | 
				
			||||||
        status,
 | 
					 | 
				
			||||||
        statusText,
 | 
					 | 
				
			||||||
        errorText,
 | 
					 | 
				
			||||||
        url,
 | 
					 | 
				
			||||||
      }), 'Error 409 (Conflict): change conflicts' +
 | 
					 | 
				
			||||||
          '\nEndpoint: /my/test/url');
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('suppress TOO_MANY_FILES error', done => {
 | 
					 | 
				
			||||||
      const showAlertStub = sandbox.stub(element, '_showAlert');
 | 
					 | 
				
			||||||
      const textSpy = sandbox.spy(() => {
 | 
					 | 
				
			||||||
        return Promise.resolve('too many files to find conflicts');
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
      element.fire('server-error', {response: {status: 500, text: textSpy}});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      assert.isTrue(textSpy.called);
 | 
					 | 
				
			||||||
      Promise.all([
 | 
					 | 
				
			||||||
        element.$.restAPI.getLoggedIn.lastCall.returnValue,
 | 
					 | 
				
			||||||
        textSpy.lastCall.returnValue,
 | 
					 | 
				
			||||||
      ]).then(() => {
 | 
					 | 
				
			||||||
        assert.isFalse(showAlertStub.called);
 | 
					 | 
				
			||||||
        done();
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('show network error', done => {
 | 
					 | 
				
			||||||
      const consoleErrorStub = sandbox.stub(console, 'error');
 | 
					 | 
				
			||||||
      const showAlertStub = sandbox.stub(element, '_showAlert');
 | 
					 | 
				
			||||||
      element.fire('network-error', {error: new Error('ZOMG')});
 | 
					 | 
				
			||||||
      flush(() => {
 | 
					 | 
				
			||||||
        assert.isTrue(showAlertStub.calledOnce);
 | 
					 | 
				
			||||||
        assert.isTrue(showAlertStub.lastCall.calledWithExactly(
 | 
					 | 
				
			||||||
            'Server unavailable'));
 | 
					 | 
				
			||||||
        assert.isTrue(consoleErrorStub.calledOnce);
 | 
					 | 
				
			||||||
        assert.isTrue(consoleErrorStub.lastCall.calledWithExactly('ZOMG'));
 | 
					 | 
				
			||||||
        done();
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('show auth refresh toast', done => {
 | 
					 | 
				
			||||||
      const refreshStub = sandbox.stub(element.$.restAPI, 'checkCredentials',
 | 
					 | 
				
			||||||
          () => { return Promise.resolve(true); });
 | 
					 | 
				
			||||||
      const toastSpy = sandbox.spy(element, '_createToastAlert');
 | 
					 | 
				
			||||||
      const windowOpen = sandbox.stub(window, 'open');
 | 
					 | 
				
			||||||
      const responseText = Promise.resolve('Authentication required\n');
 | 
					 | 
				
			||||||
      element.fire('server-error',
 | 
					 | 
				
			||||||
          {response: {status: 403, text() { return responseText; }}}
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
      Promise.all([
 | 
					 | 
				
			||||||
        element.$.restAPI.getLoggedIn.lastCall.returnValue,
 | 
					 | 
				
			||||||
        responseText,
 | 
					 | 
				
			||||||
      ]).then(() => {
 | 
					 | 
				
			||||||
        assert.isTrue(toastSpy.called);
 | 
					 | 
				
			||||||
        let toast = toastSpy.lastCall.returnValue;
 | 
					 | 
				
			||||||
        assert.isOk(toast);
 | 
					 | 
				
			||||||
        assert.include(
 | 
					 | 
				
			||||||
            Polymer.dom(toast.root).textContent, 'Auth error');
 | 
					 | 
				
			||||||
        assert.include(
 | 
					 | 
				
			||||||
            Polymer.dom(toast.root).textContent, 'Refresh credentials.');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        assert.isFalse(windowOpen.called);
 | 
					 | 
				
			||||||
        MockInteractions.tap(toast.$$('gr-button.action'));
 | 
					 | 
				
			||||||
        assert.isTrue(windowOpen.called);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // @see Issue 5822: noopener breaks closeAfterLogin
 | 
					 | 
				
			||||||
        assert.equal(windowOpen.lastCall.args[2].indexOf('noopener=yes'),
 | 
					 | 
				
			||||||
            -1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const hideToastSpy = sandbox.spy(toast, 'hide');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        element._handleWindowFocus();
 | 
					 | 
				
			||||||
        assert.isTrue(refreshStub.called);
 | 
					 | 
				
			||||||
        element.flushDebouncer('checkLoggedIn');
 | 
					 | 
				
			||||||
        flush(() => {
 | 
					        flush(() => {
 | 
				
			||||||
          assert.isTrue(refreshStub.called);
 | 
					          assert.isFalse(showAuthErrorStub.calledOnce);
 | 
				
			||||||
          assert.isTrue(hideToastSpy.called);
 | 
					          done();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          assert.notStrictEqual(toastSpy.lastCall.returnValue, toast);
 | 
					      test('show auth required for 403 with auth error and not authed before',
 | 
				
			||||||
          toast = toastSpy.lastCall.returnValue;
 | 
					          done => {
 | 
				
			||||||
          assert.isOk(toast);
 | 
					            const showAuthErrorStub = sandbox.stub(
 | 
				
			||||||
          assert.include(
 | 
					                element, '_showAuthErrorAlert'
 | 
				
			||||||
              Polymer.dom(toast.root).textContent, 'Credentials refreshed');
 | 
					            );
 | 
				
			||||||
 | 
					            const responseText = Promise.resolve('Authentication required\n');
 | 
				
			||||||
 | 
					            sinon.stub(element.$.restAPI, 'getLoggedIn')
 | 
				
			||||||
 | 
					                .returns(Promise.resolve(true));
 | 
				
			||||||
 | 
					            element.fire('server-error',
 | 
				
			||||||
 | 
					                {response: {status: 403, text() { return responseText; }}}
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            flush(() => {
 | 
				
			||||||
 | 
					              assert.isTrue(showAuthErrorStub.calledOnce);
 | 
				
			||||||
 | 
					              done();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('recheck auth for 403 with auth error if authed before', done => {
 | 
				
			||||||
 | 
					        // starts with authed state
 | 
				
			||||||
 | 
					        element.$.restAPI.getLoggedIn();
 | 
				
			||||||
 | 
					        const responseText = Promise.resolve('Authentication required\n');
 | 
				
			||||||
 | 
					        sinon.stub(element.$.restAPI, 'getLoggedIn')
 | 
				
			||||||
 | 
					            .returns(Promise.resolve(true));
 | 
				
			||||||
 | 
					        element.fire('server-error',
 | 
				
			||||||
 | 
					            {response: {status: 403, text() { return responseText; }}}
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        flush(() => {
 | 
				
			||||||
 | 
					          assert.isTrue(element.$.restAPI.getLoggedIn.calledOnce);
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('show logged in error', () => {
 | 
				
			||||||
 | 
					        sandbox.stub(element, '_showAuthErrorAlert');
 | 
				
			||||||
 | 
					        element.fire('show-auth-required');
 | 
				
			||||||
 | 
					        assert.isTrue(element._showAuthErrorAlert.calledWithExactly(
 | 
				
			||||||
 | 
					            'Log in is required to perform that action.', 'Log in.'));
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('show normal Error', done => {
 | 
				
			||||||
 | 
					        const showErrorStub = sandbox.stub(element, '_showErrorDialog');
 | 
				
			||||||
 | 
					        const textSpy = sandbox.spy(() => { return Promise.resolve('ZOMG'); });
 | 
				
			||||||
 | 
					        element.fire('server-error', {response: {status: 500, text: textSpy}});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert.isTrue(textSpy.called);
 | 
				
			||||||
 | 
					        flush(() => {
 | 
				
			||||||
 | 
					          assert.isTrue(showErrorStub.calledOnce);
 | 
				
			||||||
 | 
					          assert.isTrue(showErrorStub.lastCall.calledWithExactly(
 | 
				
			||||||
 | 
					              'Error 500: ZOMG'));
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('_constructServerErrorMsg', () => {
 | 
				
			||||||
 | 
					        const errorText = 'change conflicts';
 | 
				
			||||||
 | 
					        const status = 409;
 | 
				
			||||||
 | 
					        const statusText = 'Conflict';
 | 
				
			||||||
 | 
					        const url = '/my/test/url';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert.equal(element._constructServerErrorMsg({status}),
 | 
				
			||||||
 | 
					            'Error 409');
 | 
				
			||||||
 | 
					        assert.equal(element._constructServerErrorMsg({status, url}),
 | 
				
			||||||
 | 
					            'Error 409: \nEndpoint: /my/test/url');
 | 
				
			||||||
 | 
					        assert.equal(element._constructServerErrorMsg({status, statusText, url}),
 | 
				
			||||||
 | 
					            'Error 409 (Conflict): \nEndpoint: /my/test/url');
 | 
				
			||||||
 | 
					        assert.equal(element._constructServerErrorMsg({
 | 
				
			||||||
 | 
					          status,
 | 
				
			||||||
 | 
					          statusText,
 | 
				
			||||||
 | 
					          errorText,
 | 
				
			||||||
 | 
					          url,
 | 
				
			||||||
 | 
					        }), 'Error 409 (Conflict): change conflicts' +
 | 
				
			||||||
 | 
					        '\nEndpoint: /my/test/url');
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('suppress TOO_MANY_FILES error', done => {
 | 
				
			||||||
 | 
					        const showAlertStub = sandbox.stub(element, '_showAlert');
 | 
				
			||||||
 | 
					        const textSpy = sandbox.spy(() => {
 | 
				
			||||||
 | 
					          return Promise.resolve('too many files to find conflicts');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        element.fire('server-error', {response: {status: 500, text: textSpy}});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert.isTrue(textSpy.called);
 | 
				
			||||||
 | 
					        flush(() => {
 | 
				
			||||||
 | 
					          assert.isFalse(showAlertStub.called);
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('show network error', done => {
 | 
				
			||||||
 | 
					        const consoleErrorStub = sandbox.stub(console, 'error');
 | 
				
			||||||
 | 
					        const showAlertStub = sandbox.stub(element, '_showAlert');
 | 
				
			||||||
 | 
					        element.fire('network-error', {error: new Error('ZOMG')});
 | 
				
			||||||
 | 
					        flush(() => {
 | 
				
			||||||
 | 
					          assert.isTrue(showAlertStub.calledOnce);
 | 
				
			||||||
 | 
					          assert.isTrue(showAlertStub.lastCall.calledWithExactly(
 | 
				
			||||||
 | 
					              'Server unavailable'));
 | 
				
			||||||
 | 
					          assert.isTrue(consoleErrorStub.calledOnce);
 | 
				
			||||||
 | 
					          assert.isTrue(consoleErrorStub.lastCall.calledWithExactly('ZOMG'));
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('show auth refresh toast', done => {
 | 
				
			||||||
 | 
					        // starts with authed state
 | 
				
			||||||
 | 
					        element.$.restAPI.getLoggedIn();
 | 
				
			||||||
 | 
					        const refreshStub = sandbox.stub(element.$.restAPI, 'getAccount',
 | 
				
			||||||
 | 
					            () => { return Promise.resolve({}); });
 | 
				
			||||||
 | 
					        const toastSpy = sandbox.spy(element, '_createToastAlert');
 | 
				
			||||||
 | 
					        const windowOpen = sandbox.stub(window, 'open');
 | 
				
			||||||
 | 
					        const responseText = Promise.resolve('Authentication required\n');
 | 
				
			||||||
 | 
					        // fake failed auth
 | 
				
			||||||
 | 
					        window.fetch.returns(Promise.resolve({status: 403}));
 | 
				
			||||||
 | 
					        element.fire('server-error',
 | 
				
			||||||
 | 
					            {response: {status: 403, text() { return responseText; }}}
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        assert.equal(window.fetch.callCount, 1);
 | 
				
			||||||
 | 
					        flush(() => {
 | 
				
			||||||
 | 
					          // auth check again
 | 
				
			||||||
 | 
					          assert.equal(window.fetch.callCount, 2);
 | 
				
			||||||
 | 
					          flush(() => {
 | 
				
			||||||
 | 
					            // auth-error fired
 | 
				
			||||||
 | 
					            assert.isTrue(toastSpy.called);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // toast
 | 
				
			||||||
 | 
					            let toast = toastSpy.lastCall.returnValue;
 | 
				
			||||||
 | 
					            assert.isOk(toast);
 | 
				
			||||||
 | 
					            assert.include(
 | 
				
			||||||
 | 
					                Polymer.dom(toast.root).textContent, 'Credentails expired.');
 | 
				
			||||||
 | 
					            assert.include(
 | 
				
			||||||
 | 
					                Polymer.dom(toast.root).textContent, 'Refresh credentials');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // noInteractionOverlay
 | 
				
			||||||
 | 
					            const noInteractionOverlay = element.$.noInteractionOverlay;
 | 
				
			||||||
 | 
					            assert.isOk(noInteractionOverlay);
 | 
				
			||||||
 | 
					            sinon.spy(noInteractionOverlay, 'close');
 | 
				
			||||||
 | 
					            assert.equal(
 | 
				
			||||||
 | 
					                noInteractionOverlay.backdropElement.getAttribute('opened'),
 | 
				
			||||||
 | 
					                '');
 | 
				
			||||||
 | 
					            assert.isFalse(windowOpen.called);
 | 
				
			||||||
 | 
					            MockInteractions.tap(toast.$$('gr-button.action'));
 | 
				
			||||||
 | 
					            assert.isTrue(windowOpen.called);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // @see Issue 5822: noopener breaks closeAfterLogin
 | 
				
			||||||
 | 
					            assert.equal(windowOpen.lastCall.args[2].indexOf('noopener=yes'),
 | 
				
			||||||
 | 
					                -1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const hideToastSpy = sandbox.spy(toast, 'hide');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // now fake authed
 | 
				
			||||||
 | 
					            window.fetch.returns(Promise.resolve({status: 204}));
 | 
				
			||||||
 | 
					            element._handleWindowFocus();
 | 
				
			||||||
 | 
					            element.flushDebouncer('checkLoggedIn');
 | 
				
			||||||
 | 
					            flush(() => {
 | 
				
			||||||
 | 
					              assert.isTrue(refreshStub.called);
 | 
				
			||||||
 | 
					              assert.isTrue(hideToastSpy.called);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              // toast update
 | 
				
			||||||
 | 
					              assert.notStrictEqual(toastSpy.lastCall.returnValue, toast);
 | 
				
			||||||
 | 
					              toast = toastSpy.lastCall.returnValue;
 | 
				
			||||||
 | 
					              assert.isOk(toast);
 | 
				
			||||||
 | 
					              assert.include(
 | 
				
			||||||
 | 
					                  Polymer.dom(toast.root).textContent, 'Credentials refreshed');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              // close overlay
 | 
				
			||||||
 | 
					              assert.isTrue(noInteractionOverlay.close.called);
 | 
				
			||||||
 | 
					              done();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('show alert', () => {
 | 
				
			||||||
 | 
					        const alertObj = {message: 'foo'};
 | 
				
			||||||
 | 
					        sandbox.stub(element, '_showAlert');
 | 
				
			||||||
 | 
					        element.fire('show-alert', alertObj);
 | 
				
			||||||
 | 
					        assert.isTrue(element._showAlert.calledOnce);
 | 
				
			||||||
 | 
					        assert.equal(element._showAlert.lastCall.args[0], 'foo');
 | 
				
			||||||
 | 
					        assert.isNotOk(element._showAlert.lastCall.args[1]);
 | 
				
			||||||
 | 
					        assert.isNotOk(element._showAlert.lastCall.args[2]);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('checks stale credentials on visibility change', () => {
 | 
				
			||||||
 | 
					        const refreshStub = sandbox.stub(element,
 | 
				
			||||||
 | 
					            '_checkSignedIn');
 | 
				
			||||||
 | 
					        sandbox.stub(Date, 'now').returns(999999);
 | 
				
			||||||
 | 
					        element._lastCredentialCheck = 0;
 | 
				
			||||||
 | 
					        element._handleVisibilityChange();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Since there is no known account, it should not test credentials.
 | 
				
			||||||
 | 
					        assert.isFalse(refreshStub.called);
 | 
				
			||||||
 | 
					        assert.equal(element._lastCredentialCheck, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        element.knownAccountId = 123;
 | 
				
			||||||
 | 
					        element._handleVisibilityChange();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Should test credentials, since there is a known account.
 | 
				
			||||||
 | 
					        assert.isTrue(refreshStub.called);
 | 
				
			||||||
 | 
					        assert.equal(element._lastCredentialCheck, 999999);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('refreshes with same credentials', done => {
 | 
				
			||||||
 | 
					        const accountPromise = Promise.resolve({_account_id: 1234});
 | 
				
			||||||
 | 
					        sandbox.stub(element.$.restAPI, 'getAccount')
 | 
				
			||||||
 | 
					            .returns(accountPromise);
 | 
				
			||||||
 | 
					        const requestCheckStub = sandbox.stub(element, '_requestCheckLoggedIn');
 | 
				
			||||||
 | 
					        const handleRefreshStub = sandbox.stub(element,
 | 
				
			||||||
 | 
					            '_handleCredentialRefreshed');
 | 
				
			||||||
 | 
					        const reloadStub = sandbox.stub(element, '_reloadPage');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        element.knownAccountId = 1234;
 | 
				
			||||||
 | 
					        element._refreshingCredentials = true;
 | 
				
			||||||
 | 
					        element._checkSignedIn();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        flush(() => {
 | 
				
			||||||
 | 
					          assert.isFalse(requestCheckStub.called);
 | 
				
			||||||
 | 
					          assert.isTrue(handleRefreshStub.called);
 | 
				
			||||||
 | 
					          assert.isFalse(reloadStub.called);
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('_showAlert hides existing alerts', () => {
 | 
				
			||||||
 | 
					        element._alertElement = element._createToastAlert();
 | 
				
			||||||
 | 
					        const hideStub = sandbox.stub(element, '_hideAlert');
 | 
				
			||||||
 | 
					        element._showAlert();
 | 
				
			||||||
 | 
					        assert.isTrue(hideStub.calledOnce);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('show-error', () => {
 | 
				
			||||||
 | 
					        const openStub = sandbox.stub(element.$.errorOverlay, 'open');
 | 
				
			||||||
 | 
					        const closeStub = sandbox.stub(element.$.errorOverlay, 'close');
 | 
				
			||||||
 | 
					        const reportStub = sandbox.stub(element.$.reporting, 'reportErrorDialog');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const message = 'test message';
 | 
				
			||||||
 | 
					        element.fire('show-error', {message});
 | 
				
			||||||
 | 
					        flushAsynchronousOperations();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert.isTrue(openStub.called);
 | 
				
			||||||
 | 
					        assert.isTrue(reportStub.called);
 | 
				
			||||||
 | 
					        assert.equal(element.$.errorDialog.text, message);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        element.$.errorDialog.fire('dismiss');
 | 
				
			||||||
 | 
					        flushAsynchronousOperations();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert.isTrue(closeStub.called);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('reloads when refreshed credentials differ', done => {
 | 
				
			||||||
 | 
					        const accountPromise = Promise.resolve({_account_id: 1234});
 | 
				
			||||||
 | 
					        sandbox.stub(element.$.restAPI, 'getAccount')
 | 
				
			||||||
 | 
					            .returns(accountPromise);
 | 
				
			||||||
 | 
					        const requestCheckStub = sandbox.stub(element, '_requestCheckLoggedIn');
 | 
				
			||||||
 | 
					        const handleRefreshStub = sandbox.stub(element,
 | 
				
			||||||
 | 
					            '_handleCredentialRefreshed');
 | 
				
			||||||
 | 
					        const reloadStub = sandbox.stub(element, '_reloadPage');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        element.knownAccountId = 4321; // Different from 1234
 | 
				
			||||||
 | 
					        element._refreshingCredentials = true;
 | 
				
			||||||
 | 
					        element._checkSignedIn();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        flush(() => {
 | 
				
			||||||
 | 
					          assert.isFalse(requestCheckStub.called);
 | 
				
			||||||
 | 
					          assert.isFalse(handleRefreshStub.called);
 | 
				
			||||||
 | 
					          assert.isTrue(reloadStub.called);
 | 
				
			||||||
          done();
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    test('show alert', () => {
 | 
					    suite('when not authed', () => {
 | 
				
			||||||
      const alertObj = {message: 'foo'};
 | 
					      setup(() => {
 | 
				
			||||||
      sandbox.stub(element, '_showAlert');
 | 
					        stub('gr-rest-api-interface', {
 | 
				
			||||||
      element.fire('show-alert', alertObj);
 | 
					          getLoggedIn() { return Promise.resolve(false); },
 | 
				
			||||||
      assert.isTrue(element._showAlert.calledOnce);
 | 
					        });
 | 
				
			||||||
      assert.equal(element._showAlert.lastCall.args[0], 'foo');
 | 
					        element = fixture('basic');
 | 
				
			||||||
      assert.isNotOk(element._showAlert.lastCall.args[1]);
 | 
					 | 
				
			||||||
      assert.isNotOk(element._showAlert.lastCall.args[2]);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('checks stale credentials on visibility change', () => {
 | 
					 | 
				
			||||||
      const refreshStub = sandbox.stub(element.$.restAPI,
 | 
					 | 
				
			||||||
          'checkCredentials');
 | 
					 | 
				
			||||||
      sandbox.stub(Date, 'now').returns(999999);
 | 
					 | 
				
			||||||
      element._lastCredentialCheck = 0;
 | 
					 | 
				
			||||||
      element._handleVisibilityChange();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      // Since there is no known account, it should not test credentials.
 | 
					 | 
				
			||||||
      assert.isFalse(refreshStub.called);
 | 
					 | 
				
			||||||
      assert.equal(element._lastCredentialCheck, 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      element.knownAccountId = 123;
 | 
					 | 
				
			||||||
      element._handleVisibilityChange();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      // Should test credentials, since there is a known account.
 | 
					 | 
				
			||||||
      assert.isTrue(refreshStub.called);
 | 
					 | 
				
			||||||
      assert.equal(element._lastCredentialCheck, 999999);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('refresh loop continues on credential fail', done => {
 | 
					 | 
				
			||||||
      const accountPromise = Promise.resolve(null);
 | 
					 | 
				
			||||||
      sandbox.stub(element.$.restAPI, 'checkCredentials')
 | 
					 | 
				
			||||||
          .returns(accountPromise);
 | 
					 | 
				
			||||||
      const requestCheckStub = sandbox.stub(element, '_requestCheckLoggedIn');
 | 
					 | 
				
			||||||
      const handleRefreshStub = sandbox.stub(element,
 | 
					 | 
				
			||||||
          '_handleCredentialRefreshed');
 | 
					 | 
				
			||||||
      const reloadStub = sandbox.stub(element, '_reloadPage');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      element._refreshingCredentials = true;
 | 
					 | 
				
			||||||
      element._checkSignedIn();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      accountPromise.then(() => {
 | 
					 | 
				
			||||||
        assert.isTrue(requestCheckStub.called);
 | 
					 | 
				
			||||||
        assert.isFalse(handleRefreshStub.called);
 | 
					 | 
				
			||||||
        assert.isFalse(reloadStub.called);
 | 
					 | 
				
			||||||
        done();
 | 
					 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    test('refreshes with same credentials', done => {
 | 
					      test('refresh loop continues on credential fail', done => {
 | 
				
			||||||
      const accountPromise = Promise.resolve({_account_id: 1234});
 | 
					        const requestCheckStub = sandbox.stub(element, '_requestCheckLoggedIn');
 | 
				
			||||||
      sandbox.stub(element.$.restAPI, 'checkCredentials')
 | 
					        const handleRefreshStub = sandbox.stub(element,
 | 
				
			||||||
          .returns(accountPromise);
 | 
					            '_handleCredentialRefreshed');
 | 
				
			||||||
      const requestCheckStub = sandbox.stub(element, '_requestCheckLoggedIn');
 | 
					        const reloadStub = sandbox.stub(element, '_reloadPage');
 | 
				
			||||||
      const handleRefreshStub = sandbox.stub(element,
 | 
					 | 
				
			||||||
          '_handleCredentialRefreshed');
 | 
					 | 
				
			||||||
      const reloadStub = sandbox.stub(element, '_reloadPage');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      element.knownAccountId = 1234;
 | 
					        element._refreshingCredentials = true;
 | 
				
			||||||
      element._refreshingCredentials = true;
 | 
					        element._checkSignedIn();
 | 
				
			||||||
      element._checkSignedIn();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      accountPromise.then(() => {
 | 
					        flush(() => {
 | 
				
			||||||
        assert.isFalse(requestCheckStub.called);
 | 
					          assert.isTrue(requestCheckStub.called);
 | 
				
			||||||
        assert.isTrue(handleRefreshStub.called);
 | 
					          assert.isFalse(handleRefreshStub.called);
 | 
				
			||||||
        assert.isFalse(reloadStub.called);
 | 
					          assert.isFalse(reloadStub.called);
 | 
				
			||||||
        done();
 | 
					          done();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('reloads when refreshed credentials differ', done => {
 | 
					 | 
				
			||||||
      const accountPromise = Promise.resolve({_account_id: 1234});
 | 
					 | 
				
			||||||
      sandbox.stub(element.$.restAPI, 'checkCredentials')
 | 
					 | 
				
			||||||
          .returns(accountPromise);
 | 
					 | 
				
			||||||
      const requestCheckStub = sandbox.stub(element, '_requestCheckLoggedIn');
 | 
					 | 
				
			||||||
      const handleRefreshStub = sandbox.stub(element,
 | 
					 | 
				
			||||||
          '_handleCredentialRefreshed');
 | 
					 | 
				
			||||||
      const reloadStub = sandbox.stub(element, '_reloadPage');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      element.knownAccountId = 4321; // Different from 1234
 | 
					 | 
				
			||||||
      element._refreshingCredentials = true;
 | 
					 | 
				
			||||||
      element._checkSignedIn();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      accountPromise.then(() => {
 | 
					 | 
				
			||||||
        assert.isFalse(requestCheckStub.called);
 | 
					 | 
				
			||||||
        assert.isFalse(handleRefreshStub.called);
 | 
					 | 
				
			||||||
        assert.isTrue(reloadStub.called);
 | 
					 | 
				
			||||||
        done();
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('_showAlert hides existing alerts', () => {
 | 
					 | 
				
			||||||
      element._alertElement = element._createToastAlert();
 | 
					 | 
				
			||||||
      const hideStub = sandbox.stub(element, '_hideAlert');
 | 
					 | 
				
			||||||
      element._showAlert();
 | 
					 | 
				
			||||||
      assert.isTrue(hideStub.calledOnce);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('show-error', () => {
 | 
					 | 
				
			||||||
      const openStub = sandbox.stub(element.$.errorOverlay, 'open');
 | 
					 | 
				
			||||||
      const closeStub = sandbox.stub(element.$.errorOverlay, 'close');
 | 
					 | 
				
			||||||
      const reportStub = sandbox.stub(element.$.reporting, 'reportErrorDialog');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const message = 'test message';
 | 
					 | 
				
			||||||
      element.fire('show-error', {message});
 | 
					 | 
				
			||||||
      flushAsynchronousOperations();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      assert.isTrue(openStub.called);
 | 
					 | 
				
			||||||
      assert.isTrue(reportStub.called);
 | 
					 | 
				
			||||||
      assert.equal(element.$.errorDialog.text, message);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      element.$.errorDialog.fire('dismiss');
 | 
					 | 
				
			||||||
      flushAsynchronousOperations();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      assert.isTrue(closeStub.called);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
@@ -324,7 +324,11 @@ limitations under the License.
 | 
				
			|||||||
      element.handleEvent(element.EventType.HIGHLIGHTJS_LOADED, {hljs: testHljs});
 | 
					      element.handleEvent(element.EventType.HIGHLIGHTJS_LOADED, {hljs: testHljs});
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    test('getAccount', done => {
 | 
					    test('getLoggedIn', done => {
 | 
				
			||||||
 | 
					      // fake fetch for authCheck
 | 
				
			||||||
 | 
					      sandbox.stub(window, 'fetch', () => {
 | 
				
			||||||
 | 
					        return Promise.resolve({status: 204});
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
      plugin.restApi().getLoggedIn().then(loggedIn => {
 | 
					      plugin.restApi().getLoggedIn().then(loggedIn => {
 | 
				
			||||||
        assert.isTrue(loggedIn);
 | 
					        assert.isTrue(loggedIn);
 | 
				
			||||||
        done();
 | 
					        done();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,22 +20,86 @@
 | 
				
			|||||||
  // Prevent redefinition.
 | 
					  // Prevent redefinition.
 | 
				
			||||||
  if (window.Gerrit.Auth) { return; }
 | 
					  if (window.Gerrit.Auth) { return; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const MAX_AUTH_CHECK_WAIT_TIME_MS = 1000 * 30; // 30s
 | 
				
			||||||
  const MAX_GET_TOKEN_RETRIES = 2;
 | 
					  const MAX_GET_TOKEN_RETRIES = 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Gerrit.Auth = {
 | 
					  /**
 | 
				
			||||||
    TYPE: {
 | 
					   * Auth class.
 | 
				
			||||||
      XSRF_TOKEN: 'xsrf_token',
 | 
					   *
 | 
				
			||||||
      ACCESS_TOKEN: 'access_token',
 | 
					   * Gerrit.Auth is an instance of this class.
 | 
				
			||||||
    },
 | 
					   */
 | 
				
			||||||
 | 
					  class Auth {
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					      this._type = null;
 | 
				
			||||||
 | 
					      this._cachedTokenPromise = null;
 | 
				
			||||||
 | 
					      this._defaultOptions = {};
 | 
				
			||||||
 | 
					      this._retriesLeft = MAX_GET_TOKEN_RETRIES;
 | 
				
			||||||
 | 
					      this._status = Auth.STATUS.UNDETERMINED;
 | 
				
			||||||
 | 
					      this._authCheckPromise = null;
 | 
				
			||||||
 | 
					      this._last_auth_check_time = Date.now();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _type: null,
 | 
					    /**
 | 
				
			||||||
    _cachedTokenPromise: null,
 | 
					     * Returns if user is authed or not.
 | 
				
			||||||
    _defaultOptions: {},
 | 
					     *
 | 
				
			||||||
    _retriesLeft: MAX_GET_TOKEN_RETRIES,
 | 
					     * @returns {!Promise<boolean>}
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    authCheck() {
 | 
				
			||||||
 | 
					      if (!this._authCheckPromise ||
 | 
				
			||||||
 | 
					        (Date.now() - this._last_auth_check_time > MAX_AUTH_CHECK_WAIT_TIME_MS)
 | 
				
			||||||
 | 
					      ) {
 | 
				
			||||||
 | 
					        // Refetch after last check expired
 | 
				
			||||||
 | 
					        this._authCheckPromise = fetch('/auth-check');
 | 
				
			||||||
 | 
					        this._last_auth_check_time = Date.now();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return this._authCheckPromise.then(res => {
 | 
				
			||||||
 | 
					        // auth-check will return 204 if authed
 | 
				
			||||||
 | 
					        // treat the rest as unauthed
 | 
				
			||||||
 | 
					        if (res.status === 204) {
 | 
				
			||||||
 | 
					          this._setStatus(Auth.STATUS.AUTHED);
 | 
				
			||||||
 | 
					          return true;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          this._setStatus(Auth.STATUS.NOT_AUTHED);
 | 
				
			||||||
 | 
					          return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }).catch(e => {
 | 
				
			||||||
 | 
					        this._setStatus(Auth.STATUS.ERROR);
 | 
				
			||||||
 | 
					        // Reset _authCheckPromise to avoid caching the failed promise
 | 
				
			||||||
 | 
					        this._authCheckPromise = null;
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    clearCache() {
 | 
				
			||||||
 | 
					      this._authCheckPromise = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @param {string} status
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    _setStatus(status) {
 | 
				
			||||||
 | 
					      if (this._status === status) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (this._status === Auth.STATUS.AUTHED) {
 | 
				
			||||||
 | 
					        Gerrit.emit('auth-error', {
 | 
				
			||||||
 | 
					          message: Auth.CREDS_EXPIRED_MSG, action: 'Refresh credentials',
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      this._status = status;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    get status() {
 | 
				
			||||||
 | 
					      return this._status;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    get isAuthed() {
 | 
				
			||||||
 | 
					      return this._status === Auth.STATUS.AUTHED;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _getToken() {
 | 
					    _getToken() {
 | 
				
			||||||
      return Promise.resolve(this._cachedTokenPromise);
 | 
					      return Promise.resolve(this._cachedTokenPromise);
 | 
				
			||||||
    },
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Enable cross-domain authentication using OAuth access token.
 | 
					     * Enable cross-domain authentication using OAuth access token.
 | 
				
			||||||
@@ -51,7 +115,7 @@
 | 
				
			|||||||
    setup(getToken, defaultOptions) {
 | 
					    setup(getToken, defaultOptions) {
 | 
				
			||||||
      this._retriesLeft = MAX_GET_TOKEN_RETRIES;
 | 
					      this._retriesLeft = MAX_GET_TOKEN_RETRIES;
 | 
				
			||||||
      if (getToken) {
 | 
					      if (getToken) {
 | 
				
			||||||
        this._type = Gerrit.Auth.TYPE.ACCESS_TOKEN;
 | 
					        this._type = Auth.TYPE.ACCESS_TOKEN;
 | 
				
			||||||
        this._cachedTokenPromise = null;
 | 
					        this._cachedTokenPromise = null;
 | 
				
			||||||
        this._getToken = getToken;
 | 
					        this._getToken = getToken;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -61,7 +125,7 @@
 | 
				
			|||||||
          this._defaultOptions[p] = defaultOptions[p];
 | 
					          this._defaultOptions[p] = defaultOptions[p];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Perform network fetch with authentication.
 | 
					     * Perform network fetch with authentication.
 | 
				
			||||||
@@ -74,7 +138,7 @@
 | 
				
			|||||||
      const options = Object.assign({
 | 
					      const options = Object.assign({
 | 
				
			||||||
        headers: new Headers(),
 | 
					        headers: new Headers(),
 | 
				
			||||||
      }, this._defaultOptions, opt_options);
 | 
					      }, this._defaultOptions, opt_options);
 | 
				
			||||||
      if (this._type === Gerrit.Auth.TYPE.ACCESS_TOKEN) {
 | 
					      if (this._type === Auth.TYPE.ACCESS_TOKEN) {
 | 
				
			||||||
        return this._getAccessToken().then(
 | 
					        return this._getAccessToken().then(
 | 
				
			||||||
            accessToken =>
 | 
					            accessToken =>
 | 
				
			||||||
              this._fetchWithAccessToken(url, options, accessToken)
 | 
					              this._fetchWithAccessToken(url, options, accessToken)
 | 
				
			||||||
@@ -82,7 +146,7 @@
 | 
				
			|||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        return this._fetchWithXsrfToken(url, options);
 | 
					        return this._fetchWithXsrfToken(url, options);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _getCookie(name) {
 | 
					    _getCookie(name) {
 | 
				
			||||||
      const key = name + '=';
 | 
					      const key = name + '=';
 | 
				
			||||||
@@ -95,7 +159,7 @@
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      return result;
 | 
					      return result;
 | 
				
			||||||
    },
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _isTokenValid(token) {
 | 
					    _isTokenValid(token) {
 | 
				
			||||||
      if (!token) { return false; }
 | 
					      if (!token) { return false; }
 | 
				
			||||||
@@ -105,7 +169,7 @@
 | 
				
			|||||||
      if (Date.now() >= expiration.getTime()) { return false; }
 | 
					      if (Date.now() >= expiration.getTime()) { return false; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
    },
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _fetchWithXsrfToken(url, options) {
 | 
					    _fetchWithXsrfToken(url, options) {
 | 
				
			||||||
      if (options.method && options.method !== 'GET') {
 | 
					      if (options.method && options.method !== 'GET') {
 | 
				
			||||||
@@ -116,7 +180,7 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      options.credentials = 'same-origin';
 | 
					      options.credentials = 'same-origin';
 | 
				
			||||||
      return fetch(url, options);
 | 
					      return fetch(url, options);
 | 
				
			||||||
    },
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * @return {!Promise<string>}
 | 
					     * @return {!Promise<string>}
 | 
				
			||||||
@@ -138,7 +202,7 @@
 | 
				
			|||||||
        // Fall back to anonymous access.
 | 
					        // Fall back to anonymous access.
 | 
				
			||||||
        return null;
 | 
					        return null;
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    },
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _fetchWithAccessToken(url, options, accessToken) {
 | 
					    _fetchWithAccessToken(url, options, accessToken) {
 | 
				
			||||||
      const params = [];
 | 
					      const params = [];
 | 
				
			||||||
@@ -180,8 +244,24 @@
 | 
				
			|||||||
        url = url + (url.indexOf('?') === -1 ? '?' : '&') + params.join('&');
 | 
					        url = url + (url.indexOf('?') === -1 ? '?' : '&') + params.join('&');
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return fetch(url, options);
 | 
					      return fetch(url, options);
 | 
				
			||||||
    },
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Auth.TYPE = {
 | 
				
			||||||
 | 
					    XSRF_TOKEN: 'xsrf_token',
 | 
				
			||||||
 | 
					    ACCESS_TOKEN: 'access_token',
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  window.Gerrit.Auth = Gerrit.Auth;
 | 
					  Auth.STATUS = {
 | 
				
			||||||
 | 
					    UNDETERMINED: 0,
 | 
				
			||||||
 | 
					    AUTHED: 1,
 | 
				
			||||||
 | 
					    NOT_AUTHED: 2,
 | 
				
			||||||
 | 
					    ERROR: 3,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Auth.CREDS_EXPIRED_MSG = 'Credentails expired.';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // TODO(taoalpha): this whole thing should be moved to a service
 | 
				
			||||||
 | 
					  window.Auth = Auth;
 | 
				
			||||||
 | 
					  Gerrit.Auth = new Auth();
 | 
				
			||||||
})(window);
 | 
					})(window);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,7 +35,6 @@ limitations under the License.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    setup(() => {
 | 
					    setup(() => {
 | 
				
			||||||
      sandbox = sinon.sandbox.create();
 | 
					      sandbox = sinon.sandbox.create();
 | 
				
			||||||
      sandbox.stub(window, 'fetch').returns(Promise.resolve({ok: true}));
 | 
					 | 
				
			||||||
      auth = Gerrit.Auth;
 | 
					      auth = Gerrit.Auth;
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -43,29 +42,222 @@ limitations under the License.
 | 
				
			|||||||
      sandbox.restore();
 | 
					      sandbox.restore();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    suite('default (xsrf token header)', () => {
 | 
					    suite('Auth class methods', () => {
 | 
				
			||||||
      test('GET', () => {
 | 
					      let fakeFetch;
 | 
				
			||||||
        return auth.fetch('/url', {bar: 'bar'}).then(() => {
 | 
					      setup(() => {
 | 
				
			||||||
          const [url, options] = fetch.lastCall.args;
 | 
					        auth = new Auth();
 | 
				
			||||||
          assert.equal(url, '/url');
 | 
					        fakeFetch = sandbox.stub(window, 'fetch');
 | 
				
			||||||
          assert.equal(options.credentials, 'same-origin');
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('auth-check returns 403', done => {
 | 
				
			||||||
 | 
					        fakeFetch.returns(Promise.resolve({status: 403}));
 | 
				
			||||||
 | 
					        auth.authCheck().then(authed => {
 | 
				
			||||||
 | 
					          assert.isFalse(authed);
 | 
				
			||||||
 | 
					          assert.equal(auth.status, Auth.STATUS.NOT_AUTHED);
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      test('POST', () => {
 | 
					      test('auth-check returns 204', done => {
 | 
				
			||||||
 | 
					        fakeFetch.returns(Promise.resolve({status: 204}));
 | 
				
			||||||
 | 
					        auth.authCheck().then(authed => {
 | 
				
			||||||
 | 
					          assert.isTrue(authed);
 | 
				
			||||||
 | 
					          assert.equal(auth.status, Auth.STATUS.AUTHED);
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('auth-check returns 502', done => {
 | 
				
			||||||
 | 
					        fakeFetch.returns(Promise.resolve({status: 502}));
 | 
				
			||||||
 | 
					        auth.authCheck().then(authed => {
 | 
				
			||||||
 | 
					          assert.isFalse(authed);
 | 
				
			||||||
 | 
					          assert.equal(auth.status, Auth.STATUS.NOT_AUTHED);
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('auth-check failed', done => {
 | 
				
			||||||
 | 
					        fakeFetch.returns(Promise.reject(new Error('random error')));
 | 
				
			||||||
 | 
					        auth.authCheck().then(authed => {
 | 
				
			||||||
 | 
					          assert.isFalse(authed);
 | 
				
			||||||
 | 
					          assert.equal(auth.status, Auth.STATUS.ERROR);
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    suite('cache and events behaivor', () => {
 | 
				
			||||||
 | 
					      let fakeFetch;
 | 
				
			||||||
 | 
					      let clock;
 | 
				
			||||||
 | 
					      setup(() => {
 | 
				
			||||||
 | 
					        auth = new Auth();
 | 
				
			||||||
 | 
					        clock = sinon.useFakeTimers();
 | 
				
			||||||
 | 
					        fakeFetch = sandbox.stub(window, 'fetch');
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('cache auth-check result', done => {
 | 
				
			||||||
 | 
					        fakeFetch.returns(Promise.resolve({status: 403}));
 | 
				
			||||||
 | 
					        auth.authCheck().then(authed => {
 | 
				
			||||||
 | 
					          assert.isFalse(authed);
 | 
				
			||||||
 | 
					          assert.equal(auth.status, Auth.STATUS.NOT_AUTHED);
 | 
				
			||||||
 | 
					          fakeFetch.returns(Promise.resolve({status: 204}));
 | 
				
			||||||
 | 
					          auth.authCheck().then(authed2 => {
 | 
				
			||||||
 | 
					            assert.isFalse(authed);
 | 
				
			||||||
 | 
					            assert.equal(auth.status, Auth.STATUS.NOT_AUTHED);
 | 
				
			||||||
 | 
					            done();
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('clearCache should refetch auth-check result', done => {
 | 
				
			||||||
 | 
					        fakeFetch.returns(Promise.resolve({status: 403}));
 | 
				
			||||||
 | 
					        auth.authCheck().then(authed => {
 | 
				
			||||||
 | 
					          assert.isFalse(authed);
 | 
				
			||||||
 | 
					          assert.equal(auth.status, Auth.STATUS.NOT_AUTHED);
 | 
				
			||||||
 | 
					          fakeFetch.returns(Promise.resolve({status: 204}));
 | 
				
			||||||
 | 
					          auth.clearCache();
 | 
				
			||||||
 | 
					          auth.authCheck().then(authed2 => {
 | 
				
			||||||
 | 
					            assert.isTrue(authed2);
 | 
				
			||||||
 | 
					            assert.equal(auth.status, Auth.STATUS.AUTHED);
 | 
				
			||||||
 | 
					            done();
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('cache expired on auth-check after certain time', done => {
 | 
				
			||||||
 | 
					        fakeFetch.returns(Promise.resolve({status: 403}));
 | 
				
			||||||
 | 
					        auth.authCheck().then(authed => {
 | 
				
			||||||
 | 
					          assert.isFalse(authed);
 | 
				
			||||||
 | 
					          assert.equal(auth.status, Auth.STATUS.NOT_AUTHED);
 | 
				
			||||||
 | 
					          clock.tick(1000 * 10000);
 | 
				
			||||||
 | 
					          fakeFetch.returns(Promise.resolve({status: 204}));
 | 
				
			||||||
 | 
					          auth.authCheck().then(authed2 => {
 | 
				
			||||||
 | 
					            assert.isTrue(authed2);
 | 
				
			||||||
 | 
					            assert.equal(auth.status, Auth.STATUS.AUTHED);
 | 
				
			||||||
 | 
					            done();
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('no cache if auth-check failed', done => {
 | 
				
			||||||
 | 
					        fakeFetch.returns(Promise.reject(new Error('random error')));
 | 
				
			||||||
 | 
					        auth.authCheck().then(authed => {
 | 
				
			||||||
 | 
					          assert.isFalse(authed);
 | 
				
			||||||
 | 
					          assert.equal(auth.status, Auth.STATUS.ERROR);
 | 
				
			||||||
 | 
					          assert.equal(fakeFetch.callCount, 1);
 | 
				
			||||||
 | 
					          auth.authCheck().then(() => {
 | 
				
			||||||
 | 
					            assert.equal(fakeFetch.callCount, 2);
 | 
				
			||||||
 | 
					            done();
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('fire event when switch from authed to unauthed', done => {
 | 
				
			||||||
 | 
					        fakeFetch.returns(Promise.resolve({status: 204}));
 | 
				
			||||||
 | 
					        auth.authCheck().then(authed => {
 | 
				
			||||||
 | 
					          assert.isTrue(authed);
 | 
				
			||||||
 | 
					          assert.equal(auth.status, Auth.STATUS.AUTHED);
 | 
				
			||||||
 | 
					          clock.tick(1000 * 10000);
 | 
				
			||||||
 | 
					          fakeFetch.returns(Promise.resolve({status: 403}));
 | 
				
			||||||
 | 
					          const emitStub = sinon.stub();
 | 
				
			||||||
 | 
					          Gerrit.emit = emitStub;
 | 
				
			||||||
 | 
					          auth.authCheck().then(authed2 => {
 | 
				
			||||||
 | 
					            assert.isFalse(authed2);
 | 
				
			||||||
 | 
					            assert.equal(auth.status, Auth.STATUS.NOT_AUTHED);
 | 
				
			||||||
 | 
					            assert.isTrue(emitStub.called);
 | 
				
			||||||
 | 
					            done();
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('fire event when switch from authed to error', done => {
 | 
				
			||||||
 | 
					        fakeFetch.returns(Promise.resolve({status: 204}));
 | 
				
			||||||
 | 
					        auth.authCheck().then(authed => {
 | 
				
			||||||
 | 
					          assert.isTrue(authed);
 | 
				
			||||||
 | 
					          assert.equal(auth.status, Auth.STATUS.AUTHED);
 | 
				
			||||||
 | 
					          clock.tick(1000 * 10000);
 | 
				
			||||||
 | 
					          fakeFetch.returns(Promise.reject(new Error('random error')));
 | 
				
			||||||
 | 
					          const emitStub = sinon.stub();
 | 
				
			||||||
 | 
					          Gerrit.emit = emitStub;
 | 
				
			||||||
 | 
					          auth.authCheck().then(authed2 => {
 | 
				
			||||||
 | 
					            assert.isFalse(authed2);
 | 
				
			||||||
 | 
					            assert.isTrue(emitStub.called);
 | 
				
			||||||
 | 
					            assert.equal(auth.status, Auth.STATUS.ERROR);
 | 
				
			||||||
 | 
					            done();
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('no event from non-authed to other status', done => {
 | 
				
			||||||
 | 
					        fakeFetch.returns(Promise.resolve({status: 403}));
 | 
				
			||||||
 | 
					        auth.authCheck().then(authed => {
 | 
				
			||||||
 | 
					          assert.isFalse(authed);
 | 
				
			||||||
 | 
					          assert.equal(auth.status, Auth.STATUS.NOT_AUTHED);
 | 
				
			||||||
 | 
					          clock.tick(1000 * 10000);
 | 
				
			||||||
 | 
					          fakeFetch.returns(Promise.resolve({status: 204}));
 | 
				
			||||||
 | 
					          const emitStub = sinon.stub();
 | 
				
			||||||
 | 
					          Gerrit.emit = emitStub;
 | 
				
			||||||
 | 
					          auth.authCheck().then(authed2 => {
 | 
				
			||||||
 | 
					            assert.isTrue(authed2);
 | 
				
			||||||
 | 
					            assert.isFalse(emitStub.called);
 | 
				
			||||||
 | 
					            assert.equal(auth.status, Auth.STATUS.AUTHED);
 | 
				
			||||||
 | 
					            done();
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('no event from non-authed to other status', done => {
 | 
				
			||||||
 | 
					        fakeFetch.returns(Promise.resolve({status: 403}));
 | 
				
			||||||
 | 
					        auth.authCheck().then(authed => {
 | 
				
			||||||
 | 
					          assert.isFalse(authed);
 | 
				
			||||||
 | 
					          assert.equal(auth.status, Auth.STATUS.NOT_AUTHED);
 | 
				
			||||||
 | 
					          clock.tick(1000 * 10000);
 | 
				
			||||||
 | 
					          fakeFetch.returns(Promise.reject(new Error('random error')));
 | 
				
			||||||
 | 
					          const emitStub = sinon.stub();
 | 
				
			||||||
 | 
					          Gerrit.emit = emitStub;
 | 
				
			||||||
 | 
					          auth.authCheck().then(authed2 => {
 | 
				
			||||||
 | 
					            assert.isFalse(authed2);
 | 
				
			||||||
 | 
					            assert.isFalse(emitStub.called);
 | 
				
			||||||
 | 
					            assert.equal(auth.status, Auth.STATUS.ERROR);
 | 
				
			||||||
 | 
					            done();
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    suite('default (xsrf token header)', () => {
 | 
				
			||||||
 | 
					      setup(() => {
 | 
				
			||||||
 | 
					        sandbox.stub(window, 'fetch').returns(Promise.resolve({ok: true}));
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('GET', done => {
 | 
				
			||||||
 | 
					        auth.fetch('/url', {bar: 'bar'}).then(() => {
 | 
				
			||||||
 | 
					          const [url, options] = fetch.lastCall.args;
 | 
				
			||||||
 | 
					          assert.equal(url, '/url');
 | 
				
			||||||
 | 
					          assert.equal(options.credentials, 'same-origin');
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      test('POST', done => {
 | 
				
			||||||
        sandbox.stub(auth, '_getCookie')
 | 
					        sandbox.stub(auth, '_getCookie')
 | 
				
			||||||
            .withArgs('XSRF_TOKEN')
 | 
					            .withArgs('XSRF_TOKEN')
 | 
				
			||||||
            .returns('foobar');
 | 
					            .returns('foobar');
 | 
				
			||||||
        return auth.fetch('/url', {method: 'POST'}).then(() => {
 | 
					        auth.fetch('/url', {method: 'POST'}).then(() => {
 | 
				
			||||||
          const [url, options] = fetch.lastCall.args;
 | 
					          const [url, options] = fetch.lastCall.args;
 | 
				
			||||||
          assert.equal(url, '/url');
 | 
					          assert.equal(url, '/url');
 | 
				
			||||||
          assert.equal(options.credentials, 'same-origin');
 | 
					          assert.equal(options.credentials, 'same-origin');
 | 
				
			||||||
          assert.equal(options.headers.get('X-Gerrit-Auth'), 'foobar');
 | 
					          assert.equal(options.headers.get('X-Gerrit-Auth'), 'foobar');
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    suite('cors (access token)', () => {
 | 
					    suite('cors (access token)', () => {
 | 
				
			||||||
 | 
					      setup(() => {
 | 
				
			||||||
 | 
					        sandbox.stub(window, 'fetch').returns(Promise.resolve({ok: true}));
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      let getToken;
 | 
					      let getToken;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const makeToken = opt_accessToken => {
 | 
					      const makeToken = opt_accessToken => {
 | 
				
			||||||
@@ -81,62 +273,68 @@ limitations under the License.
 | 
				
			|||||||
        auth.setup(getToken);
 | 
					        auth.setup(getToken);
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      test('base url support', () => {
 | 
					      test('base url support', done => {
 | 
				
			||||||
        const baseUrl = 'http://foo';
 | 
					        const baseUrl = 'http://foo';
 | 
				
			||||||
        sandbox.stub(Gerrit.BaseUrlBehavior, 'getBaseUrl').returns(baseUrl);
 | 
					        sandbox.stub(Gerrit.BaseUrlBehavior, 'getBaseUrl').returns(baseUrl);
 | 
				
			||||||
        return auth.fetch(baseUrl + '/url', {bar: 'bar'}).then(() => {
 | 
					        auth.fetch(baseUrl + '/url', {bar: 'bar'}).then(() => {
 | 
				
			||||||
          const [url] = fetch.lastCall.args;
 | 
					          const [url] = fetch.lastCall.args;
 | 
				
			||||||
          assert.equal(url, 'http://foo/a/url?access_token=zbaz');
 | 
					          assert.equal(url, 'http://foo/a/url?access_token=zbaz');
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      test('fetch not signed in', () => {
 | 
					      test('fetch not signed in', done => {
 | 
				
			||||||
        getToken.returns(Promise.resolve());
 | 
					        getToken.returns(Promise.resolve());
 | 
				
			||||||
        return auth.fetch('/url', {bar: 'bar'}).then(() => {
 | 
					        auth.fetch('/url', {bar: 'bar'}).then(() => {
 | 
				
			||||||
          const [url, options] = fetch.lastCall.args;
 | 
					          const [url, options] = fetch.lastCall.args;
 | 
				
			||||||
          assert.equal(url, '/url');
 | 
					          assert.equal(url, '/url');
 | 
				
			||||||
          assert.equal(options.bar, 'bar');
 | 
					          assert.equal(options.bar, 'bar');
 | 
				
			||||||
          assert.equal(Object.keys(options.headers).length, 0);
 | 
					          assert.equal(Object.keys(options.headers).length, 0);
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      test('fetch signed in', () => {
 | 
					      test('fetch signed in', done => {
 | 
				
			||||||
        return auth.fetch('/url', {bar: 'bar'}).then(() => {
 | 
					        auth.fetch('/url', {bar: 'bar'}).then(() => {
 | 
				
			||||||
          const [url, options] = fetch.lastCall.args;
 | 
					          const [url, options] = fetch.lastCall.args;
 | 
				
			||||||
          assert.equal(url, '/a/url?access_token=zbaz');
 | 
					          assert.equal(url, '/a/url?access_token=zbaz');
 | 
				
			||||||
          assert.equal(options.bar, 'bar');
 | 
					          assert.equal(options.bar, 'bar');
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      test('getToken calls are cached', () => {
 | 
					      test('getToken calls are cached', done => {
 | 
				
			||||||
        return Promise.all([
 | 
					        Promise.all([
 | 
				
			||||||
          auth.fetch('/url-one'), auth.fetch('/url-two')]).then(() => {
 | 
					          auth.fetch('/url-one'), auth.fetch('/url-two')]).then(() => {
 | 
				
			||||||
          assert.equal(getToken.callCount, 1);
 | 
					          assert.equal(getToken.callCount, 1);
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      test('getToken refreshes token', () => {
 | 
					      test('getToken refreshes token', done => {
 | 
				
			||||||
        sandbox.stub(auth, '_isTokenValid');
 | 
					        sandbox.stub(auth, '_isTokenValid');
 | 
				
			||||||
        auth._isTokenValid
 | 
					        auth._isTokenValid
 | 
				
			||||||
            .onFirstCall().returns(true)
 | 
					            .onFirstCall().returns(true)
 | 
				
			||||||
            .onSecondCall().returns(false)
 | 
					            .onSecondCall().returns(false)
 | 
				
			||||||
            .onThirdCall().returns(true);
 | 
					            .onThirdCall().returns(true);
 | 
				
			||||||
        return auth.fetch('/url-one').then(() => {
 | 
					        auth.fetch('/url-one').then(() => {
 | 
				
			||||||
          getToken.returns(Promise.resolve(makeToken('bzzbb')));
 | 
					          getToken.returns(Promise.resolve(makeToken('bzzbb')));
 | 
				
			||||||
          return auth.fetch('/url-two');
 | 
					          return auth.fetch('/url-two');
 | 
				
			||||||
        }).then(() => {
 | 
					        }).then(() => {
 | 
				
			||||||
          const [[firstUrl], [secondUrl]] = fetch.args;
 | 
					          const [[firstUrl], [secondUrl]] = fetch.args;
 | 
				
			||||||
          assert.equal(firstUrl, '/a/url-one?access_token=zbaz');
 | 
					          assert.equal(firstUrl, '/a/url-one?access_token=zbaz');
 | 
				
			||||||
          assert.equal(secondUrl, '/a/url-two?access_token=bzzbb');
 | 
					          assert.equal(secondUrl, '/a/url-two?access_token=bzzbb');
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      test('signed in token error falls back to anonymous', () => {
 | 
					      test('signed in token error falls back to anonymous', done => {
 | 
				
			||||||
        getToken.returns(Promise.resolve('rubbish'));
 | 
					        getToken.returns(Promise.resolve('rubbish'));
 | 
				
			||||||
        return auth.fetch('/url', {bar: 'bar'}).then(() => {
 | 
					        auth.fetch('/url', {bar: 'bar'}).then(() => {
 | 
				
			||||||
          const [url, options] = fetch.lastCall.args;
 | 
					          const [url, options] = fetch.lastCall.args;
 | 
				
			||||||
          assert.equal(url, '/url');
 | 
					          assert.equal(url, '/url');
 | 
				
			||||||
          assert.equal(options.bar, 'bar');
 | 
					          assert.equal(options.bar, 'bar');
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -154,12 +352,12 @@ limitations under the License.
 | 
				
			|||||||
        }));
 | 
					        }));
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      test('HTTP PUT with content type', () => {
 | 
					      test('HTTP PUT with content type', done => {
 | 
				
			||||||
        const originalOptions = {
 | 
					        const originalOptions = {
 | 
				
			||||||
          method: 'PUT',
 | 
					          method: 'PUT',
 | 
				
			||||||
          headers: new Headers({'Content-Type': 'mail/pigeon'}),
 | 
					          headers: new Headers({'Content-Type': 'mail/pigeon'}),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        return auth.fetch('/url', originalOptions).then(() => {
 | 
					        auth.fetch('/url', originalOptions).then(() => {
 | 
				
			||||||
          assert.isTrue(getToken.called);
 | 
					          assert.isTrue(getToken.called);
 | 
				
			||||||
          const [url, options] = fetch.lastCall.args;
 | 
					          const [url, options] = fetch.lastCall.args;
 | 
				
			||||||
          assert.include(url, '$ct=mail%2Fpigeon');
 | 
					          assert.include(url, '$ct=mail%2Fpigeon');
 | 
				
			||||||
@@ -167,14 +365,15 @@ limitations under the License.
 | 
				
			|||||||
          assert.include(url, 'access_token=zbaz');
 | 
					          assert.include(url, 'access_token=zbaz');
 | 
				
			||||||
          assert.equal(options.method, 'POST');
 | 
					          assert.equal(options.method, 'POST');
 | 
				
			||||||
          assert.equal(options.headers.get('Content-Type'), 'text/plain');
 | 
					          assert.equal(options.headers.get('Content-Type'), 'text/plain');
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      test('HTTP PUT without content type', () => {
 | 
					      test('HTTP PUT without content type', done => {
 | 
				
			||||||
        const originalOptions = {
 | 
					        const originalOptions = {
 | 
				
			||||||
          method: 'PUT',
 | 
					          method: 'PUT',
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        return auth.fetch('/url', originalOptions).then(() => {
 | 
					        auth.fetch('/url', originalOptions).then(() => {
 | 
				
			||||||
          assert.isTrue(getToken.called);
 | 
					          assert.isTrue(getToken.called);
 | 
				
			||||||
          const [url, options] = fetch.lastCall.args;
 | 
					          const [url, options] = fetch.lastCall.args;
 | 
				
			||||||
          assert.include(url, '$ct=text%2Fplain');
 | 
					          assert.include(url, '$ct=text%2Fplain');
 | 
				
			||||||
@@ -182,6 +381,7 @@ limitations under the License.
 | 
				
			|||||||
          assert.include(url, 'access_token=zbaz');
 | 
					          assert.include(url, 'access_token=zbaz');
 | 
				
			||||||
          assert.equal(options.method, 'POST');
 | 
					          assert.equal(options.method, 'POST');
 | 
				
			||||||
          assert.equal(options.headers.get('Content-Type'), 'text/plain');
 | 
					          assert.equal(options.headers.get('Content-Type'), 'text/plain');
 | 
				
			||||||
 | 
					          done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -66,12 +66,6 @@
 | 
				
			|||||||
     * @event network-error
 | 
					     * @event network-error
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Fired when credentials were rejected by server (e.g. expired).
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @event auth-error
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Fired after an RPC completes.
 | 
					     * Fired after an RPC completes.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
@@ -89,10 +83,6 @@
 | 
				
			|||||||
          type: Object,
 | 
					          type: Object,
 | 
				
			||||||
          value: new SiteBasedCache(), // Shared across instances.
 | 
					          value: new SiteBasedCache(), // Shared across instances.
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        _credentialCheck: {
 | 
					 | 
				
			||||||
          type: Object,
 | 
					 | 
				
			||||||
          value: {checking: false}, // Shared across instances.
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        _sharedFetchPromises: {
 | 
					        _sharedFetchPromises: {
 | 
				
			||||||
          type: Object,
 | 
					          type: Object,
 | 
				
			||||||
          value: new FetchPromisesCache(), // Shared across instances.
 | 
					          value: new FetchPromisesCache(), // Shared across instances.
 | 
				
			||||||
@@ -112,40 +102,12 @@
 | 
				
			|||||||
          type: Object,
 | 
					          type: Object,
 | 
				
			||||||
          value: {}, // Intentional to share the object across instances.
 | 
					          value: {}, // Intentional to share the object across instances.
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        _auth: {
 | 
					 | 
				
			||||||
          type: Object,
 | 
					 | 
				
			||||||
          value: Gerrit.Auth, // Share across instances.
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    created() {
 | 
					    created() {
 | 
				
			||||||
      super.created();
 | 
					      super.created();
 | 
				
			||||||
      /* Polymer 1 and Polymer 2 have slightly different lifecycle.
 | 
					      this._auth = Gerrit.Auth;
 | 
				
			||||||
      * Differences are not very well documented (see
 | 
					 | 
				
			||||||
      * https://github.com/Polymer/old-docs-site/issues/2322).
 | 
					 | 
				
			||||||
      * In Polymer 1, created() is called when properties values is not set
 | 
					 | 
				
			||||||
      * and ready() is always called later, even if element is not added
 | 
					 | 
				
			||||||
      * to a DOM. I.e. in Polymer 1 _cache and other properties are undefined,
 | 
					 | 
				
			||||||
      * while in Polymer 2 they are set to default values.
 | 
					 | 
				
			||||||
      * In Polymer 2, created() is called after properties values set and
 | 
					 | 
				
			||||||
      * ready() is called only after element is attached to a DOM.
 | 
					 | 
				
			||||||
      * There are several places in the code, where element is created with
 | 
					 | 
				
			||||||
      * document.createElement('gr-rest-api-interface') and is not added
 | 
					 | 
				
			||||||
      * to a DOM.
 | 
					 | 
				
			||||||
      * In such cases, Polymer 1 calls both created() and ready() methods,
 | 
					 | 
				
			||||||
      * but Polymer 2 calls only created() method.
 | 
					 | 
				
			||||||
      * To workaround these differences, we should try to create _restApiHelper
 | 
					 | 
				
			||||||
      * in both methods.
 | 
					 | 
				
			||||||
      */
 | 
					 | 
				
			||||||
      //
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      this._initRestApiHelper();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ready() {
 | 
					 | 
				
			||||||
      super.ready();
 | 
					 | 
				
			||||||
      // See comments in created()
 | 
					 | 
				
			||||||
      this._initRestApiHelper();
 | 
					      this._initRestApiHelper();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -153,10 +115,9 @@
 | 
				
			|||||||
      if (this._restApiHelper) {
 | 
					      if (this._restApiHelper) {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (this._cache && this._auth && this._sharedFetchPromises &&
 | 
					      if (this._cache && this._auth && this._sharedFetchPromises) {
 | 
				
			||||||
          this._credentialCheck) {
 | 
					 | 
				
			||||||
        this._restApiHelper = new GrRestApiHelper(this._cache, this._auth,
 | 
					        this._restApiHelper = new GrRestApiHelper(this._cache, this._auth,
 | 
				
			||||||
            this._sharedFetchPromises, this._credentialCheck, this);
 | 
					            this._sharedFetchPromises, this);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -850,11 +811,7 @@
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getLoggedIn() {
 | 
					    getLoggedIn() {
 | 
				
			||||||
      return this.getAccount().then(account => {
 | 
					      return this._auth.authCheck();
 | 
				
			||||||
        return account != null;
 | 
					 | 
				
			||||||
      }).catch(() => {
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getIsAdmin() {
 | 
					    getIsAdmin() {
 | 
				
			||||||
@@ -869,10 +826,6 @@
 | 
				
			|||||||
      });
 | 
					      });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    checkCredentials() {
 | 
					 | 
				
			||||||
      return this._restApiHelper.checkCredentials();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    getDefaultPreferences() {
 | 
					    getDefaultPreferences() {
 | 
				
			||||||
      return this._fetchSharedCacheURL({
 | 
					      return this._fetchSharedCacheURL({
 | 
				
			||||||
        url: '/config/server/preferences',
 | 
					        url: '/config/server/preferences',
 | 
				
			||||||
@@ -1347,6 +1300,10 @@
 | 
				
			|||||||
      this._restApiHelper.invalidateFetchPromisesPrefix('/projects/?');
 | 
					      this._restApiHelper.invalidateFetchPromisesPrefix('/projects/?');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    invalidateAccountsCache() {
 | 
				
			||||||
 | 
					      this._restApiHelper.invalidateFetchPromisesPrefix('/accounts/');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * @param {string} filter
 | 
					     * @param {string} filter
 | 
				
			||||||
     * @param {number} groupsPerPage
 | 
					     * @param {number} groupsPerPage
 | 
				
			||||||
@@ -2805,4 +2762,4 @@
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  customElements.define(GrRestApiInterface.is, GrRestApiInterface);
 | 
					  customElements.define(GrRestApiInterface.is, GrRestApiInterface);
 | 
				
			||||||
})();
 | 
					})();
 | 
				
			||||||
@@ -48,8 +48,6 @@ limitations under the License.
 | 
				
			|||||||
      window.CANONICAL_PATH = `test${ctr}`;
 | 
					      window.CANONICAL_PATH = `test${ctr}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      sandbox = sinon.sandbox.create();
 | 
					      sandbox = sinon.sandbox.create();
 | 
				
			||||||
      element = fixture('basic');
 | 
					 | 
				
			||||||
      element._projectLookup = {};
 | 
					 | 
				
			||||||
      const testJSON = ')]}\'\n{"hello": "bonjour"}';
 | 
					      const testJSON = ')]}\'\n{"hello": "bonjour"}';
 | 
				
			||||||
      sandbox.stub(window, 'fetch').returns(Promise.resolve({
 | 
					      sandbox.stub(window, 'fetch').returns(Promise.resolve({
 | 
				
			||||||
        ok: true,
 | 
					        ok: true,
 | 
				
			||||||
@@ -57,6 +55,10 @@ limitations under the License.
 | 
				
			|||||||
          return Promise.resolve(testJSON);
 | 
					          return Promise.resolve(testJSON);
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      }));
 | 
					      }));
 | 
				
			||||||
 | 
					      // fake auth
 | 
				
			||||||
 | 
					      sandbox.stub(Gerrit.Auth, 'authCheck').returns(Promise.resolve(true));
 | 
				
			||||||
 | 
					      element = fixture('basic');
 | 
				
			||||||
 | 
					      element._projectLookup = {};
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    teardown(() => {
 | 
					    teardown(() => {
 | 
				
			||||||
@@ -365,117 +367,6 @@ limitations under the License.
 | 
				
			|||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    test('auth failure', done => {
 | 
					 | 
				
			||||||
      const fakeAuthResponse = {
 | 
					 | 
				
			||||||
        ok: false,
 | 
					 | 
				
			||||||
        status: 403,
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
      window.fetch.onFirstCall().returns(
 | 
					 | 
				
			||||||
          Promise.reject(new Error('Failed to fetch')));
 | 
					 | 
				
			||||||
      window.fetch.onSecondCall().returns(Promise.resolve(fakeAuthResponse));
 | 
					 | 
				
			||||||
      // Emulate logged in.
 | 
					 | 
				
			||||||
      element._restApiHelper._cache.set('/accounts/self/detail', {});
 | 
					 | 
				
			||||||
      const serverErrorStub = sandbox.stub();
 | 
					 | 
				
			||||||
      element.addEventListener('server-error', serverErrorStub);
 | 
					 | 
				
			||||||
      const authErrorStub = sandbox.stub();
 | 
					 | 
				
			||||||
      element.addEventListener('auth-error', authErrorStub);
 | 
					 | 
				
			||||||
      element._restApiHelper.fetchJSON({url: '/bar'}).finally(r => {
 | 
					 | 
				
			||||||
        flush(() => {
 | 
					 | 
				
			||||||
          assert.isTrue(authErrorStub.called);
 | 
					 | 
				
			||||||
          assert.isFalse(serverErrorStub.called);
 | 
					 | 
				
			||||||
          assert.isFalse(element._cache.has('/accounts/self/detail'));
 | 
					 | 
				
			||||||
          done();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('auth failure - test all failed to fetch', done => {
 | 
					 | 
				
			||||||
      window.fetch.returns(
 | 
					 | 
				
			||||||
          Promise.reject(new Error('Failed to fetch')));
 | 
					 | 
				
			||||||
      // Emulate logged in.
 | 
					 | 
				
			||||||
      element._cache.set('/accounts/self/detail', {});
 | 
					 | 
				
			||||||
      const serverErrorStub = sandbox.stub();
 | 
					 | 
				
			||||||
      element.addEventListener('server-error', serverErrorStub);
 | 
					 | 
				
			||||||
      const authErrorStub = sandbox.stub();
 | 
					 | 
				
			||||||
      element.addEventListener('auth-error', authErrorStub);
 | 
					 | 
				
			||||||
      element._restApiHelper.fetchJSON({url: '/bar'}).finally(r => {
 | 
					 | 
				
			||||||
        flush(() => {
 | 
					 | 
				
			||||||
          assert.isTrue(authErrorStub.called);
 | 
					 | 
				
			||||||
          assert.isFalse(serverErrorStub.called);
 | 
					 | 
				
			||||||
          assert.isFalse(element._cache.has('/accounts/self/detail'));
 | 
					 | 
				
			||||||
          done();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('getLoggedIn returns false when network/auth failure', done => {
 | 
					 | 
				
			||||||
      window.fetch.returns(
 | 
					 | 
				
			||||||
          Promise.reject(new Error('Failed to fetch')));
 | 
					 | 
				
			||||||
      element.getLoggedIn().then(isLoggedIn => {
 | 
					 | 
				
			||||||
        assert.isFalse(isLoggedIn);
 | 
					 | 
				
			||||||
        done();
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('checkCredentials', done => {
 | 
					 | 
				
			||||||
      const responses = [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          ok: false,
 | 
					 | 
				
			||||||
          status: 403,
 | 
					 | 
				
			||||||
          text() { return Promise.resolve(); },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          ok: true,
 | 
					 | 
				
			||||||
          status: 200,
 | 
					 | 
				
			||||||
          text() { return Promise.resolve(')]}\'{}'); },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      ];
 | 
					 | 
				
			||||||
      window.fetch.restore();
 | 
					 | 
				
			||||||
      sandbox.stub(window, 'fetch', url => {
 | 
					 | 
				
			||||||
        if (url === window.CANONICAL_PATH + '/accounts/self/detail') {
 | 
					 | 
				
			||||||
          return Promise.resolve(responses.shift());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      element.getLoggedIn().then(account => {
 | 
					 | 
				
			||||||
        assert.isNotOk(account);
 | 
					 | 
				
			||||||
        element.checkCredentials().then(account => {
 | 
					 | 
				
			||||||
          assert.isOk(account);
 | 
					 | 
				
			||||||
          done();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('checkCredentials promise rejection', () => {
 | 
					 | 
				
			||||||
      window.fetch.restore();
 | 
					 | 
				
			||||||
      element._cache.set('/accounts/self/detail', true);
 | 
					 | 
				
			||||||
      const checkCredentialsSpy =
 | 
					 | 
				
			||||||
          sandbox.spy(element._restApiHelper, 'checkCredentials');
 | 
					 | 
				
			||||||
      sandbox.stub(window, 'fetch', url => {
 | 
					 | 
				
			||||||
        return Promise.reject(new Error('Failed to fetch'));
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
      return element.getConfig(true)
 | 
					 | 
				
			||||||
          .catch(err => undefined)
 | 
					 | 
				
			||||||
          .then(() => {
 | 
					 | 
				
			||||||
            // When the top-level fetch call throws an error, it invokes
 | 
					 | 
				
			||||||
            // checkCredentials, which in turn makes another fetch call.
 | 
					 | 
				
			||||||
            // The second fetch call also fails, which leads to a second
 | 
					 | 
				
			||||||
            // invocation of checkCredentials, which should immediately
 | 
					 | 
				
			||||||
            // return instead of making further fetch calls.
 | 
					 | 
				
			||||||
            assert.isTrue(checkCredentialsSpy .calledTwice);
 | 
					 | 
				
			||||||
            assert.isTrue(window.fetch.calledTwice);
 | 
					 | 
				
			||||||
          });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('checkCredentials accepts only json', () => {
 | 
					 | 
				
			||||||
      const authFetchStub = sandbox.stub(element._auth, 'fetch')
 | 
					 | 
				
			||||||
          .returns(Promise.resolve());
 | 
					 | 
				
			||||||
      element.checkCredentials();
 | 
					 | 
				
			||||||
      assert.isTrue(authFetchStub.called);
 | 
					 | 
				
			||||||
      assert.equal(authFetchStub.lastCall.args[1].headers.get('Accept'),
 | 
					 | 
				
			||||||
          'application/json');
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('legacy n,z key in change url is replaced', () => {
 | 
					    test('legacy n,z key in change url is replaced', () => {
 | 
				
			||||||
      const stub = sandbox.stub(element._restApiHelper, 'fetchJSON')
 | 
					      const stub = sandbox.stub(element._restApiHelper, 'fetchJSON')
 | 
				
			||||||
          .returns(Promise.resolve([]));
 | 
					          .returns(Promise.resolve([]));
 | 
				
			||||||
@@ -922,6 +813,18 @@ limitations under the License.
 | 
				
			|||||||
      assert.isFalse(element._cache.has(url));
 | 
					      assert.isFalse(element._cache.has(url));
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('invalidateAccountsCache', () => {
 | 
				
			||||||
 | 
					      const url = '/accounts/self/detail';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      element._cache.set(url, {});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      element.invalidateAccountsCache();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      assert.isUndefined(element._sharedFetchPromises[url]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      assert.isFalse(element._cache.has(url));
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    suite('getRepos', () => {
 | 
					    suite('getRepos', () => {
 | 
				
			||||||
      const defaultQuery = 'state%3Aactive%20OR%20state%3Aread-only';
 | 
					      const defaultQuery = 'state%3Aactive%20OR%20state%3Aread-only';
 | 
				
			||||||
      let fetchCacheURLStub;
 | 
					      let fetchCacheURLStub;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,7 +18,6 @@
 | 
				
			|||||||
  'use strict';
 | 
					  'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const JSON_PREFIX = ')]}\'';
 | 
					  const JSON_PREFIX = ')]}\'';
 | 
				
			||||||
  const FAILED_TO_FETCH_ERROR = 'Failed to fetch';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Wrapper around Map for caching server responses. Site-based so that
 | 
					   * Wrapper around Map for caching server responses. Site-based so that
 | 
				
			||||||
@@ -107,15 +106,13 @@
 | 
				
			|||||||
     * @param {SiteBasedCache} cache
 | 
					     * @param {SiteBasedCache} cache
 | 
				
			||||||
     * @param {object} auth
 | 
					     * @param {object} auth
 | 
				
			||||||
     * @param {FetchPromisesCache} fetchPromisesCache
 | 
					     * @param {FetchPromisesCache} fetchPromisesCache
 | 
				
			||||||
     * @param {object} credentialCheck
 | 
					 | 
				
			||||||
     * @param {object} restApiInterface
 | 
					     * @param {object} restApiInterface
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    constructor(cache, auth, fetchPromisesCache, credentialCheck,
 | 
					    constructor(cache, auth, fetchPromisesCache,
 | 
				
			||||||
        restApiInterface) {
 | 
					        restApiInterface) {
 | 
				
			||||||
      this._cache = cache;// TODO: make it public
 | 
					      this._cache = cache;// TODO: make it public
 | 
				
			||||||
      this._auth = auth;
 | 
					      this._auth = auth;
 | 
				
			||||||
      this._fetchPromisesCache = fetchPromisesCache;
 | 
					      this._fetchPromisesCache = fetchPromisesCache;
 | 
				
			||||||
      this._credentialCheck = credentialCheck;
 | 
					 | 
				
			||||||
      this._restApiInterface = restApiInterface;
 | 
					      this._restApiInterface = restApiInterface;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -190,15 +187,10 @@
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        return res;
 | 
					        return res;
 | 
				
			||||||
      }).catch(err => {
 | 
					      }).catch(err => {
 | 
				
			||||||
        const isLoggedIn = !!this._cache.get('/accounts/self/detail');
 | 
					        if (req.errFn) {
 | 
				
			||||||
        if (isLoggedIn && err && err.message === FAILED_TO_FETCH_ERROR) {
 | 
					          req.errFn.call(undefined, null, err);
 | 
				
			||||||
          this.checkCredentials();
 | 
					 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          if (req.errFn) {
 | 
					          this.fire('network-error', {error: err});
 | 
				
			||||||
            req.errFn.call(undefined, null, err);
 | 
					 | 
				
			||||||
          } else {
 | 
					 | 
				
			||||||
            this.fire('network-error', {error: err});
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        throw err;
 | 
					        throw err;
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
@@ -384,37 +376,6 @@
 | 
				
			|||||||
      return xhr;
 | 
					      return xhr;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    checkCredentials() {
 | 
					 | 
				
			||||||
      if (this._credentialCheck.checking) {
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      this._credentialCheck.checking = true;
 | 
					 | 
				
			||||||
      let req = {url: '/accounts/self/detail', reportUrlAsIs: true};
 | 
					 | 
				
			||||||
      req = this.addAcceptJsonHeader(req);
 | 
					 | 
				
			||||||
      // Skip the REST response cache.
 | 
					 | 
				
			||||||
      return this.fetchRawJSON(req).then(res => {
 | 
					 | 
				
			||||||
        if (!res) { return; }
 | 
					 | 
				
			||||||
        if (res.status === 403) {
 | 
					 | 
				
			||||||
          this.fire('auth-error');
 | 
					 | 
				
			||||||
          this._cache.delete('/accounts/self/detail');
 | 
					 | 
				
			||||||
        } else if (res.ok) {
 | 
					 | 
				
			||||||
          return this.getResponseObject(res);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }).then(res => {
 | 
					 | 
				
			||||||
        this._credentialCheck.checking = false;
 | 
					 | 
				
			||||||
        if (res) {
 | 
					 | 
				
			||||||
          this._cache.set('/accounts/self/detail', res);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return res;
 | 
					 | 
				
			||||||
      }).catch(err => {
 | 
					 | 
				
			||||||
        this._credentialCheck.checking = false;
 | 
					 | 
				
			||||||
        if (err && err.message === FAILED_TO_FETCH_ERROR) {
 | 
					 | 
				
			||||||
          this.fire('auth-error');
 | 
					 | 
				
			||||||
          this._cache.delete('/accounts/self/detail');
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * @param {string} prefix
 | 
					     * @param {string} prefix
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
@@ -428,4 +389,3 @@
 | 
				
			|||||||
  window.FetchPromisesCache = FetchPromisesCache;
 | 
					  window.FetchPromisesCache = FetchPromisesCache;
 | 
				
			||||||
  window.GrRestApiHelper = GrRestApiHelper;
 | 
					  window.GrRestApiHelper = GrRestApiHelper;
 | 
				
			||||||
})(window);
 | 
					})(window);
 | 
				
			||||||
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,7 +41,6 @@ limitations under the License.
 | 
				
			|||||||
      sandbox = sinon.sandbox.create();
 | 
					      sandbox = sinon.sandbox.create();
 | 
				
			||||||
      cache = new SiteBasedCache();
 | 
					      cache = new SiteBasedCache();
 | 
				
			||||||
      fetchPromisesCache = new FetchPromisesCache();
 | 
					      fetchPromisesCache = new FetchPromisesCache();
 | 
				
			||||||
      const credentialCheck = {checking: false};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      window.CANONICAL_PATH = 'testhelper';
 | 
					      window.CANONICAL_PATH = 'testhelper';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -59,7 +58,7 @@ limitations under the License.
 | 
				
			|||||||
      }));
 | 
					      }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      helper = new GrRestApiHelper(cache, Gerrit.Auth, fetchPromisesCache,
 | 
					      helper = new GrRestApiHelper(cache, Gerrit.Auth, fetchPromisesCache,
 | 
				
			||||||
          credentialCheck, mockRestApiInterface);
 | 
					          mockRestApiInterface);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    teardown(() => {
 | 
					    teardown(() => {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user