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