Add collaboration feature
[sdc.git] / openecomp-ui / src / sdc-app / common / merge / MergeEditorActionHelper.js
1 /*!
2  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 import {actionTypes, rules, dataRules, SyncStates} from './MergeEditorConstants.js';
17 import cloneDeep from 'lodash/cloneDeep.js';
18 import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js';
19 import Configuration from 'sdc-app/config/Configuration.js';
20 import ItemsHelper from '../../common/helpers/ItemsHelper.js';
21 import {modalContentMapper} from 'sdc-app/common/modal/ModalContentMapper.js';
22 import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js';
23 import i18n from 'nfvo-utils/i18n/i18n.js';
24 import {optionsInputValues as epOptionsValues} from 'sdc-app/onboarding/licenseModel/entitlementPools/EntitlementPoolsConstants.js';
25 import {optionsInputValues as laOptionsValues} from 'sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementConstants.js';
26 import {optionsInputValues as processOptionValues} from 'sdc-app/onboarding/softwareProduct/components/processes/SoftwareProductComponentProcessesConstants.js';
27 import {selectValues as limitSelectValues} from 'sdc-app/onboarding/licenseModel/limits/LimitEditorConstants.js';
28 import FeatureGroupsActionHelper from 'sdc-app/onboarding/licenseModel/featureGroups/FeatureGroupsActionHelper.js';
29 import LicenseAgreementActionHelper from 'sdc-app/onboarding/licenseModel/licenseAgreement/LicenseAgreementActionHelper.js';
30 import moment from 'moment';
31 import {DATE_FORMAT} from 'sdc-app/onboarding/OnboardingConstants.js';
32 import ScreensHelper from 'sdc-app/common/helpers/ScreensHelper.js';
33
34 function softwareProductCategoriesUrl() {
35         const restATTPrefix = Configuration.get('restATTPrefix');
36         return `${restATTPrefix}/v1/categories/resources/`;
37 }
38
39 function versionUrl(itemId, versionId) {
40         const restPrefix = Configuration.get('restPrefix');
41         return `${restPrefix}/v1.0/items/${itemId}/versions/${versionId}`;
42 }
43
44 function baseUrl(itemId, version, conflictId) {
45         const versionId = version.id;
46         const restPrefix = Configuration.get('restPrefix');
47         let baseUrl = `${restPrefix}/v1.0/items/${itemId}/versions/${versionId}/conflicts`;
48         return conflictId ? `${baseUrl}/${conflictId}` : baseUrl;
49 }
50
51 function fetchConflicts({itemId, version}) {
52         return RestAPIUtil.fetch(`${baseUrl(itemId, version)}`);
53 }
54
55 function fetchConflictById({itemId, version, cid}) {
56         return RestAPIUtil.fetch(`${baseUrl(itemId, version, cid)}`);
57 }
58
59 function resolveConflict({itemId, version, conflictId, resolution}) {
60         return RestAPIUtil.put(`${baseUrl(itemId, version, conflictId)}`, {resolution});
61 }
62
63 function fetchCategories() {
64         return RestAPIUtil.fetch(softwareProductCategoriesUrl());
65 }
66
67 function fetchVersion({vendorId, licensingVersion}) {
68         return RestAPIUtil.fetch(versionUrl(vendorId, licensingVersion));
69 }
70
71 function createCategoryStr(data, {categories}) {
72
73         let {category, subCategory} = data;
74         let foundCat = categories.find(element => element.uniqueId === category);
75         if (!foundCat) { return ''; }
76
77         let catName = foundCat.name;
78         let foundSub =  foundCat.subcategories.find(element => element.uniqueId === subCategory);
79         if (!foundSub) { return `${catName}`; }
80
81         let subcatName = foundSub.name;
82         return `${catName} - ${subcatName}`;
83
84 }
85
86 function getEnumValues({enums, list}) {
87
88         if (!list) { return ''; }
89         return list.map(item => enums.find(el => el.enum === item).title);
90
91 }
92
93 const MergeEditorActionHelper = {
94
95         analyzeSyncResult(dispatch, {itemId, version}) {
96                 return ItemsHelper.checkItemStatus(dispatch, {itemId, versionId: version.id}).then((response) => {
97                         let inMerge = response && response.state && response.state.synchronizationState === SyncStates.MERGE;
98                         if (inMerge) {
99                                 MergeEditorActionHelper.fetchConflicts(dispatch, {itemId, version}).then(() =>
100                                         dispatch({
101                                                 type: modalActionTypes.GLOBAL_MODAL_SHOW,
102                                                 data: {
103                                                         modalComponentName: modalContentMapper.MERGE_EDITOR,
104                                                         modalClassName: 'merge-editor-modal',
105                                                         title: `${i18n('Merge Required')} - ${version.description}`,
106                                                         onDeclined: () => {
107                                                                 dispatch({
108                                                                         type: modalActionTypes.GLOBAL_MODAL_CLOSE
109                                                                 });
110                                                         },
111                                                         modalComponentProps: {
112                                                                 size: 'lg',
113                                                                 type: 'default'
114                                                         }
115                                                 }
116                                         })
117                                 );
118                         }
119                         return Promise.resolve({updatedVersion: response, inMerge, isDirty: response.state.dirty});
120                 });
121         },
122
123         fetchConflicts(dispatch, {itemId, version}) {
124                 return fetchConflicts({itemId, version}).then(
125                         (data) => {
126                                 dispatch({
127                                         type: actionTypes.LOAD_CONFLICTS,
128                                         data
129                                 });
130                                 return data;
131                         }
132                 );
133         },
134
135         fetchConflict(dispatch, {itemId, version, cid}) {
136                 fetchConflictById({itemId, version, cid}).then(
137                         (data) => {
138                                 let newData = {};
139                                 newData = MergeEditorActionHelper.processConflict(dispatch, {conflict: data, itemId, cid, version});
140                                 dispatch({
141                                         type: actionTypes.LOAD_CONFLICT,
142                                         data: newData
143                                 });
144                         }
145                 );
146         },
147
148         resolveConflict(dispatch, {itemId, version, conflictId, resolution, currentScreen}) {
149                 resolveConflict({itemId, version, conflictId, resolution}).then(() => {
150                         MergeEditorActionHelper.fetchConflicts(dispatch, {itemId, version}).then(conflicts => {
151                                 if(conflicts.conflictInfoList && conflicts.conflictInfoList.length === 0) {
152                                         dispatch({
153                                                 type: modalActionTypes.GLOBAL_MODAL_CLOSE
154                                         });
155                                         ScreensHelper.loadLandingScreen(dispatch, {previousScreenName: currentScreen.screen, props: currentScreen.props});
156                                         ItemsHelper.checkItemStatus(dispatch, {itemId, versionId: version.id});
157                                 }
158                         });
159                 });
160         },
161
162         createConflictObject(data, {cid, conflict, dispatch, itemId, version, isYours}) {
163
164                 let newData = {};
165
166                 for (let key in data) {
167
168                         if (data.hasOwnProperty(key)) {
169                                 let value = data[key];
170                                 let fieldRule = dataRules[conflict.type] && dataRules[conflict.type][key] || dataRules.general[key];
171
172                                 if (fieldRule) {
173                                         switch (fieldRule.rule) {
174
175                                                 case rules.SKIP:
176                                                         break;
177
178                                                 case rules.BOOLEAN:
179                                                         let {trueValue, falseValue} = fieldRule;
180                                                         newData[key] = value === trueValue ? true : value === falseValue ? false : undefined;
181                                                         break;
182
183                                                 case rules.PARSE:
184                                                         let {moveFields, subFields} = fieldRule;
185                                                         if (moveFields) {
186                                                                 let fields = subFields || Object.keys(value);
187                                                                 fields.forEach(field => {
188                                                                         newData[field] = MergeEditorActionHelper.createConflictObject(
189                                                                                 value[field], {cid, conflict, dispatch, itemId, version, isYours}
190                                                                         );
191                                                                 });
192                                                         } else {
193                                                                 newData[key] = MergeEditorActionHelper.createConflictObject(
194                                                                         value, {cid, conflict, dispatch, itemId, version, isYours}
195                                                                 );
196                                                         }
197                                                         break;
198
199                                                 case rules.FUNCTION:
200                                                         let {args, functionName} = fieldRule;
201                                                         newData[key] = MergeEditorActionHelper[functionName](data, {
202                                                                 cid, conflict, dispatch, version, fieldName: key, isYours, itemId, args
203                                                         });
204                                                         break;
205
206                                                 default:
207                                                         newData[key] = value;
208                                                         break;
209                                         }
210
211                                 } else {
212                                         newData[key] = value;
213
214                                 }
215                         }
216                 }
217
218                 return newData;
219
220         },
221
222         getNamesFromIDs(data, {version, cid, dispatch, itemId, fieldName, isYours, args}) {
223
224                 let idList = data[fieldName] || [];
225                 let {fetchFunction, fetchField} = args;
226
227                 let promises = idList.map(id =>
228                         new Promise(resolve =>
229                                 MergeEditorActionHelper[fetchFunction](
230                                         dispatch, {licenseModelId: itemId, [fetchField]: id, version}
231                                 ).then(item => resolve(item.name))
232                         )
233                 );
234
235                 Promise.all(promises).then(fetchedItems => {
236                         let yoursOrTheirs = isYours ? 'yoursField' : 'theirsField';
237                         dispatch({
238                                 type: actionTypes.DATA_PROCESSED,
239                                 data: {
240                                         cid,
241                                         [yoursOrTheirs]: { name: fieldName, value: fetchedItems }
242                                 }
243                         });
244                 });
245
246                 return idList;
247
248         },
249
250         getFeatureGroups(data, {version, cid, dispatch, itemId, fieldName, isYours}) {
251
252                 let featureGroups = data[fieldName] || [];
253                 if (!(featureGroups instanceof Array)) {
254                         featureGroups = [featureGroups];
255                 }
256
257                 let promises = featureGroups.map(featureGroupId =>
258                         new Promise(resolve =>
259                                 FeatureGroupsActionHelper.fetchFeatureGroup(
260                                         dispatch, {licenseModelId: itemId, featureGroupId, version}
261                                 ).then(featureGroup => resolve(featureGroup.name))
262                                 .catch(reason => console.log(`getFeatureGroups Promise rejected ('${reason}')`))
263                         )
264                 );
265
266                 Promise.all(promises).then(fetchedGroups => {
267                         let yoursOrTheirs = isYours ? 'yoursField' : 'theirsField';
268                         dispatch({
269                                 type: actionTypes.DATA_PROCESSED,
270                                 data: {
271                                         cid,
272                                         [yoursOrTheirs]: { name: fieldName, value: fetchedGroups }
273                                 }
274                         });
275                 });
276
277                 return featureGroups;
278
279         },
280
281         getLicenseAgreements(data, {version, cid, dispatch, itemId, fieldName, isYours}) {
282
283                 let licenseAgreements = data[fieldName] || [];
284                 if (!(licenseAgreements instanceof Array)) {
285                         licenseAgreements = [licenseAgreements];
286                 }
287
288                 let promises = licenseAgreements.map(licenseAgreementId =>
289                         new Promise(resolve =>
290                                 LicenseAgreementActionHelper.fetchLicenseAgreement(
291                                         dispatch, {licenseModelId: itemId, licenseAgreementId, version}
292                                 ).then(licenseAgreement => resolve(licenseAgreement.name))
293                                 .catch(reason => console.log(`getLicenseAgreements Promise rejected ('${reason}')`))
294                         )
295                 );
296
297                 Promise.all(promises).then(fetchedAgreements => {
298                         let yoursOrTheirs = isYours ? 'yoursField' : 'theirsField';
299                         dispatch({
300                                 type: actionTypes.DATA_PROCESSED,
301                                 data: {
302                                         cid,
303                                         [yoursOrTheirs]: { name: fieldName, value: fetchedAgreements }
304                                 }
305                         });
306                 });
307
308                 return licenseAgreements;
309
310         },
311
312         processConflict(dispatch, {conflict, cid, version, itemId,}) {
313
314                 let {id, type, yours, theirs} = conflict;
315
316                 let newYours = MergeEditorActionHelper.createConflictObject(
317                         cloneDeep(yours), {cid, conflict, dispatch, itemId, version, isYours: true}
318                 );
319                 let newTheirs = MergeEditorActionHelper.createConflictObject(
320                         cloneDeep(theirs), {cid, conflict, dispatch, itemId, version, isYours: false}
321                 );
322
323                 return {
324                         id,
325                         type,
326                         yours: newYours,
327                         theirs: newTheirs
328                 };
329
330         },
331
332         reduceList(data, {fieldName, args}) {
333
334                 let {subField} = args;
335                 return data[fieldName].map(el => el[subField]);
336
337         },
338
339         getEnumList({fieldName}) {
340
341                 const enumLists = {
342                         'licenseTerm': laOptionsValues.LICENSE_MODEL_TYPE,
343                         'operationalScope': epOptionsValues.OPERATIONAL_SCOPE,
344                         'processType': processOptionValues.PROCESS_TYPE,
345                         'limitType': [
346                                 {title: 'Service Provider', enum: 'ServiceProvider'},
347                                 {title: 'Vendor', enum: 'Vendor'}
348                         ],
349                         'limitUnit': limitSelectValues.UNIT
350                 };
351
352                 return enumLists[fieldName];
353
354         },
355
356         getEnumValue(data, {fieldName, args = {}}) {
357
358                 let value = data[fieldName];
359                 let enumValues = MergeEditorActionHelper.getEnumList({fieldName: args.listName || fieldName});
360                 let enumValue = enumValues.find(el => el.enum === value);
361
362                 return enumValue && enumValue.title || value;
363
364         },
365
366         processChoice(data, {fieldName, args = {}}) {
367
368                 let value = data[fieldName];
369                 let enumValues = MergeEditorActionHelper.getEnumList({fieldName: args.listName || fieldName});
370                 let newValue = value.other || enumValues && enumValues.find(el => el.enum === value.choice).title || value.choice;
371
372                 return newValue;
373
374         },
375
376         processChoices(data, {fieldName, args = {}}) {
377
378                 let value = data[fieldName];
379                 let enumValues = MergeEditorActionHelper.getEnumList({fieldName: args.listName || fieldName});
380                 let newValue = value.other || getEnumValues({enums: enumValues, list: value.choices}) || value.choices;
381
382                 return newValue;
383
384         },
385
386         convertArrayToObject(data, {fieldName}) {
387                 let value = data[fieldName];
388                 let newValue = {};
389                 value.forEach((el, index) => {
390                         newValue[index] = el;
391                 });
392                 return newValue;
393         },
394
395         fetchCategory(data, {cid, isYours, fieldName, dispatch}) {
396
397                 fetchCategories().then((categories) => {
398                         let value = createCategoryStr(data, {categories});
399                         let yoursOrTheirs = isYours ? 'yoursField' : 'theirsField';
400
401                         dispatch({
402                                 type: actionTypes.DATA_PROCESSED,
403                                 data: {
404                                         cid,
405                                         [yoursOrTheirs]: { name: fieldName, value }
406                                 }
407                         });
408
409                 });
410         },
411
412         fetchLMVersion(data, {cid, dispatch, isYours}) {
413
414                 let {licensingVersion, vendorId} = data;
415                 let yoursOrTheirs = isYours ? 'yoursField' : 'theirsField';
416
417                 if (licensingVersion) {
418                         fetchVersion({licensingVersion, vendorId}).then(response => {
419                                 dispatch({
420                                         type: actionTypes.DATA_PROCESSED,
421                                         data: {
422                                                 cid,
423                                                 [yoursOrTheirs]: {
424                                                         name: 'licensingVersion',
425                                                         value: response.name
426                                                 }
427                                         }
428                                 });
429                         });
430                 }
431
432         },
433
434         parseDate(data, {fieldName}) {
435
436                 let date = data[fieldName];
437                 return date && moment(date, DATE_FORMAT).format(DATE_FORMAT);
438
439         }
440
441 };
442
443 export default MergeEditorActionHelper;