X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=skyquake%2Fplugins%2Fcomposer%2Fsrc%2Fsrc%2Fstores%2FCatalogPackageManagerStore.js;fp=skyquake%2Fplugins%2Fcomposer%2Fsrc%2Fsrc%2Fstores%2FCatalogPackageManagerStore.js;h=733e938a6f0895e967809836caecfb4ab4d7be16;hb=e29efc315df33d546237e270470916e26df391d6;hp=0000000000000000000000000000000000000000;hpb=9c5e457509ba5a1822c316635c6308874e61b4b9;p=osm%2FUI.git diff --git a/skyquake/plugins/composer/src/src/stores/CatalogPackageManagerStore.js b/skyquake/plugins/composer/src/src/stores/CatalogPackageManagerStore.js new file mode 100644 index 000000000..733e938a6 --- /dev/null +++ b/skyquake/plugins/composer/src/src/stores/CatalogPackageManagerStore.js @@ -0,0 +1,243 @@ + +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +'use strict'; + +import _ from 'lodash' +import alt from '../alt' +import guid from '../libraries/guid' +import numeral from 'numeral' +import moment from 'moment' +import utils from '../libraries/utils' +import CatalogPackageManagerSource from '../sources/CatalogPackageManagerSource' +import CatalogPackageManagerActions from '../actions/CatalogPackageManagerActions' +import CatalogDataSource from '../sources/CatalogDataSource' + +import imgDownload from '../../../node_modules/open-iconic/svg/cloud-download.svg' +import imgOnboard from '../../../node_modules/open-iconic/svg/cloud-upload.svg' +import imgUpdate from '../../../node_modules/open-iconic/svg/data-transfer-upload.svg' + +const defaults = { + downloadPackage: { + id: '', + name: '', + icon: imgDownload, + catalogItems: [], + transactionId: '', + progress: 0, + message: 'Requesting catalog package export...', + pending: false, + success: false, + error: false, + url: '', + urlValidUntil: '' + }, + checkStatusDelayInSeconds: 2, + downloadUrlTimeToLiveInMinutes: 5 +}; + +const exception = function ignoreException() {}; + +const packagePropertyNames = Object.keys(defaults.downloadPackage); + +function getCatalogPackageManagerServerOrigin() { + return utils.getSearchParams(window.location).upload_server + ':4567'; +} + +function delayStatusCheck(statusCheckFunction, catalogPackage) { + if (!catalogPackage.checkStatusTimeoutId) { + const delayCallback = function () { + delete catalogPackage.checkStatusTimeoutId; + statusCheckFunction(catalogPackage).catch(exception); + }; + catalogPackage.checkStatusTimeoutId = _.delay(delayCallback, defaults.checkStatusDelayInSeconds * 1000); + } +} + +class CatalogPackageManagerStore { + + constructor() { + + this.packages = []; + + this.registerAsync(CatalogDataSource); + this.registerAsync(CatalogPackageManagerSource); + this.bindAction(CatalogPackageManagerActions.REMOVE_CATALOG_PACKAGE, this.removeCatalogPackage); + this.bindAction(CatalogPackageManagerActions.DOWNLOAD_CATALOG_PACKAGE, this.downloadCatalogPackage); + this.bindAction(CatalogPackageManagerActions.DOWNLOAD_CATALOG_PACKAGE_STATUS_UPDATED, this.onDownloadCatalogPackageStatusUpdated); + this.bindAction(CatalogPackageManagerActions.DOWNLOAD_CATALOG_PACKAGE_ERROR, this.onDownloadCatalogPackageError); + this.bindAction(CatalogPackageManagerActions.UPLOAD_CATALOG_PACKAGE, this.uploadCatalogPackage); + this.bindAction(CatalogPackageManagerActions.UPLOAD_CATALOG_PACKAGE_STATUS_UPDATED, this.onUploadCatalogPackageStatusUpdated); + this.bindAction(CatalogPackageManagerActions.UPLOAD_CATALOG_PACKAGE_ERROR, this.onUploadCatalogPackageError); + + } + + addPackage(catalogPackage) { + const packages = [catalogPackage].concat(this.packages); + this.setState({packages: packages}); + } + + updatePackage(catalogPackage) { + const packages = this.packages.map(d => { + if (d.id === catalogPackage.id) { + return Object.assign({}, d, catalogPackage); + } + return d; + }); + this.setState({packages: packages}); + } + + removeCatalogPackage(catalogPackage) { + const packages = this.packages.filter(d => d.id !== catalogPackage.id); + this.setState({packages: packages}); + } + + uploadCatalogPackage(file) { + file.id = file.id || guid(); + const catalogPackage = _.pick(file, packagePropertyNames); + catalogPackage.icon = file.riftAction === 'onboard' ? imgOnboard : imgUpdate; + catalogPackage.type = 'upload'; + this.addPackage(catalogPackage); + // note DropZone.js handles the async upload so we don't have to invoke any async action creators + } + + onUploadCatalogPackageStatusUpdated(response) { + const upload = updateStatusInfo(response); + this.updatePackage(upload); + // if pending with no transaction id - do nothing + // bc DropZone.js will notify upload progress + if (upload.pending && upload.transactionId) { + delayStatusCheck(this.getInstance().requestCatalogPackageUploadStatus, upload); + } else if (upload.success) { + this.getInstance().loadCatalogs(); + } + } + + onUploadCatalogPackageError(response) { + console.warn('onUploadCatalogPackageError', response); + const catalogPackage = updateStatusInfo(response); + this.updatePackage(catalogPackage); + } + + downloadCatalogPackage(data) { + let catalogItems = data['selectedItems'] || []; + let format = data['selectedFormat'] || 'mano'; + let grammar = data['selectedGrammar'] || 'osm'; + if (catalogItems.length) { + const catalogPackage = Object.assign({}, defaults.downloadPackage, {id: guid()}); + catalogPackage.name = catalogItems[0].name; + catalogPackage.type = 'download'; + if (catalogItems.length > 1) { + catalogPackage.name += ' (' + catalogItems.length + ' items)'; + } + catalogPackage.ids = catalogItems.map(d => d.id).sort().toString(); + catalogPackage.catalogItems = catalogItems; + this.addPackage(catalogPackage); + this.getInstance().requestCatalogPackageDownload(catalogPackage, format, grammar).catch(exception); + } + } + + onDownloadCatalogPackageStatusUpdated(response) { + const download = updateStatusInfo(response); + this.updatePackage(download); + if (download.pending) { + delayStatusCheck(this.getInstance().requestCatalogPackageDownloadStatus, download); + } + } + + onDownloadCatalogPackageError(response) { + console.warn('onDownloadCatalogPackageError', response); + const catalogPackage = updateStatusInfo(response); + this.updatePackage(catalogPackage); + } + +} + +function calculateUploadProgressMessage(size = 0, progress = 0, bytesSent = 0) { + const amount = parseFloat(progress) || 0; + const loaded = amount === 100 ? size : size * amount / 100; + let progressText; + if (amount === 100) { + progressText = numeral(loaded).format('0.0b') + ' loaded '; + } else if (typeof amount === 'number' && amount != 0) { + progressText = numeral(bytesSent).format('0.0b') + ' out of ' + numeral(size).format('0.0b'); + } else { + progressText = progress; + } + return progressText; +} + +function updateStatusInfo(response) { + // returns the catalogPackage object with the status fields updated based on the server response + const statusInfo = { + pending: false, + success: false, + error: false + }; + const responseData = response.data; + const catalogPackage = response.state; + switch(responseData.status) { + case 'upload-progress': + statusInfo.pending = true; + statusInfo.progress = parseFloat(responseData.progress) || 0; + statusInfo.message = calculateUploadProgressMessage(catalogPackage.size, responseData.progress, responseData.bytesSent); + break; + case 'upload-success': + statusInfo.pending = true; + statusInfo.progress = 100; + statusInfo.message = 'Upload completed.'; + statusInfo.transactionId = responseData.transaction_id; + break; + case 'upload-error': + statusInfo.error = true; + statusInfo.message = responseData.message; + break; + case 'download-requested': + statusInfo.pending = true; + statusInfo.progress = 25; + statusInfo.transactionId = responseData.transaction_id; + break; + case 'pending': + statusInfo.pending = true; + statusInfo.progress = 50; + statusInfo.message = responseData.events[responseData.events.length - 1].text; + break; + case 'success': + statusInfo.success = true; + statusInfo.progress = 100; + statusInfo.message = responseData.events[responseData.events.length - 1].text; + if (catalogPackage.type === 'download') { + statusInfo.urlValidUntil = moment().add(defaults.downloadUrlTimeToLiveInMinutes, 'minutes').toISOString(); + if (responseData.filename) { + statusInfo.url = getCatalogPackageManagerServerOrigin() + '/api/export/' + responseData.filename; + } else { + statusInfo.url = getCatalogPackageManagerServerOrigin() + '/api/export/' + catalogPackage.transactionId + '.tar.gz'; + } + } + break; + case 'failure': + statusInfo.error = true; + statusInfo.message = responseData.errors[0].value; + break; + default: + throw new ReferenceError('a status of "request", "success", "failure", "pending", "upload-completed", "upload-error", "download-requested", "upload-progress", "upload-action" is required'); + } + return Object.assign({}, catalogPackage, statusInfo); +} + +export default alt.createStore(CatalogPackageManagerStore, 'CatalogPackageManagerStore');