2 * Copyright © 2016-2018 European Support Limited
3 * Modifications copyright (c) 2021 Nokia
4 * Modifications Copyright (C) 2021 Nordix Foundation.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
15 * or implied. See the License for the specific language governing
16 * permissions and limitations under the License.
18 import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js';
19 import showFileSaveDialog from 'nfvo-utils/ShowFileSaveDialog.js';
20 import Configuration from 'sdc-app/config/Configuration.js';
21 import i18n from 'nfvo-utils/i18n/i18n.js';
22 import LicenseModelActionHelper from 'sdc-app/onboarding/licenseModel/LicenseModelActionHelper.js';
23 import LicenseAgreementActionHelper from 'sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementActionHelper.js';
24 import FeatureGroupsActionHelper from 'sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js';
28 onboardingOriginTypes,
29 PRODUCT_QUESTIONNAIRE,
31 } from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js';
32 import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js';
33 import SoftwareProductComponentsActionHelper from './components/SoftwareProductComponentsActionHelper.js';
34 import { actionsEnum as VersionControllerActionsEnum } from 'nfvo-components/panel/versionController/VersionControllerConstants.js';
35 import { actionTypes as HeatSetupActions } from 'sdc-app/onboarding/softwareProduct/attachments/setup/HeatSetupConstants.js';
36 import { actionTypes as featureGroupsActionConstants } from 'sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsConstants.js';
37 import { actionTypes as licenseAgreementActionTypes } from 'sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConstants.js';
38 import { actionTypes as componentActionTypes } from './components/SoftwareProductComponentsConstants.js';
39 import ValidationHelper from 'sdc-app/common/helpers/ValidationHelper.js';
40 import { actionTypes as modalActionTypes } from 'nfvo-components/modal/GlobalModalConstants.js';
41 import { modalContentMapper } from 'sdc-app/common/modal/ModalContentMapper.js';
42 import { default as ItemsHelper } from 'sdc-app/common/helpers/ItemsHelper.js';
43 import ScreensHelper from 'sdc-app/common/helpers/ScreensHelper.js';
44 import { enums, screenTypes } from 'sdc-app/onboarding/OnboardingConstants.js';
45 import MergeEditorActionHelper from 'sdc-app/common/merge/MergeEditorActionHelper.js';
46 import { CommitModalType } from 'nfvo-components/panel/versionController/components/CommitCommentModal.jsx';
47 import { actionTypes as commonActionTypes } from 'sdc-app/common/reducers/PlainDataReducerConstants.js';
48 import versionPageActionHelper from 'sdc-app/onboarding/versionsPage/VersionsPageActionHelper.js';
49 import { itemTypes } from 'sdc-app/onboarding/versionsPage/VersionsPageConstants.js';
50 import getValue from 'nfvo-utils/getValue.js';
54 } from 'sdc-app/common/helpers/ItemsHelperConstants.js';
56 let shouldDisplayTimingValidationInfo = true;
57 let alertValidationTiming = Configuration.get(
58 'displayAlertValidationAfterMilisec'
61 function getLicensingData(licensingData = {}) {
62 const { licenseAgreement, featureGroups } = licensingData;
63 const newlicenseAgreement = getValue(licenseAgreement);
64 const newfeatureGroups = getValue(featureGroups);
65 return newlicenseAgreement
67 licenseAgreement: newlicenseAgreement,
68 featureGroups: newfeatureGroups
73 function getTimingInfoWarning() {
75 type: modalActionTypes.GLOBAL_MODAL_WARNING,
77 title: 'Please be patient',
78 msg: 'Large files processing may take up to several minutes.',
79 cancelButtonText: 'OK'
84 function displayTimingValidationInfo(dispatch) {
85 shouldDisplayTimingValidationInfo = true;
87 if (shouldDisplayTimingValidationInfo) {
88 dispatch(getTimingInfoWarning());
90 }, alertValidationTiming);
93 function closeTimingValidationInfo(dispatch) {
94 shouldDisplayTimingValidationInfo = false;
96 type: modalActionTypes.GLOBAL_MODAL_CLOSE
101 const restPrefix = Configuration.get('restPrefix');
102 return `${restPrefix}/v1.0/vendor-software-products/`;
105 function softwareProductCategoriesUrl() {
106 const restCatalogPrefix = Configuration.get('restCatalogPrefix');
107 return `${restCatalogPrefix}/v1/categories/resources/`;
110 function getModelUrl() {
111 const restCatalogPrefix = Configuration.get('restCatalogPrefix');
112 return `${restCatalogPrefix}/v1/catalog/model?modelType=normative`;
115 function uploadFile(vspId, formData, version) {
116 return RestAPIUtil.post(
117 `${baseUrl()}${vspId}/versions/${
119 }/orchestration-template-candidate`,
124 function uploadVNFFile(csarId, softwareProductId, version) {
125 let verId = typeof version === 'object' ? version.id : version;
126 return RestAPIUtil.post(
127 `${baseUrl()}${softwareProductId}/versions/${verId}/vnfrepository/vnfpackage/${csarId}/import`
130 function putSoftwareProduct({ softwareProduct, version }) {
131 return RestAPIUtil.put(
132 `${baseUrl()}${softwareProduct.id}/versions/${version.id}`,
134 name: softwareProduct.name,
135 description: softwareProduct.description,
136 category: softwareProduct.category,
137 subCategory: softwareProduct.subCategory,
138 vendorId: softwareProduct.vendorId,
139 vendorName: softwareProduct.vendorName,
140 licensingVersion: softwareProduct.licensingVersion
141 ? softwareProduct.licensingVersion
143 icon: softwareProduct.icon,
144 licenseType: softwareProduct.licenseType,
145 selectedModelList: softwareProduct.selectedModelList,
146 licensingData: getLicensingData(softwareProduct.licensingData)
151 function putSoftwareProductQuestionnaire(vspId, qdata, version) {
152 return RestAPIUtil.put(
153 `${baseUrl()}${vspId}/versions/${version.id}/questionnaire`,
158 function putSoftwareProductAction(id, action, version) {
159 return RestAPIUtil.put(`${baseUrl()}${id}/versions/${version.id}/actions`, {
164 function fetchSoftwareProductList() {
165 return RestAPIUtil.fetch(
166 `${baseUrl()}?versionFilter=${versionStatus.DRAFT}`
170 function fetchArchivedSoftwareProductList() {
171 return RestAPIUtil.fetch(`${baseUrl()}?Status=${itemStatus.ARCHIVED}`);
174 function fetchFinalizedSoftwareProductList() {
175 return RestAPIUtil.fetch(
176 `${baseUrl()}?versionFilter=${versionStatus.CERTIFIED}`
180 function fetchSoftwareProduct(vspId, version) {
181 return RestAPIUtil.fetch(`${baseUrl()}${vspId}/versions/${version.id}`);
184 function fetchSoftwareProductQuestionnaire(vspId, version) {
185 return RestAPIUtil.fetch(
186 `${baseUrl()}${vspId}/versions/${version.id}/questionnaire`
190 function updateSoftwareProductHeatCandidate(
195 return RestAPIUtil.put(
196 `${baseUrl()}${softwareProductId}/versions/${
198 }/orchestration-template-candidate/manifest`,
202 function validateHeatCandidate(softwareProductId, version) {
203 return RestAPIUtil.put(
204 `${baseUrl()}${softwareProductId}/versions/${
206 }/orchestration-template-candidate/process`
210 function fetchOrchestrationTemplateCandidate(softwareProductId, version) {
211 return RestAPIUtil.fetch(
212 `${baseUrl()}${softwareProductId}/versions/${
214 }/orchestration-template-candidate`,
215 { dataType: 'binary' }
219 function abortValidationProcess(softwareProductId, version) {
220 return RestAPIUtil.destroy(
221 `${baseUrl()}${softwareProductId}/versions/${
223 }/orchestration-template-candidate`
227 function objToString(obj) {
229 if (obj instanceof Array) {
230 obj.forEach(item => {
231 str += objToString(item) + '\n';
235 if (obj.hasOwnProperty(p)) {
236 str += obj[p] + '\n';
240 return str.replace(/\n$/, '');
243 function parseUploadErrorMsg(error) {
245 for (let key in error) {
246 if (error.hasOwnProperty(key)) {
247 message += objToString(error[key]) + '\n';
250 return message.replace(/\n$/, '');
253 function showWarningValidationInfo(dispatch, errors) {
255 type: modalActionTypes.GLOBAL_MODAL_WARNING,
257 title: 'Validation messages',
258 msg: parseUploadErrorMsg(errors),
259 cancelButtonText: 'OK'
264 function fetchSoftwareProductCategories(dispatch) {
265 let handleResponse = response =>
267 type: actionTypes.SOFTWARE_PRODUCT_CATEGORIES_LOADED,
268 softwareProductCategories: response
270 return RestAPIUtil.fetch(softwareProductCategoriesUrl())
271 .then(handleResponse)
272 .catch(() => handleResponse(null));
275 function fetchModelList(dispatch) {
276 let handleResponse = response =>
278 type: actionTypes.SOFTWARE_PRODUCT_MODELS_LOADED,
281 RestAPIUtil.fetch(getModelUrl())
282 .then(handleResponse)
283 .catch(() => handleResponse(null));
286 function loadLicensingData(dispatch, { licenseModelId, licensingVersion }) {
287 return ItemsHelper.fetchVersion({
288 itemId: licenseModelId,
289 versionId: licensingVersion
292 LicenseAgreementActionHelper.fetchLicenseAgreementList(dispatch, {
294 version: { id: licensingVersion }
296 FeatureGroupsActionHelper.fetchFeatureGroupsList(dispatch, {
298 version: { id: licensingVersion }
304 function getExpandedItemsId(items, itemIdToToggle) {
305 for (let i = 0; i < items.length; i++) {
306 if (items[i].id === itemIdToToggle) {
307 if (items[i].expanded) {
310 return { [itemIdToToggle]: true };
312 } else if (items[i].items && items[i].items.length > 0) {
313 let mapOfExpandedIds = getExpandedItemsId(
317 if (mapOfExpandedIds !== false) {
318 mapOfExpandedIds[items[i].id] = true;
319 return mapOfExpandedIds;
326 function migrateSoftwareProduct(vspId, version) {
327 return RestAPIUtil.put(`${baseUrl()}${vspId}/versions/${version.id}/heal`);
330 const SoftwareProductActionHelper = {
331 fetchFinalizedSoftwareProductList(dispatch) {
332 return fetchFinalizedSoftwareProductList().then(response =>
334 type: actionTypes.FINALIZED_SOFTWARE_PRODUCT_LIST_LOADED,
340 fetchArchivedSoftwareProductList(dispatch) {
341 return fetchArchivedSoftwareProductList().then(response =>
343 type: actionTypes.ARCHIVED_SOFTWARE_PRODUCT_LIST_LOADED,
349 loadSoftwareProductAssociatedData(dispatch) {
350 fetchSoftwareProductCategories(dispatch);
351 fetchModelList(dispatch);
352 LicenseModelActionHelper.fetchFinalizedLicenseModels(dispatch);
355 loadSoftwareProductDetailsData(
357 { licenseModelId, licensingVersion }
359 SoftwareProductActionHelper.loadSoftwareProductAssociatedData(dispatch);
360 if (licensingVersion) {
361 return loadLicensingData(dispatch, {
366 return Promise.resolve();
369 fetchSoftwareProductList(dispatch) {
370 return fetchSoftwareProductList().then(response =>
372 type: actionTypes.SOFTWARE_PRODUCT_LIST_LOADED,
378 loadSoftwareProductHeatCandidate(dispatch, { softwareProductId, version }) {
379 return RestAPIUtil.fetch(
380 `${baseUrl()}${softwareProductId}/versions/${
382 }/orchestration-template-candidate/manifest`
385 type: HeatSetupActions.MANIFEST_LOADED,
391 loadLicensingVersionsList(dispatch, { licenseModelId }) {
392 return ItemsHelper.fetchVersions({ itemId: licenseModelId }).then(
395 type: actionTypes.LOAD_LICENSING_VERSIONS_LIST,
396 licensingVersionsList: response.results
401 updateSoftwareProductHeatCandidate(
403 { softwareProductId, heatCandidate, version }
405 return updateSoftwareProductHeatCandidate(
412 processAndValidateHeatCandidate(dispatch, { softwareProductId, version }) {
413 displayTimingValidationInfo(dispatch);
414 return validateHeatCandidate(softwareProductId, version).then(
416 if (response.status === 'Success') {
417 SoftwareProductComponentsActionHelper.fetchSoftwareProductComponents(
419 { softwareProductId, version }
421 SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {
426 SoftwareProductActionHelper.fetchSoftwareProduct(dispatch, {
431 closeTimingValidationInfo(dispatch);
438 { softwareProductId, formData, failedNotificationTitle, version }
441 type: HeatSetupActions.FILL_HEAT_SETUP_CACHE,
444 displayTimingValidationInfo(dispatch);
446 .then(() => uploadFile(softwareProductId, formData, version))
448 if (response.status === 'Success') {
450 type: commonActionTypes.DATA_CHANGED,
452 onboardingOrigin: response.onboardingOrigin
454 formName: forms.VENDOR_SOFTWARE_PRODUCT_DETAILS
456 switch (response.onboardingOrigin) {
457 case onboardingOriginTypes.ZIP:
458 ScreensHelper.loadScreen(dispatch, {
461 .SOFTWARE_PRODUCT_ATTACHMENTS_SETUP,
462 screenType: screenTypes.SOFTWARE_PRODUCT,
463 props: { softwareProductId, version }
466 type: actionTypes.CANDIDATE_IN_PROCESS,
470 case onboardingOriginTypes.CSAR:
471 ScreensHelper.loadScreen(dispatch, {
474 .SOFTWARE_PRODUCT_ATTACHMENTS_VALIDATION,
475 screenType: screenTypes.SOFTWARE_PRODUCT,
476 props: { softwareProductId, version }
480 closeTimingValidationInfo(dispatch);
482 response.errors !== null &&
483 Object.keys(response.errors).length !== 0
485 showWarningValidationInfo(dispatch, response.errors);
488 throw new Error(parseUploadErrorMsg(response.errors));
494 type: modalActionTypes.GLOBAL_MODAL_ERROR,
496 title: failedNotificationTitle,
499 (error.responseJSON &&
500 error.responseJSON.message) ||
501 parseUploadErrorMsg(error.responseJSON.errors)
504 closeTimingValidationInfo(dispatch)
511 { csarId, failedNotificationTitle, softwareProductId, version }
514 type: HeatSetupActions.FILL_HEAT_SETUP_CACHE,
519 .then(() => uploadVNFFile(csarId, softwareProductId, version))
521 if (response.status === 'Success') {
523 type: commonActionTypes.DATA_CHANGED,
525 onboardingOrigin: response.onboardingOrigin
527 formName: forms.VENDOR_SOFTWARE_PRODUCT_DETAILS
529 switch (response.onboardingOrigin) {
530 case onboardingOriginTypes.ZIP:
531 OnboardingActionHelper.navigateToSoftwareProductAttachmentsSetupTab(
533 { softwareProductId, version }
536 case onboardingOriginTypes.CSAR:
537 OnboardingActionHelper.navigateToSoftwareProductAttachmentsValidationTab(
539 { softwareProductId, version }
544 throw new Error(parseUploadErrorMsg(response.errors));
549 type: modalActionTypes.GLOBAL_MODAL_ERROR,
551 title: failedNotificationTitle,
559 { softwareProductId, heatCandidate, isReadOnlyMode, version }
561 let p = isReadOnlyMode
563 : SoftwareProductActionHelper.updateSoftwareProductHeatCandidate(
572 fetchOrchestrationTemplateCandidate(
578 headers: response.headers,
579 defaultFilename: 'HEAT_file.zip',
583 }, null /* do not download if data was not saved correctly*/);
586 hideUploadConfirm(dispatch) {
588 type: actionTypes.softwareProductEditor.UPLOAD_CONFIRMATION
591 updateSoftwareProduct(dispatch, { softwareProduct, version, qdata }) {
593 SoftwareProductActionHelper.updateSoftwareProductData(dispatch, {
598 type: actionTypes.SOFTWARE_PRODUCT_LIST_EDIT,
599 payload: { softwareProduct }
602 SoftwareProductActionHelper.updateSoftwareProductQuestionnaire(
605 softwareProductId: softwareProduct.id,
613 updateSoftwareProductData(dispatch, { softwareProduct, version }) {
614 return putSoftwareProduct({ softwareProduct, version });
617 updateSoftwareProductQuestionnaire(
619 { softwareProductId, qdata, version }
621 return putSoftwareProductQuestionnaire(
628 softwareProductEditorDataChanged(dispatch, { deltaData }) {
630 type: actionTypes.softwareProductEditor.DATA_CHANGED,
635 softwareProductQuestionnaireUpdate(dispatch, { data }) {
637 type: actionTypes.SOFTWARE_PRODUCT_QUESTIONNAIRE_UPDATE,
638 payload: { qdata: data }
642 softwareProductEditorVendorChanged(dispatch, { deltaData, formName }) {
643 if (deltaData.licensingVersion) {
644 return loadLicensingData(dispatch, {
645 licenseModelId: deltaData.vendorId,
646 licensingVersion: deltaData.licensingVersion
648 ValidationHelper.dataChanged(dispatch, { deltaData, formName });
649 return Promise.resolve();
651 } else if (deltaData.vendorId) {
652 ValidationHelper.dataChanged(dispatch, { deltaData, formName });
653 return SoftwareProductActionHelper.loadLicensingVersionsList(
656 licenseModelId: deltaData.vendorId
659 OnboardingActionHelper.forceBreadCrumbsUpdate(dispatch)
662 ValidationHelper.dataChanged(dispatch, { deltaData, formName });
665 type: licenseAgreementActionTypes.LICENSE_AGREEMENT_LIST_LOADED,
666 response: { results: [] }
670 type: featureGroupsActionConstants.FEATURE_GROUPS_LIST_LOADED,
671 response: { results: [] }
676 setIsValidityData(dispatch, { isValidityData }) {
678 type: actionTypes.softwareProductEditor.IS_VALIDITY_DATA_CHANGED,
683 fetchSoftwareProduct(dispatch, { softwareProductId, version }) {
685 fetchSoftwareProduct(softwareProductId, version).then(response => {
687 type: actionTypes.SOFTWARE_PRODUCT_LOADED,
692 fetchSoftwareProductQuestionnaire(softwareProductId, version).then(
694 ValidationHelper.qDataLoaded(dispatch, {
697 ? JSON.parse(response.data)
699 qschema: JSON.parse(response.schema)
701 qName: PRODUCT_QUESTIONNAIRE
708 manageSubmitAction(dispatch, { softwareProductId, version, isDirty }) {
710 const onCommit = comment => {
711 return this.performVCAction(dispatch, {
713 action: VersionControllerActionsEnum.COMMIT,
717 return this.performSubmitAction(dispatch, {
724 type: modalActionTypes.GLOBAL_MODAL_SHOW,
726 modalComponentName: modalContentMapper.COMMIT_COMMENT,
727 modalComponentProps: {
729 type: CommitModalType.COMMIT_SUBMIT
731 title: i18n('Commit & Submit')
734 return Promise.resolve(version);
736 return this.performSubmitAction(dispatch, {
742 performSubmitAction(dispatch, { softwareProductId, version }) {
743 return putSoftwareProductAction(
745 VersionControllerActionsEnum.SUBMIT,
749 return putSoftwareProductAction(
751 VersionControllerActionsEnum.CREATE_PACKAGE,
754 return ItemsHelper.checkItemStatus(dispatch, {
755 itemId: softwareProductId,
756 versionId: version.id
757 }).then(updatedVersion => {
759 type: modalActionTypes.GLOBAL_MODAL_SUCCESS,
761 title: i18n('Submit Succeeded'),
763 'This software product successfully submitted'
765 cancelButtonText: i18n('OK'),
769 versionPageActionHelper.fetchVersions(dispatch, {
770 itemType: itemTypes.SOFTWARE_PRODUCT,
771 itemId: softwareProductId
773 return Promise.resolve(updatedVersion);
779 type: modalActionTypes.GLOBAL_MODAL_ERROR,
782 modalContentMapper.SUMBIT_ERROR_RESPONSE,
783 title: i18n('Submit Failed'),
784 modalComponentProps: {
785 validationResponse: error.responseJSON
787 cancelButtonText: i18n('OK')
790 return Promise.reject(error.responseJSON);
795 performVCAction(dispatch, { softwareProductId, action, version, comment }) {
796 return MergeEditorActionHelper.analyzeSyncResult(dispatch, {
797 itemId: softwareProductId,
799 }).then(({ inMerge, isDirty, updatedVersion }) => {
801 (updatedVersion.status === versionStatus.CERTIFIED ||
802 updatedVersion.archivedStatus === itemStatus.ARCHIVED) &&
803 (action === VersionControllerActionsEnum.COMMIT ||
804 action === VersionControllerActionsEnum.SYNC)
806 versionPageActionHelper.fetchVersions(dispatch, {
807 itemType: itemTypes.SOFTWARE_PRODUCT,
808 itemId: softwareProductId
811 updatedVersion.archivedStatus === itemStatus.ARCHIVED
812 ? i18n('Item was Archived')
813 : i18n('Item version already Certified');
815 type: modalActionTypes.GLOBAL_MODAL_WARNING,
817 title: i18n('Commit error'),
819 cancelButtonText: i18n('Cancel')
822 return Promise.resolve(updatedVersion);
825 if (action === VersionControllerActionsEnum.SUBMIT) {
826 return this.manageSubmitAction(dispatch, {
832 let isCallActionValid =
833 action !== VersionControllerActionsEnum.COMMIT ||
835 if (isCallActionValid) {
836 return ItemsHelper.performVCAction({
837 itemId: softwareProductId,
842 versionPageActionHelper.fetchVersions(dispatch, {
843 itemType: itemTypes.LICENSE_MODEL,
844 itemId: softwareProductId
846 if (action === VersionControllerActionsEnum.SYNC) {
847 return MergeEditorActionHelper.analyzeSyncResult(
849 { itemId: softwareProductId, version }
850 ).then(({ updatedVersion }) => {
851 return Promise.resolve(updatedVersion);
854 return ItemsHelper.checkItemStatus(dispatch, {
855 itemId: softwareProductId,
856 versionId: version.id
862 type: modalActionTypes.GLOBAL_MODAL_ERROR,
864 title: i18n('Commit Failed'),
865 msg: i18n('There is nothing to commit')
874 toggleNavigationItems(dispatch, { items, itemIdToExpand }) {
875 let mapOfExpandedIds = getExpandedItemsId(items, itemIdToExpand);
877 type: actionTypes.TOGGLE_NAVIGATION_ITEM,
882 /** for the next verision */
883 addComponent(dispatch, { softwareProductId, version }) {
884 SoftwareProductComponentsActionHelper.clearComponentCreationData(
888 type: componentActionTypes.COMPONENT_CREATE_OPEN
891 type: modalActionTypes.GLOBAL_MODAL_SHOW,
893 modalComponentName: modalContentMapper.COMPONENT_CREATION,
894 modalComponentProps: { softwareProductId, version },
895 title: 'Create Virtual Function Component'
900 migrateSoftwareProduct(dispatch, { softwareProduct }) {
901 let { id: softwareProductId, version } = softwareProduct;
902 const newVer = version.id;
903 migrateSoftwareProduct(softwareProductId, version).then(() =>
904 ScreensHelper.loadScreen(dispatch, {
905 screen: enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE,
906 screenType: screenTypes.SOFTWARE_PRODUCT,
909 version: { id: newVer, label: newVer }
915 abortCandidateValidation(dispatch, { softwareProductId, version }) {
916 return abortValidationProcess(softwareProductId, version);
920 export default SoftwareProductActionHelper;