733e938a6f0895e967809836caecfb4ab4d7be16
[osm/UI.git] / skyquake / plugins / composer / src / src / stores / CatalogPackageManagerStore.js
1
2 /*
3 *
4 * Copyright 2016 RIFT.IO Inc
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 */
19 'use strict';
20
21 import _ from 'lodash'
22 import alt from '../alt'
23 import guid from '../libraries/guid'
24 import numeral from 'numeral'
25 import moment from 'moment'
26 import utils from '../libraries/utils'
27 import CatalogPackageManagerSource from '../sources/CatalogPackageManagerSource'
28 import CatalogPackageManagerActions from '../actions/CatalogPackageManagerActions'
29 import CatalogDataSource from '../sources/CatalogDataSource'
30
31 import imgDownload from '../../../node_modules/open-iconic/svg/cloud-download.svg'
32 import imgOnboard from '../../../node_modules/open-iconic/svg/cloud-upload.svg'
33 import imgUpdate from '../../../node_modules/open-iconic/svg/data-transfer-upload.svg'
34
35 const defaults = {
36 downloadPackage: {
37 id: '',
38 name: '',
39 icon: imgDownload,
40 catalogItems: [],
41 transactionId: '',
42 progress: 0,
43 message: 'Requesting catalog package export...',
44 pending: false,
45 success: false,
46 error: false,
47 url: '',
48 urlValidUntil: ''
49 },
50 checkStatusDelayInSeconds: 2,
51 downloadUrlTimeToLiveInMinutes: 5
52 };
53
54 const exception = function ignoreException() {};
55
56 const packagePropertyNames = Object.keys(defaults.downloadPackage);
57
58 function getCatalogPackageManagerServerOrigin() {
59 return utils.getSearchParams(window.location).upload_server + ':4567';
60 }
61
62 function delayStatusCheck(statusCheckFunction, catalogPackage) {
63 if (!catalogPackage.checkStatusTimeoutId) {
64 const delayCallback = function () {
65 delete catalogPackage.checkStatusTimeoutId;
66 statusCheckFunction(catalogPackage).catch(exception);
67 };
68 catalogPackage.checkStatusTimeoutId = _.delay(delayCallback, defaults.checkStatusDelayInSeconds * 1000);
69 }
70 }
71
72 class CatalogPackageManagerStore {
73
74 constructor() {
75
76 this.packages = [];
77
78 this.registerAsync(CatalogDataSource);
79 this.registerAsync(CatalogPackageManagerSource);
80 this.bindAction(CatalogPackageManagerActions.REMOVE_CATALOG_PACKAGE, this.removeCatalogPackage);
81 this.bindAction(CatalogPackageManagerActions.DOWNLOAD_CATALOG_PACKAGE, this.downloadCatalogPackage);
82 this.bindAction(CatalogPackageManagerActions.DOWNLOAD_CATALOG_PACKAGE_STATUS_UPDATED, this.onDownloadCatalogPackageStatusUpdated);
83 this.bindAction(CatalogPackageManagerActions.DOWNLOAD_CATALOG_PACKAGE_ERROR, this.onDownloadCatalogPackageError);
84 this.bindAction(CatalogPackageManagerActions.UPLOAD_CATALOG_PACKAGE, this.uploadCatalogPackage);
85 this.bindAction(CatalogPackageManagerActions.UPLOAD_CATALOG_PACKAGE_STATUS_UPDATED, this.onUploadCatalogPackageStatusUpdated);
86 this.bindAction(CatalogPackageManagerActions.UPLOAD_CATALOG_PACKAGE_ERROR, this.onUploadCatalogPackageError);
87
88 }
89
90 addPackage(catalogPackage) {
91 const packages = [catalogPackage].concat(this.packages);
92 this.setState({packages: packages});
93 }
94
95 updatePackage(catalogPackage) {
96 const packages = this.packages.map(d => {
97 if (d.id === catalogPackage.id) {
98 return Object.assign({}, d, catalogPackage);
99 }
100 return d;
101 });
102 this.setState({packages: packages});
103 }
104
105 removeCatalogPackage(catalogPackage) {
106 const packages = this.packages.filter(d => d.id !== catalogPackage.id);
107 this.setState({packages: packages});
108 }
109
110 uploadCatalogPackage(file) {
111 file.id = file.id || guid();
112 const catalogPackage = _.pick(file, packagePropertyNames);
113 catalogPackage.icon = file.riftAction === 'onboard' ? imgOnboard : imgUpdate;
114 catalogPackage.type = 'upload';
115 this.addPackage(catalogPackage);
116 // note DropZone.js handles the async upload so we don't have to invoke any async action creators
117 }
118
119 onUploadCatalogPackageStatusUpdated(response) {
120 const upload = updateStatusInfo(response);
121 this.updatePackage(upload);
122 // if pending with no transaction id - do nothing
123 // bc DropZone.js will notify upload progress
124 if (upload.pending && upload.transactionId) {
125 delayStatusCheck(this.getInstance().requestCatalogPackageUploadStatus, upload);
126 } else if (upload.success) {
127 this.getInstance().loadCatalogs();
128 }
129 }
130
131 onUploadCatalogPackageError(response) {
132 console.warn('onUploadCatalogPackageError', response);
133 const catalogPackage = updateStatusInfo(response);
134 this.updatePackage(catalogPackage);
135 }
136
137 downloadCatalogPackage(data) {
138 let catalogItems = data['selectedItems'] || [];
139 let format = data['selectedFormat'] || 'mano';
140 let grammar = data['selectedGrammar'] || 'osm';
141 if (catalogItems.length) {
142 const catalogPackage = Object.assign({}, defaults.downloadPackage, {id: guid()});
143 catalogPackage.name = catalogItems[0].name;
144 catalogPackage.type = 'download';
145 if (catalogItems.length > 1) {
146 catalogPackage.name += ' (' + catalogItems.length + ' items)';
147 }
148 catalogPackage.ids = catalogItems.map(d => d.id).sort().toString();
149 catalogPackage.catalogItems = catalogItems;
150 this.addPackage(catalogPackage);
151 this.getInstance().requestCatalogPackageDownload(catalogPackage, format, grammar).catch(exception);
152 }
153 }
154
155 onDownloadCatalogPackageStatusUpdated(response) {
156 const download = updateStatusInfo(response);
157 this.updatePackage(download);
158 if (download.pending) {
159 delayStatusCheck(this.getInstance().requestCatalogPackageDownloadStatus, download);
160 }
161 }
162
163 onDownloadCatalogPackageError(response) {
164 console.warn('onDownloadCatalogPackageError', response);
165 const catalogPackage = updateStatusInfo(response);
166 this.updatePackage(catalogPackage);
167 }
168
169 }
170
171 function calculateUploadProgressMessage(size = 0, progress = 0, bytesSent = 0) {
172 const amount = parseFloat(progress) || 0;
173 const loaded = amount === 100 ? size : size * amount / 100;
174 let progressText;
175 if (amount === 100) {
176 progressText = numeral(loaded).format('0.0b') + ' loaded ';
177 } else if (typeof amount === 'number' && amount != 0) {
178 progressText = numeral(bytesSent).format('0.0b') + ' out of ' + numeral(size).format('0.0b');
179 } else {
180 progressText = progress;
181 }
182 return progressText;
183 }
184
185 function updateStatusInfo(response) {
186 // returns the catalogPackage object with the status fields updated based on the server response
187 const statusInfo = {
188 pending: false,
189 success: false,
190 error: false
191 };
192 const responseData = response.data;
193 const catalogPackage = response.state;
194 switch(responseData.status) {
195 case 'upload-progress':
196 statusInfo.pending = true;
197 statusInfo.progress = parseFloat(responseData.progress) || 0;
198 statusInfo.message = calculateUploadProgressMessage(catalogPackage.size, responseData.progress, responseData.bytesSent);
199 break;
200 case 'upload-success':
201 statusInfo.pending = true;
202 statusInfo.progress = 100;
203 statusInfo.message = 'Upload completed.';
204 statusInfo.transactionId = responseData.transaction_id;
205 break;
206 case 'upload-error':
207 statusInfo.error = true;
208 statusInfo.message = responseData.message;
209 break;
210 case 'download-requested':
211 statusInfo.pending = true;
212 statusInfo.progress = 25;
213 statusInfo.transactionId = responseData.transaction_id;
214 break;
215 case 'pending':
216 statusInfo.pending = true;
217 statusInfo.progress = 50;
218 statusInfo.message = responseData.events[responseData.events.length - 1].text;
219 break;
220 case 'success':
221 statusInfo.success = true;
222 statusInfo.progress = 100;
223 statusInfo.message = responseData.events[responseData.events.length - 1].text;
224 if (catalogPackage.type === 'download') {
225 statusInfo.urlValidUntil = moment().add(defaults.downloadUrlTimeToLiveInMinutes, 'minutes').toISOString();
226 if (responseData.filename) {
227 statusInfo.url = getCatalogPackageManagerServerOrigin() + '/api/export/' + responseData.filename;
228 } else {
229 statusInfo.url = getCatalogPackageManagerServerOrigin() + '/api/export/' + catalogPackage.transactionId + '.tar.gz';
230 }
231 }
232 break;
233 case 'failure':
234 statusInfo.error = true;
235 statusInfo.message = responseData.errors[0].value;
236 break;
237 default:
238 throw new ReferenceError('a status of "request", "success", "failure", "pending", "upload-completed", "upload-error", "download-requested", "upload-progress", "upload-action" is required');
239 }
240 return Object.assign({}, catalogPackage, statusInfo);
241 }
242
243 export default alt.createStore(CatalogPackageManagerStore, 'CatalogPackageManagerStore');