summaryrefslogtreecommitdiffstats
path: root/openecomp-ui/src/sdc-app/common
diff options
context:
space:
mode:
Diffstat (limited to 'openecomp-ui/src/sdc-app/common')
-rw-r--r--openecomp-ui/src/sdc-app/common/activity-log/ActivityLog.js5
-rw-r--r--openecomp-ui/src/sdc-app/common/activity-log/ActivityLogActionHelper.js11
-rw-r--r--openecomp-ui/src/sdc-app/common/activity-log/ActivityLogView.jsx13
-rw-r--r--openecomp-ui/src/sdc-app/common/helpers/ItemsHelper.js78
-rw-r--r--openecomp-ui/src/sdc-app/common/helpers/ScreensHelper.js290
-rw-r--r--openecomp-ui/src/sdc-app/common/merge/MergeEditor.js37
-rw-r--r--openecomp-ui/src/sdc-app/common/merge/MergeEditorActionHelper.js443
-rw-r--r--openecomp-ui/src/sdc-app/common/merge/MergeEditorConstants.js224
-rw-r--r--openecomp-ui/src/sdc-app/common/merge/MergeEditorReducer.js66
-rw-r--r--openecomp-ui/src/sdc-app/common/merge/MergeEditorView.jsx256
-rw-r--r--openecomp-ui/src/sdc-app/common/modal/ModalContentMapper.js22
11 files changed, 1426 insertions, 19 deletions
diff --git a/openecomp-ui/src/sdc-app/common/activity-log/ActivityLog.js b/openecomp-ui/src/sdc-app/common/activity-log/ActivityLog.js
index f7354f96e2..a1ad437f5b 100644
--- a/openecomp-ui/src/sdc-app/common/activity-log/ActivityLog.js
+++ b/openecomp-ui/src/sdc-app/common/activity-log/ActivityLog.js
@@ -16,11 +16,12 @@
import {connect} from 'react-redux';
import ActivityLogView from './ActivityLogView.jsx';
-export const mapStateToProps = ({licenseModel: {activityLog}}) => {
+export const mapStateToProps = ({users: {usersList}, licenseModel: {activityLog}}) => {
let activities = activityLog;
return {
- activities
+ activities: activities.map(activity => ({...activity, user: {id: activity.user, name: usersList.find(userObject => userObject.userId === activity.user).fullName}})),
+ usersList
};
};
diff --git a/openecomp-ui/src/sdc-app/common/activity-log/ActivityLogActionHelper.js b/openecomp-ui/src/sdc-app/common/activity-log/ActivityLogActionHelper.js
index 01a27abbc5..729d8fb5f3 100644
--- a/openecomp-ui/src/sdc-app/common/activity-log/ActivityLogActionHelper.js
+++ b/openecomp-ui/src/sdc-app/common/activity-log/ActivityLogActionHelper.js
@@ -13,19 +13,12 @@
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
-import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js';
-import Configuration from 'sdc-app/config/Configuration.js';
import ActivityLogConstants from './ActivityLogConstants.js';
-
-
-function baseUrl(itemId, versionId) {
- const restPrefix = Configuration.get('restPrefix');
- return `${restPrefix}/v1.0/activity-logs/${itemId}/versions/${versionId}`;
-}
+import ItemHelper from 'sdc-app/common/helpers/ItemsHelper.js';
export default {
fetchActivityLog(dispatch, {itemId, versionId}){
- return RestAPIUtil.fetch(baseUrl(itemId, versionId)).then(response => dispatch({type: ActivityLogConstants.ACTIVITY_LOG_UPDATED, response}));
+ return ItemHelper.fetchActivityLog({itemId, versionId}).then(response => dispatch({type: ActivityLogConstants.ACTIVITY_LOG_UPDATED, response}));
}
};
diff --git a/openecomp-ui/src/sdc-app/common/activity-log/ActivityLogView.jsx b/openecomp-ui/src/sdc-app/common/activity-log/ActivityLogView.jsx
index 9abddea542..5b8c29b719 100644
--- a/openecomp-ui/src/sdc-app/common/activity-log/ActivityLogView.jsx
+++ b/openecomp-ui/src/sdc-app/common/activity-log/ActivityLogView.jsx
@@ -67,9 +67,9 @@ export function ActivityListItem({activity, isHeader, isDes, onSort}) {
return (
<li className={`activity-list-item ${isHeader ? 'header' : ''}`} data-test-id='activity-list-item'>
<div className='table-cell activity-date' data-test-id='activity-date'><ActivityLogSortableCellHeader isHeader={isHeader} data={timestamp} isDes={isDes} onSort={onSort}/></div>
- <div className='table-cell activity-action' data-test-id='activity-action'>{type}</div>
- <div className='table-cell activity-comment' title={comment} data-test-id='activity-comment'><span>{comment}</span></div>
- <div className='table-cell activity-username' data-test-id='activity-username'>{user}</div>
+ <div className='table-cell activity-action' data-test-id='activity-action'>{i18n(type)}</div>
+ <div className='table-cell activity-comment' title={isHeader ? '' : comment} data-test-id='activity-comment'><span>{i18n(comment)}</span></div>
+ <div className='table-cell activity-username' data-test-id='activity-username'>{isHeader ? i18n(activity.user) : `${i18n(user.name)} (${user.id})`}</div>
<div className='table-cell activity-status' data-test-id='activity-status'><ActivityLogStatus isHeader={isHeader} status={status}/></div>
</li>
);
@@ -91,10 +91,10 @@ class ActivityLogView extends Component {
filterValue={this.state.localFilter}
onFilter={filter => this.setState({localFilter: filter})}>
<ActivityListItem
- isHeader={true}
activity={{timestamp: 'Date', type: 'Action', comment: 'Comment', user: 'Username', status: 'Status'}}
isDes={this.state.sortDescending}
- onSort={() => this.setState({sortDescending: !this.state.sortDescending})}/>
+ onSort={() => this.setState({sortDescending: !this.state.sortDescending})}
+ isHeader/>
{this.sortActivities(this.filterActivities(), this.state.sortDescending).map(activity => <ActivityListItem key={activity.id} activity={activity}/>)}
</ListEditorView>
</div>
@@ -106,7 +106,8 @@ class ActivityLogView extends Component {
let {localFilter} = this.state;
if (localFilter.trim()) {
const filter = new RegExp(escape(localFilter), 'i');
- return activities.filter(({user = '', comment = '', type = ''}) => escape(user).match(filter) || escape(comment).match(filter) || escape(type).match(filter));
+ return activities.filter(({user = {id: '', name: ''}, comment = '', type = ''}) =>
+ escape(user.id).match(filter) || escape(user.name).match(filter) || escape(comment).match(filter) || escape(type).match(filter));
}
else {
return activities;
diff --git a/openecomp-ui/src/sdc-app/common/helpers/ItemsHelper.js b/openecomp-ui/src/sdc-app/common/helpers/ItemsHelper.js
new file mode 100644
index 0000000000..b82bc92017
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/common/helpers/ItemsHelper.js
@@ -0,0 +1,78 @@
+/*!
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js';
+import Configuration from 'sdc-app/config/Configuration.js';
+import {permissionTypes} from 'sdc-app/onboarding/permissions/PermissionsConstants.js';
+import {actionsEnum as VersionControllerActionsEnum} from 'nfvo-components/panel/versionController/VersionControllerConstants.js';
+import {actionTypes as onboardingActionTypes} from 'sdc-app/onboarding/OnboardingConstants.js';
+
+function baseUrl() {
+ const restPrefix = Configuration.get('restPrefix');
+ return `${restPrefix}/v1.0/items`;
+}
+
+const ItemsHelper = {
+ performVCAction({itemId, version, action, comment}) {
+ const {id: versionId} = version;
+ const data = {
+ action,
+ ...action === VersionControllerActionsEnum.COMMIT && {commitRequest: {message: comment}}
+ };
+ return RestAPIUtil.put(`${baseUrl()}/${itemId}/versions/${versionId}/actions`, data);
+ },
+
+ fetchVersions({itemId}) {
+ return RestAPIUtil.fetch(`${baseUrl()}/${itemId}/versions`);
+ },
+
+ fetchVersion({itemId, versionId}) {
+ return RestAPIUtil.fetch(`${baseUrl()}/${itemId}/versions/${versionId}`);
+ },
+
+ fetchActivityLog({itemId, versionId}) {
+ return RestAPIUtil.fetch(`${baseUrl()}/${itemId}/versions/${versionId}/activity-logs`);
+ },
+
+ fetchUsers({itemId}) {
+ return RestAPIUtil.fetch(`${baseUrl()}/${itemId}/permissions`);
+ },
+
+ updateContributors({itemId, removedUsersIds, addedUsersIds}) {
+ return RestAPIUtil.put(`${baseUrl()}/${itemId}/permissions/${permissionTypes.CONTRIBUTOR}`, {removedUsersIds, addedUsersIds});
+ },
+
+ changeOwner({itemId, ownerId}) {
+ return RestAPIUtil.put(`${baseUrl()}/${itemId}/permissions/${permissionTypes.OWNER}`, {removedUsersIds: [], addedUsersIds: [ownerId]});
+ },
+
+ checkItemStatus(dispatch, {itemId, versionId}) {
+ return ItemsHelper.fetchVersion({itemId, versionId}).then(response => {
+ let state = response && response.state || {};
+ const {baseId, description, id, name, status} = response;
+
+ dispatch({
+ type: onboardingActionTypes.UPDATE_ITEM_STATUS,
+ itemState: state,
+ itemStatus: response.status,
+ updatedVersion: {baseId, description, id, name, status}
+ });
+ return Promise.resolve(response);
+ });
+
+ },
+};
+
+export default ItemsHelper;
diff --git a/openecomp-ui/src/sdc-app/common/helpers/ScreensHelper.js b/openecomp-ui/src/sdc-app/common/helpers/ScreensHelper.js
new file mode 100644
index 0000000000..d8fa02664f
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/common/helpers/ScreensHelper.js
@@ -0,0 +1,290 @@
+import {itemTypes} from 'sdc-app/onboarding/versionsPage/VersionsPageConstants.js';
+import {enums, screenTypes} from 'sdc-app/onboarding/OnboardingConstants.js';
+import OnboardingActionHelper from 'sdc-app/onboarding/OnboardingActionHelper.js';
+import {actionTypes as SoftwareProductActionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js';
+import ItemsHelper from 'sdc-app/common/helpers/ItemsHelper.js';
+import versionPageActionHelper from 'sdc-app/onboarding/versionsPage/VersionsPageActionHelper.js';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js';
+
+const ScreensHelper = {
+ loadScreen(dispatch, {screen, screenType, props}) {
+ if(screen === enums.SCREEN.ONBOARDING_CATALOG) {
+ OnboardingActionHelper.navigateToOnboardingCatalog(dispatch);
+ return;
+ }
+
+ screenType = !screenType ? this.getScreenType(screen) : screenType;
+
+ if(screenType === screenTypes.LICENSE_MODEL) {
+ const {licenseModelId, version, licenseModel, usersList} = props;
+ let itemStatusPromise = version && screen ?
+ ItemsHelper.checkItemStatus(dispatch, {itemId: licenseModelId, versionId: version.id}) :
+ Promise.resolve();
+ itemStatusPromise.then((updatedVersion) => {
+ if (updatedVersion && updatedVersion.status !== version.status) {
+ dispatch({
+ type: modalActionTypes.GLOBAL_MODAL_WARNING,
+ data: {
+ title: i18n('Commit error'),
+ msg: i18n('Item version was certified by Owner'),
+ cancelButtonText: i18n('Cancel')
+ }
+ });
+ versionPageActionHelper.fetchVersions(dispatch, {itemType: itemTypes.LICENSE_MODEL, itemId: licenseModelId});
+ }
+ let newVersion = updatedVersion ? updatedVersion : version;
+ switch (screen) {
+ case enums.SCREEN.LICENSE_MODEL_OVERVIEW:
+ OnboardingActionHelper.navigateToLicenseModelOverview(dispatch, {licenseModelId, version: newVersion});
+ break;
+ case enums.SCREEN.LICENSE_AGREEMENTS:
+ OnboardingActionHelper.navigateToLicenseAgreements(dispatch, {licenseModelId, version: newVersion});
+ break;
+ case enums.SCREEN.FEATURE_GROUPS:
+ OnboardingActionHelper.navigateToFeatureGroups(dispatch, {licenseModelId, version: newVersion});
+ break;
+ case enums.SCREEN.ENTITLEMENT_POOLS:
+ OnboardingActionHelper.navigateToEntitlementPools(dispatch, {licenseModelId, version: newVersion});
+ break;
+ case enums.SCREEN.LICENSE_KEY_GROUPS:
+ OnboardingActionHelper.navigateToLicenseKeyGroups(dispatch, {licenseModelId, version: newVersion});
+ break;
+ case enums.SCREEN.ACTIVITY_LOG:
+ OnboardingActionHelper.navigateToLicenseModelActivityLog(dispatch, {licenseModelId, version: newVersion});
+ break;
+ case enums.SCREEN.VERSIONS_PAGE:
+ default:
+ OnboardingActionHelper.navigateToVersionsPage(dispatch, {
+ itemId: licenseModelId,
+ itemType: itemTypes.LICENSE_MODEL,
+ itemName: licenseModel.name,
+ users: usersList
+ });
+ break;
+ }
+ });
+ }
+
+ else if(screenType === screenTypes.SOFTWARE_PRODUCT) {
+ const {softwareProductId, componentId, version, softwareProduct, usersList} = props;
+ let itemStatusPromise = version && screen ?
+ ItemsHelper.checkItemStatus(dispatch, {itemId: softwareProductId, versionId: version.id}) :
+ Promise.resolve();
+ itemStatusPromise.then((updatedVersion) => {
+ if (updatedVersion && updatedVersion.status !== version.status) {
+ dispatch({
+ type: modalActionTypes.GLOBAL_MODAL_WARNING,
+ data: {
+ title: i18n('Commit error'),
+ msg: i18n('Item version already Certified'),
+ cancelButtonText: i18n('Cancel')
+ }
+ });
+ versionPageActionHelper.fetchVersions(dispatch, {itemType: itemTypes.SOFTWARE_PRODUCT, itemId: softwareProductId});
+ }
+
+ let newVersion = updatedVersion ? updatedVersion : version;
+ if (screen === screenTypes.SOFTWARE_PRODUCT_COMPONENT_DEFAULT_GENERAL) {
+ OnboardingActionHelper.navigateToSoftwareProductComponentGeneralAndUpdateLeftPanel(dispatch, {
+ softwareProductId,
+ componentId,
+ version: newVersion
+ });
+ }
+ if (componentId) {
+ switch (screen) {
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS:
+ OnboardingActionHelper.navigateToSoftwareProductComponentGeneralAndUpdateLeftPanel(dispatch, {
+ softwareProductId,
+ componentId,
+ version: newVersion
+ });
+ break;
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL:
+ OnboardingActionHelper.navigateToSoftwareProductComponentGeneral(dispatch, {
+ softwareProductId,
+ componentId,
+ version: newVersion
+ });
+ break;
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE:
+ OnboardingActionHelper.navigateToComponentCompute(dispatch, {
+ softwareProductId,
+ componentId,
+ version: newVersion
+ });
+ break;
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING:
+ OnboardingActionHelper.navigateToComponentLoadBalancing(dispatch, {
+ softwareProductId,
+ componentId,
+ version: newVersion
+ });
+ break;
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK:
+ OnboardingActionHelper.navigateToComponentNetwork(dispatch, {
+ softwareProductId,
+ componentId,
+ version: newVersion
+ });
+ break;
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE:
+ OnboardingActionHelper.navigateToComponentStorage(dispatch, {
+ softwareProductId,
+ componentId,
+ version: newVersion
+ });
+ break;
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES:
+ OnboardingActionHelper.navigateToSoftwareProductComponentProcesses(dispatch, {
+ softwareProductId,
+ componentId,
+ version: newVersion
+ });
+ break;
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING:
+ OnboardingActionHelper.navigateToSoftwareProductComponentMonitoring(dispatch, {
+ softwareProductId,
+ componentId,
+ version: newVersion
+ });
+ break;
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_IMAGES:
+ OnboardingActionHelper.navigateToComponentImages(dispatch, {
+ softwareProductId,
+ componentId,
+ version: newVersion
+ });
+ break;
+ }
+ }
+ else {
+ switch (screen) {
+ case enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE:
+ OnboardingActionHelper.navigateToSoftwareProductLandingPage(dispatch, {
+ softwareProductId,
+ version: newVersion
+ });
+ break;
+ case enums.SCREEN.SOFTWARE_PRODUCT_DETAILS:
+ OnboardingActionHelper.navigateToSoftwareProductDetails(dispatch, {
+ softwareProductId,
+ version: newVersion
+ });
+ break;
+ case enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS_SETUP:
+ OnboardingActionHelper.navigateToSoftwareProductAttachmentsSetupTab(dispatch, {
+ softwareProductId,
+ version
+ });
+ break;
+ case enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS_VALIDATION:
+ OnboardingActionHelper.navigateToSoftwareProductAttachmentsValidationTab(dispatch, {
+ softwareProductId,
+ version
+ });
+ break;
+ case enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES:
+ OnboardingActionHelper.navigateToSoftwareProductProcesses(dispatch, {
+ softwareProductId,
+ version: newVersion
+ });
+ break;
+ case enums.SCREEN.SOFTWARE_PRODUCT_DEPLOYMENT:
+ OnboardingActionHelper.navigateToSoftwareProductDeployment(dispatch, {
+ softwareProductId,
+ version: newVersion
+ });
+ break;
+ case enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS:
+ OnboardingActionHelper.navigateToSoftwareProductNetworks(dispatch, {
+ softwareProductId,
+ version: newVersion
+ });
+ break;
+ case enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES:
+ OnboardingActionHelper.navigateToSoftwareProductDependencies(dispatch, {
+ softwareProductId,
+ version: newVersion
+ });
+ break;
+ case enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG:
+ OnboardingActionHelper.navigateToSoftwareProductActivityLog(dispatch, {
+ softwareProductId,
+ version: newVersion
+ });
+ break;
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS:
+ OnboardingActionHelper.navigateToSoftwareProductComponents(dispatch, {
+ softwareProductId,
+ version: newVersion
+ });
+ dispatch({
+ type: SoftwareProductActionTypes.TOGGLE_NAVIGATION_ITEM,
+ mapOfExpandedIds: {
+ [enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS]: true
+ }
+ });
+ break;
+ case enums.SCREEN.SOFTWARE_PRODUCT_VERSIONS_PAGE:
+ default:
+ OnboardingActionHelper.navigateToVersionsPage(dispatch, {
+ itemId: softwareProductId,
+ itemType: itemTypes.SOFTWARE_PRODUCT,
+ itemName: softwareProduct.name,
+ users: usersList,
+ additionalProps: {
+ licenseModelId: softwareProduct.vendorId,
+ licensingVersion: softwareProduct.licensingVersion
+ }
+ });
+ break;
+ }
+ }
+ });
+ }
+ },
+
+ getScreenType(screen) {
+ switch (screen) {
+ case enums.SCREEN.LICENSE_MODEL_OVERVIEW:
+ case enums.SCREEN.LICENSE_AGREEMENTS:
+ case enums.SCREEN.FEATURE_GROUPS:
+ case enums.SCREEN.ENTITLEMENT_POOLS:
+ case enums.SCREEN.LICENSE_KEY_GROUPS:
+ case enums.SCREEN.ACTIVITY_LOG:
+ return screenTypes.LICENSE_MODEL;
+ case screenTypes.SOFTWARE_PRODUCT_COMPONENT_DEFAULT_GENERAL:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_GENERAL:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_COMPUTE:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_LOAD_BALANCING:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_NETWORK:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_STORAGE:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_PROCESSES:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_MONITORING:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENT_IMAGES:
+ case enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE:
+ case enums.SCREEN.SOFTWARE_PRODUCT_DETAILS:
+ case enums.SCREEN.SOFTWARE_PRODUCT_ATTACHMENTS:
+ case enums.SCREEN.SOFTWARE_PRODUCT_PROCESSES:
+ case enums.SCREEN.SOFTWARE_PRODUCT_DEPLOYMENT:
+ case enums.SCREEN.SOFTWARE_PRODUCT_NETWORKS:
+ case enums.SCREEN.SOFTWARE_PRODUCT_DEPENDENCIES:
+ case enums.SCREEN.SOFTWARE_PRODUCT_ACTIVITY_LOG:
+ case enums.SCREEN.SOFTWARE_PRODUCT_COMPONENTS:
+ return screenTypes.SOFTWARE_PRODUCT;
+ }
+ },
+
+ loadLandingScreen(dispatch, {previousScreenName, props: {licenseModelId, softwareProductId, version}}) {
+ const screenType = this.getScreenType(previousScreenName);
+ let screen = screenType === screenTypes.SOFTWARE_PRODUCT ?
+ enums.SCREEN.SOFTWARE_PRODUCT_LANDING_PAGE :
+ enums.SCREEN.LICENSE_MODEL_OVERVIEW;
+ let props = {licenseModelId, softwareProductId, version};
+ return this.loadScreen(dispatch, {screen, screenType, props});
+ }
+};
+
+export default ScreensHelper;
diff --git a/openecomp-ui/src/sdc-app/common/merge/MergeEditor.js b/openecomp-ui/src/sdc-app/common/merge/MergeEditor.js
new file mode 100644
index 0000000000..baf00cf0cf
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/common/merge/MergeEditor.js
@@ -0,0 +1,37 @@
+/*!
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+import {connect} from 'react-redux';
+import MergeEditorView from './MergeEditorView.jsx';
+import MergeEditorActionHelper from './MergeEditorActionHelper.js';
+
+export const mapStateToProps = ({mergeEditor, currentScreen}) => {
+ let {props} = currentScreen;
+ let item = {
+ id: props.softwareProductId || props.licenseModelId,
+ version: props.version
+ };
+ return {...mergeEditor, item, currentScreen};
+};
+
+export const mapActionsToProps = (dispatch) => {
+ return {
+ fetchConflict: ({cid, itemId, version}) => MergeEditorActionHelper.fetchConflict(dispatch, {itemId, version, cid}),
+ onResolveConflict: ({conflictId, resolution, itemId, version, currentScreen}) =>
+ MergeEditorActionHelper.resolveConflict(dispatch, {itemId, version, conflictId, resolution, currentScreen})
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps)(MergeEditorView);
diff --git a/openecomp-ui/src/sdc-app/common/merge/MergeEditorActionHelper.js b/openecomp-ui/src/sdc-app/common/merge/MergeEditorActionHelper.js
new file mode 100644
index 0000000000..3885ee4051
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/common/merge/MergeEditorActionHelper.js
@@ -0,0 +1,443 @@
+/*!
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+import {actionTypes, rules, dataRules, SyncStates} from './MergeEditorConstants.js';
+import cloneDeep from 'lodash/cloneDeep.js';
+import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js';
+import Configuration from 'sdc-app/config/Configuration.js';
+import ItemsHelper from '../../common/helpers/ItemsHelper.js';
+import {modalContentMapper} from 'sdc-app/common/modal/ModalContentMapper.js';
+import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import {optionsInputValues as epOptionsValues} from 'sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConstants.js';
+import {optionsInputValues as laOptionsValues} from 'sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConstants.js';
+import {optionsInputValues as processOptionValues} from 'sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesConstants.js';
+import {selectValues as limitSelectValues} from 'sdc-app/onboarding/licenseModel/limits/LimitEditorConstants.js';
+import FeatureGroupsActionHelper from 'sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js';
+import LicenseAgreementActionHelper from 'sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementActionHelper.js';
+import moment from 'moment';
+import {DATE_FORMAT} from 'sdc-app/onboarding/OnboardingConstants.js';
+import ScreensHelper from 'sdc-app/common/helpers/ScreensHelper.js';
+
+function softwareProductCategoriesUrl() {
+ const restATTPrefix = Configuration.get('restATTPrefix');
+ return `${restATTPrefix}/v1/categories/resources/`;
+}
+
+function versionUrl(itemId, versionId) {
+ const restPrefix = Configuration.get('restPrefix');
+ return `${restPrefix}/v1.0/items/${itemId}/versions/${versionId}`;
+}
+
+function baseUrl(itemId, version, conflictId) {
+ const versionId = version.id;
+ const restPrefix = Configuration.get('restPrefix');
+ let baseUrl = `${restPrefix}/v1.0/items/${itemId}/versions/${versionId}/conflicts`;
+ return conflictId ? `${baseUrl}/${conflictId}` : baseUrl;
+}
+
+function fetchConflicts({itemId, version}) {
+ return RestAPIUtil.fetch(`${baseUrl(itemId, version)}`);
+}
+
+function fetchConflictById({itemId, version, cid}) {
+ return RestAPIUtil.fetch(`${baseUrl(itemId, version, cid)}`);
+}
+
+function resolveConflict({itemId, version, conflictId, resolution}) {
+ return RestAPIUtil.put(`${baseUrl(itemId, version, conflictId)}`, {resolution});
+}
+
+function fetchCategories() {
+ return RestAPIUtil.fetch(softwareProductCategoriesUrl());
+}
+
+function fetchVersion({vendorId, licensingVersion}) {
+ return RestAPIUtil.fetch(versionUrl(vendorId, licensingVersion));
+}
+
+function createCategoryStr(data, {categories}) {
+
+ let {category, subCategory} = data;
+ let foundCat = categories.find(element => element.uniqueId === category);
+ if (!foundCat) { return ''; }
+
+ let catName = foundCat.name;
+ let foundSub = foundCat.subcategories.find(element => element.uniqueId === subCategory);
+ if (!foundSub) { return `${catName}`; }
+
+ let subcatName = foundSub.name;
+ return `${catName} - ${subcatName}`;
+
+}
+
+function getEnumValues({enums, list}) {
+
+ if (!list) { return ''; }
+ return list.map(item => enums.find(el => el.enum === item).title);
+
+}
+
+const MergeEditorActionHelper = {
+
+ analyzeSyncResult(dispatch, {itemId, version}) {
+ return ItemsHelper.checkItemStatus(dispatch, {itemId, versionId: version.id}).then((response) => {
+ let inMerge = response && response.state && response.state.synchronizationState === SyncStates.MERGE;
+ if (inMerge) {
+ MergeEditorActionHelper.fetchConflicts(dispatch, {itemId, version}).then(() =>
+ dispatch({
+ type: modalActionTypes.GLOBAL_MODAL_SHOW,
+ data: {
+ modalComponentName: modalContentMapper.MERGE_EDITOR,
+ modalClassName: 'merge-editor-modal',
+ title: `${i18n('Merge Required')} - ${version.description}`,
+ onDeclined: () => {
+ dispatch({
+ type: modalActionTypes.GLOBAL_MODAL_CLOSE
+ });
+ },
+ modalComponentProps: {
+ size: 'lg',
+ type: 'default'
+ }
+ }
+ })
+ );
+ }
+ return Promise.resolve({updatedVersion: response, inMerge, isDirty: response.state.dirty});
+ });
+ },
+
+ fetchConflicts(dispatch, {itemId, version}) {
+ return fetchConflicts({itemId, version}).then(
+ (data) => {
+ dispatch({
+ type: actionTypes.LOAD_CONFLICTS,
+ data
+ });
+ return data;
+ }
+ );
+ },
+
+ fetchConflict(dispatch, {itemId, version, cid}) {
+ fetchConflictById({itemId, version, cid}).then(
+ (data) => {
+ let newData = {};
+ newData = MergeEditorActionHelper.processConflict(dispatch, {conflict: data, itemId, cid, version});
+ dispatch({
+ type: actionTypes.LOAD_CONFLICT,
+ data: newData
+ });
+ }
+ );
+ },
+
+ resolveConflict(dispatch, {itemId, version, conflictId, resolution, currentScreen}) {
+ resolveConflict({itemId, version, conflictId, resolution}).then(() => {
+ MergeEditorActionHelper.fetchConflicts(dispatch, {itemId, version}).then(conflicts => {
+ if(conflicts.conflictInfoList && conflicts.conflictInfoList.length === 0) {
+ dispatch({
+ type: modalActionTypes.GLOBAL_MODAL_CLOSE
+ });
+ ScreensHelper.loadLandingScreen(dispatch, {previousScreenName: currentScreen.screen, props: currentScreen.props});
+ ItemsHelper.checkItemStatus(dispatch, {itemId, versionId: version.id});
+ }
+ });
+ });
+ },
+
+ createConflictObject(data, {cid, conflict, dispatch, itemId, version, isYours}) {
+
+ let newData = {};
+
+ for (let key in data) {
+
+ if (data.hasOwnProperty(key)) {
+ let value = data[key];
+ let fieldRule = dataRules[conflict.type] && dataRules[conflict.type][key] || dataRules.general[key];
+
+ if (fieldRule) {
+ switch (fieldRule.rule) {
+
+ case rules.SKIP:
+ break;
+
+ case rules.BOOLEAN:
+ let {trueValue, falseValue} = fieldRule;
+ newData[key] = value === trueValue ? true : value === falseValue ? false : undefined;
+ break;
+
+ case rules.PARSE:
+ let {moveFields, subFields} = fieldRule;
+ if (moveFields) {
+ let fields = subFields || Object.keys(value);
+ fields.forEach(field => {
+ newData[field] = MergeEditorActionHelper.createConflictObject(
+ value[field], {cid, conflict, dispatch, itemId, version, isYours}
+ );
+ });
+ } else {
+ newData[key] = MergeEditorActionHelper.createConflictObject(
+ value, {cid, conflict, dispatch, itemId, version, isYours}
+ );
+ }
+ break;
+
+ case rules.FUNCTION:
+ let {args, functionName} = fieldRule;
+ newData[key] = MergeEditorActionHelper[functionName](data, {
+ cid, conflict, dispatch, version, fieldName: key, isYours, itemId, args
+ });
+ break;
+
+ default:
+ newData[key] = value;
+ break;
+ }
+
+ } else {
+ newData[key] = value;
+
+ }
+ }
+ }
+
+ return newData;
+
+ },
+
+ getNamesFromIDs(data, {version, cid, dispatch, itemId, fieldName, isYours, args}) {
+
+ let idList = data[fieldName] || [];
+ let {fetchFunction, fetchField} = args;
+
+ let promises = idList.map(id =>
+ new Promise(resolve =>
+ MergeEditorActionHelper[fetchFunction](
+ dispatch, {licenseModelId: itemId, [fetchField]: id, version}
+ ).then(item => resolve(item.name))
+ )
+ );
+
+ Promise.all(promises).then(fetchedItems => {
+ let yoursOrTheirs = isYours ? 'yoursField' : 'theirsField';
+ dispatch({
+ type: actionTypes.DATA_PROCESSED,
+ data: {
+ cid,
+ [yoursOrTheirs]: { name: fieldName, value: fetchedItems }
+ }
+ });
+ });
+
+ return idList;
+
+ },
+
+ getFeatureGroups(data, {version, cid, dispatch, itemId, fieldName, isYours}) {
+
+ let featureGroups = data[fieldName] || [];
+ if (!(featureGroups instanceof Array)) {
+ featureGroups = [featureGroups];
+ }
+
+ let promises = featureGroups.map(featureGroupId =>
+ new Promise(resolve =>
+ FeatureGroupsActionHelper.fetchFeatureGroup(
+ dispatch, {licenseModelId: itemId, featureGroupId, version}
+ ).then(featureGroup => resolve(featureGroup.name))
+ .catch(reason => console.log(`getFeatureGroups Promise rejected ('${reason}')`))
+ )
+ );
+
+ Promise.all(promises).then(fetchedGroups => {
+ let yoursOrTheirs = isYours ? 'yoursField' : 'theirsField';
+ dispatch({
+ type: actionTypes.DATA_PROCESSED,
+ data: {
+ cid,
+ [yoursOrTheirs]: { name: fieldName, value: fetchedGroups }
+ }
+ });
+ });
+
+ return featureGroups;
+
+ },
+
+ getLicenseAgreements(data, {version, cid, dispatch, itemId, fieldName, isYours}) {
+
+ let licenseAgreements = data[fieldName] || [];
+ if (!(licenseAgreements instanceof Array)) {
+ licenseAgreements = [licenseAgreements];
+ }
+
+ let promises = licenseAgreements.map(licenseAgreementId =>
+ new Promise(resolve =>
+ LicenseAgreementActionHelper.fetchLicenseAgreement(
+ dispatch, {licenseModelId: itemId, licenseAgreementId, version}
+ ).then(licenseAgreement => resolve(licenseAgreement.name))
+ .catch(reason => console.log(`getLicenseAgreements Promise rejected ('${reason}')`))
+ )
+ );
+
+ Promise.all(promises).then(fetchedAgreements => {
+ let yoursOrTheirs = isYours ? 'yoursField' : 'theirsField';
+ dispatch({
+ type: actionTypes.DATA_PROCESSED,
+ data: {
+ cid,
+ [yoursOrTheirs]: { name: fieldName, value: fetchedAgreements }
+ }
+ });
+ });
+
+ return licenseAgreements;
+
+ },
+
+ processConflict(dispatch, {conflict, cid, version, itemId,}) {
+
+ let {id, type, yours, theirs} = conflict;
+
+ let newYours = MergeEditorActionHelper.createConflictObject(
+ cloneDeep(yours), {cid, conflict, dispatch, itemId, version, isYours: true}
+ );
+ let newTheirs = MergeEditorActionHelper.createConflictObject(
+ cloneDeep(theirs), {cid, conflict, dispatch, itemId, version, isYours: false}
+ );
+
+ return {
+ id,
+ type,
+ yours: newYours,
+ theirs: newTheirs
+ };
+
+ },
+
+ reduceList(data, {fieldName, args}) {
+
+ let {subField} = args;
+ return data[fieldName].map(el => el[subField]);
+
+ },
+
+ getEnumList({fieldName}) {
+
+ const enumLists = {
+ 'licenseTerm': laOptionsValues.LICENSE_MODEL_TYPE,
+ 'operationalScope': epOptionsValues.OPERATIONAL_SCOPE,
+ 'processType': processOptionValues.PROCESS_TYPE,
+ 'limitType': [
+ {title: 'Service Provider', enum: 'ServiceProvider'},
+ {title: 'Vendor', enum: 'Vendor'}
+ ],
+ 'limitUnit': limitSelectValues.UNIT
+ };
+
+ return enumLists[fieldName];
+
+ },
+
+ getEnumValue(data, {fieldName, args = {}}) {
+
+ let value = data[fieldName];
+ let enumValues = MergeEditorActionHelper.getEnumList({fieldName: args.listName || fieldName});
+ let enumValue = enumValues.find(el => el.enum === value);
+
+ return enumValue && enumValue.title || value;
+
+ },
+
+ processChoice(data, {fieldName, args = {}}) {
+
+ let value = data[fieldName];
+ let enumValues = MergeEditorActionHelper.getEnumList({fieldName: args.listName || fieldName});
+ let newValue = value.other || enumValues && enumValues.find(el => el.enum === value.choice).title || value.choice;
+
+ return newValue;
+
+ },
+
+ processChoices(data, {fieldName, args = {}}) {
+
+ let value = data[fieldName];
+ let enumValues = MergeEditorActionHelper.getEnumList({fieldName: args.listName || fieldName});
+ let newValue = value.other || getEnumValues({enums: enumValues, list: value.choices}) || value.choices;
+
+ return newValue;
+
+ },
+
+ convertArrayToObject(data, {fieldName}) {
+ let value = data[fieldName];
+ let newValue = {};
+ value.forEach((el, index) => {
+ newValue[index] = el;
+ });
+ return newValue;
+ },
+
+ fetchCategory(data, {cid, isYours, fieldName, dispatch}) {
+
+ fetchCategories().then((categories) => {
+ let value = createCategoryStr(data, {categories});
+ let yoursOrTheirs = isYours ? 'yoursField' : 'theirsField';
+
+ dispatch({
+ type: actionTypes.DATA_PROCESSED,
+ data: {
+ cid,
+ [yoursOrTheirs]: { name: fieldName, value }
+ }
+ });
+
+ });
+ },
+
+ fetchLMVersion(data, {cid, dispatch, isYours}) {
+
+ let {licensingVersion, vendorId} = data;
+ let yoursOrTheirs = isYours ? 'yoursField' : 'theirsField';
+
+ if (licensingVersion) {
+ fetchVersion({licensingVersion, vendorId}).then(response => {
+ dispatch({
+ type: actionTypes.DATA_PROCESSED,
+ data: {
+ cid,
+ [yoursOrTheirs]: {
+ name: 'licensingVersion',
+ value: response.name
+ }
+ }
+ });
+ });
+ }
+
+ },
+
+ parseDate(data, {fieldName}) {
+
+ let date = data[fieldName];
+ return date && moment(date, DATE_FORMAT).format(DATE_FORMAT);
+
+ }
+
+};
+
+export default MergeEditorActionHelper;
diff --git a/openecomp-ui/src/sdc-app/common/merge/MergeEditorConstants.js b/openecomp-ui/src/sdc-app/common/merge/MergeEditorConstants.js
new file mode 100644
index 0000000000..f7f6d4195e
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/common/merge/MergeEditorConstants.js
@@ -0,0 +1,224 @@
+/*!
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+import keyMirror from 'nfvo-utils/KeyMirror.js';
+
+export const actionTypes = keyMirror({
+ LOAD_CONFLICTS: null,
+ ADD_ACTIONS: null,
+ LOAD_CONFLICT: null,
+ DATA_PROCESSED: null
+});
+
+export const rules = {
+ SKIP: 'skip',
+ PARSE: 'parse',
+ FUNCTION: 'function',
+ BOOLEAN: 'boolean'
+};
+
+export const SyncStates = {
+ MERGE : 'Merging',
+ OUT_OF_SYNC: 'OutOfSync',
+ UP_TO_DATE: 'UpToDate'
+};
+
+export const ResolutionTypes = {
+ YOURS: 'YOURS',
+ THEIRS: 'THEIRS'
+};
+
+export const fileTypes = {
+ LKG : 'LicenseKeyGroup',
+ VLM : 'VendorLicenseModel',
+ EP : 'EntitlementPool',
+ FG : 'FeatureGroup',
+ LA : 'LicenseAgreement',
+ VSP : 'VendorSoftwareProduct',
+ LIMIT : 'Limit',
+ VSP_Q : 'VSPQuestionnaire',
+ COMPONENT : 'Component',
+ COMPONENT_Q : 'ComponentQuestionnaire',
+ COMPONENT_DEP : 'ComponentDependencies',
+ COMPUTE_Q : 'ComputeQuestionnaire',
+ COMPUTE : 'Compute',
+ COMPUTE_FLAVOR: 'ComputeFlavor',
+ NIC : 'Nic',
+ NIC_Q : 'NicQuestionnaire',
+ IMAGE : 'Image',
+ IMAGE_Q : 'ImageQuestionnaire',
+ PROCESS : 'Process',
+ DEPLOYMENT_FLAVOR : 'DeploymentFlavor',
+ VENDOR : 'Vendor',
+ NETWORK : 'Network',
+ ORCHESTRATION_TEMPLATE_CANDIDATE : 'OrchestrationTemplateCandidate'
+};
+
+export const dataRules = {
+ general: {
+ id: {
+ rule: rules.SKIP
+ },
+ questionareData: {
+ rule: rules.PARSE,
+ moveFields: true
+ },
+ startDate: {
+ rule: rules.FUNCTION,
+ functionName: 'parseDate'
+ },
+ expiryDate: {
+ rule: rules.FUNCTION,
+ functionName: 'parseDate'
+ },
+ featureGroups: {
+ rule: rules.FUNCTION,
+ functionName: 'reduceList',
+ args: {subField: 'name'}
+ },
+ licenseKeyGroups: {
+ rule: rules.FUNCTION,
+ functionName: 'reduceList',
+ args: {subField: 'name'}
+ },
+ entitlementPools: {
+ rule: rules.FUNCTION,
+ functionName: 'reduceList',
+ args: {subField: 'name'}
+ },
+ },
+ [fileTypes.COMPONENT] : {
+ },
+ [fileTypes.COMPUTE_FLAVOR] : {
+ associatedToDeploymentFlavor: {
+ rule: rules.BOOLEAN,
+ trueValue: 'true'
+ }
+ },
+ [fileTypes.COMPUTE_Q] : {
+ },
+ [fileTypes.COMPONENT_Q] : {
+ isComponentMandatory: {
+ rule: rules.BOOLEAN,
+ trueValue: 'YES',
+ falseValue: 'NO'
+ }
+ },
+ [fileTypes.EP] : {
+ referencingFeatureGroups: {
+ rule: rules.SKIP,
+ functionName: 'getFeatureGroups'
+ },
+ operationalScope: {
+ rule: rules.FUNCTION,
+ functionName: 'processChoices'
+ },
+ },
+ [fileTypes.FG] : {
+ referencingLicenseAgreements: {
+ rule: rules.SKIP,
+ functionName: 'getLicenseAgreements'
+ }
+ },
+ [fileTypes.LA] : {
+ licenseTerm : {
+ rule: rules.FUNCTION,
+ functionName: 'processChoice'
+ }
+ },
+ [fileTypes.LIMIT] : {
+ type: {
+ rule: rules.FUNCTION,
+ functionName: 'getEnumValue',
+ args: {listName: 'limitType'}
+ },
+ unit: {
+ rule: rules.FUNCTION,
+ functionName: 'getEnumValue',
+ args: {listName: 'limitUnit'}
+ }
+ },
+ [fileTypes.LKG] : {
+ operationalScope: {
+ rule: rules.FUNCTION,
+ functionName: 'processChoices'
+ },
+ referencingFeatureGroups: {
+ rule: rules.SKIP,
+ functionName: 'getFeatureGroups'
+ },
+ },
+ [fileTypes.NIC] : {
+ networkId: {
+ rule: rules.SKIP
+ }
+ },
+ [fileTypes.NIC_Q] : {
+ },
+ [fileTypes.PROCESS] : {
+ type: {
+ rule: rules.FUNCTION,
+ functionName: 'getEnumValue',
+ args: {listName: 'processType'}
+ }
+ },
+ [fileTypes.VLM] : {
+ iconRef: {
+ rule: rules.SKIP
+ }
+ },
+ [fileTypes.VSP] : {
+ vendorId: {
+ rule: rules.SKIP
+ },
+ onboardingMethod: {
+ rule: rules.SKIP
+ },
+ validationData: {
+ rule: rules.SKIP
+ },
+ isOldVersion: {
+ rule: rules.SKIP
+ },
+ licensingVersion: {
+ rule: rules.FUNCTION,
+ functionName: 'fetchLMVersion'
+ },
+ category: {
+ rule: rules.FUNCTION,
+ functionName: 'fetchCategory'
+ },
+ subCategory: {
+ rule: rules.SKIP
+ },
+ },
+ [fileTypes.VSP_Q] : {
+ affinityData: {
+ rule: rules.SKIP
+ },
+ storageReplicationAcrossRegion: {
+ rule: rules.BOOLEAN,
+ trueValue: 'true',
+ falseValue: 'false'
+ }
+ },
+ [fileTypes.ORCHESTRATION_TEMPLATE_CANDIDATE] : {
+ modules: {
+ rule: rules.FUNCTION,
+ functionName: 'convertArrayToObject'
+ },
+ },
+};
diff --git a/openecomp-ui/src/sdc-app/common/merge/MergeEditorReducer.js b/openecomp-ui/src/sdc-app/common/merge/MergeEditorReducer.js
new file mode 100644
index 0000000000..6985fcfaca
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/common/merge/MergeEditorReducer.js
@@ -0,0 +1,66 @@
+/*!
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+import {actionTypes} from './MergeEditorConstants.js';
+
+export default (state = [], action) => {
+ switch (action.type) {
+ case actionTypes.LOAD_CONFLICT: {
+ let cdata = {...action.data};
+ // let data = state.conflicts ? {...state.conflicts.data} : {} ;
+ // data[cdata.id] = cdata;
+ let conflicts = state.conflicts ? {...state.conflicts} : {};
+ conflicts[cdata.id] = cdata;
+ return {
+ ...state,
+ conflicts
+ };
+ }
+ case actionTypes.DATA_PROCESSED: {
+ let conflicts = {...state.conflicts};
+ let {data} = action;
+ if (data && data.cid) {
+ let yours = {...conflicts[data.cid].yours};
+ let theirs = {...conflicts[data.cid].theirs};
+ let {yoursField, theirsField} = data;
+ if (yoursField) {
+ yours[yoursField.name] = yoursField.value;
+ conflicts[data.cid].yours = yours;
+ }
+ if (theirsField) {
+ theirs[theirsField.name] = theirsField.value;
+ conflicts[data.cid].theirs = theirs;
+ }
+ }
+ return {
+ ...state,
+ conflicts: {
+ ...conflicts
+ }
+ };
+ }
+ case actionTypes.LOAD_CONFLICTS:
+ let conflictFiles = [];
+ if (action.data) {
+ conflictFiles = [...action.data.conflictInfoList];
+ }
+ return {
+ inMerge: conflictFiles.length > 0,
+ conflictFiles
+ };
+ default:
+ return state;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/common/merge/MergeEditorView.jsx b/openecomp-ui/src/sdc-app/common/merge/MergeEditorView.jsx
new file mode 100644
index 0000000000..34d86419e7
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/common/merge/MergeEditorView.jsx
@@ -0,0 +1,256 @@
+/*!
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+import React from 'react';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import union from 'lodash/union.js';
+import Button from 'sdc-ui/lib/react/Button.js';
+// import Checkbox from 'sdc-ui/lib/react/Checkbox.js';
+import Input from 'nfvo-components/input/validation/Input.jsx';
+import GridSection from 'nfvo-components/grid/GridSection.jsx';
+import GridItem from 'nfvo-components/grid/GridItem.jsx';
+import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js';
+import Radio from 'sdc-ui/lib/react/Radio.js';
+import equal from 'deep-equal';
+import {ResolutionTypes} from './MergeEditorConstants.js';
+
+class ConflictCategory extends React.Component {
+ state = {
+ resolution: ResolutionTypes.YOURS
+ };
+
+ getTitle(conflictType, conflictName) {
+ if (typeof conflictName === 'undefined' || conflictType === conflictName) {
+ return i18n(conflictType);
+ } else {
+ return `${i18n(conflictType)}: ${conflictName}`;
+ }
+ }
+
+ render() {
+ let {collapseExpand, conflict: {id: conflictId, type, name}, isCollapsed, item: {id: itemId, version}, onResolveConflict} = this.props;
+ let {resolution} = this.state;
+ const iconClass = isCollapsed ? 'merge-chevron' : 'merge-chevron right';
+
+ return (
+ <div key={'conflictCategory_' + conflictId} >
+ <GridSection className='conflict-section'>
+ <GridItem >
+ <div className='collapsible-section' onClick={collapseExpand}>
+ <SVGIcon name={isCollapsed ? 'chevronDown' : 'chevronUp'} iconClassName={iconClass} />
+ <div className='conflict-title'>{this.getTitle(type, name)}</div>
+ </div>
+ </GridItem>
+ <GridItem className='yours'>
+ <Radio name={'radio_' + conflictId} checked={resolution === ResolutionTypes.YOURS} value='yours'
+ onChange={() => this.setState({resolution: ResolutionTypes.YOURS})} data-test-id={'radio_' + conflictId + '_yours'} />
+ </GridItem>
+ <GridItem className='theirs'>
+ <Radio name={'radio_' + conflictId} checked={resolution === ResolutionTypes.THEIRS} value='theirs'
+ onChange={() => this.setState({resolution: ResolutionTypes.THEIRS})} data-test-id={'radio_' + conflictId + '_theirs'} /></GridItem>
+ <GridItem className='resolve'>
+ <Button className='conflict-resolve-btn' btnType='outline' color='gray'
+ onClick={() => onResolveConflict({conflictId, resolution, itemId, version})}>
+ {i18n('Resolve')}
+ </Button>
+ </GridItem>
+ </GridSection>
+ <div>
+ {isCollapsed && this.props.children}
+ </div>
+ </div>
+ );
+ }
+
+};
+
+class TextCompare extends React.Component {
+ render() {
+ // let rand = Math.random() * (3000 - 1) + 1;
+ let {yours, theirs, field, type, isObjName, conflictsOnly} = this.props;
+ let typeYours = typeof yours;
+ let typeTheirs = typeof theirs;
+
+ let parsedType = `${type}/${field}`.replace(/\/[0-9]+/g,'/index');
+ let level = type.split('/').length;
+
+ if (typeYours === 'boolean' || typeTheirs === 'boolean') {
+ yours = yours ? i18n('Yes') : i18n('No');
+ theirs = theirs ? i18n('Yes') : i18n('No');
+ }
+
+
+ /*if ((typeYours !== 'string' && typeYours !== 'undefined') || (typeTheirs !== 'string' && typeTheirs !== 'undefined')) {
+ return (<div className='merge-editor-text-field field-error'>{field} cannot be parsed for display</div>);
+ }*/
+ let isDiff = yours !== theirs;
+ if (!isObjName &&
+ ((!isDiff && conflictsOnly) ||
+ (yours === '' && theirs === '') ||
+ (typeYours === 'undefined' && typeTheirs === 'undefined')
+ )
+ ) {
+ return null;
+ }
+
+ return (
+ <GridSection className={isDiff ? 'merge-editor-text-field diff' : 'merge-editor-text-field'}>
+ <GridItem className='field-col grid-col-title' stretch>
+ <div className={`field ${isDiff ? 'diff' : ''} field-name level-${level} ${isObjName ? 'field-object-name' : ''}`}>
+ {i18n(parsedType)}
+ </div>
+ </GridItem>
+ <GridItem className='field-col grid-col-yours' stretch>
+ <div className={`field field-yours ${!yours ? 'empty-field' : ''}`} >{yours || (isObjName ? '' : '━━')}</div>
+ </GridItem>
+ <GridItem className='field-col grid-col-theirs' stretch>
+ <div className={`field field-theirs ${!theirs ? 'empty-field' : ''}`}>{theirs || (isObjName ? '' : '━━')}</div>
+ </GridItem>
+ <GridItem stretch/>
+ </GridSection>
+ );
+ }
+};
+
+class MergeEditorView extends React.Component {
+ state = {
+ collapsingSections: {},
+ conflictsOnly: false
+ };
+
+ render() {
+ let {conflicts, item, conflictFiles, onResolveConflict, currentScreen, resolution} = this.props;
+
+ return (
+ <div className='merge-editor'>
+ {conflictFiles && this.renderConflictTableTitles()}
+ <div className='merge-editor-body'>
+ {conflictFiles && conflictFiles.sort((a, b) => a.type > b.type).map(file => (
+ <ConflictCategory key={'conflict_' + file.id} conflict={file} item={item} isCollapsed={this.state.collapsingSections[file.id]}
+ collapseExpand={()=>{this.updateCollapseState(file.id);}}
+ onResolveConflict={cDetails => onResolveConflict({...cDetails, currentScreen})}>
+ {(conflicts && conflicts[file.id]) &&
+ this.getUnion(conflicts[file.id].yours, conflicts[file.id].theirs).map(field => {
+ return this.renderField(field, file, conflicts[file.id].yours[field], conflicts[file.id].theirs[field], resolution);
+ })}
+ </ConflictCategory>))}
+ </div>
+ </div>);
+ }
+
+ renderConflictTableTitles()
+ {
+ return (<GridSection className='conflict-titles-section'>
+ <GridItem>
+ {i18n('Page')}
+ </GridItem>
+ <GridItem className='yours'>
+ {i18n('Local (Me)')}
+ </GridItem>
+ <GridItem className='theirs'>
+ {i18n('Last Committed')}
+ </GridItem>
+ <GridItem className='resolve'>
+ <Input
+ label={i18n('Show Conflicts Only')}
+ type='checkbox'
+ value={this.state.conflictsOnly}
+ onChange={e => this.setState({conflictsOnly: e}) } />
+ </GridItem>
+ </GridSection>);
+ }
+ // <Checkbox
+ // label={i18n('Show Conflicts Only')}
+ // value={this.state.conflictsOnly}
+ // checked={this.state.conflictsOnly}
+ // onChange={checked => this.setState({conflictsOnly: checked})} />
+
+ renderObjects(yours, theirs, fileType, field, id, resolution) {
+ if (equal(yours, theirs)) {
+ return;
+ }
+ let {conflictsOnly} = this.state;
+ return (
+ <div key={`obj_${fileType}/${field}_${id}`}>
+ <TextCompare field={field} type={fileType} conflictsOnly={conflictsOnly} yours='' theirs='' isObjName resolution={resolution} />
+ <div className='field-objects'>
+ <div>
+ {this.getUnion(yours, theirs).map(key =>
+ this.renderField(
+ key,
+ {type: `${fileType}/${field}`, id},
+ yours && yours[key],
+ theirs && theirs[key]
+ )
+ )}
+ </div>
+ </div>
+ </div>
+ );
+ }
+
+ renderList(yours = [], theirs = [], type, field, id, resolution) {
+ let theirsList = theirs.join(', ');
+ let yoursList = yours.join(', ');
+ let {conflictsOnly} = this.state;
+ return (<TextCompare key={'text_' + id + '_' + field}
+ field={field} type={type} yours={yoursList} theirs={theirsList} conflictsOnly={conflictsOnly} resolution={resolution} />);
+ }
+
+ renderField(field, file, yours, theirs, resolution) {
+ if (yours) {
+ if (Array.isArray(yours)) {
+ return this.renderList(yours, theirs, file.type, field, file.id, resolution);
+ }
+ else if (typeof yours === 'object') {
+ return this.renderObjects(yours, theirs, file.type, field, file.id, resolution);
+ }
+ } else if (theirs) {
+ if (Array.isArray(theirs)) {
+ return this.renderList(yours, theirs, file.type, field, file.id, resolution);
+ }
+ else if (typeof theirs === 'object') {
+ return this.renderObjects(yours, theirs, file.type, field, file.id, resolution);
+ }
+ }
+ let {conflictsOnly} = this.state;
+ return (<TextCompare key={'text_' + file.id + '_' + field} resolution={resolution}
+ field={field} type={file.type} yours={yours} theirs={theirs} conflictsOnly={conflictsOnly} />);
+ }
+
+ getUnion(yours = {},theirs = {}) {
+ let yoursKeys = Object.keys(yours);
+ let theirsKeys = Object.keys(theirs);
+ let myUn = union(yoursKeys, theirsKeys);
+ return myUn;//.sort((a, b) => a > b);
+ }
+
+ updateCollapseState(conflictId) {
+ const {fetchConflict, item: {id: itemId, version}, /*conflicts*/} = this.props;
+ let isCollapsed = this.state.collapsingSections[conflictId];
+ // if (!isCollapsed && !(conflicts && conflictId in conflicts)) {
+ if (!isCollapsed) {
+ fetchConflict({cid: conflictId, itemId, version});
+ }
+ this.setState({
+ collapsingSections: {
+ ...this.state.collapsingSections,
+ [conflictId]: !isCollapsed
+ }
+ });
+ }
+}
+
+export default MergeEditorView;
diff --git a/openecomp-ui/src/sdc-app/common/modal/ModalContentMapper.js b/openecomp-ui/src/sdc-app/common/modal/ModalContentMapper.js
index 8c10beb952..487ada0d88 100644
--- a/openecomp-ui/src/sdc-app/common/modal/ModalContentMapper.js
+++ b/openecomp-ui/src/sdc-app/common/modal/ModalContentMapper.js
@@ -17,12 +17,18 @@
import SoftwareProductCreation from 'sdc-app/onboarding/softwareProduct/creation/SoftwareProductCreation.js';
import LicenseModelCreation from 'sdc-app/onboarding/licenseModel/creation/LicenseModelCreation.js';
import SoftwareProductComponentImageEditor from 'sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageEditor.js';
+import VersionPageCreation from 'sdc-app/onboarding/versionsPage/creation/VersionsPageCreation.js';
import SubmitErrorResponse from 'nfvo-components/SubmitErrorResponse.jsx';
import ComputeFlavorEditor from 'sdc-app/onboarding/softwareProduct/components/compute/computeComponents/computeFlavor/ComputeFlavorEditor.js';
import NICCreation from 'sdc-app/onboarding/softwareProduct/components/network/NICCreation/NICCreation.js';
import SoftwareProductComponentsNICEditor from 'sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js';
import ComponentCreation from 'sdc-app/onboarding/softwareProduct/components/creation/SoftwareProductComponentCreation.js';
import SoftwareProductDeploymentEditor from 'sdc-app/onboarding/softwareProduct/deployment/editor/SoftwareProductDeploymentEditor.js';
+import PermissionsManager from 'sdc-app/onboarding/permissions/PermissionsManager.js';
+import CommitCommentModal from 'nfvo-components/panel/versionController/components/CommitCommentModal.jsx';
+import Tree from 'nfvo-components/tree/Tree.jsx';
+import MergeEditor from 'sdc-app/common/merge/MergeEditor.js';
+import Revisions from 'sdc-app/onboarding/revisions/Revisions.js';
export const modalContentMapper = {
SOFTWARE_PRODUCT_CREATION: 'SOFTWARE_PRODUCT_CREATION',
@@ -33,17 +39,29 @@ export const modalContentMapper = {
NIC_CREATION: 'NIC_CREATION',
COMPONENT_CREATION: 'COMPONENT_CREATION',
SOFTWARE_PRODUCT_COMPONENT_IMAGE_EDITOR : 'SOFTWARE_PRODUCT_COMPONENT_IMAGE_EDITOR',
- DEPLOYMENT_FLAVOR_EDITOR: 'DEPLOYMENT_FLAVOR_EDITOR'
+ DEPLOYMENT_FLAVOR_EDITOR: 'DEPLOYMENT_FLAVOR_EDITOR',
+ MANAGE_PERMISSIONS: 'MANAGE_PERMISSIONS',
+ VERSION_CREATION: 'VERSION_CREATION',
+ COMMIT_COMMENT: 'COMMIT_COMMENT',
+ VERSION_TREE: 'VERSION_TREE',
+ MERGE_EDITOR: 'MERGE_EDITOR',
+ REVISIONS_LIST: 'REVISIONS_LIST'
};
export const modalContentComponents = {
SUMBIT_ERROR_RESPONSE: SubmitErrorResponse,
SOFTWARE_PRODUCT_CREATION: SoftwareProductCreation,
+ VERSION_CREATION: VersionPageCreation,
LICENSE_MODEL_CREATION: LicenseModelCreation,
COMPONENT_COMPUTE_FLAVOR_EDITOR: ComputeFlavorEditor,
NIC_EDITOR: SoftwareProductComponentsNICEditor,
NIC_CREATION: NICCreation,
COMPONENT_CREATION: ComponentCreation,
SOFTWARE_PRODUCT_COMPONENT_IMAGE_EDITOR : SoftwareProductComponentImageEditor,
- DEPLOYMENT_FLAVOR_EDITOR: SoftwareProductDeploymentEditor
+ DEPLOYMENT_FLAVOR_EDITOR: SoftwareProductDeploymentEditor,
+ MANAGE_PERMISSIONS: PermissionsManager,
+ COMMIT_COMMENT: CommitCommentModal,
+ VERSION_TREE: Tree,
+ MERGE_EDITOR: MergeEditor,
+ REVISIONS_LIST: Revisions
};