4 * Copyright 2016 RIFT.IO Inc
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 import _delay
from 'lodash/delay'
22 import _pick
from 'lodash/pick'
23 import alt
from '../alt'
24 import guid
from '../libraries/guid'
25 import numeral
from 'numeral'
26 import moment
from 'moment'
27 import utils
from '../libraries/utils'
28 import CatalogPackageManagerSource
from '../sources/CatalogPackageManagerSource'
29 import CatalogPackageManagerActions
from '../actions/CatalogPackageManagerActions'
30 import CatalogDataSource
from '../sources/CatalogDataSource'
32 import imgDownload
from '../../../node_modules/open-iconic/svg/cloud-download.svg'
33 import imgOnboard
from '../../../node_modules/open-iconic/svg/cloud-upload.svg'
34 import imgUpdate
from '../../../node_modules/open-iconic/svg/data-transfer-upload.svg'
35 import imgCopy
from '../../../node_modules/open-iconic/svg/layers.svg'
57 message
: 'Requesting catalog package export...',
64 checkStatusDelayInSeconds
: 2,
65 downloadUrlTimeToLiveInMinutes
: 5
68 const exception
= function ignoreException() {};
70 const packagePropertyNames
= Object
.keys(defaults
.downloadPackage
);
71 const API_SERVER
= utils
.getSearchParams(window
.location
).api_server
;
72 const FILE_SERVER
= window
.location
.hostname
=== 'localhost'
73 ? API_SERVER
: window
.location
.protocol
+ '//' + window
.location
.hostname
;
75 function getCatalogPackageManagerServerOrigin() {
76 return FILE_SERVER
+ ':8008';
79 function delayStatusCheck(statusCheckFunction
, operation
) {
80 if (!operation
.checkStatusTimeoutId
) {
81 const delayCallback = function () {
82 delete operation
.checkStatusTimeoutId
;
83 statusCheckFunction(operation
).catch(exception
);
85 operation
.checkStatusTimeoutId
= _delay(delayCallback
, defaults
.checkStatusDelayInSeconds
* 1000);
89 class CatalogPackageManagerStore
{
95 this.registerAsync(CatalogDataSource
);
96 this.registerAsync(CatalogPackageManagerSource
);
97 this.bindAction(CatalogPackageManagerActions
.REMOVE_CATALOG_OPERATION
, this.removeCatalogOperation
);
98 this.bindAction(CatalogPackageManagerActions
.DOWNLOAD_CATALOG_PACKAGE
, this.downloadCatalogPackage
);
99 this.bindAction(CatalogPackageManagerActions
.DOWNLOAD_CATALOG_PACKAGE_STATUS_UPDATED
, this.onDownloadCatalogPackageStatusUpdated
);
100 this.bindAction(CatalogPackageManagerActions
.DOWNLOAD_CATALOG_PACKAGE_ERROR
, this.onDownloadCatalogPackageError
);
101 this.bindAction(CatalogPackageManagerActions
.UPLOAD_CATALOG_PACKAGE
, this.uploadCatalogPackage
);
102 this.bindAction(CatalogPackageManagerActions
.UPLOAD_CATALOG_PACKAGE_STATUS_UPDATED
, this.onUploadCatalogPackageStatusUpdated
);
103 this.bindAction(CatalogPackageManagerActions
.UPLOAD_CATALOG_PACKAGE_ERROR
, this.onUploadCatalogPackageError
);
104 this.bindAction(CatalogPackageManagerActions
.COPY_CATALOG_PACKAGE
, this.copyCatalogPackage
);
105 this.bindAction(CatalogPackageManagerActions
.UPDATE_STATUS
, this.updateOperationStatus
);
108 addOperation(operation
) {
109 const operations
= [operation
].concat(this.operations
);
110 this.setState({operations
});
113 updateOperation(operation
) {
114 const operations
= this.operations
.map(d
=> {
115 if (d
.id
=== operation
.id
) {
116 return Object
.assign({}, d
, operation
);
120 this.setState({operations
});
123 removeCatalogOperation(operation
) {
124 const operations
= this.operations
.filter(d
=> d
.id
!== operation
.id
);
125 this.setState({operations
});
128 copyCatalogPackage(sourcePackage
) {
129 let operationInfo
= Object
.assign({}, defaults
.operation
);
131 operationInfo
.args
.packageType
= sourcePackage
['uiState']['type'].toUpperCase();
132 operationInfo
.args
.id
= sourcePackage
.id
;
133 operationInfo
.args
.name
= sourcePackage
.name
+ ' copy';
135 operationInfo
.id
= guid();
136 operationInfo
.icon
= imgCopy
;
137 operationInfo
.type
= 'copy';
138 operationInfo
.name
= "Creating " + operationInfo
.args
.name
;
139 operationInfo
.message
= "Requesting duplication";
141 this.addOperation(operationInfo
);
142 this.getInstance().requestCatalogPackageCopy(operationInfo
, sourcePackage
);
145 updateOperationStatus(operation
) {
146 console
.debug('package manager operation status update', operation
);
147 this.updateOperation(operation
);
148 if (operation
.pending
) {
149 delayStatusCheck(this.getInstance().requestCatalogPackageCopyStatus
, operation
);
153 uploadCatalogPackage(file
) {
154 file
.id
= file
.id
|| guid();
155 const operation
= _pick(file
, packagePropertyNames
);
156 operation
.icon
= file
.riftAction
=== 'onboard' ? imgOnboard
: imgUpdate
;
157 operation
.type
= 'upload';
158 this.addOperation(operation
);
159 // note DropZone.js handles the async upload so we don't have to invoke any async action creators
162 onUploadCatalogPackageStatusUpdated(response
) {
163 const upload
= updateStatusInfo(response
);
164 this.updateOperation(upload
);
165 console
.log('updating package upload')
166 // if pending with no transaction id - do nothing
167 // bc DropZone.js will notify upload progress
168 if (upload
.pending
&& upload
.transactionId
) {
169 console
.log('checking status')
170 delayStatusCheck(this.getInstance().requestCatalogPackageUploadStatus
, upload
);
171 } else if (upload
.success
) {
172 this.getInstance().loadCatalogs();
173 console
.log('finished uploading to node, requesting status from rest')
177 onUploadCatalogPackageError(response
) {
178 console
.warn('onUploadCatalogPackageError', response
);
179 const operation
= updateStatusInfo(response
);
180 this.updateOperation(operation
);
183 downloadCatalogPackage(data
) {
184 let catalogItems
= data
['selectedItems'] || [];
185 let schema
= data
['selectedFormat'] || 'mano';
186 let grammar
= data
['selectedGrammar'] || 'osm';
188 if (catalogItems
.length
) {
189 const operation
= Object
.assign({}, defaults
.downloadPackage
, {id
: guid()});
190 operation
.name
= catalogItems
[0].name
;
191 operation
.type
= 'download';
192 if (catalogItems
.length
> 1) {
193 operation
.name
+= ' (' + catalogItems
.length
+ ' items)';
195 operation
.ids
= catalogItems
.map(d
=> d
.id
).sort().toString();
196 operation
.catalogItems
= catalogItems
;
197 this.addOperation(operation
);
198 this.getInstance().requestCatalogPackageDownload(operation
, format
, grammar
, schema
).catch(exception
);
202 onDownloadCatalogPackageStatusUpdated(response
) {
203 const download
= updateStatusInfo(response
);
204 this.updateOperation(download
);
205 if (download
.pending
) {
206 delayStatusCheck(this.getInstance().requestCatalogPackageDownloadStatus
, download
);
210 onDownloadCatalogPackageError(response
) {
211 console
.warn('onDownloadCatalogPackageError', response
);
212 const operation
= updateStatusInfo(response
);
213 this.updateOperation(operation
);
218 function calculateUploadProgressMessage(size
= 0, progress
= 0, bytesSent
= 0) {
219 const amount
= parseFloat(progress
) || 0;
220 const loaded
= amount
=== 100 ? size
: size
* amount
/ 100;
222 if (amount
=== 100) {
223 progressText
= numeral(loaded
).format('0.0b') + ' loaded ';
224 } else if (typeof amount
=== 'number' && amount
!= 0) {
225 progressText
= numeral(bytesSent
).format('0.0b') + ' out of ' + numeral(size
).format('0.0b');
227 progressText
= progress
;
232 function updateStatusInfo(response
) {
233 // returns the operation object with the status fields updated based on the server response
239 const responseData
= (response
.data
.output
) ? response
.data
.output
: response
.data
;
240 const operation
= response
.state
;
241 if ( typeof response
.data
.status
!== "number" ) {
242 switch(response
.data
.status
) {
243 case 'upload-progress':
244 statusInfo
.pending
= true;
245 statusInfo
.progress
= parseFloat(responseData
.progress
) || 0;
246 statusInfo
.message
= calculateUploadProgressMessage(operation
.size
, responseData
.progress
, responseData
.bytesSent
);
248 case 'upload-success':
249 statusInfo
.pending
= true;
250 statusInfo
.progress
= 50;
251 statusInfo
.message
= 'Upload completed.';
252 statusInfo
.transactionId
= responseData
['transaction_id'] || responseData
['transaction-id'] || operation
.transactionId
;
255 statusInfo
.error
= true;
256 statusInfo
.message
= responseData
.message
;
258 case 'download-requested':
259 statusInfo
.pending
= true;
260 statusInfo
.progress
= 25;
261 statusInfo
.transactionId
= responseData
['transaction_id'] || responseData
['transaction-id'] || operation
.transactionId
;
264 statusInfo
.pending
= true;
265 statusInfo
.progress
= 50;
266 statusInfo
.message
= responseData
.events
&& responseData
.events
.length
&& responseData
.events
[responseData
.events
.length
- 1].text
;
269 statusInfo
.success
= true;
270 statusInfo
.progress
= 100;
271 statusInfo
.message
= responseData
.events
[responseData
.events
.length
- 1].text
;
272 if (operation
.type
=== 'download') {
273 statusInfo
.urlValidUntil
= moment().add(defaults
.downloadUrlTimeToLiveInMinutes
, 'minutes').toISOString();
274 if (responseData
.filename
) {
275 statusInfo
.url
= getCatalogPackageManagerServerOrigin() + '/mano/export/' + responseData
.filename
;
277 statusInfo
.url
= getCatalogPackageManagerServerOrigin() + '/mano/export/' + operation
.transactionId
+ '.tar.gz';
282 statusInfo
.error
= true;
283 statusInfo
.message
= responseData
.errors
[0].value
;
286 statusInfo
.success
= true;
287 statusInfo
.progress
= 100;
288 statusInfo
.message
= "Onboarding completed";
291 statusInfo
.pending
= true;
292 statusInfo
.progress
= (operation
.progress
|| 25) + ((100 - operation
.progress
) / 2);
293 statusInfo
.message
= "Onboarding in progress";
296 statusInfo
.error
= true;
297 statusInfo
.message
= "Operation failed";
300 statusInfo
.error
= true;
301 statusInfo
.message
= responseData
.message
|| "Operation failed";
304 // typically get here due to unexpected development errors (backend exceptions, down/up load server access issues)
305 statusInfo
.error
= true;
306 statusInfo
.message
= responseData
.statusText
|| 'Error';
308 return Object
.assign({}, operation
, statusInfo
);
311 export default alt
.createStore(CatalogPackageManagerStore
, 'CatalogPackageManagerStore');