Skip to content

Commit

Permalink
Chore: Add download reachability checks to Document and Image viewers (
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeremy Press authored Feb 12, 2018
1 parent ad4a4ff commit dd259b0
Show file tree
Hide file tree
Showing 15 changed files with 382 additions and 51 deletions.
2 changes: 2 additions & 0 deletions src/i18n/en-US.properties
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ notification_button_default_text=Okay
notification_annotation_point_mode=Click anywhere to add a comment to the document
# Notification message shown when user enters drawing annotation mode
notification_annotation_draw_mode=Press down and drag the pointer to draw on the document
# Notification message shown when the user has a degraded preview experience due to blocked download hosts
notification_degraded_preview=It looks like your connection to {1} is being blocked. We think we can make file previews in Box faster for you but we will need your firewall settings to be updated. To do that, please request your Box Admin to configure firewall settings for Box.

# File Types
# 360 degree video file type
Expand Down
6 changes: 3 additions & 3 deletions src/lib/Preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,9 @@ class Preview extends EventEmitter {
this.throttledMousemoveHandler
);

// Set up the notification
this.ui.setupNotification();

// Update navigation
this.ui.showNavigation(this.file.id, this.collection);

Expand Down Expand Up @@ -1128,9 +1131,6 @@ class Preview extends EventEmitter {
// Hide the loading indicator
this.ui.hideLoadingIndicator();

// Set up the notification
this.ui.setupNotification();

// Prefetch next few files
this.prefetchNextFiles();
}
Expand Down
6 changes: 1 addition & 5 deletions src/lib/__tests__/Preview-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,7 @@ describe('lib/Preview', () => {
previewUIMock.expects('showLoadingIndicator');
previewUIMock.expects('startProgressBar');
previewUIMock.expects('showNavigation');
previewUIMock.expects('setupNotification');

preview.setupUI();
})
Expand Down Expand Up @@ -1722,11 +1723,6 @@ describe('lib/Preview', () => {
expect(stubs.hideLoadingIndicator).to.be.called;
});

it('should set up the notification', () => {
preview.finishLoading();
expect(stubs.setupNotification).to.be.called;
});

it('should prefetch next files', () => {
preview.finishLoading();
expect(stubs.prefetchNextFiles).to.be.called;
Expand Down
80 changes: 79 additions & 1 deletion src/lib/__tests__/util-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,64 @@ describe('lib/util', () => {
sandbox.verifyAndRestore();
});

describe('isCustomDownloadHost()', () => {
it('should be true if the url does not start with the default host prefix and is a dl host', () => {
let url = 'https://dl3.boxcloud.com/foo';
let result = util.isCustomDownloadHost(url)
expect(result).to.be.true;

url = 'https://dl.boxcloud.com/foo';
expect(util.isCustomDownloadHost(url)).to.be.false;

url = 'https://www.google.com';
expect(util.isCustomDownloadHost(url)).to.be.false;
});
});

describe('replaceDownloadHostWithDefault()', () => {
it('should return a download URL with the default host', () => {
let url = 'https://dl3.boxcloud.com/foo';
const result = util.replaceDownloadHostWithDefault(url);

expect(result).to.equal('https://dl.boxcloud.com/foo');
});
});

describe('setDownloadHostFallback()', () => {
it('should set the value in sessionStorage', () => {
util.setDownloadHostFallback();

expect(sessionStorage.getItem('download_host_fallback')).to.equal('true');
});
});

describe('shouldShowDegradedDownloadNotification()', () => {
beforeEach(() => {
sessionStorage.setItem('download_host_fallback', 'false');
});

it('should return true if we do not have an entry for the given host and our session indicates we are falling back to the default host', () => {
let result = util.shouldShowDegradedDownloadNotification();
expect(result).to.be.false;

sessionStorage.setItem('download_host_fallback', 'true');
result = util.shouldShowDegradedDownloadNotification();
expect(result).to.be.true;

const shownHostsArr = ['dl5.boxcloud.com'];
localStorage.setItem('download_host_notification_shown', JSON.stringify(shownHostsArr));
result = util.shouldShowDegradedDownloadNotification(shownHostsArr[0]);
expect(result).to.be.false;

});
});


describe('get()', () => {
const url = 'foo?bar=bum';
let url;
beforeEach(() => {
url = 'foo?bar=bum';
});

afterEach(() => {
fetchMock.restore();
Expand Down Expand Up @@ -95,6 +151,23 @@ describe('lib/util', () => {
expect(response).to.be.an.object;
});
});

it('set the download host fallback and try again if we\'re fetching from a non default host', () => {
url = 'dl7.boxcloud.com'
fetchMock.get(url, {
status: 500
});

return util.get(url, 'any')
.then(() => {
expect(response.status).to.equal(200);
})
.catch(() => {
fetchMock.get(url, {
status: 200
});
})
});
});

describe('post()', () => {
Expand Down Expand Up @@ -316,6 +389,11 @@ describe('lib/util', () => {
it('should return correct content url when no asset_path', () => {
expect(util.createContentUrl('foo', 'bar')).to.equal('foo');
});

it('should replace the download host with the default if we are falling back', () => {
sessionStorage.setItem('download_host_fallback', 'true');
expect(util.createContentUrl('https://dl6.boxcloud.com', 'bar')).to.equal('https://dl.boxcloud.com');
});
});

describe('createAssetUrlCreator()', () => {
Expand Down
83 changes: 82 additions & 1 deletion src/lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ const HEADER_CLIENT_NAME = 'X-Box-Client-Name';
const HEADER_CLIENT_VERSION = 'X-Box-Client-Version';
const CLIENT_NAME_KEY = 'box_client_name';
const CLIENT_VERSION_KEY = 'box_client_version';

const DEFAULT_DOWNLOAD_HOST_PREFIX = 'https://dl.';
const DOWNLOAD_NOTIFICATION_SHOWN_KEY = 'download_host_notification_shown';
const DOWNLOAD_HOST_FALLBACK_KEY = 'download_host_fallback';

/* eslint-disable no-undef */
const CLIENT_NAME = __NAME__;
const CLIENT_VERSION = __VERSION__;
Expand Down Expand Up @@ -111,6 +116,63 @@ function createDownloadIframe() {
return iframe;
}

/**
* Checks if the url is a download host, but not the default download host.
*
* @public
* @param {string} url - The URL to check
* @return {boolean} - HTTP response
*/
export function isCustomDownloadHost(url) {
return !url.startsWith(DEFAULT_DOWNLOAD_HOST_PREFIX) && !!url.match(/^https:\/\/dl\d+\./);
}

/**
* Replaces the hostname of a download URL with the default hostname, https://dl.
*
* @private
* @param {string} url - The URL to modify
* @return {string} - The updated download URL
*/
export function replaceDownloadHostWithDefault(url) {
return url.replace(/^https:\/\/dl\d+\./, DEFAULT_DOWNLOAD_HOST_PREFIX);
}

/**
* Sets session storage to use the default download host
*
* @public
* @return {void}
*/
export function setDownloadHostFallback() {
sessionStorage.setItem(DOWNLOAD_HOST_FALLBACK_KEY, 'true');
}

/**
* Stores the host in an array via localstorage so that we don't show a notification for it again
*
* @public
* @param {string} downloadHost - Download URL host
* @return {void}
*/
export function setDownloadHostNotificationShown(downloadHost) {
const shownHostsArr = JSON.parse(localStorage.getItem(DOWNLOAD_NOTIFICATION_SHOWN_KEY)) || [];
shownHostsArr.push(downloadHost);
localStorage.setItem(DOWNLOAD_NOTIFICATION_SHOWN_KEY, JSON.stringify(shownHostsArr));
}

/**
* Determines if the degraded download notification should be shown.
*
* @public
* @param {string} downloadHost - Download URL host
* @return {boolean} Should the notification be shown
*/
export function shouldShowDegradedDownloadNotification(downloadHost) {
const shownHostsArr = JSON.parse(localStorage.getItem(DOWNLOAD_NOTIFICATION_SHOWN_KEY)) || [];
return sessionStorage.getItem(DOWNLOAD_HOST_FALLBACK_KEY) === 'true' && !shownHostsArr.includes(downloadHost);
}

/**
* HTTP GETs a URL
* Usage:
Expand Down Expand Up @@ -158,7 +220,20 @@ export function get(url, ...rest) {

return fetch(url, { headers })
.then(checkStatus)
.then(parser);
.then(parser)
.catch((e) => {
// Make sure we are making a dl call
if (isCustomDownloadHost(url)) {
// Retry download with default host.
setDownloadHostFallback();
return get(url, ...rest);
}

/* eslint-disable no-console */
console.error(e);
/* eslint-enable no-console */
return Promise.reject(e);
});
}

/**
Expand Down Expand Up @@ -397,6 +472,12 @@ export function appendAuthParams(url, token = '', sharedLink = '', password = ''
* @return {string} Content url
*/
export function createContentUrl(template, asset) {
if (sessionStorage.getItem(DOWNLOAD_HOST_FALLBACK_KEY) === 'true') {
/* eslint-disable no-param-reassign */
template = replaceDownloadHostWithDefault(template);
/* eslint-enable no-param-reassign */
}

return template.replace('{+asset_path}', asset || '');
}

Expand Down
46 changes: 43 additions & 3 deletions src/lib/viewers/BaseViewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,17 @@ import {
getProp,
appendQueryParams,
appendAuthParams,
getHeaders,
createContentUrl,
getHeaders,
isCustomDownloadHost,
loadStylesheets,
loadScripts,
prefetchAssets,
createAssetUrlCreator
setDownloadHostFallback,
setDownloadHostNotificationShown,
shouldShowDegradedDownloadNotification,
createAssetUrlCreator,
replacePlaceholders
} from '../util';
import Browser from '../Browser';
import {
Expand Down Expand Up @@ -271,6 +276,27 @@ class BaseViewer extends EventEmitter {
this.destroyed = true;
}

/**
* Handles a download error when using a non default host.
*
* @param {Error} err - Load error
* @param {string} downloadURL - download URL
* @return {void}
*/
handleDownloadError(err, downloadURL) {
if (isCustomDownloadHost(downloadURL)) {
setDownloadHostFallback();
this.load();
return;
}

/* eslint-disable no-console */
console.error(err);
/* eslint-enable no-console */

this.triggerError(err);
}

/**
* Emits error event with refresh message.
*
Expand Down Expand Up @@ -384,13 +410,27 @@ class BaseViewer extends EventEmitter {
}

/**
* Handles the viewer load to potentially set up Box Annotations.
* Handles the viewer load to finish viewer setup after loading.
*
* @private
* @param {Object} event - load event data
* @return {void}
*/
viewerLoadHandler(event) {
const contentHost = document.createElement('a');
const contentTemplate = getProp(this.options, 'representation.content.url_template', null);
contentHost.href = contentTemplate;
const contentHostname = contentHost.hostname;
if (contentTemplate && shouldShowDegradedDownloadNotification(contentHostname)) {
this.previewUI.notification.show(
replacePlaceholders(__('notification_degraded_preview'), [contentHostname]),
__('notification_button_default_text'),
true
);

setDownloadHostNotificationShown(contentHostname);
}

if (event && event.scale) {
this.scale = event.scale;
}
Expand Down
Loading

0 comments on commit dd259b0

Please sign in to comment.