347d328193a50eda0c3b88cd32b185bb95c9ce89
[sdc.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.openecomp.sdc.versioning.impl;
22
23 import org.openecomp.sdc.common.errors.CoreException;
24 import org.openecomp.sdc.common.errors.ErrorCategory;
25 import org.openecomp.sdc.common.errors.ErrorCode;
26 import org.openecomp.sdc.datatypes.error.ErrorLevel;
27 import org.openecomp.sdc.logging.api.Logger;
28 import org.openecomp.sdc.logging.api.LoggerFactory;
29 import org.openecomp.sdc.logging.context.impl.MdcDataDebugMessage;
30 import org.openecomp.sdc.logging.context.impl.MdcDataErrorMessage;
31 import org.openecomp.sdc.logging.types.LoggerConstants;
32 import org.openecomp.sdc.logging.types.LoggerErrorCode;
33 import org.openecomp.sdc.logging.types.LoggerErrorDescription;
34 import org.openecomp.sdc.logging.types.LoggerTragetServiceName;
35 import org.openecomp.sdc.versioning.ItemManager;
36 import org.openecomp.sdc.versioning.VersionCalculator;
37 import org.openecomp.sdc.versioning.VersioningManager;
38 import org.openecomp.sdc.versioning.dao.VersionDao;
39 import org.openecomp.sdc.versioning.dao.VersionInfoDao;
40 import org.openecomp.sdc.versioning.dao.VersionInfoDeletedDao;
41 import org.openecomp.sdc.versioning.dao.VersionableEntityDaoFactory;
42 import org.openecomp.sdc.versioning.dao.types.Revision;
43 import org.openecomp.sdc.versioning.dao.types.SynchronizationState;
44 import org.openecomp.sdc.versioning.dao.types.UserCandidateVersion;
45 import org.openecomp.sdc.versioning.dao.types.Version;
46 import org.openecomp.sdc.versioning.dao.types.VersionInfoDeletedEntity;
47 import org.openecomp.sdc.versioning.dao.types.VersionInfoEntity;
48 import org.openecomp.sdc.versioning.dao.types.VersionStatus;
49 import org.openecomp.sdc.versioning.errors.CheckinOnEntityLockedByOtherErrorBuilder;
50 import org.openecomp.sdc.versioning.errors.CheckinOnUnlockedEntityErrorBuilder;
51 import org.openecomp.sdc.versioning.errors.CheckoutOnLockedEntityErrorBuilder;
52 import org.openecomp.sdc.versioning.errors.DeleteOnLockedEntityErrorBuilder;
53 import org.openecomp.sdc.versioning.errors.EditOnEntityLockedByOtherErrorBuilder;
54 import org.openecomp.sdc.versioning.errors.EditOnUnlockedEntityErrorBuilder;
55 import org.openecomp.sdc.versioning.errors.EntityAlreadyExistErrorBuilder;
56 import org.openecomp.sdc.versioning.errors.EntityAlreadyFinalizedErrorBuilder;
57 import org.openecomp.sdc.versioning.errors.EntityNotExistErrorBuilder;
58 import org.openecomp.sdc.versioning.errors.SubmitLockedEntityNotAllowedErrorBuilder;
59 import org.openecomp.sdc.versioning.errors.UndoCheckoutOnEntityLockedByOtherErrorBuilder;
60 import org.openecomp.sdc.versioning.errors.UndoCheckoutOnUnlockedEntityErrorBuilder;
61 import org.openecomp.sdc.versioning.types.VersionCreationMethod;
62 import org.openecomp.sdc.versioning.types.VersionInfo;
63 import org.openecomp.sdc.versioning.types.VersionableEntityAction;
64 import org.openecomp.sdc.versioning.types.VersionableEntityMetadata;
65 import org.slf4j.MDC;
66
67 import java.util.Collection;
68 import java.util.HashMap;
69 import java.util.HashSet;
70 import java.util.List;
71 import java.util.Map;
72 import java.util.Set;
73 import java.util.stream.Collectors;
74
75 public class VersioningManagerImpl implements VersioningManager {
76   private static final Logger LOGGER = LoggerFactory.getLogger(VersioningManagerImpl.class);
77   private static final Version INITIAL_ACTIVE_VERSION = new Version(0, 0);
78   private static final MdcDataDebugMessage MDC_DATA_DEBUG_MESSAGE = new MdcDataDebugMessage();
79   private static final Map<String, Set<VersionableEntityMetadata>> VERSIONABLE_ENTITIES =
80       new HashMap<>();
81
82   private final VersionInfoDao versionInfoDao;
83   private final VersionInfoDeletedDao versionInfoDeletedDao;
84   private VersionDao versionDao;
85   private VersionCalculator versionCalculator;
86   private ItemManager itemManager;
87
88   public VersioningManagerImpl(VersionInfoDao versionInfoDao,
89                                VersionInfoDeletedDao versionInfoDeletedDao,
90                                VersionDao versionDao,
91                                VersionCalculator versionCalculator,
92                                ItemManager itemManager) {
93     this.versionInfoDao = versionInfoDao;
94     this.versionInfoDeletedDao = versionInfoDeletedDao;
95     this.versionDao = versionDao;
96     this.versionCalculator = versionCalculator;
97     this.itemManager = itemManager;
98   }
99
100   private static VersionInfo getVersionInfo(VersionInfoEntity versionInfoEntity, String user,
101                                             VersionableEntityAction action) {
102     return getVersionInfo(versionInfoEntity.getEntityId(),
103         versionInfoEntity.getEntityType(),
104         versionInfoEntity.getActiveVersion(),
105         versionInfoEntity.getCandidate(),
106         versionInfoEntity.getStatus(),
107         versionInfoEntity.getLatestFinalVersion(),
108         versionInfoEntity.getViewableVersions(),
109         action,
110         user);
111   }
112
113   private static VersionInfo getVersionInfo(VersionInfoDeletedEntity versionInfoEntity, String user,
114                                             VersionableEntityAction action) {
115     return getVersionInfo(versionInfoEntity.getEntityId(),
116         versionInfoEntity.getEntityType(),
117         versionInfoEntity.getActiveVersion(),
118         versionInfoEntity.getCandidate(),
119         versionInfoEntity.getStatus(),
120         versionInfoEntity.getLatestFinalVersion(),
121         versionInfoEntity.getViewableVersions(),
122         action,
123         user);
124   }
125
126   private static VersionInfo getVersionInfo(String entityId, String entityType, Version activeVer,
127                                             UserCandidateVersion candidate, VersionStatus status,
128                                             Version latestFinalVersion,
129                                             Set<Version> viewableVersions,
130                                             VersionableEntityAction action, String user) {
131
132
133     MDC_DATA_DEBUG_MESSAGE.debugEntryMessage("entity Id", entityId);
134
135     Version activeVersion;
136
137     if (action == VersionableEntityAction.Write) {
138       if (candidate != null) {
139         if (user.equals(candidate.getUser())) {
140           activeVersion = candidate.getVersion();
141         } else {
142           MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
143               LoggerTragetServiceName.GET_VERSION_INFO, ErrorLevel.ERROR.name(),
144               LoggerErrorCode.PERMISSION_ERROR.getErrorCode(), "Can't get entity version info");
145           throw new CoreException(
146               new EditOnEntityLockedByOtherErrorBuilder(entityType, entityId, candidate.getUser())
147                   .build());
148         }
149       } else {
150         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
151             LoggerTragetServiceName.GET_VERSION_INFO, ErrorLevel.ERROR.name(),
152             LoggerErrorCode.PERMISSION_ERROR.getErrorCode(), "Can't get entity version info");
153         throw new CoreException(new EditOnUnlockedEntityErrorBuilder(entityType, entityId).build());
154       }
155     } else {
156       if (candidate != null && user.equals(candidate.getUser())) {
157         activeVersion = candidate.getVersion();
158       } else {
159         activeVersion = activeVer;
160       }
161     }
162
163     VersionInfo versionInfo = new VersionInfo();
164     versionInfo.setStatus(status);
165     activeVersion.setStatus(status);
166     if (latestFinalVersion != null) {
167       latestFinalVersion.setStatus(status);
168     }
169     if (viewableVersions != null) {
170       viewableVersions.forEach(version -> version.setStatus(status));
171     }
172     versionInfo.setActiveVersion(activeVersion);
173     versionInfo.setLatestFinalVersion(latestFinalVersion);
174     versionInfo.setViewableVersions(toSortedList(viewableVersions));
175     versionInfo.setFinalVersions(getFinalVersions(viewableVersions));
176     if (candidate != null) {
177       candidate.getVersion().setStatus(status);
178       versionInfo.setLockingUser(candidate.getUser());
179       if (user.equals(candidate.getUser())) {
180         versionInfo.getViewableVersions().add(candidate.getVersion());
181       }
182     }
183
184     MDC_DATA_DEBUG_MESSAGE.debugExitMessage("entity Id", entityId);
185     return versionInfo;
186   }
187
188   private static List<Version> toSortedList(
189       Set<Version> versions) { // changing the Set to List in DB will require migration...
190     return versions.stream().sorted((o1, o2) -> o1.getMajor() > o2.getMajor() ? 1
191         : o1.getMajor() == o2.getMajor() ? (o1.getMinor() > o2.getMinor() ? 1
192             : o1.getMinor() == o2.getMinor() ? 0 : -1) : -1).collect(Collectors.toList());
193   }
194
195   private static List<Version> getFinalVersions(Set<Version> versions) {
196     return versions.stream().filter(Version::isFinal).collect(Collectors.toList());
197   }
198
199   @Override
200   public void register(String entityType, VersionableEntityMetadata entityMetadata) {
201     Set<VersionableEntityMetadata> entitiesMetadata =
202         VERSIONABLE_ENTITIES.computeIfAbsent(entityType, k -> new HashSet<>());
203     entitiesMetadata.add(entityMetadata);
204   }
205
206   @Override
207   public Version create(String entityType, String entityId, String user) {
208     VersionInfoEntity
209         versionInfoEntity = versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
210     if (versionInfoEntity != null) {
211       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
212           LoggerTragetServiceName.CREATE_ENTITY, ErrorLevel.ERROR.name(),
213           LoggerErrorCode.DATA_ERROR.getErrorCode(), "Can't create versionable entity");
214       throw new CoreException(new EntityAlreadyExistErrorBuilder(entityType, entityId).build());
215     }
216
217     versionInfoEntity = new VersionInfoEntity(entityType, entityId);
218     versionInfoEntity.setActiveVersion(INITIAL_ACTIVE_VERSION);
219     markAsCheckedOut(versionInfoEntity, user);
220     versionInfoDao.create(versionInfoEntity);
221
222     return versionInfoEntity.getCandidate().getVersion();
223   }
224
225   @Override
226   public void delete(String entityType, String entityId, String user) {
227     VersionInfoEntity versionInfoEntity =
228         versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
229     if (versionInfoEntity == null) {
230       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
231           LoggerTragetServiceName.DELETE_ENTITY, ErrorLevel.ERROR.name(),
232           LoggerErrorCode.DATA_ERROR.getErrorCode(), "Can't delete versionable entity");
233       throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
234     }
235
236     switch (versionInfoEntity.getStatus()) {
237       case Locked:
238         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
239             LoggerTragetServiceName.DELETE_ENTITY, ErrorLevel.ERROR.name(),
240             LoggerErrorCode.PERMISSION_ERROR.getErrorCode(), "Can't delete versionable entity");
241         throw new CoreException(new DeleteOnLockedEntityErrorBuilder(entityType, entityId,
242             versionInfoEntity.getCandidate().getUser()).build());
243       default:
244         //do nothing
245         break;
246     }
247
248     doDelete(versionInfoEntity);
249   }
250
251   @Override
252   public void undoDelete(String entityType, String entityId, String user) {
253     VersionInfoDeletedEntity versionInfoDeletedEntity =
254         versionInfoDeletedDao.get(new VersionInfoDeletedEntity(entityType, entityId));
255     if (versionInfoDeletedEntity == null) {
256       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
257           LoggerTragetServiceName.UNDO_DELETE_ENTITY, ErrorLevel.ERROR.name(),
258           LoggerErrorCode.DATA_ERROR.getErrorCode(), "Can't undo delete for versionable entity");
259       throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
260     }
261
262     doUndoDelete(versionInfoDeletedEntity);
263   }
264
265   @Override
266   public Version checkout(String entityType, String entityId, String user) {
267     VersionInfoEntity versionInfoEntity =
268         versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
269     MDC.put(LoggerConstants.TARGET_SERVICE_NAME, LoggerTragetServiceName.CHECKOUT_ENTITY);
270     if (versionInfoEntity == null) {
271       MDC.put(LoggerConstants.ERROR_CATEGORY, ErrorLevel.ERROR.name());
272       MDC.put(LoggerConstants.TARGET_ENTITY, LoggerConstants.TARGET_ENTITY_DB);
273       MDC.put(LoggerConstants.ERROR_DESCRIPTION, LoggerErrorDescription.CHECKOUT_ENTITY);
274       throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
275     }
276
277     Version checkoutVersion = null;
278     switch (versionInfoEntity.getStatus()) {
279       case Locked:
280         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
281             LoggerTragetServiceName.CHECKOUT_ENTITY, ErrorLevel.ERROR.name(),
282             LoggerErrorCode.PERMISSION_ERROR.getErrorCode(), "Can't checkout versionable entity");
283         throw new CoreException(new CheckoutOnLockedEntityErrorBuilder(entityType, entityId,
284             versionInfoEntity.getCandidate().getUser()).build());
285       case Certified:
286       case Draft:
287         checkoutVersion = doCheckout(versionInfoEntity, user);
288         break;
289       default:
290         //do nothing
291         break;
292     }
293
294     return checkoutVersion;
295   }
296
297   @Override
298   public Version undoCheckout(String entityType, String entityId, String user) {
299     VersionInfoEntity versionInfoEntity =
300         versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
301     if (versionInfoEntity == null) {
302       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
303           LoggerTragetServiceName.UNDO_CHECKOUT_ENTITY, ErrorLevel.ERROR.name(),
304           LoggerErrorCode.DATA_ERROR.getErrorCode(), "Can't undo checkout for versionable entity");
305       throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
306     }
307
308     Version activeVersion = null;
309     switch (versionInfoEntity.getStatus()) {
310       case Locked:
311         if (!user.equals(versionInfoEntity.getCandidate().getUser())) {
312           MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
313               LoggerTragetServiceName.UNDO_CHECKOUT_ENTITY, ErrorLevel.ERROR.name(),
314               LoggerErrorCode.PERMISSION_ERROR.getErrorCode(),
315               "Can't undo checkout for versionable entity");
316           throw new CoreException(
317               new UndoCheckoutOnEntityLockedByOtherErrorBuilder(entityType, entityId,
318                   versionInfoEntity.getCandidate().getUser()).build());
319         }
320         activeVersion = undoCheckout(versionInfoEntity);
321         break;
322       case Certified:
323       case Draft:
324         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
325             LoggerTragetServiceName.UNDO_CHECKOUT_ENTITY, ErrorLevel.ERROR.name(),
326             LoggerErrorCode.PERMISSION_ERROR.getErrorCode(),
327             "Can't undo checkout for versionable entity");
328         throw new CoreException(
329             new UndoCheckoutOnUnlockedEntityErrorBuilder(entityType, entityId).build());
330       default:
331         //do nothing
332         break;
333     }
334
335     return activeVersion;
336   }
337
338   private Version undoCheckout(VersionInfoEntity versionInfoEntity) {
339     deleteVersionFromEntity(versionInfoEntity.getEntityType(), versionInfoEntity.getEntityId(),
340         versionInfoEntity.getCandidate().getVersion(), versionInfoEntity.getActiveVersion());
341
342     versionInfoEntity
343         .setStatus(versionInfoEntity.getActiveVersion().isFinal() ? VersionStatus.Certified
344             : VersionStatus.Draft);
345     versionInfoEntity.setCandidate(null);
346     versionInfoDao.update(versionInfoEntity);
347     return versionInfoEntity.getActiveVersion();
348   }
349
350   @Override
351   public Version checkin(String entityType, String entityId, String user,
352                          String checkinDescription) {
353     VersionInfoEntity versionInfoEntity =
354         versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
355     if (versionInfoEntity == null) {
356       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
357           LoggerTragetServiceName.CHECKIN_ENTITY, ErrorLevel.ERROR.name(),
358           LoggerErrorCode.DATA_ERROR.getErrorCode(), "Can't checkin versionable entity");
359       throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
360     }
361
362     Version checkedInVersion = null;
363     switch (versionInfoEntity.getStatus()) {
364       case Draft:
365       case Certified:
366         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
367             LoggerTragetServiceName.CHECKIN_ENTITY, ErrorLevel.ERROR.name(),
368             LoggerErrorCode.PERMISSION_ERROR.getErrorCode(), "Can't checkin versionable entity");
369         throw new CoreException(
370             new CheckinOnUnlockedEntityErrorBuilder(entityType, entityId).build());
371       case Locked:
372         if (!user.equals(versionInfoEntity.getCandidate().getUser())) {
373           MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
374               LoggerTragetServiceName.CHECKIN_ENTITY, ErrorLevel.ERROR.name(),
375               LoggerErrorCode.PERMISSION_ERROR.getErrorCode(), "Can't checkin versionable entity");
376           throw new CoreException(new CheckinOnEntityLockedByOtherErrorBuilder(entityType, entityId,
377               versionInfoEntity.getCandidate().getUser()).build());
378         }
379         checkedInVersion = doCheckin(versionInfoEntity, checkinDescription);
380         break;
381       default:
382         //do nothing
383         break;
384     }
385
386     return checkedInVersion;
387   }
388
389   @Override
390   public Version submit(String entityType, String entityId, String user, String submitDescription) {
391     VersionInfoEntity versionInfoEntity =
392         versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
393     if (versionInfoEntity == null) {
394       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
395           LoggerTragetServiceName.SUBMIT_ENTITY, ErrorLevel.ERROR.name(),
396           LoggerErrorCode.DATA_ERROR.getErrorCode(), "Can't submit versionable entity");
397       throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
398     }
399
400     Version submitVersion = null;
401     switch (versionInfoEntity.getStatus()) {
402       case Certified:
403         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
404             LoggerTragetServiceName.SUBMIT_ENTITY, ErrorLevel.ERROR.name(),
405             LoggerErrorCode.DATA_ERROR.getErrorCode(), "Can't submit versionable entity");
406         throw new CoreException(
407             new EntityAlreadyFinalizedErrorBuilder(entityType, entityId).build());
408       case Locked:
409         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
410             LoggerTragetServiceName.SUBMIT_ENTITY, ErrorLevel.ERROR.name(),
411             LoggerErrorCode.PERMISSION_ERROR.getErrorCode(), "Can't submit versionable entity");
412         throw new CoreException(new SubmitLockedEntityNotAllowedErrorBuilder(entityType, entityId,
413             versionInfoEntity.getCandidate().getUser()).build());
414       case Draft:
415         submitVersion = doSubmit(versionInfoEntity, user, submitDescription);
416         break;
417       default:
418         //do nothing
419         break;
420     }
421
422     return submitVersion;
423   }
424
425   @Override
426   public VersionInfo getEntityVersionInfo(String entityType, String entityId, String user,
427                                           VersionableEntityAction action) {
428     VersionInfoEntity versionInfoEntity =
429         versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
430     if (versionInfoEntity == null) {
431       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
432           LoggerTragetServiceName.GET_VERSION_INFO, ErrorLevel.ERROR.name(),
433           LoggerErrorCode.DATA_ERROR.getErrorCode(), "Can't get entity version info");
434       throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
435     }
436     return getVersionInfo(versionInfoEntity, user, action);
437   }
438
439   @Override
440   public Map<String, VersionInfo> listEntitiesVersionInfo(String entityType, String user,
441                                                           VersionableEntityAction action) {
442     Collection<VersionInfoEntity> versionInfoEntities =
443         versionInfoDao.list(new VersionInfoEntity(entityType, null));
444     Map<String, VersionInfo> activeVersions = new HashMap<>();
445     for (VersionInfoEntity versionInfoEntity : versionInfoEntities) {
446       activeVersions
447           .put(versionInfoEntity.getEntityId(), getVersionInfo(versionInfoEntity, user, action));
448     }
449     return activeVersions;
450   }
451
452   @Override
453   public Map<String, VersionInfo> listDeletedEntitiesVersionInfo(String entityType, String user,
454                                                                  VersionableEntityAction action) {
455     Collection<VersionInfoDeletedEntity> versionInfoDeletedEntities =
456         versionInfoDeletedDao.list(new VersionInfoDeletedEntity(entityType, null));
457     Map<String, VersionInfo> activeVersions = new HashMap<>();
458
459
460     for (VersionInfoDeletedEntity versionInfoDeletedEntity : versionInfoDeletedEntities) {
461       activeVersions.put(versionInfoDeletedEntity.getEntityId(),
462           getVersionInfo(versionInfoDeletedEntity, user, action));
463     }
464     return activeVersions;
465   }
466
467   @Override
468   public List<Version> list(String itemId) {
469
470     List<Version> versions = versionDao.list(itemId);
471     Set<String> versionsNames = versions.stream().map(Version::getName).collect(Collectors.toSet());
472     versions.forEach(version -> {
473       version.setAdditionalInfo(new HashMap<>());
474       versionCalculator.injectAdditionalInfo(version, versionsNames);
475     });
476     return versions;
477   }
478
479   @Override
480   public Version get(String itemId, Version version) {
481     return versionDao.get(itemId, version)
482         .map(retrievedVersion -> getUpdateRetrievedVersion(itemId, retrievedVersion))
483         .orElseGet(() -> getSyncedVersion(itemId, version));
484   }
485
486   private Version getUpdateRetrievedVersion(String itemId, Version version) {
487     if (version.getStatus() == VersionStatus.Certified &&
488         (version.getState().getSynchronizationState() == SynchronizationState.OutOfSync ||
489             version.getState().isDirty())) {
490       forceSync(itemId, version);
491       LOGGER.info("Item Id {}, version Id {}: Force sync is done", itemId, version.getId());
492       version = versionDao.get(itemId, version)
493           .orElseThrow(() -> new IllegalStateException(
494               "Get version after a successful force sync must return the version"));
495     }
496     return version;
497   }
498
499   private Version getSyncedVersion(String itemId, Version version) {
500     sync(itemId, version);
501     LOGGER.info("Item Id {}, version Id {}: First time sync is done", itemId, version.getId());
502     return versionDao.get(itemId, version)
503         .orElseThrow(() -> new IllegalStateException(
504             "Get version after a successful sync must return the version"));
505   }
506
507   @Override
508   public Version create(String itemId, Version version, VersionCreationMethod creationMethod) {
509     String baseVersionName = null;
510     if (version.getBaseId() == null) {
511       version.setDescription("Initial version");
512     } else {
513       baseVersionName = get(itemId, new Version(version.getBaseId())).getName();
514     }
515     String versionName = versionCalculator.calculate(baseVersionName, creationMethod);
516     validateVersionName(itemId, versionName);
517     version.setName(versionName);
518
519     versionDao.create(itemId, version);
520     itemManager.updateVersionStatus(itemId, VersionStatus.Draft, null);
521
522     publish(itemId, version, String.format("Create version: %s", version.getName()));
523     return version;
524   }
525
526   private void validateVersionName(String itemId, String versionName) {
527     if (versionDao.list(itemId).stream()
528         .anyMatch(version -> versionName.equals(version.getName()))) {
529       String errorDescription = String
530           .format("Item %s: create version failed, a version with the name %s already exist",
531               itemId, versionName);
532
533       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY,
534           LoggerTragetServiceName.CREATE_VERSION, ErrorLevel.ERROR.name(),
535           LoggerErrorCode.DATA_ERROR.getErrorCode(), errorDescription);
536
537       throw new CoreException(new ErrorCode.ErrorCodeBuilder()
538           .withCategory(ErrorCategory.APPLICATION)
539           .withId("VERSION_NAME_ALREADY_EXIST")
540           .withMessage(errorDescription)
541           .build());
542     }
543   }
544
545   @Override
546   public void submit(String itemId, Version version, String submitDescription) {
547     version = get(itemId, version);
548
549     validateSubmit(itemId, version);
550
551     version.setStatus(VersionStatus.Certified);
552     versionDao.update(itemId, version);
553
554     publish(itemId, version, submitDescription);
555
556     itemManager.updateVersionStatus(itemId, VersionStatus.Certified, VersionStatus.Draft);
557   }
558
559   private void validateSubmit(String itemId, Version version) {
560     if (version.getStatus() == VersionStatus.Certified) {
561       String errorDescription = String
562           .format("Item %s: submit version failed, version %s is already Certified", itemId,
563               version.getId());
564
565       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
566           LoggerTragetServiceName.SUBMIT_ENTITY, ErrorLevel.ERROR.name(),
567           LoggerErrorCode.DATA_ERROR.getErrorCode(), errorDescription);
568
569       throw new CoreException(new ErrorCode.ErrorCodeBuilder()
570           .withCategory(ErrorCategory.APPLICATION)
571           .withId("VERSION_ALREADY_CERTIFIED")
572           .withMessage(errorDescription)
573           .build());
574     }
575   }
576
577   @Override
578   public void publish(String itemId, Version version, String message) {
579     versionDao.publish(itemId, version, message);
580   }
581
582
583   @Override
584   public void sync(String itemId, Version version) {
585     versionDao.sync(itemId, version);
586   }
587
588   @Override
589   public void forceSync(String itemId, Version version) {
590     versionDao.forceSync(itemId, version);
591   }
592
593   @Override
594   public void revert(String itemId, Version version, String revisionId) {
595     versionDao.revert(itemId, version, revisionId);
596   }
597
598   @Override
599   public List<Revision> listRevisions(String itemId, Version version) {
600     return versionDao.listRevisions(itemId, version);
601   }
602
603   private void markAsCheckedOut(VersionInfoEntity versionInfoEntity, String checkingOutUser) {
604     versionInfoEntity.setStatus(VersionStatus.Locked);
605     versionInfoEntity.setCandidate(new UserCandidateVersion(checkingOutUser,
606         versionInfoEntity.getActiveVersion().calculateNextCandidate()));
607   }
608
609   private Version doCheckout(VersionInfoEntity versionInfoEntity, String user) {
610     markAsCheckedOut(versionInfoEntity, user);
611     versionInfoDao.update(versionInfoEntity);
612
613     initVersionOnEntity(versionInfoEntity.getEntityType(), versionInfoEntity.getEntityId(),
614         versionInfoEntity.getActiveVersion(), versionInfoEntity.getCandidate().getVersion());
615
616     return versionInfoEntity.getCandidate().getVersion();
617   }
618
619   private void doDelete(VersionInfoEntity versionInfoEntity) {
620     VersionInfoDeletedEntity versionInfoDeletedEntity = new VersionInfoDeletedEntity();
621     versionInfoDeletedEntity.setStatus(versionInfoEntity.getStatus());
622     versionInfoDeletedEntity.setViewableVersions(versionInfoEntity.getViewableVersions());
623     versionInfoDeletedEntity.setActiveVersion(versionInfoEntity.getActiveVersion());
624     versionInfoDeletedEntity.setCandidate(versionInfoEntity.getCandidate());
625     versionInfoDeletedEntity.setEntityId(versionInfoEntity.getEntityId());
626     versionInfoDeletedEntity.setEntityType(versionInfoEntity.getEntityType());
627     versionInfoDeletedEntity.setLatestFinalVersion(versionInfoEntity.getLatestFinalVersion());
628     versionInfoDeletedDao.create(versionInfoDeletedEntity);
629     versionInfoDao.delete(versionInfoEntity);
630   }
631
632   private void doUndoDelete(VersionInfoDeletedEntity versionInfoDeletedEntity) {
633     VersionInfoEntity versionInfoEntity = new VersionInfoEntity();
634     versionInfoEntity.setStatus(versionInfoDeletedEntity.getStatus());
635     versionInfoEntity.setViewableVersions(versionInfoDeletedEntity.getViewableVersions());
636     versionInfoEntity.setActiveVersion(versionInfoDeletedEntity.getActiveVersion());
637     versionInfoEntity.setCandidate(versionInfoDeletedEntity.getCandidate());
638     versionInfoEntity.setEntityId(versionInfoDeletedEntity.getEntityId());
639     versionInfoEntity.setEntityType(versionInfoDeletedEntity.getEntityType());
640     versionInfoEntity.setLatestFinalVersion(versionInfoDeletedEntity.getLatestFinalVersion());
641     versionInfoDao.create(versionInfoEntity);
642     versionInfoDeletedDao.delete(versionInfoDeletedEntity);
643   }
644
645   private Version doCheckin(VersionInfoEntity versionInfoEntity, String checkinDescription) {
646     UserCandidateVersion userCandidateVersion = versionInfoEntity.getCandidate();
647     versionInfoEntity.setCandidate(null);
648     versionInfoEntity.setActiveVersion(userCandidateVersion.getVersion());
649     versionInfoEntity.getViewableVersions().add(versionInfoEntity.getActiveVersion());
650     versionInfoEntity.setStatus(VersionStatus.Draft);
651
652     closeVersionOnEntity(versionInfoEntity.getEntityType(), versionInfoEntity.getEntityId(),
653         versionInfoEntity.getActiveVersion());
654
655     versionInfoDao.update(versionInfoEntity);
656
657     return versionInfoEntity.getActiveVersion();
658   }
659
660   private Version doSubmit(VersionInfoEntity versionInfoEntity, String submittingUser,
661                            String submitDescription) {
662     Version finalVersion = versionInfoEntity.getActiveVersion().calculateNextFinal();
663     initVersionOnEntity(versionInfoEntity.getEntityType(), versionInfoEntity.getEntityId(),
664         versionInfoEntity.getActiveVersion(), finalVersion);
665     closeVersionOnEntity(versionInfoEntity.getEntityType(), versionInfoEntity.getEntityId(),
666         finalVersion);
667
668     Set<Version> viewableVersions = new HashSet<>();
669     for (Version version : versionInfoEntity.getViewableVersions()) {
670       if (version.isFinal()) {
671         viewableVersions.add(version);
672       }
673     }
674     viewableVersions.add(finalVersion);
675     versionInfoEntity.setViewableVersions(viewableVersions);
676     versionInfoEntity.setActiveVersion(finalVersion);
677     versionInfoEntity.setLatestFinalVersion(finalVersion);
678     versionInfoEntity.setStatus(VersionStatus.Certified);
679     versionInfoDao.update(versionInfoEntity);
680
681     return finalVersion;
682   }
683
684   private void initVersionOnEntity(String entityType, String entityId, Version baseVersion,
685                                    Version newVersion) {
686     Set<VersionableEntityMetadata> entityMetadatas = VERSIONABLE_ENTITIES.get(entityType);
687     if (entityMetadatas != null) {
688       for (VersionableEntityMetadata entityMetadata : entityMetadatas) {
689         VersionableEntityDaoFactory.getInstance().createInterface(entityMetadata.getStoreType())
690             .initVersion(entityMetadata, entityId, baseVersion, newVersion);
691       }
692     }
693   }
694
695   private void deleteVersionFromEntity(String entityType, String entityId,
696                                        Version versionToDelete, Version backToVersion) {
697     Set<VersionableEntityMetadata> entityMetadatas = VERSIONABLE_ENTITIES.get(entityType);
698     if (entityMetadatas != null) {
699       for (VersionableEntityMetadata entityMetadata : entityMetadatas) {
700         VersionableEntityDaoFactory.getInstance().createInterface(entityMetadata.getStoreType())
701             .deleteVersion(entityMetadata, entityId, versionToDelete, backToVersion);
702       }
703     }
704   }
705
706   private void closeVersionOnEntity(String entityType, String entityId, Version versionToClose) {
707     Set<VersionableEntityMetadata> entityMetadatas = VERSIONABLE_ENTITIES.get(entityType);
708     if (entityMetadatas != null) {
709       for (VersionableEntityMetadata entityMetadata : entityMetadatas) {
710         VersionableEntityDaoFactory.getInstance().createInterface(entityMetadata.getStoreType())
711             .closeVersion(entityMetadata, entityId, versionToClose);
712       }
713     }
714   }
715 }