2 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
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';
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';
38 const restPrefix = Configuration.get('restPrefix');
39 return `${restPrefix}/v1.0/vendor-software-products/`;
41 function softwareProductCategoriesUrl() {
42 const restATTPrefix = Configuration.get('restATTPrefix');
43 return `${restATTPrefix}/v1/categories/resources/`;
46 function uploadFile(vspId, formData, version) {
47 return RestAPIUtil.post(`${baseUrl()}${vspId}/versions/${version.id}/orchestration-template-candidate`, formData);
51 function uploadVNFFile(csarId, softwareProductId, version) {
52 return RestAPIUtil.post(`${baseUrl()}${softwareProductId}/versions/${version.id}/vnfrepository/vnfpackage/${csarId}/import`);
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
72 function putSoftwareProductQuestionnaire(vspId, qdata, version) {
73 return RestAPIUtil.put(`${baseUrl()}${vspId}/versions/${version.id}/questionnaire`, qdata);
76 function putSoftwareProductAction(id, action, version) {
77 return RestAPIUtil.put(`${baseUrl()}${id}/versions/${version.id}/actions`, {action: action});
80 function fetchSoftwareProductList() {
81 return RestAPIUtil.fetch(baseUrl());
84 function fetchFinalizedSoftwareProductList() {
85 return RestAPIUtil.fetch(`${baseUrl()}?versionFilter=Final`);
88 function fetchSoftwareProduct(vspId, version) {
89 return RestAPIUtil.fetch(`${baseUrl()}${vspId}/versions/${version.id}`);
92 function fetchSoftwareProductQuestionnaire(vspId, version) {
93 return RestAPIUtil.fetch(`${baseUrl()}${vspId}/versions/${version.id}/questionnaire`);
96 function updateSoftwareProductHeatCandidate(softwareProductId, heatCandidate, version) {
97 return RestAPIUtil.put(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate/manifest`, heatCandidate);
99 function validateHeatCandidate(softwareProductId, version) {
100 return RestAPIUtil.put(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate/process`);
103 function fetchOrchestrationTemplateCandidate(softwareProductId, version, ) {
104 return RestAPIUtil.fetch(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate`, {dataType: 'binary'});
107 function objToString(obj) {
109 if (obj instanceof Array) {
110 obj.forEach((item) => {
111 str += objToString(item) + '\n';
116 if (obj.hasOwnProperty(p)) {
117 str += obj[p] + '\n';
124 function parseUploadErrorMsg(error) {
126 for (let key in error) {
127 if (error.hasOwnProperty(key)) {
128 message += objToString(error[key]) + '\n';
134 function fetchSoftwareProductCategories(dispatch) {
135 let handleResponse = response => dispatch({
136 type: actionTypes.SOFTWARE_PRODUCT_CATEGORIES_LOADED,
137 softwareProductCategories: response
139 return RestAPIUtil.fetch(softwareProductCategoriesUrl())
140 .then(handleResponse)
141 .catch(() => handleResponse(null));
144 function loadLicensingData(dispatch, {licenseModelId, licensingVersion}) {
146 LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId, version: licensingVersion}),
147 FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId, version: licensingVersion})
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) {
158 return {[itemIdToToggle]: true};
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;
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())}`;
178 function showFileSaveDialog({blob, xhr, defaultFilename, addTimestamp}) {
180 let contentDisposition = xhr.getResponseHeader('content-disposition') ? xhr.getResponseHeader('content-disposition') : '';
181 let match = contentDisposition.match(/filename=(.*?)(;|$)/);
186 filename = defaultFilename;
190 filename = filename.replace(/(^.*?)\.([^.]+$)/, `$1_${getTimestampString()}.$2`);
193 let link = document.createElement('a');
194 let url = URL.createObjectURL(blob);
196 link.download = filename;
197 link.style.display = 'none';
198 document.body.appendChild(link);
200 setTimeout(function(){
201 document.body.removeChild(link);
202 URL.revokeObjectURL(url);
206 function migrateSoftwareProduct(vspId, version) {
207 return RestAPIUtil.put(`${baseUrl()}${vspId}/versions/${version.id}/heal`);
210 function adjustMinorVersion(version, value) {
211 let ar = version.split('.');
212 return ar[0] + '.' + (parseInt(ar[1]) + value);
215 function adjustMajorVersion(version, value) {
216 let ar = version.split('.');
217 return (parseInt(ar[0]) + value) + '.0';
220 const SoftwareProductActionHelper = {
222 fetchFinalizedSoftwareProductList(dispatch) {
223 return fetchFinalizedSoftwareProductList().then(response => dispatch({
224 type: actionTypes.FINALIZED_SOFTWARE_PRODUCT_LIST_LOADED,
229 loadSoftwareProductAssociatedData(dispatch) {
230 fetchSoftwareProductCategories(dispatch);
231 LicenseModelActionHelper.fetchFinalizedLicenseModels(dispatch);
234 loadSoftwareProductDetailsData(dispatch, {licenseModelId, licensingVersion}) {
235 SoftwareProductActionHelper.loadSoftwareProductAssociatedData(dispatch);
236 return loadLicensingData(dispatch, {licenseModelId, licensingVersion});
239 fetchSoftwareProductList(dispatch) {
240 return fetchSoftwareProductList().then(response => dispatch({
241 type: actionTypes.SOFTWARE_PRODUCT_LIST_LOADED,
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,
253 updateSoftwareProductHeatCandidate(dispatch, {softwareProductId, heatCandidate, version}){
254 return updateSoftwareProductHeatCandidate(softwareProductId, heatCandidate, version);
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});
266 uploadFile(dispatch, {softwareProductId, formData, failedNotificationTitle, version}) {
268 type: HeatSetupActions.FILL_HEAT_SETUP_CACHE,
273 .then(() => uploadFile(softwareProductId, formData, version))
275 if (response.status === 'Success') {
277 type: commonActionTypes.DATA_CHANGED,
278 deltaData: {onboardingOrigin: response.onboardingOrigin},
279 formName: forms.VENDOR_SOFTWARE_PRODUCT_DETAILS
281 switch(response.onboardingOrigin){
282 case onboardingOriginTypes.ZIP:
283 OnboardingActionHelper.navigateToSoftwareProductAttachmentsSetupTab(dispatch, {softwareProductId, version});
285 case onboardingOriginTypes.CSAR:
286 OnboardingActionHelper.navigateToSoftwareProductAttachmentsValidationTab(dispatch, {softwareProductId, version});
291 throw new Error(parseUploadErrorMsg(response.errors));
296 type: modalActionTypes.GLOBAL_MODAL_ERROR,
298 title: failedNotificationTitle,
305 uploadVNFFile(dispatch, {csarId, failedNotificationTitle, softwareProductId, version}) {
307 type: HeatSetupActions.FILL_HEAT_SETUP_CACHE,
312 .then(() => uploadVNFFile(csarId, softwareProductId, version))
314 if (response.status === 'Success') {
316 type: commonActionTypes.DATA_CHANGED,
317 deltaData: {onboardingOrigin: response.onboardingOrigin},
318 formName: forms.VENDOR_SOFTWARE_PRODUCT_DETAILS
320 switch(response.onboardingOrigin){
321 case onboardingOriginTypes.ZIP:
322 OnboardingActionHelper.navigateToSoftwareProductAttachmentsSetupTab(dispatch, {softwareProductId, version});
324 case onboardingOriginTypes.CSAR:
325 OnboardingActionHelper.navigateToSoftwareProductAttachmentsValidationTab(dispatch, {softwareProductId, version});
330 throw new Error(parseUploadErrorMsg(response.errors));
335 type: modalActionTypes.GLOBAL_MODAL_ERROR,
337 title: failedNotificationTitle,
344 downloadHeatFile(dispatch, {softwareProductId, heatCandidate, isReadOnlyMode, version}){
345 let p = isReadOnlyMode ? Promise.resolve() : SoftwareProductActionHelper.updateSoftwareProductHeatCandidate(dispatch, {softwareProductId, heatCandidate, version});
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*/);
352 hideUploadConfirm (dispatch) {
354 type: actionTypes.softwareProductEditor.UPLOAD_CONFIRMATION
357 updateSoftwareProduct(dispatch, {softwareProduct, qdata}) {
359 SoftwareProductActionHelper.updateSoftwareProductData(dispatch, {softwareProduct}).then(
361 type: actionTypes.SOFTWARE_PRODUCT_LIST_EDIT,
362 payload: {softwareProduct}
365 SoftwareProductActionHelper.updateSoftwareProductQuestionnaire(dispatch, {
366 softwareProductId: softwareProduct.id,
368 version: softwareProduct.version
373 updateSoftwareProductData(dispatch, {softwareProduct}) {
374 return putSoftwareProduct(softwareProduct);
377 updateSoftwareProductQuestionnaire(dispatch, {softwareProductId, qdata, version}) {
378 return putSoftwareProductQuestionnaire(softwareProductId, qdata, version);
381 softwareProductEditorDataChanged(dispatch, {deltaData}) {
383 type: actionTypes.softwareProductEditor.DATA_CHANGED,
388 softwareProductQuestionnaireUpdate(dispatch, {data}) {
390 type: actionTypes.SOFTWARE_PRODUCT_QUESTIONNAIRE_UPDATE,
391 payload: {qdata: data}
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}
402 FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {
403 licenseModelId: deltaData.vendorId,
404 version: {id: deltaData.licensingVersion.id}
407 ValidationHelper.dataChanged(dispatch, {deltaData, formName});
410 ValidationHelper.dataChanged(dispatch, {deltaData, formName});
413 type: licenseAgreementActionTypes.LICENSE_AGREEMENT_LIST_LOADED,
414 response: {results: []}
418 type: featureGroupsActionConstants.FEATURE_GROUPS_LIST_LOADED,
419 response: {results: []}
425 setIsValidityData(dispatch, {isValidityData}) {
427 type: actionTypes.softwareProductEditor.IS_VALIDITY_DATA_CHANGED,
432 addSoftwareProduct(dispatch, {softwareProduct}) {
434 type: actionTypes.ADD_SOFTWARE_PRODUCT,
439 fetchSoftwareProduct(dispatch, {softwareProductId, version}) {
441 fetchSoftwareProduct(softwareProductId, version).then(response => {
443 type: actionTypes.SOFTWARE_PRODUCT_LOADED,
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});
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(() => {
460 type: modalActionTypes.GLOBAL_MODAL_SUCCESS,
462 title: i18n('Submit Succeeded'),
463 msg: i18n('This software product successfully submitted'),
464 cancelButtonText: i18n('OK'),
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}});
473 }, error => dispatch({
474 type: modalActionTypes.GLOBAL_MODAL_ERROR,
476 modalComponentName: modalContentMapper.SUMBIT_ERROR_RESPONSE,
477 title: i18n('Submit Failed'),
478 modalComponentProps: {
479 validationResponse: error.responseJSON
481 cancelButtonText: i18n('Ok')
486 return putSoftwareProductAction(softwareProductId, action, version).then(() => {
487 let newVersionId = version.id;
489 TODO Temorary switch to change version label
492 case VersionControllerActionsEnum.CHECK_OUT:
493 newVersionId = adjustMinorVersion(version.label, 1);
495 case VersionControllerActionsEnum.UNDO_CHECK_OUT:
496 newVersionId = adjustMinorVersion(version.label, -1);
499 OnboardingActionHelper.updateCurrentScreenVersion(dispatch, {label: newVersionId, id: newVersionId});
500 SoftwareProductActionHelper.fetchSoftwareProduct(dispatch,{softwareProductId, version:{id: newVersionId}});
501 return Promise.resolve({newVersion: {id: newVersionId}});
506 switchVersion(dispatch, {softwareProductId, licenseModelId, version}) {
507 OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {
514 toggleNavigationItems(dispatch, {items, itemIdToExpand}) {
515 let mapOfExpandedIds = getExpandedItemsId(items, itemIdToExpand);
517 type: actionTypes.TOGGLE_NAVIGATION_ITEM,
522 /** for the next verision */
523 addComponent(dispatch, {softwareProductId, modalClassName}) {
524 SoftwareProductComponentsActionHelper.clearComponentCreationData(dispatch);
526 type: componentActionTypes.COMPONENT_CREATE_OPEN
529 type: modalActionTypes.GLOBAL_MODAL_SHOW,
531 modalComponentName: modalContentMapper.COMPONENT_CREATION,
532 modalComponentProps: {softwareProductId},
534 title: 'Create Virtual Function Component'
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}));
550 export default SoftwareProductActionHelper;