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} 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';
37 const restPrefix = Configuration.get('restPrefix');
38 return `${restPrefix}/v1.0/vendor-software-products/`;
40 function softwareProductCategoriesUrl() {
41 const restATTPrefix = Configuration.get('restATTPrefix');
42 return `${restATTPrefix}/v1/categories/resources/`;
45 function uploadFile(vspId, formData, version) {
46 return RestAPIUtil.post(`${baseUrl()}${vspId}/versions/${version.id}/orchestration-template-candidate`, formData);
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
64 function putSoftwareProductQuestionnaire(vspId, qdata, version) {
65 return RestAPIUtil.put(`${baseUrl()}${vspId}/versions/${version.id}/questionnaire`, qdata);
68 function putSoftwareProductAction(id, action, version) {
69 return RestAPIUtil.put(`${baseUrl()}${id}/versions/${version.id}/actions`, {action: action});
72 function fetchSoftwareProductList() {
73 return RestAPIUtil.fetch(baseUrl());
76 function fetchFinalizedSoftwareProductList() {
77 return RestAPIUtil.fetch(`${baseUrl()}?versionFilter=Final`);
80 function fetchSoftwareProduct(vspId, version) {
81 return RestAPIUtil.fetch(`${baseUrl()}${vspId}/versions/${version.id}`);
84 function fetchSoftwareProductQuestionnaire(vspId, version) {
85 return RestAPIUtil.fetch(`${baseUrl()}${vspId}/versions/${version.id}/questionnaire`);
88 function updateSoftwareProductHeatCandidate(softwareProductId, heatCandidate, version) {
89 return RestAPIUtil.put(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate/manifest`, heatCandidate);
91 function validateHeatCandidate(softwareProductId, version) {
92 return RestAPIUtil.put(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate/process`);
95 function fetchOrchestrationTemplateCandidate(softwareProductId, version, ) {
96 return RestAPIUtil.fetch(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate`, {dataType: 'binary'});
99 function objToString(obj) {
101 if (obj instanceof Array) {
102 obj.forEach((item) => {
103 str += objToString(item) + '\n';
108 if (obj.hasOwnProperty(p)) {
109 str += obj[p] + '\n';
116 function parseUploadErrorMsg(error) {
118 for (let key in error) {
119 if (error.hasOwnProperty(key)) {
120 message += objToString(error[key]) + '\n';
126 function fetchSoftwareProductCategories(dispatch) {
127 let handleResponse = response => dispatch({
128 type: actionTypes.SOFTWARE_PRODUCT_CATEGORIES_LOADED,
129 softwareProductCategories: response
131 return RestAPIUtil.fetch(softwareProductCategoriesUrl())
132 .then(handleResponse)
133 .catch(() => handleResponse(null));
136 function loadLicensingData(dispatch, {licenseModelId, licensingVersion}) {
138 LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId, version: licensingVersion}),
139 FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId, version: licensingVersion})
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) {
150 return {[itemIdToToggle]: true};
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;
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())}`;
170 function showFileSaveDialog({blob, xhr, defaultFilename, addTimestamp}) {
172 let contentDisposition = xhr.getResponseHeader('content-disposition') ? xhr.getResponseHeader('content-disposition') : '';
173 let match = contentDisposition.match(/filename=(.*?)(;|$)/);
178 filename = defaultFilename;
182 filename = filename.replace(/(^.*?)\.([^.]+$)/, `$1_${getTimestampString()}.$2`);
185 let link = document.createElement('a');
186 let url = URL.createObjectURL(blob);
188 link.download = filename;
189 link.style.display = 'none';
190 document.body.appendChild(link);
192 setTimeout(function(){
193 document.body.removeChild(link);
194 URL.revokeObjectURL(url);
198 function migrateSoftwareProduct(vspId, version) {
199 return RestAPIUtil.put(`${baseUrl()}${vspId}/versions/${version.id}/heal`);
202 function adjustMinorVersion(version, value) {
203 let ar = version.split('.');
204 return ar[0] + '.' + (parseInt(ar[1]) + value);
207 function adjustMajorVersion(version, value) {
208 let ar = version.split('.');
209 return (parseInt(ar[0]) + value) + '.0';
212 const SoftwareProductActionHelper = {
214 fetchFinalizedSoftwareProductList(dispatch) {
215 return fetchFinalizedSoftwareProductList().then(response => dispatch({
216 type: actionTypes.FINALIZED_SOFTWARE_PRODUCT_LIST_LOADED,
221 loadSoftwareProductAssociatedData(dispatch) {
222 fetchSoftwareProductCategories(dispatch);
223 LicenseModelActionHelper.fetchFinalizedLicenseModels(dispatch);
226 loadSoftwareProductDetailsData(dispatch, {licenseModelId, licensingVersion}) {
227 SoftwareProductActionHelper.loadSoftwareProductAssociatedData(dispatch);
228 return loadLicensingData(dispatch, {licenseModelId, licensingVersion});
231 fetchSoftwareProductList(dispatch) {
232 return fetchSoftwareProductList().then(response => dispatch({
233 type: actionTypes.SOFTWARE_PRODUCT_LIST_LOADED,
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,
245 updateSoftwareProductHeatCandidate(dispatch, {softwareProductId, heatCandidate, version}){
246 return updateSoftwareProductHeatCandidate(softwareProductId, heatCandidate, version);
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});
258 uploadFile(dispatch, {softwareProductId, formData, failedNotificationTitle, version}) {
260 type: HeatSetupActions.FILL_HEAT_SETUP_CACHE,
265 .then(() => uploadFile(softwareProductId, formData, version))
267 if (response.status === 'Success') {
268 OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId, version});
271 throw new Error(parseUploadErrorMsg(response.errors));
276 type: modalActionTypes.GLOBAL_MODAL_ERROR,
278 title: failedNotificationTitle,
285 downloadHeatFile(dispatch, {softwareProductId, heatCandidate, isReadOnlyMode, version}){
286 let p = isReadOnlyMode ? Promise.resolve() : SoftwareProductActionHelper.updateSoftwareProductHeatCandidate(dispatch, {softwareProductId, heatCandidate, version});
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*/);
293 hideUploadConfirm (dispatch) {
295 type: actionTypes.softwareProductEditor.UPLOAD_CONFIRMATION
298 updateSoftwareProduct(dispatch, {softwareProduct, qdata}) {
300 SoftwareProductActionHelper.updateSoftwareProductData(dispatch, {softwareProduct}).then(
302 type: actionTypes.SOFTWARE_PRODUCT_LIST_EDIT,
303 payload: {softwareProduct}
306 SoftwareProductActionHelper.updateSoftwareProductQuestionnaire(dispatch, {
307 softwareProductId: softwareProduct.id,
309 version: softwareProduct.version
314 updateSoftwareProductData(dispatch, {softwareProduct}) {
315 return putSoftwareProduct(softwareProduct);
318 updateSoftwareProductQuestionnaire(dispatch, {softwareProductId, qdata, version}) {
319 return putSoftwareProductQuestionnaire(softwareProductId, qdata, version);
322 softwareProductEditorDataChanged(dispatch, {deltaData}) {
324 type: actionTypes.softwareProductEditor.DATA_CHANGED,
329 softwareProductQuestionnaireUpdate(dispatch, {data}) {
331 type: actionTypes.SOFTWARE_PRODUCT_QUESTIONNAIRE_UPDATE,
332 payload: {qdata: data}
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}
343 FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {
344 licenseModelId: deltaData.vendorId,
345 version: {id: deltaData.licensingVersion.id}
348 ValidationHelper.dataChanged(dispatch, {deltaData, formName});
351 ValidationHelper.dataChanged(dispatch, {deltaData, formName});
354 type: licenseAgreementActionTypes.LICENSE_AGREEMENT_LIST_LOADED,
355 response: {results: []}
359 type: featureGroupsActionConstants.FEATURE_GROUPS_LIST_LOADED,
360 response: {results: []}
366 setIsValidityData(dispatch, {isValidityData}) {
368 type: actionTypes.softwareProductEditor.IS_VALIDITY_DATA_CHANGED,
373 addSoftwareProduct(dispatch, {softwareProduct}) {
375 type: actionTypes.ADD_SOFTWARE_PRODUCT,
380 fetchSoftwareProduct(dispatch, {softwareProductId, version}) {
382 fetchSoftwareProduct(softwareProductId, version).then(response => {
384 type: actionTypes.SOFTWARE_PRODUCT_LOADED,
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});
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(() => {
401 type: modalActionTypes.GLOBAL_MODAL_SUCCESS,
403 title: i18n('Submit Succeeded'),
404 msg: i18n('This software product successfully submitted'),
405 cancelButtonText: i18n('OK'),
409 const newVersionId = adjustMajorVersion(version.label, 1);
410 SoftwareProductActionHelper.fetchSoftwareProduct(dispatch,{softwareProductId, version: {id: newVersionId}});
411 return Promise.resolve({newVersion: {id: newVersionId}});
413 }, error => dispatch({
414 type: modalActionTypes.GLOBAL_MODAL_ERROR,
416 modalComponentName: modalContentMapper.SUMBIT_ERROR_RESPONSE,
417 title: i18n('Submit Failed'),
418 modalComponentProps: {
419 validationResponse: error.responseJSON
421 cancelButtonText: i18n('Ok')
426 return putSoftwareProductAction(softwareProductId, action, version).then(() => {
427 let newVersionId = version.id;
429 TODO Temorary switch to change version label
432 case VersionControllerActionsEnum.CHECK_OUT:
433 newVersionId = adjustMinorVersion(version.label, 1);
435 case VersionControllerActionsEnum.UNDO_CHECK_OUT:
436 newVersionId = adjustMinorVersion(version.label, -1);
439 SoftwareProductActionHelper.fetchSoftwareProduct(dispatch,{softwareProductId, version:{id: newVersionId}});
440 return Promise.resolve({newVersion: {id: newVersionId}});
445 switchVersion(dispatch, {softwareProductId, licenseModelId, version}) {
446 OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {
453 toggleNavigationItems(dispatch, {items, itemIdToExpand}) {
454 let mapOfExpandedIds = getExpandedItemsId(items, itemIdToExpand);
456 type: actionTypes.TOGGLE_NAVIGATION_ITEM,
461 /** for the next verision */
462 addComponent(dispatch) {
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}));
477 export default SoftwareProductActionHelper;