Upload and Update status and endpoints updated
[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 console.log('updating package upload')
123 // if pending with no transaction id - do nothing
124 // bc DropZone.js will notify upload progress
125 if (upload.pending && upload.transactionId) {
126 console.log('checking status')
127 delayStatusCheck(this.getInstance().requestCatalogPackageUploadStatus, upload);
128 } else if (upload.success) {
129 this.getInstance().loadCatalogs();
130 console.log('finished uploading to node, requesting status from rest')
131 }
132 }
133
134 onUploadCatalogPackageError(response) {
135 console.warn('onUploadCatalogPackageError', response);
136 const catalogPackage = updateStatusInfo(response);
137 this.updatePackage(catalogPackage);
138 }
139
140 downloadCatalogPackage(data) {
141 let catalogItems = data['selectedItems'] || [];
142 let schema = data['selectedFormat'] || 'mano';
143 let grammar = data['selectedGrammar'] || 'osm';
144 let format = "YAML";
145 if (catalogItems.length) {
146 const catalogPackage = Object.assign({}, defaults.downloadPackage, {id: guid()});
147 catalogPackage.name = catalogItems[0].name;
148 catalogPackage.type = 'download';
149 if (catalogItems.length > 1) {
150 catalogPackage.name += ' (' + catalogItems.length + ' items)';
151 }
152 catalogPackage.ids = catalogItems.map(d => d.id).sort().toString();
153 catalogPackage.catalogItems = catalogItems;
154 this.addPackage(catalogPackage);
155 this.getInstance().requestCatalogPackageDownload(catalogPackage, format, grammar, schema).catch(exception);
156 }
157 }
158
159 onDownloadCatalogPackageStatusUpdated(response) {
160 const download = updateStatusInfo(response);
161 this.updatePackage(download);
162 if (download.pending) {
163 delayStatusCheck(this.getInstance().requestCatalogPackageDownloadStatus, download);
164 }
165 }
166
167 onDownloadCatalogPackageError(response) {
168 console.warn('onDownloadCatalogPackageError', response);
169 const catalogPackage = updateStatusInfo(response);
170 this.updatePackage(catalogPackage);
171 }
172
173 }
174
175 function calculateUploadProgressMessage(size = 0, progress = 0, bytesSent = 0) {
176 const amount = parseFloat(progress) || 0;
177 const loaded = amount === 100 ? size : size * amount / 100;
178 let progressText;
179 if (amount === 100) {
180 progressText = numeral(loaded).format('0.0b') + ' loaded ';
181 } else if (typeof amount === 'number' && amount != 0) {
182 progressText = numeral(bytesSent).format('0.0b') + ' out of ' + numeral(size).format('0.0b');
183 } else {
184 progressText = progress;
185 }
186 return progressText;
187 }
188
189 function updateStatusInfo(response) {
190 // returns the catalogPackage object with the status fields updated based on the server response
191 const statusInfo = {
192 pending: false,
193 success: false,
194 error: false
195 };
196 const responseData = (response.data.output) ? response.data.output : response.data;
197 const catalogPackage = response.state;
198 switch(response.data.status) {
199 case 'upload-progress':
200 statusInfo.pending = true;
201 statusInfo.progress = parseFloat(responseData.progress) || 0;
202 statusInfo.message = calculateUploadProgressMessage(catalogPackage.size, responseData.progress, responseData.bytesSent);
203 break;
204 case 'upload-success':
205 statusInfo.pending = true;
206 statusInfo.progress = 100;
207 statusInfo.message = 'Upload completed.';
208 statusInfo.transactionId = responseData['transaction_id'] || responseData['transaction-id'] || catalogPackage.transactionId;
209 break;
210 case 'upload-error':
211 statusInfo.error = true;
212 statusInfo.message = responseData.message;
213 break;
214 case 'download-requested':
215 statusInfo.pending = true;
216 statusInfo.progress = 25;
217 statusInfo.transactionId = responseData['transaction_id'] || responseData['transaction-id'] || catalogPackage.transactionId;
218 break;
219 case 'pending':
220 statusInfo.pending = true;
221 statusInfo.progress = 50;
222 statusInfo.message = responseData.events[responseData.events.length - 1].text;
223 break;
224 case 'success':
225 statusInfo.success = true;
226 statusInfo.progress = 100;
227 statusInfo.message = responseData.events[responseData.events.length - 1].text;
228 if (catalogPackage.type === 'download') {
229 statusInfo.urlValidUntil = moment().add(defaults.downloadUrlTimeToLiveInMinutes, 'minutes').toISOString();
230 if (responseData.filename) {
231 statusInfo.url = getCatalogPackageManagerServerOrigin() + '/api/export/' + responseData.filename;
232 } else {
233 statusInfo.url = getCatalogPackageManagerServerOrigin() + '/api/export/' + catalogPackage.transactionId + '.tar.gz';
234 }
235 }
236 break;
237 case 'failure':
238 statusInfo.error = true;
239 statusInfo.message = responseData.errors[0].value;
240 break;
241 default:
242 throw new ReferenceError('a status of "request", "success", "failure", "pending", "upload-completed", "upload-error", "download-requested", "upload-progress", "upload-action" is required');
243 }
244 return Object.assign({}, catalogPackage, statusInfo);
245 }
246
247 export default alt.createStore(CatalogPackageManagerStore, 'CatalogPackageManagerStore');