41306a1c1aeb69662c34bac4aaee67de4b145c6d
[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, onboardingOriginTypes, PRODUCT_QUESTIONNAIRE, forms} from 'sdc-app/onboarding/softwareProduct/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 {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js';
33 import {modalContentMapper} from 'sdc-app/common/modal/ModalContentMapper.js';
34 import {statusEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js';
35 import {actionTypes as commonActionTypes} from 'sdc-app/common/reducers/PlainDataReducerConstants.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                 networkPackageName: softwareProduct.networkPackageName,
64                 onboardingOrigin: softwareProduct.onboardingOrigin
65         });
66 }
67
68 function putSoftwareProductQuestionnaire(vspId, qdata, version) {
69         return RestAPIUtil.put(`${baseUrl()}${vspId}/versions/${version.id}/questionnaire`, qdata);
70 }
71
72 function putSoftwareProductAction(id, action, version) {
73         return RestAPIUtil.put(`${baseUrl()}${id}/versions/${version.id}/actions`, {action: action});
74 }
75
76 function fetchSoftwareProductList() {
77         return RestAPIUtil.fetch(baseUrl());
78 }
79
80 function fetchFinalizedSoftwareProductList() {
81         return RestAPIUtil.fetch(`${baseUrl()}?versionFilter=Final`);
82 }
83
84 function fetchSoftwareProduct(vspId, version) {
85         return RestAPIUtil.fetch(`${baseUrl()}${vspId}/versions/${version.id}`);
86 }
87
88 function fetchSoftwareProductQuestionnaire(vspId, version) {
89         return RestAPIUtil.fetch(`${baseUrl()}${vspId}/versions/${version.id}/questionnaire`);
90 }
91
92 function updateSoftwareProductHeatCandidate(softwareProductId, heatCandidate, version) {
93         return RestAPIUtil.put(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate/manifest`, heatCandidate);
94 }
95 function validateHeatCandidate(softwareProductId, version) {
96         return RestAPIUtil.put(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate/process`);
97 }
98
99 function fetchOrchestrationTemplateCandidate(softwareProductId, version, ) {
100         return RestAPIUtil.fetch(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate`, {dataType: 'binary'});
101 }
102
103 function objToString(obj) {
104         let str = '';
105         if (obj instanceof Array) {
106                 obj.forEach((item) => {
107                         str += objToString(item) + '\n';
108                 });
109         }
110         else {
111                 for (let p in obj) {
112                         if (obj.hasOwnProperty(p)) {
113                                 str += obj[p] + '\n';
114                         }
115                 }
116         }
117         return str;
118 }
119
120 function parseUploadErrorMsg(error) {
121         let message = '';
122         for (let key in error) {
123                 if (error.hasOwnProperty(key)) {
124                         message += objToString(error[key]) + '\n';
125                 }
126         }
127         return message;
128 }
129
130 function fetchSoftwareProductCategories(dispatch) {
131         let handleResponse = response => dispatch({
132                 type: actionTypes.SOFTWARE_PRODUCT_CATEGORIES_LOADED,
133                 softwareProductCategories: response
134         });
135         return RestAPIUtil.fetch(softwareProductCategoriesUrl())
136                 .then(handleResponse)
137                 .catch(() => handleResponse(null));
138 }
139
140 function loadLicensingData(dispatch, {licenseModelId, licensingVersion}) {
141         return Promise.all([
142                 LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId, version: licensingVersion}),
143                 FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId, version: licensingVersion})
144         ]);
145 }
146
147 function getExpandedItemsId(items, itemIdToToggle) {
148         for (let i = 0; i < items.length; i++) {
149                 if (items[i].id === itemIdToToggle) {
150                         if (items[i].expanded) {
151                                 return {};
152                         }
153                         else {
154                                 return {[itemIdToToggle]: true};
155                         }
156                 }
157                 else if (items[i].items && items[i].items.length > 0) {
158                         let mapOfExpandedIds = getExpandedItemsId(items[i].items, itemIdToToggle);
159                         if (mapOfExpandedIds !== false) {
160                                 mapOfExpandedIds[items[i].id] = true;
161                                 return mapOfExpandedIds;
162                         }
163                 }
164         }
165         return false;
166 }
167
168 function getTimestampString() {
169         let date = new Date();
170         let z = n => n < 10 ? '0' + n : n;
171         return `${date.getFullYear()}-${z(date.getMonth())}-${z(date.getDate())}_${z(date.getHours())}-${z(date.getMinutes())}`;
172 }
173
174 function showFileSaveDialog({blob, xhr, defaultFilename, addTimestamp}) {
175         let filename;
176         let contentDisposition = xhr.getResponseHeader('content-disposition') ? xhr.getResponseHeader('content-disposition') : '';
177         let match = contentDisposition.match(/filename=(.*?)(;|$)/);
178         if (match) {
179                 filename = match[1];
180         }
181         else {
182                 filename = defaultFilename;
183         }
184
185         if (addTimestamp) {
186                 filename = filename.replace(/(^.*?)\.([^.]+$)/, `$1_${getTimestampString()}.$2`);
187         }
188
189         let link = document.createElement('a');
190         let url = URL.createObjectURL(blob);
191         link.href = url;
192         link.download = filename;
193         link.style.display = 'none';
194         document.body.appendChild(link);
195         link.click();
196         setTimeout(function(){
197                 document.body.removeChild(link);
198                 URL.revokeObjectURL(url);
199         }, 0);
200 }
201
202 function migrateSoftwareProduct(vspId, version) {
203         return RestAPIUtil.put(`${baseUrl()}${vspId}/versions/${version.id}/heal`);
204 }
205
206 function adjustMinorVersion(version, value) {
207         let ar = version.split('.');
208         return ar[0] + '.' + (parseInt(ar[1]) + value);
209 }
210
211 function adjustMajorVersion(version, value) {
212         let ar = version.split('.');
213         return (parseInt(ar[0]) + value) + '.0';
214 }
215
216 const SoftwareProductActionHelper = {
217
218         fetchFinalizedSoftwareProductList(dispatch) {
219                 return fetchFinalizedSoftwareProductList().then(response => dispatch({
220                         type: actionTypes.FINALIZED_SOFTWARE_PRODUCT_LIST_LOADED,
221                         response
222                 }));
223         },
224
225         loadSoftwareProductAssociatedData(dispatch) {
226                 fetchSoftwareProductCategories(dispatch);
227                 LicenseModelActionHelper.fetchFinalizedLicenseModels(dispatch);
228         },
229
230         loadSoftwareProductDetailsData(dispatch, {licenseModelId, licensingVersion}) {
231                 SoftwareProductActionHelper.loadSoftwareProductAssociatedData(dispatch);
232                 return loadLicensingData(dispatch, {licenseModelId, licensingVersion});
233         },
234
235         fetchSoftwareProductList(dispatch) {
236                 return fetchSoftwareProductList().then(response => dispatch({
237                         type: actionTypes.SOFTWARE_PRODUCT_LIST_LOADED,
238                         response
239                 }));
240         },
241
242         loadSoftwareProductHeatCandidate(dispatch, {softwareProductId, version}){
243                 return RestAPIUtil.fetch(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate/manifest`).then(response => dispatch({
244                         type: HeatSetupActions.MANIFEST_LOADED,
245                         response
246                 }));
247         },
248
249         updateSoftwareProductHeatCandidate(dispatch, {softwareProductId, heatCandidate, version}){
250                 return updateSoftwareProductHeatCandidate(softwareProductId, heatCandidate, version);
251         },
252
253         processAndValidateHeatCandidate(dispatch, {softwareProductId, version}){
254                 return validateHeatCandidate(softwareProductId, version).then(response => {
255                         if (response.status === 'Success') {
256                                 SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version});
257                                 SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {softwareProductId, version});
258                         }
259                 });
260         },
261
262         uploadFile(dispatch, {softwareProductId, formData, failedNotificationTitle, version}) {
263                 dispatch({
264                         type: HeatSetupActions.FILL_HEAT_SETUP_CACHE,
265                         payload: {}
266                 });
267
268                 Promise.resolve()
269                         .then(() => uploadFile(softwareProductId, formData, version))
270                         .then(response => {
271                                 if (response.status === 'Success') {
272                                         dispatch({
273                                                 type: commonActionTypes.DATA_CHANGED,
274                                                 deltaData: {onboardingOrigin: response.onboardingOrigin},
275                                                 formName: forms.VENDOR_SOFTWARE_PRODUCT_DETAILS
276                                         });
277                                         switch(response.onboardingOrigin){
278                                                 case onboardingOriginTypes.ZIP:
279                                                         OnboardingActionHelper.navigateToSoftwareProductAttachmentsSetupTab(dispatch, {softwareProductId, version});
280                                                         break;
281                                                 case onboardingOriginTypes.CSAR:
282                                                         OnboardingActionHelper.navigateToSoftwareProductAttachmentsValidationTab(dispatch, {softwareProductId, version});
283                                                         break;
284                                         }
285                                 }
286                                 else {
287                                         throw new Error(parseUploadErrorMsg(response.errors));
288                                 }
289                         })
290                         .catch(error => {
291                                 dispatch({
292                                         type: modalActionTypes.GLOBAL_MODAL_ERROR,
293                                         data: {
294                                                 title: failedNotificationTitle,
295                                                 msg: error.message
296                                         }
297                                 });
298                         });
299         },
300
301         downloadHeatFile(dispatch, {softwareProductId, heatCandidate, isReadOnlyMode, version}){
302                 let p = isReadOnlyMode ? Promise.resolve() : SoftwareProductActionHelper.updateSoftwareProductHeatCandidate(dispatch, {softwareProductId, heatCandidate, version});
303                 p.then(() => {
304                         fetchOrchestrationTemplateCandidate(softwareProductId, version)
305                                 .then((blob, statusText, xhr) => showFileSaveDialog({blob, xhr, defaultFilename: 'HEAT_file.zip', addTimestamp: true}));
306                 }, null/* do not download if data was not saved correctly*/);
307         },
308
309         hideUploadConfirm (dispatch) {
310                 dispatch({
311                         type: actionTypes.softwareProductEditor.UPLOAD_CONFIRMATION
312                 });
313         },
314         updateSoftwareProduct(dispatch, {softwareProduct, qdata}) {
315                 return Promise.all([
316                         SoftwareProductActionHelper.updateSoftwareProductData(dispatch, {softwareProduct}).then(
317                                 () => dispatch({
318                                         type: actionTypes.SOFTWARE_PRODUCT_LIST_EDIT,
319                                         payload: {softwareProduct}
320                                 })
321                         ),
322                         SoftwareProductActionHelper.updateSoftwareProductQuestionnaire(dispatch, {
323                                 softwareProductId: softwareProduct.id,
324                                 qdata,
325                                 version: softwareProduct.version
326                         })
327                 ]);
328         },
329
330         updateSoftwareProductData(dispatch, {softwareProduct}) {
331                 return putSoftwareProduct(softwareProduct);
332         },
333
334         updateSoftwareProductQuestionnaire(dispatch, {softwareProductId, qdata, version}) {
335                 return putSoftwareProductQuestionnaire(softwareProductId, qdata, version);
336         },
337
338         softwareProductEditorDataChanged(dispatch, {deltaData}) {
339                 dispatch({
340                         type: actionTypes.softwareProductEditor.DATA_CHANGED,
341                         deltaData
342                 });
343         },
344
345         softwareProductQuestionnaireUpdate(dispatch, {data}) {
346                 dispatch({
347                         type: actionTypes.SOFTWARE_PRODUCT_QUESTIONNAIRE_UPDATE,
348                         payload: {qdata: data}
349                 });
350         },
351
352         softwareProductEditorVendorChanged(dispatch, {deltaData, formName}) {
353                 if (deltaData.licensingVersion.id){
354                         let p = Promise.all([
355                                 LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {
356                                         licenseModelId: deltaData.vendorId,
357                                         version: {id: deltaData.licensingVersion.id}
358                                 }),
359                                 FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {
360                                         licenseModelId: deltaData.vendorId,
361                                         version: {id: deltaData.licensingVersion.id}
362                                 })
363                         ]);
364                         ValidationHelper.dataChanged(dispatch, {deltaData, formName});
365                         return p;
366                 } else {
367                         ValidationHelper.dataChanged(dispatch, {deltaData, formName});
368
369                         dispatch({
370                                 type: licenseAgreementActionTypes.LICENSE_AGREEMENT_LIST_LOADED,
371                                 response: {results: []}
372                         });
373
374                         dispatch({
375                                 type: featureGroupsActionConstants.FEATURE_GROUPS_LIST_LOADED,
376                                 response: {results: []}
377                         });
378                 }
379
380         },
381
382         setIsValidityData(dispatch, {isValidityData}) {
383                 dispatch({
384                         type: actionTypes.softwareProductEditor.IS_VALIDITY_DATA_CHANGED,
385                         isValidityData
386                 });
387         },
388
389         addSoftwareProduct(dispatch, {softwareProduct}) {
390                 dispatch({
391                         type: actionTypes.ADD_SOFTWARE_PRODUCT,
392                         softwareProduct
393                 });
394         },
395
396         fetchSoftwareProduct(dispatch, {softwareProductId, version}) {
397                 return Promise.all([
398                         fetchSoftwareProduct(softwareProductId, version).then(response => {
399                                 dispatch({
400                                         type: actionTypes.SOFTWARE_PRODUCT_LOADED,
401                                         response
402                                 });
403                                 return response;
404                         }),
405                         fetchSoftwareProductQuestionnaire(softwareProductId, version).then(response => {
406                                 ValidationHelper.qDataLoaded(dispatch, {response: {qdata: response.data ? JSON.parse(response.data) : {},
407                                         qschema: JSON.parse(response.schema)}, qName: PRODUCT_QUESTIONNAIRE});
408                         })
409                 ]);
410         },
411
412         performVCAction(dispatch, {softwareProductId, action, version}) {
413                 if (action === VersionControllerActionsEnum.SUBMIT) {
414                         return putSoftwareProductAction(softwareProductId, action, version).then(() => {
415                                 return putSoftwareProductAction(softwareProductId, VersionControllerActionsEnum.CREATE_PACKAGE, version).then(() => {
416                                         dispatch({
417                                                 type: modalActionTypes.GLOBAL_MODAL_SUCCESS,
418                                                 data: {
419                                                         title: i18n('Submit Succeeded'),
420                                                         msg: i18n('This software product successfully submitted'),
421                                                         cancelButtonText: i18n('OK'),
422                                                         timeout: 2000
423                                                 }
424                                         });
425                                         const newVersionId = adjustMajorVersion(version.label, 1);
426                                         OnboardingActionHelper.updateCurrentScreenVersion(dispatch, {label: newVersionId, id: newVersionId});
427                                         SoftwareProductActionHelper.fetchSoftwareProduct(dispatch,{softwareProductId, version: {id: newVersionId}});
428                                         return Promise.resolve({newVersion: {id: newVersionId}});
429                                 });
430                         }, error => dispatch({
431                                 type: modalActionTypes.GLOBAL_MODAL_ERROR,
432                                 data: {
433                                         modalComponentName: modalContentMapper.SUMBIT_ERROR_RESPONSE,
434                                         title: i18n('Submit Failed'),
435                                         modalComponentProps: {
436                                                 validationResponse: error.responseJSON
437                                         },
438                                         cancelButtonText: i18n('Ok')
439                                 }
440                         }));
441                 }
442                 else {
443                         return putSoftwareProductAction(softwareProductId, action, version).then(() => {
444                                 let newVersionId = version.id;
445                                 /*
446                                   TODO Temorary switch to change version label
447                                 */
448                                 switch(action) {
449                                         case VersionControllerActionsEnum.CHECK_OUT:
450                                                 newVersionId = adjustMinorVersion(version.label, 1);
451                                                 break;
452                                         case VersionControllerActionsEnum.UNDO_CHECK_OUT:
453                                                 newVersionId = adjustMinorVersion(version.label, -1);
454                                                 break;
455                                 }
456                                 OnboardingActionHelper.updateCurrentScreenVersion(dispatch, {label: newVersionId, id: newVersionId});
457                                 SoftwareProductActionHelper.fetchSoftwareProduct(dispatch,{softwareProductId, version:{id: newVersionId}});
458                                 return Promise.resolve({newVersion: {id: newVersionId}});
459                         });
460                 }
461         },
462
463         switchVersion(dispatch, {softwareProductId, licenseModelId, version}) {
464                 OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {
465                         softwareProductId,
466                         licenseModelId,
467                         version
468                 });
469         },
470
471         toggleNavigationItems(dispatch, {items, itemIdToExpand}) {
472                 let mapOfExpandedIds = getExpandedItemsId(items, itemIdToExpand);
473                 dispatch({
474                         type: actionTypes.TOGGLE_NAVIGATION_ITEM,
475                         mapOfExpandedIds
476                 });
477         },
478
479         /** for the next verision */
480         addComponent(dispatch, {softwareProductId, modalClassName}) {
481                 SoftwareProductComponentsActionHelper.clearComponentCreationData(dispatch);
482                 dispatch({
483                         type: componentActionTypes.COMPONENT_CREATE_OPEN
484                 });
485                 dispatch({
486                         type: modalActionTypes.GLOBAL_MODAL_SHOW,
487                         data: {
488                                 modalComponentName: modalContentMapper.COMPONENT_CREATION,
489                                 modalComponentProps: {softwareProductId},
490                                 modalClassName,
491                                 title: 'Create Virtual Function Component'
492                         }
493                 });
494         },
495
496         migrateSoftwareProduct(dispatch, {softwareProduct}) {
497                 let {licenseModelId, licensingVersion, id: softwareProductId, version, status} = softwareProduct;
498                 const  newVer = status === statusEnum.CHECK_IN_STATUS || status === statusEnum.SUBMIT_STATUS ?
499                         adjustMinorVersion(version.id, 1) : version.id;
500                 migrateSoftwareProduct(softwareProductId, version)
501                         .then(() =>OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch,
502                                 {softwareProductId, version: {id: newVer, label: newVer}, licenseModelId, licensingVersion}));
503         }
504
505 };
506
507 export default SoftwareProductActionHelper;