[SDC] Onboarding 1710 rebase.
[sdc.git] / openecomp-ui / src / sdc-app / onboarding / softwareProduct / SoftwareProductActionHelper.js
1 /*!
2  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13  * or implied. See the License for the specific language governing
14  * permissions and limitations under the License.
15  */
16 import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js';
17 import Configuration from 'sdc-app/config/Configuration.js';
18 import i18n from 'nfvo-utils/i18n/i18n.js';
19 import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js';
20 import LicenseAgreementActionHelper from 'sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementActionHelper.js';
21 import FeatureGroupsActionHelper from 'sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js';
22
23 import {actionTypes} from './SoftwareProductConstants.js';
24 import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js';
25 import SoftwareProductComponentsActionHelper from './components/SoftwareProductComponentsActionHelper.js';
26 import {actionsEnum as VersionControllerActionsEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js';
27 import {actionTypes as HeatSetupActions} from 'sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupConstants.js';
28 import {actionTypes as featureGroupsActionConstants} from 'sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConstants.js';
29 import {actionTypes as licenseAgreementActionTypes} from 'sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConstants.js';
30 import {actionTypes as componentActionTypes} from './components/SoftwareProductComponentsConstants.js';
31 import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js';
32 import {PRODUCT_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js';
33 import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js';
34 import {modalContentMapper} from 'sdc-app/common/modal/ModalContentMapper.js';
35 import {statusEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js';
36
37 function baseUrl() {
38         const restPrefix = Configuration.get('restPrefix');
39         return `${restPrefix}/v1.0/vendor-software-products/`;
40 }
41 function softwareProductCategoriesUrl() {
42         const restATTPrefix = Configuration.get('restATTPrefix');
43         return `${restATTPrefix}/v1/categories/resources/`;
44 }
45
46 function uploadFile(vspId, formData, version) {
47         return RestAPIUtil.post(`${baseUrl()}${vspId}/versions/${version.id}/orchestration-template-candidate`, formData);
48
49 }
50
51 function putSoftwareProduct(softwareProduct) {
52         return RestAPIUtil.put(`${baseUrl()}${softwareProduct.id}/versions/${softwareProduct.version.id}`, {
53                 name: softwareProduct.name,
54                 description: softwareProduct.description,
55                 category: softwareProduct.category,
56                 subCategory: softwareProduct.subCategory,
57                 vendorId: softwareProduct.vendorId,
58                 vendorName: softwareProduct.vendorName,
59                 licensingVersion: softwareProduct.licensingVersion && softwareProduct.licensingVersion.id ? softwareProduct.licensingVersion : {} ,
60                 icon: softwareProduct.icon,
61                 licensingData: softwareProduct.licensingData,
62                 onboardingMethod: softwareProduct.onboardingMethod
63         });
64 }
65
66 function putSoftwareProductQuestionnaire(vspId, qdata, version) {
67         return RestAPIUtil.put(`${baseUrl()}${vspId}/versions/${version.id}/questionnaire`, qdata);
68 }
69
70 function putSoftwareProductAction(id, action, version) {
71         return RestAPIUtil.put(`${baseUrl()}${id}/versions/${version.id}/actions`, {action: action});
72 }
73
74 function fetchSoftwareProductList() {
75         return RestAPIUtil.fetch(baseUrl());
76 }
77
78 function fetchFinalizedSoftwareProductList() {
79         return RestAPIUtil.fetch(`${baseUrl()}?versionFilter=Final`);
80 }
81
82 function fetchSoftwareProduct(vspId, version) {
83         return RestAPIUtil.fetch(`${baseUrl()}${vspId}/versions/${version.id}`);
84 }
85
86 function fetchSoftwareProductQuestionnaire(vspId, version) {
87         return RestAPIUtil.fetch(`${baseUrl()}${vspId}/versions/${version.id}/questionnaire`);
88 }
89
90 function updateSoftwareProductHeatCandidate(softwareProductId, heatCandidate, version) {
91         return RestAPIUtil.put(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate/manifest`, heatCandidate);
92 }
93 function validateHeatCandidate(softwareProductId, version) {
94         return RestAPIUtil.put(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate/process`);
95 }
96
97 function fetchOrchestrationTemplateCandidate(softwareProductId, version, ) {
98         return RestAPIUtil.fetch(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate`, {dataType: 'binary'});
99 }
100
101 function objToString(obj) {
102         let str = '';
103         if (obj instanceof Array) {
104                 obj.forEach((item) => {
105                         str += objToString(item) + '\n';
106                 });
107         }
108         else {
109                 for (let p in obj) {
110                         if (obj.hasOwnProperty(p)) {
111                                 str += obj[p] + '\n';
112                         }
113                 }
114         }
115         return str;
116 }
117
118 function parseUploadErrorMsg(error) {
119         let message = '';
120         for (let key in error) {
121                 if (error.hasOwnProperty(key)) {
122                         message += objToString(error[key]) + '\n';
123                 }
124         }
125         return message;
126 }
127
128 function fetchSoftwareProductCategories(dispatch) {
129         let handleResponse = response => dispatch({
130                 type: actionTypes.SOFTWARE_PRODUCT_CATEGORIES_LOADED,
131                 softwareProductCategories: response
132         });
133         return RestAPIUtil.fetch(softwareProductCategoriesUrl())
134                 .then(handleResponse)
135                 .catch(() => handleResponse(null));
136 }
137
138 function loadLicensingData(dispatch, {licenseModelId, licensingVersion}) {
139         return Promise.all([
140                 LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId, version: licensingVersion}),
141                 FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId, version: licensingVersion})
142         ]);
143 }
144
145 function getExpandedItemsId(items, itemIdToToggle) {
146         for (let i = 0; i < items.length; i++) {
147                 if (items[i].id === itemIdToToggle) {
148                         if (items[i].expanded) {
149                                 return {};
150                         }
151                         else {
152                                 return {[itemIdToToggle]: true};
153                         }
154                 }
155                 else if (items[i].items && items[i].items.length > 0) {
156                         let mapOfExpandedIds = getExpandedItemsId(items[i].items, itemIdToToggle);
157                         if (mapOfExpandedIds !== false) {
158                                 mapOfExpandedIds[items[i].id] = true;
159                                 return mapOfExpandedIds;
160                         }
161                 }
162         }
163         return false;
164 }
165
166 function getTimestampString() {
167         let date = new Date();
168         let z = n => n < 10 ? '0' + n : n;
169         return `${date.getFullYear()}-${z(date.getMonth())}-${z(date.getDate())}_${z(date.getHours())}-${z(date.getMinutes())}`;
170 }
171
172 function showFileSaveDialog({blob, xhr, defaultFilename, addTimestamp}) {
173         let filename;
174         let contentDisposition = xhr.getResponseHeader('content-disposition') ? xhr.getResponseHeader('content-disposition') : '';
175         let match = contentDisposition.match(/filename=(.*?)(;|$)/);
176         if (match) {
177                 filename = match[1];
178         }
179         else {
180                 filename = defaultFilename;
181         }
182
183         if (addTimestamp) {
184                 filename = filename.replace(/(^.*?)\.([^.]+$)/, `$1_${getTimestampString()}.$2`);
185         }
186
187         let link = document.createElement('a');
188         let url = URL.createObjectURL(blob);
189         link.href = url;
190         link.download = filename;
191         link.style.display = 'none';
192         document.body.appendChild(link);
193         link.click();
194         setTimeout(function(){
195                 document.body.removeChild(link);
196                 URL.revokeObjectURL(url);
197         }, 0);
198 }
199
200 function migrateSoftwareProduct(vspId, version) {
201         return RestAPIUtil.put(`${baseUrl()}${vspId}/versions/${version.id}/heal`);
202 }
203
204 function adjustMinorVersion(version, value) {
205         let ar = version.split('.');
206         return ar[0] + '.' + (parseInt(ar[1]) + value);
207 }
208
209 function adjustMajorVersion(version, value) {
210         let ar = version.split('.');
211         return (parseInt(ar[0]) + value) + '.0';
212 }
213
214 const SoftwareProductActionHelper = {
215
216         fetchFinalizedSoftwareProductList(dispatch) {
217                 return fetchFinalizedSoftwareProductList().then(response => dispatch({
218                         type: actionTypes.FINALIZED_SOFTWARE_PRODUCT_LIST_LOADED,
219                         response
220                 }));
221         },
222
223         loadSoftwareProductAssociatedData(dispatch) {
224                 fetchSoftwareProductCategories(dispatch);
225                 LicenseModelActionHelper.fetchFinalizedLicenseModels(dispatch);
226         },
227
228         loadSoftwareProductDetailsData(dispatch, {licenseModelId, licensingVersion}) {
229                 SoftwareProductActionHelper.loadSoftwareProductAssociatedData(dispatch);
230                 return loadLicensingData(dispatch, {licenseModelId, licensingVersion});
231         },
232
233         fetchSoftwareProductList(dispatch) {
234                 return fetchSoftwareProductList().then(response => dispatch({
235                         type: actionTypes.SOFTWARE_PRODUCT_LIST_LOADED,
236                         response
237                 }));
238         },
239
240         loadSoftwareProductHeatCandidate(dispatch, {softwareProductId, version}){
241                 return RestAPIUtil.fetch(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate/manifest`).then(response => dispatch({
242                         type: HeatSetupActions.MANIFEST_LOADED,
243                         response
244                 }));
245         },
246
247         updateSoftwareProductHeatCandidate(dispatch, {softwareProductId, heatCandidate, version}){
248                 return updateSoftwareProductHeatCandidate(softwareProductId, heatCandidate, version);
249         },
250
251         processAndValidateHeatCandidate(dispatch, {softwareProductId, version}){
252                 return validateHeatCandidate(softwareProductId, version).then(response => {
253                         if (response.status === 'Success') {
254                                 let isFetchImageDetails = true;
255                                 SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version, isFetchImageDetails});
256                                 SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {softwareProductId, version});
257                         }
258                 });
259         },
260
261         uploadFile(dispatch, {softwareProductId, formData, failedNotificationTitle, version}) {
262                 dispatch({
263                         type: HeatSetupActions.FILL_HEAT_SETUP_CACHE,
264                         payload: {}
265                 });
266
267                 Promise.resolve()
268                         .then(() => uploadFile(softwareProductId, formData, version))
269                         .then(response => {
270                                 if (response.status === 'Success') {
271                                         OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId, version});
272                                 }
273                                 else {
274                                         throw new Error(parseUploadErrorMsg(response.errors));
275                                 }
276                         })
277                         .catch(error => {
278                                 dispatch({
279                                         type: modalActionTypes.GLOBAL_MODAL_ERROR,
280                                         data: {
281                                                 title: failedNotificationTitle,
282                                                 msg: error.message
283                                         }
284                                 });
285                         });
286         },
287
288         downloadHeatFile(dispatch, {softwareProductId, heatCandidate, isReadOnlyMode, version}){
289                 let p = isReadOnlyMode ? Promise.resolve() : SoftwareProductActionHelper.updateSoftwareProductHeatCandidate(dispatch, {softwareProductId, heatCandidate, version});
290                 p.then(() => {
291                         fetchOrchestrationTemplateCandidate(softwareProductId, version)
292                                 .then((blob, statusText, xhr) => showFileSaveDialog({blob, xhr, defaultFilename: 'HEAT_file.zip', addTimestamp: true}));
293                 }, null/* do not download if data was not saved correctly*/);
294         },
295
296         hideUploadConfirm (dispatch) {
297                 dispatch({
298                         type: actionTypes.softwareProductEditor.UPLOAD_CONFIRMATION
299                 });
300         },
301         updateSoftwareProduct(dispatch, {softwareProduct, qdata}) {
302                 return Promise.all([
303                         SoftwareProductActionHelper.updateSoftwareProductData(dispatch, {softwareProduct}).then(
304                                 () => dispatch({
305                                         type: actionTypes.SOFTWARE_PRODUCT_LIST_EDIT,
306                                         payload: {softwareProduct}
307                                 })
308                         ),
309                         SoftwareProductActionHelper.updateSoftwareProductQuestionnaire(dispatch, {
310                                 softwareProductId: softwareProduct.id,
311                                 qdata,
312                                 version: softwareProduct.version
313                         })
314                 ]);
315         },
316
317         updateSoftwareProductData(dispatch, {softwareProduct}) {
318                 return putSoftwareProduct(softwareProduct);
319         },
320
321         updateSoftwareProductQuestionnaire(dispatch, {softwareProductId, qdata, version}) {
322                 return putSoftwareProductQuestionnaire(softwareProductId, qdata, version);
323         },
324
325         softwareProductEditorDataChanged(dispatch, {deltaData}) {
326                 dispatch({
327                         type: actionTypes.softwareProductEditor.DATA_CHANGED,
328                         deltaData
329                 });
330         },
331
332         softwareProductQuestionnaireUpdate(dispatch, {data}) {
333                 dispatch({
334                         type: actionTypes.SOFTWARE_PRODUCT_QUESTIONNAIRE_UPDATE,
335                         payload: {qdata: data}
336                 });
337         },
338
339         softwareProductEditorVendorChanged(dispatch, {deltaData, formName}) {
340                 if (deltaData.licensingVersion.id){
341                         let p = Promise.all([
342                                 LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {
343                                         licenseModelId: deltaData.vendorId,
344                                         version: {id: deltaData.licensingVersion.id}
345                                 }),
346                                 FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {
347                                         licenseModelId: deltaData.vendorId,
348                                         version: {id: deltaData.licensingVersion.id}
349                                 })
350                         ]);
351                         ValidationHelper.dataChanged(dispatch, {deltaData, formName});
352                         return p;
353                 } else {
354                         ValidationHelper.dataChanged(dispatch, {deltaData, formName});
355
356                         dispatch({
357                                 type: licenseAgreementActionTypes.LICENSE_AGREEMENT_LIST_LOADED,
358                                 response: {results: []}
359                         });
360
361                         dispatch({
362                                 type: featureGroupsActionConstants.FEATURE_GROUPS_LIST_LOADED,
363                                 response: {results: []}
364                         });
365                 }
366
367         },
368
369         setIsValidityData(dispatch, {isValidityData}) {
370                 dispatch({
371                         type: actionTypes.softwareProductEditor.IS_VALIDITY_DATA_CHANGED,
372                         isValidityData
373                 });
374         },
375
376         addSoftwareProduct(dispatch, {softwareProduct}) {
377                 dispatch({
378                         type: actionTypes.ADD_SOFTWARE_PRODUCT,
379                         softwareProduct
380                 });
381         },
382
383         fetchSoftwareProduct(dispatch, {softwareProductId, version}) {
384                 return Promise.all([
385                         fetchSoftwareProduct(softwareProductId, version).then(response => {
386                                 dispatch({
387                                         type: actionTypes.SOFTWARE_PRODUCT_LOADED,
388                                         response
389                                 });
390                                 return response;
391                         }),
392                         fetchSoftwareProductQuestionnaire(softwareProductId, version).then(response => {
393                                 ValidationHelper.qDataLoaded(dispatch, {response: {qdata: response.data ? JSON.parse(response.data) : {},
394                                         qschema: JSON.parse(response.schema)}, qName: PRODUCT_QUESTIONNAIRE});
395                         })
396                 ]);
397         },
398
399         performVCAction(dispatch, {softwareProductId, action, version}) {
400                 if (action === VersionControllerActionsEnum.SUBMIT) {
401                         return putSoftwareProductAction(softwareProductId, action, version).then(() => {
402                                 return putSoftwareProductAction(softwareProductId, VersionControllerActionsEnum.CREATE_PACKAGE, version).then(() => {
403                                         dispatch({
404                                                 type: modalActionTypes.GLOBAL_MODAL_SUCCESS,
405                                                 data: {
406                                                         title: i18n('Submit Succeeded'),
407                                                         msg: i18n('This software product successfully submitted'),
408                                                         cancelButtonText: i18n('OK'),
409                                                         timeout: 2000
410                                                 }
411                                         });
412                                         const newVersionId = adjustMajorVersion(version.label, 1);
413                                         SoftwareProductActionHelper.fetchSoftwareProduct(dispatch,{softwareProductId, version: {id: newVersionId}});
414                                         return Promise.resolve({newVersion: {id: newVersionId}});
415                                 });
416                         }, error => dispatch({
417                                 type: modalActionTypes.GLOBAL_MODAL_ERROR,
418                                 data: {
419                                         modalComponentName: modalContentMapper.SUMBIT_ERROR_RESPONSE,
420                                         title: i18n('Submit Failed'),
421                                         modalComponentProps: {
422                                                 validationResponse: error.responseJSON
423                                         },
424                                         cancelButtonText: i18n('Ok')
425                                 }
426                         }));
427                 }
428                 else {
429                         return putSoftwareProductAction(softwareProductId, action, version).then(() => {
430                                 let newVersionId = version.id;
431                                 /*
432                                   TODO Temorary switch to change version label
433                                 */
434                                 switch(action) {
435                                         case VersionControllerActionsEnum.CHECK_OUT:
436                                                 newVersionId = adjustMinorVersion(version.label, 1);
437                                                 break;
438                                         case VersionControllerActionsEnum.UNDO_CHECK_OUT:
439                                                 newVersionId = adjustMinorVersion(version.label, -1);
440                                                 break;
441                                 }
442                                 SoftwareProductActionHelper.fetchSoftwareProduct(dispatch,{softwareProductId, version:{id: newVersionId}});
443                                 return Promise.resolve({newVersion: {id: newVersionId}});
444                         });
445                 }
446         },
447
448         switchVersion(dispatch, {softwareProductId, licenseModelId, version}) {
449                 OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {
450                         softwareProductId,
451                         licenseModelId,
452                         version
453                 });
454         },
455
456         toggleNavigationItems(dispatch, {items, itemIdToExpand}) {
457                 let mapOfExpandedIds = getExpandedItemsId(items, itemIdToExpand);
458                 dispatch({
459                         type: actionTypes.TOGGLE_NAVIGATION_ITEM,
460                         mapOfExpandedIds
461                 });
462         },
463
464         /** for the next verision */
465         addComponent(dispatch, {softwareProductId, modalClassName}) {
466                 SoftwareProductComponentsActionHelper.clearComponentCreationData(dispatch);
467                 dispatch({
468                         type: componentActionTypes.COMPONENT_CREATE_OPEN
469                 });
470                 dispatch({
471                         type: modalActionTypes.GLOBAL_MODAL_SHOW,
472                         data: {
473                                 modalComponentName: modalContentMapper.COMPONENT_CREATION,
474                                 modalComponentProps: {softwareProductId},
475                                 modalClassName,
476                                 title: 'Create Virtual Function Component'
477                         }
478                 });
479         },
480
481         migrateSoftwareProduct(dispatch, {softwareProduct}) {
482                 let {licenseModelId, licensingVersion, id: softwareProductId, version, status} = softwareProduct;
483                 const  newVer = status === statusEnum.CHECK_IN_STATUS || status === statusEnum.SUBMIT_STATUS ?
484                         adjustMinorVersion(version.id, 1) : version.id;
485                 migrateSoftwareProduct(softwareProductId, version)
486                         .then(() =>OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch,
487                                 {softwareProductId, version: {id: newVer, label: newVer}, licenseModelId, licensingVersion}));
488         }
489
490 };
491
492 export default SoftwareProductActionHelper;