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 {actionTypes as componentActionTypes} from './components/SoftwareProductComponentsConstants.js';
31 import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js';
32 import {PRODUCT_QUESTIONNAIRE} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js';
33 import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js';
34 import {modalContentMapper} from 'sdc-app/common/modal/ModalContentMapper.js';
35 import {statusEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.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 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
66 function putSoftwareProductQuestionnaire(vspId, qdata, version) {
67 return RestAPIUtil.put(`${baseUrl()}${vspId}/versions/${version.id}/questionnaire`, qdata);
70 function putSoftwareProductAction(id, action, version) {
71 return RestAPIUtil.put(`${baseUrl()}${id}/versions/${version.id}/actions`, {action: action});
74 function fetchSoftwareProductList() {
75 return RestAPIUtil.fetch(baseUrl());
78 function fetchFinalizedSoftwareProductList() {
79 return RestAPIUtil.fetch(`${baseUrl()}?versionFilter=Final`);
82 function fetchSoftwareProduct(vspId, version) {
83 return RestAPIUtil.fetch(`${baseUrl()}${vspId}/versions/${version.id}`);
86 function fetchSoftwareProductQuestionnaire(vspId, version) {
87 return RestAPIUtil.fetch(`${baseUrl()}${vspId}/versions/${version.id}/questionnaire`);
90 function updateSoftwareProductHeatCandidate(softwareProductId, heatCandidate, version) {
91 return RestAPIUtil.put(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate/manifest`, heatCandidate);
93 function validateHeatCandidate(softwareProductId, version) {
94 return RestAPIUtil.put(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate/process`);
97 function fetchOrchestrationTemplateCandidate(softwareProductId, version, ) {
98 return RestAPIUtil.fetch(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate`, {dataType: 'binary'});
101 function objToString(obj) {
103 if (obj instanceof Array) {
104 obj.forEach((item) => {
105 str += objToString(item) + '\n';
110 if (obj.hasOwnProperty(p)) {
111 str += obj[p] + '\n';
118 function parseUploadErrorMsg(error) {
120 for (let key in error) {
121 if (error.hasOwnProperty(key)) {
122 message += objToString(error[key]) + '\n';
128 function fetchSoftwareProductCategories(dispatch) {
129 let handleResponse = response => dispatch({
130 type: actionTypes.SOFTWARE_PRODUCT_CATEGORIES_LOADED,
131 softwareProductCategories: response
133 return RestAPIUtil.fetch(softwareProductCategoriesUrl())
134 .then(handleResponse)
135 .catch(() => handleResponse(null));
138 function loadLicensingData(dispatch, {licenseModelId, licensingVersion}) {
140 LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {licenseModelId, version: licensingVersion}),
141 FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {licenseModelId, version: licensingVersion})
145 function getExpandedItemsId(items, itemIdToToggle) {
146 for (let i = 0; i < items.length; i++) {
147 if (items[i].id === itemIdToToggle) {
148 if (items[i].expanded) {
152 return {[itemIdToToggle]: true};
155 else if (items[i].items && items[i].items.length > 0) {
156 let mapOfExpandedIds = getExpandedItemsId(items[i].items, itemIdToToggle);
157 if (mapOfExpandedIds !== false) {
158 mapOfExpandedIds[items[i].id] = true;
159 return mapOfExpandedIds;
166 function getTimestampString() {
167 let date = new Date();
168 let z = n => n < 10 ? '0' + n : n;
169 return `${date.getFullYear()}-${z(date.getMonth())}-${z(date.getDate())}_${z(date.getHours())}-${z(date.getMinutes())}`;
172 function showFileSaveDialog({blob, xhr, defaultFilename, addTimestamp}) {
174 let contentDisposition = xhr.getResponseHeader('content-disposition') ? xhr.getResponseHeader('content-disposition') : '';
175 let match = contentDisposition.match(/filename=(.*?)(;|$)/);
180 filename = defaultFilename;
184 filename = filename.replace(/(^.*?)\.([^.]+$)/, `$1_${getTimestampString()}.$2`);
187 let link = document.createElement('a');
188 let url = URL.createObjectURL(blob);
190 link.download = filename;
191 link.style.display = 'none';
192 document.body.appendChild(link);
194 setTimeout(function(){
195 document.body.removeChild(link);
196 URL.revokeObjectURL(url);
200 function migrateSoftwareProduct(vspId, version) {
201 return RestAPIUtil.put(`${baseUrl()}${vspId}/versions/${version.id}/heal`);
204 function adjustMinorVersion(version, value) {
205 let ar = version.split('.');
206 return ar[0] + '.' + (parseInt(ar[1]) + value);
209 function adjustMajorVersion(version, value) {
210 let ar = version.split('.');
211 return (parseInt(ar[0]) + value) + '.0';
214 const SoftwareProductActionHelper = {
216 fetchFinalizedSoftwareProductList(dispatch) {
217 return fetchFinalizedSoftwareProductList().then(response => dispatch({
218 type: actionTypes.FINALIZED_SOFTWARE_PRODUCT_LIST_LOADED,
223 loadSoftwareProductAssociatedData(dispatch) {
224 fetchSoftwareProductCategories(dispatch);
225 LicenseModelActionHelper.fetchFinalizedLicenseModels(dispatch);
228 loadSoftwareProductDetailsData(dispatch, {licenseModelId, licensingVersion}) {
229 SoftwareProductActionHelper.loadSoftwareProductAssociatedData(dispatch);
230 return loadLicensingData(dispatch, {licenseModelId, licensingVersion});
233 fetchSoftwareProductList(dispatch) {
234 return fetchSoftwareProductList().then(response => dispatch({
235 type: actionTypes.SOFTWARE_PRODUCT_LIST_LOADED,
240 loadSoftwareProductHeatCandidate(dispatch, {softwareProductId, version}){
241 return RestAPIUtil.fetch(`${baseUrl()}${softwareProductId}/versions/${version.id}/orchestration-template-candidate/manifest`).then(response => dispatch({
242 type: HeatSetupActions.MANIFEST_LOADED,
247 updateSoftwareProductHeatCandidate(dispatch, {softwareProductId, heatCandidate, version}){
248 return updateSoftwareProductHeatCandidate(softwareProductId, heatCandidate, version);
251 processAndValidateHeatCandidate(dispatch, {softwareProductId, version}){
252 return validateHeatCandidate(softwareProductId, version).then(response => {
253 if (response.status === 'Success') {
254 let isFetchImageDetails = true;
255 SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(dispatch, {softwareProductId, version, isFetchImageDetails});
256 SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {softwareProductId, version});
261 uploadFile(dispatch, {softwareProductId, formData, failedNotificationTitle, version}) {
263 type: HeatSetupActions.FILL_HEAT_SETUP_CACHE,
268 .then(() => uploadFile(softwareProductId, formData, version))
270 if (response.status === 'Success') {
271 OnboardingActionHelper.navigateToSoftwareProductAttachments(dispatch, {softwareProductId, version});
274 throw new Error(parseUploadErrorMsg(response.errors));
279 type: modalActionTypes.GLOBAL_MODAL_ERROR,
281 title: failedNotificationTitle,
288 downloadHeatFile(dispatch, {softwareProductId, heatCandidate, isReadOnlyMode, version}){
289 let p = isReadOnlyMode ? Promise.resolve() : SoftwareProductActionHelper.updateSoftwareProductHeatCandidate(dispatch, {softwareProductId, heatCandidate, version});
291 fetchOrchestrationTemplateCandidate(softwareProductId, version)
292 .then((blob, statusText, xhr) => showFileSaveDialog({blob, xhr, defaultFilename: 'HEAT_file.zip', addTimestamp: true}));
293 }, null/* do not download if data was not saved correctly*/);
296 hideUploadConfirm (dispatch) {
298 type: actionTypes.softwareProductEditor.UPLOAD_CONFIRMATION
301 updateSoftwareProduct(dispatch, {softwareProduct, qdata}) {
303 SoftwareProductActionHelper.updateSoftwareProductData(dispatch, {softwareProduct}).then(
305 type: actionTypes.SOFTWARE_PRODUCT_LIST_EDIT,
306 payload: {softwareProduct}
309 SoftwareProductActionHelper.updateSoftwareProductQuestionnaire(dispatch, {
310 softwareProductId: softwareProduct.id,
312 version: softwareProduct.version
317 updateSoftwareProductData(dispatch, {softwareProduct}) {
318 return putSoftwareProduct(softwareProduct);
321 updateSoftwareProductQuestionnaire(dispatch, {softwareProductId, qdata, version}) {
322 return putSoftwareProductQuestionnaire(softwareProductId, qdata, version);
325 softwareProductEditorDataChanged(dispatch, {deltaData}) {
327 type: actionTypes.softwareProductEditor.DATA_CHANGED,
332 softwareProductQuestionnaireUpdate(dispatch, {data}) {
334 type: actionTypes.SOFTWARE_PRODUCT_QUESTIONNAIRE_UPDATE,
335 payload: {qdata: data}
339 softwareProductEditorVendorChanged(dispatch, {deltaData, formName}) {
340 if (deltaData.licensingVersion.id){
341 let p = Promise.all([
342 LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {
343 licenseModelId: deltaData.vendorId,
344 version: {id: deltaData.licensingVersion.id}
346 FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {
347 licenseModelId: deltaData.vendorId,
348 version: {id: deltaData.licensingVersion.id}
351 ValidationHelper.dataChanged(dispatch, {deltaData, formName});
354 ValidationHelper.dataChanged(dispatch, {deltaData, formName});
357 type: licenseAgreementActionTypes.LICENSE_AGREEMENT_LIST_LOADED,
358 response: {results: []}
362 type: featureGroupsActionConstants.FEATURE_GROUPS_LIST_LOADED,
363 response: {results: []}
369 setIsValidityData(dispatch, {isValidityData}) {
371 type: actionTypes.softwareProductEditor.IS_VALIDITY_DATA_CHANGED,
376 addSoftwareProduct(dispatch, {softwareProduct}) {
378 type: actionTypes.ADD_SOFTWARE_PRODUCT,
383 fetchSoftwareProduct(dispatch, {softwareProductId, version}) {
385 fetchSoftwareProduct(softwareProductId, version).then(response => {
387 type: actionTypes.SOFTWARE_PRODUCT_LOADED,
392 fetchSoftwareProductQuestionnaire(softwareProductId, version).then(response => {
393 ValidationHelper.qDataLoaded(dispatch, {response: {qdata: response.data ? JSON.parse(response.data) : {},
394 qschema: JSON.parse(response.schema)}, qName: PRODUCT_QUESTIONNAIRE});
399 performVCAction(dispatch, {softwareProductId, action, version}) {
400 if (action === VersionControllerActionsEnum.SUBMIT) {
401 return putSoftwareProductAction(softwareProductId, action, version).then(() => {
402 return putSoftwareProductAction(softwareProductId, VersionControllerActionsEnum.CREATE_PACKAGE, version).then(() => {
404 type: modalActionTypes.GLOBAL_MODAL_SUCCESS,
406 title: i18n('Submit Succeeded'),
407 msg: i18n('This software product successfully submitted'),
408 cancelButtonText: i18n('OK'),
412 const newVersionId = adjustMajorVersion(version.label, 1);
413 SoftwareProductActionHelper.fetchSoftwareProduct(dispatch,{softwareProductId, version: {id: newVersionId}});
414 return Promise.resolve({newVersion: {id: newVersionId}});
416 }, error => dispatch({
417 type: modalActionTypes.GLOBAL_MODAL_ERROR,
419 modalComponentName: modalContentMapper.SUMBIT_ERROR_RESPONSE,
420 title: i18n('Submit Failed'),
421 modalComponentProps: {
422 validationResponse: error.responseJSON
424 cancelButtonText: i18n('Ok')
429 return putSoftwareProductAction(softwareProductId, action, version).then(() => {
430 let newVersionId = version.id;
432 TODO Temorary switch to change version label
435 case VersionControllerActionsEnum.CHECK_OUT:
436 newVersionId = adjustMinorVersion(version.label, 1);
438 case VersionControllerActionsEnum.UNDO_CHECK_OUT:
439 newVersionId = adjustMinorVersion(version.label, -1);
442 SoftwareProductActionHelper.fetchSoftwareProduct(dispatch,{softwareProductId, version:{id: newVersionId}});
443 return Promise.resolve({newVersion: {id: newVersionId}});
448 switchVersion(dispatch, {softwareProductId, licenseModelId, version}) {
449 OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {
456 toggleNavigationItems(dispatch, {items, itemIdToExpand}) {
457 let mapOfExpandedIds = getExpandedItemsId(items, itemIdToExpand);
459 type: actionTypes.TOGGLE_NAVIGATION_ITEM,
464 /** for the next verision */
465 addComponent(dispatch, {softwareProductId, modalClassName}) {
466 SoftwareProductComponentsActionHelper.clearComponentCreationData(dispatch);
468 type: componentActionTypes.COMPONENT_CREATE_OPEN
471 type: modalActionTypes.GLOBAL_MODAL_SHOW,
473 modalComponentName: modalContentMapper.COMPONENT_CREATION,
474 modalComponentProps: {softwareProductId},
476 title: 'Create Virtual Function Component'
481 migrateSoftwareProduct(dispatch, {softwareProduct}) {
482 let {licenseModelId, licensingVersion, id: softwareProductId, version, status} = softwareProduct;
483 const newVer = status === statusEnum.CHECK_IN_STATUS || status === statusEnum.SUBMIT_STATUS ?
484 adjustMinorVersion(version.id, 1) : version.id;
485 migrateSoftwareProduct(softwareProductId, version)
486 .then(() =>OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch,
487 {softwareProductId, version: {id: newVer, label: newVer}, licenseModelId, licensingVersion}));
492 export default SoftwareProductActionHelper;