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