7f983477cf6b91232a663cba2cb3974f6445e492
[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 MdcDataDebugMessage mdcDataDebugMessage = new MdcDataDebugMessage();
79   private static Map<String, Set<VersionableEntityMetadata>> versionableEntities = new HashMap<>();
80
81   private final VersionInfoDao versionInfoDao;
82   private final VersionInfoDeletedDao versionInfoDeletedDao;
83   private VersionDao versionDao;
84   private VersionCalculator versionCalculator;
85   private ItemManager itemManager;
86
87   public VersioningManagerImpl(VersionInfoDao versionInfoDao,
88                                VersionInfoDeletedDao versionInfoDeletedDao,
89                                VersionDao versionDao,
90                                VersionCalculator versionCalculator,
91                                ItemManager itemManager) {
92     this.versionInfoDao = versionInfoDao;
93     this.versionInfoDeletedDao = versionInfoDeletedDao;
94     this.versionDao = versionDao;
95     this.versionCalculator = versionCalculator;
96     this.itemManager = itemManager;
97   }
98
99   private static VersionInfo getVersionInfo(VersionInfoEntity versionInfoEntity, String user,
100                                             VersionableEntityAction action) {
101     return getVersionInfo(versionInfoEntity.getEntityId(),
102         versionInfoEntity.getEntityType(),
103         versionInfoEntity.getActiveVersion(),
104         versionInfoEntity.getCandidate(),
105         versionInfoEntity.getStatus(),
106         versionInfoEntity.getLatestFinalVersion(),
107         versionInfoEntity.getViewableVersions(),
108         action,
109         user);
110   }
111
112   private static VersionInfo getVersionInfo(VersionInfoDeletedEntity versionInfoEntity, String user,
113                                             VersionableEntityAction action) {
114     return getVersionInfo(versionInfoEntity.getEntityId(),
115         versionInfoEntity.getEntityType(),
116         versionInfoEntity.getActiveVersion(),
117         versionInfoEntity.getCandidate(),
118         versionInfoEntity.getStatus(),
119         versionInfoEntity.getLatestFinalVersion(),
120         versionInfoEntity.getViewableVersions(),
121         action,
122         user);
123   }
124
125   private static VersionInfo getVersionInfo(String entityId, String entityType, Version activeVer,
126                                             UserCandidateVersion candidate, VersionStatus status,
127                                             Version latestFinalVersion,
128                                             Set<Version> viewableVersions,
129                                             VersionableEntityAction action, String user) {
130
131
132     mdcDataDebugMessage.debugEntryMessage("entity Id", entityId);
133
134     Version activeVersion;
135
136     if (action == VersionableEntityAction.Write) {
137       if (candidate != null) {
138         if (user.equals(candidate.getUser())) {
139           activeVersion = candidate.getVersion();
140         } else {
141           MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
142               LoggerTragetServiceName.GET_VERSION_INFO, ErrorLevel.ERROR.name(),
143               LoggerErrorCode.PERMISSION_ERROR.getErrorCode(), "Can't get entity version info");
144           throw new CoreException(
145               new EditOnEntityLockedByOtherErrorBuilder(entityType, entityId, candidate.getUser())
146                   .build());
147         }
148       } else {
149         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
150             LoggerTragetServiceName.GET_VERSION_INFO, ErrorLevel.ERROR.name(),
151             LoggerErrorCode.PERMISSION_ERROR.getErrorCode(), "Can't get entity version info");
152         throw new CoreException(new EditOnUnlockedEntityErrorBuilder(entityType, entityId).build());
153       }
154     } else {
155       if (candidate != null && user.equals(candidate.getUser())) {
156         activeVersion = candidate.getVersion();
157       } else {
158         activeVersion = activeVer;
159       }
160     }
161
162     VersionInfo versionInfo = new VersionInfo();
163     versionInfo.setStatus(status);
164     activeVersion.setStatus(status);
165     if (latestFinalVersion != null) {
166       latestFinalVersion.setStatus(status);
167     }
168     if (viewableVersions != null) {
169       viewableVersions.forEach(version -> version.setStatus(status));
170     }
171     versionInfo.setActiveVersion(activeVersion);
172     versionInfo.setLatestFinalVersion(latestFinalVersion);
173     versionInfo.setViewableVersions(toSortedList(viewableVersions));
174     versionInfo.setFinalVersions(getFinalVersions(viewableVersions));
175     if (candidate != null) {
176       candidate.getVersion().setStatus(status);
177       versionInfo.setLockingUser(candidate.getUser());
178       if (user.equals(candidate.getUser())) {
179         versionInfo.getViewableVersions().add(candidate.getVersion());
180       }
181     }
182
183     mdcDataDebugMessage.debugExitMessage("entity Id", entityId);
184     return versionInfo;
185   }
186
187   private static List<Version> toSortedList(
188       Set<Version> versions) { // changing the Set to List in DB will require migration...
189     return versions.stream().sorted((o1, o2) -> o1.getMajor() > o2.getMajor() ? 1
190         : o1.getMajor() == o2.getMajor() ? (o1.getMinor() > o2.getMinor() ? 1
191             : o1.getMinor() == o2.getMinor() ? 0 : -1) : -1).collect(Collectors.toList());
192   }
193
194   private static List<Version> getFinalVersions(Set<Version> versions) {
195     return versions.stream().filter(Version::isFinal).collect(Collectors.toList());
196   }
197
198   @Override
199   public void register(String entityType, VersionableEntityMetadata entityMetadata) {
200     Set<VersionableEntityMetadata> entitiesMetadata =
201         versionableEntities.computeIfAbsent(entityType, k -> new HashSet<>());
202     entitiesMetadata.add(entityMetadata);
203   }
204
205   @Override
206   public Version create(String entityType, String entityId, String user) {
207     VersionInfoEntity
208         versionInfoEntity = versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
209     if (versionInfoEntity != null) {
210       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
211           LoggerTragetServiceName.CREATE_ENTITY, ErrorLevel.ERROR.name(),
212           LoggerErrorCode.DATA_ERROR.getErrorCode(), "Can't create versionable entity");
213       throw new CoreException(new EntityAlreadyExistErrorBuilder(entityType, entityId).build());
214     }
215
216     versionInfoEntity = new VersionInfoEntity(entityType, entityId);
217     versionInfoEntity.setActiveVersion(INITIAL_ACTIVE_VERSION);
218     markAsCheckedOut(versionInfoEntity, user);
219     versionInfoDao.create(versionInfoEntity);
220
221     return versionInfoEntity.getCandidate().getVersion();
222   }
223
224   @Override
225   public void delete(String entityType, String entityId, String user) {
226     VersionInfoEntity versionInfoEntity =
227         versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
228     if (versionInfoEntity == null) {
229       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
230           LoggerTragetServiceName.DELETE_ENTITY, ErrorLevel.ERROR.name(),
231           LoggerErrorCode.DATA_ERROR.getErrorCode(), "Can't delete versionable entity");
232       throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
233     }
234
235     switch (versionInfoEntity.getStatus()) {
236       case Locked:
237         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
238             LoggerTragetServiceName.DELETE_ENTITY, ErrorLevel.ERROR.name(),
239             LoggerErrorCode.PERMISSION_ERROR.getErrorCode(), "Can't delete versionable entity");
240         throw new CoreException(new DeleteOnLockedEntityErrorBuilder(entityType, entityId,
241             versionInfoEntity.getCandidate().getUser()).build());
242       default:
243         //do nothing
244         break;
245     }
246
247     doDelete(versionInfoEntity);
248   }
249
250   @Override
251   public void undoDelete(String entityType, String entityId, String user) {
252     VersionInfoDeletedEntity versionInfoDeletedEntity =
253         versionInfoDeletedDao.get(new VersionInfoDeletedEntity(entityType, entityId));
254     if (versionInfoDeletedEntity == null) {
255       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
256           LoggerTragetServiceName.UNDO_DELETE_ENTITY, ErrorLevel.ERROR.name(),
257           LoggerErrorCode.DATA_ERROR.getErrorCode(), "Can't undo delete for versionable entity");
258       throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
259     }
260
261     doUndoDelete(versionInfoDeletedEntity);
262   }
263
264   @Override
265   public Version checkout(String entityType, String entityId, String user) {
266     VersionInfoEntity versionInfoEntity =
267         versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
268     MDC.put(LoggerConstants.TARGET_SERVICE_NAME, LoggerTragetServiceName.CHECKOUT_ENTITY);
269     if (versionInfoEntity == null) {
270       MDC.put(LoggerConstants.ERROR_CATEGORY, ErrorLevel.ERROR.name());
271       MDC.put(LoggerConstants.TARGET_ENTITY, LoggerConstants.TARGET_ENTITY_DB);
272       MDC.put(LoggerConstants.ERROR_DESCRIPTION, LoggerErrorDescription.CHECKOUT_ENTITY);
273       throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
274     }
275
276     Version checkoutVersion = null;
277     switch (versionInfoEntity.getStatus()) {
278       case Locked:
279         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
280             LoggerTragetServiceName.CHECKOUT_ENTITY, ErrorLevel.ERROR.name(),
281             LoggerErrorCode.PERMISSION_ERROR.getErrorCode(), "Can't checkout versionable entity");
282         throw new CoreException(new CheckoutOnLockedEntityErrorBuilder(entityType, entityId,
283             versionInfoEntity.getCandidate().getUser()).build());
284       case Certified:
285       case Draft:
286         checkoutVersion = doCheckout(versionInfoEntity, user);
287         break;
288       default:
289         //do nothing
290         break;
291     }
292
293     return checkoutVersion;
294   }
295
296   @Override
297   public Version undoCheckout(String entityType, String entityId, String user) {
298     VersionInfoEntity versionInfoEntity =
299         versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
300     if (versionInfoEntity == null) {
301       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
302           LoggerTragetServiceName.UNDO_CHECKOUT_ENTITY, ErrorLevel.ERROR.name(),
303           LoggerErrorCode.DATA_ERROR.getErrorCode(), "Can't undo checkout for versionable entity");
304       throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
305     }
306
307     Version activeVersion = null;
308     switch (versionInfoEntity.getStatus()) {
309       case Locked:
310         if (!user.equals(versionInfoEntity.getCandidate().getUser())) {
311           MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
312               LoggerTragetServiceName.UNDO_CHECKOUT_ENTITY, ErrorLevel.ERROR.name(),
313               LoggerErrorCode.PERMISSION_ERROR.getErrorCode(),
314               "Can't undo checkout for versionable entity");
315           throw new CoreException(
316               new UndoCheckoutOnEntityLockedByOtherErrorBuilder(entityType, entityId,
317                   versionInfoEntity.getCandidate().getUser()).build());
318         }
319         activeVersion = undoCheckout(versionInfoEntity);
320         break;
321       case Certified:
322       case Draft:
323         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
324             LoggerTragetServiceName.UNDO_CHECKOUT_ENTITY, ErrorLevel.ERROR.name(),
325             LoggerErrorCode.PERMISSION_ERROR.getErrorCode(),
326             "Can't undo checkout for versionable entity");
327         throw new CoreException(
328             new UndoCheckoutOnUnlockedEntityErrorBuilder(entityType, entityId).build());
329       default:
330         //do nothing
331         break;
332     }
333
334     return activeVersion;
335   }
336
337   private Version undoCheckout(VersionInfoEntity versionInfoEntity) {
338     deleteVersionFromEntity(versionInfoEntity.getEntityType(), versionInfoEntity.getEntityId(),
339         versionInfoEntity.getCandidate().getVersion(), versionInfoEntity.getActiveVersion());
340
341     versionInfoEntity
342         .setStatus(versionInfoEntity.getActiveVersion().isFinal() ? VersionStatus.Certified
343             : VersionStatus.Draft);
344     versionInfoEntity.setCandidate(null);
345     versionInfoDao.update(versionInfoEntity);
346     return versionInfoEntity.getActiveVersion();
347   }
348
349   @Override
350   public Version checkin(String entityType, String entityId, String user,
351                          String checkinDescription) {
352     VersionInfoEntity versionInfoEntity =
353         versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
354     if (versionInfoEntity == null) {
355       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
356           LoggerTragetServiceName.CHECKIN_ENTITY, ErrorLevel.ERROR.name(),
357           LoggerErrorCode.DATA_ERROR.getErrorCode(), "Can't checkin versionable entity");
358       throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
359     }
360
361     Version checkedInVersion = null;
362     switch (versionInfoEntity.getStatus()) {
363       case Draft:
364       case Certified:
365         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
366             LoggerTragetServiceName.CHECKIN_ENTITY, ErrorLevel.ERROR.name(),
367             LoggerErrorCode.PERMISSION_ERROR.getErrorCode(), "Can't checkin versionable entity");
368         throw new CoreException(
369             new CheckinOnUnlockedEntityErrorBuilder(entityType, entityId).build());
370       case Locked:
371         if (!user.equals(versionInfoEntity.getCandidate().getUser())) {
372           MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
373               LoggerTragetServiceName.CHECKIN_ENTITY, ErrorLevel.ERROR.name(),
374               LoggerErrorCode.PERMISSION_ERROR.getErrorCode(), "Can't checkin versionable entity");
375           throw new CoreException(new CheckinOnEntityLockedByOtherErrorBuilder(entityType, entityId,
376               versionInfoEntity.getCandidate().getUser()).build());
377         }
378         checkedInVersion = doCheckin(versionInfoEntity, checkinDescription);
379         break;
380       default:
381         //do nothing
382         break;
383     }
384
385     return checkedInVersion;
386   }
387
388   @Override
389   public Version submit(String entityType, String entityId, String user, String submitDescription) {
390     VersionInfoEntity versionInfoEntity =
391         versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
392     if (versionInfoEntity == null) {
393       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
394           LoggerTragetServiceName.SUBMIT_ENTITY, ErrorLevel.ERROR.name(),
395           LoggerErrorCode.DATA_ERROR.getErrorCode(), "Can't submit versionable entity");
396       throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
397     }
398
399     Version submitVersion = null;
400     switch (versionInfoEntity.getStatus()) {
401       case Certified:
402         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
403             LoggerTragetServiceName.SUBMIT_ENTITY, ErrorLevel.ERROR.name(),
404             LoggerErrorCode.DATA_ERROR.getErrorCode(), "Can't submit versionable entity");
405         throw new CoreException(
406             new EntityAlreadyFinalizedErrorBuilder(entityType, entityId).build());
407       case Locked:
408         MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
409             LoggerTragetServiceName.SUBMIT_ENTITY, ErrorLevel.ERROR.name(),
410             LoggerErrorCode.PERMISSION_ERROR.getErrorCode(), "Can't submit versionable entity");
411         throw new CoreException(new SubmitLockedEntityNotAllowedErrorBuilder(entityType, entityId,
412             versionInfoEntity.getCandidate().getUser()).build());
413       case Draft:
414         submitVersion = doSubmit(versionInfoEntity, user, submitDescription);
415         break;
416       default:
417         //do nothing
418         break;
419     }
420
421     return submitVersion;
422   }
423
424   @Override
425   public VersionInfo getEntityVersionInfo(String entityType, String entityId, String user,
426                                           VersionableEntityAction action) {
427     VersionInfoEntity versionInfoEntity =
428         versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
429     if (versionInfoEntity == null) {
430       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
431           LoggerTragetServiceName.GET_VERSION_INFO, ErrorLevel.ERROR.name(),
432           LoggerErrorCode.DATA_ERROR.getErrorCode(), "Can't get entity version info");
433       throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
434     }
435     return getVersionInfo(versionInfoEntity, user, action);
436   }
437
438   @Override
439   public Map<String, VersionInfo> listEntitiesVersionInfo(String entityType, String user,
440                                                           VersionableEntityAction action) {
441     Collection<VersionInfoEntity> versionInfoEntities =
442         versionInfoDao.list(new VersionInfoEntity(entityType, null));
443     Map<String, VersionInfo> activeVersions = new HashMap<>();
444     for (VersionInfoEntity versionInfoEntity : versionInfoEntities) {
445       activeVersions
446           .put(versionInfoEntity.getEntityId(), getVersionInfo(versionInfoEntity, user, action));
447     }
448     return activeVersions;
449   }
450
451   @Override
452   public Map<String, VersionInfo> listDeletedEntitiesVersionInfo(String entityType, String user,
453                                                                  VersionableEntityAction action) {
454     Collection<VersionInfoDeletedEntity> versionInfoDeletedEntities =
455         versionInfoDeletedDao.list(new VersionInfoDeletedEntity(entityType, null));
456     Map<String, VersionInfo> activeVersions = new HashMap<>();
457
458
459     for (VersionInfoDeletedEntity versionInfoDeletedEntity : versionInfoDeletedEntities) {
460       activeVersions.put(versionInfoDeletedEntity.getEntityId(),
461           getVersionInfo(versionInfoDeletedEntity, user, action));
462     }
463     return activeVersions;
464   }
465
466   @Override
467   public List<Version> list(String itemId) {
468
469     List<Version> versions = versionDao.list(itemId);
470     Set<String> versionsNames = versions.stream().map(Version::getName).collect(Collectors.toSet());
471     versions.forEach(version -> {
472       version.setAdditionalInfo(new HashMap<>());
473       versionCalculator.injectAdditionalInfo(version, versionsNames);
474     });
475     return versions;
476   }
477
478   @Override
479   public Version get(String itemId, Version version) {
480     return versionDao.get(itemId, version)
481         .map(retrievedVersion -> getUpdateRetrievedVersion(itemId, retrievedVersion))
482         .orElseGet(() -> getSyncedVersion(itemId, version));
483   }
484
485   private Version getUpdateRetrievedVersion(String itemId, Version version) {
486     if (version.getStatus() == VersionStatus.Certified &&
487         (version.getState().getSynchronizationState() == SynchronizationState.OutOfSync ||
488             version.getState().isDirty())) {
489       forceSync(itemId, version);
490       LOGGER.info("Item Id {}, version Id {}: Force sync is done", itemId, version.getId());
491       version = versionDao.get(itemId, version)
492           .orElseThrow(() -> new IllegalStateException(
493               "Get version after a successful force sync must return the version"));
494     }
495     return version;
496   }
497
498   private Version getSyncedVersion(String itemId, Version version) {
499     sync(itemId, version);
500     LOGGER.info("Item Id {}, version Id {}: First time sync is done", itemId, version.getId());
501     return versionDao.get(itemId, version)
502         .orElseThrow(() -> new IllegalStateException(
503             "Get version after a successful sync must return the version"));
504   }
505
506   @Override
507   public Version create(String itemId, Version version, VersionCreationMethod creationMethod) {
508     String baseVersionName = null;
509     if (version.getBaseId() == null) {
510       version.setDescription("Initial version");
511     } else {
512       baseVersionName = get(itemId, new Version(version.getBaseId())).getName();
513     }
514     String versionName = versionCalculator.calculate(baseVersionName, creationMethod);
515     validateVersionName(itemId, versionName);
516     version.setName(versionName);
517
518     versionDao.create(itemId, version);
519     itemManager.updateVersionStatus(itemId, VersionStatus.Draft, null);
520
521     publish(itemId, version, String.format("Initial version: %s ", version.getName()));
522     return version;
523   }
524
525   private void validateVersionName(String itemId, String versionName) {
526     if (versionDao.list(itemId).stream()
527         .anyMatch(version -> versionName.equals(version.getName()))) {
528       String errorDescription = String
529           .format("Item %s: create version failed, a version with the name %s already exist",
530               itemId, versionName);
531
532       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY,
533           LoggerTragetServiceName.CREATE_VERSION, ErrorLevel.ERROR.name(),
534           LoggerErrorCode.DATA_ERROR.getErrorCode(), errorDescription);
535
536       throw new CoreException(new ErrorCode.ErrorCodeBuilder()
537           .withCategory(ErrorCategory.APPLICATION)
538           .withId("VERSION_NAME_ALREADY_EXIST")
539           .withMessage(errorDescription)
540           .build());
541     }
542   }
543
544   @Override
545   public void submit(String itemId, Version version, String submitDescription) {
546     version = get(itemId, version);
547
548     validateSubmit(itemId, version);
549
550     version.setStatus(VersionStatus.Certified);
551     versionDao.update(itemId, version);
552
553     publish(itemId, version, submitDescription);
554
555     itemManager.updateVersionStatus(itemId, VersionStatus.Certified, VersionStatus.Draft);
556   }
557
558   private void validateSubmit(String itemId, Version version) {
559     if (version.getStatus() == VersionStatus.Certified) {
560       String errorDescription = String
561           .format("Item %s: submit version failed, version %s is already Certified", itemId,
562               version.getId());
563
564       MdcDataErrorMessage.createErrorMessageAndUpdateMdc(LoggerConstants.TARGET_ENTITY_DB,
565           LoggerTragetServiceName.SUBMIT_ENTITY, ErrorLevel.ERROR.name(),
566           LoggerErrorCode.DATA_ERROR.getErrorCode(), errorDescription);
567
568       throw new CoreException(new ErrorCode.ErrorCodeBuilder()
569           .withCategory(ErrorCategory.APPLICATION)
570           .withId("VERSION_ALREADY_CERTIFIED")
571           .withMessage(errorDescription)
572           .build());
573     }
574   }
575
576   @Override
577   public void publish(String itemId, Version version, String message) {
578     versionDao.publish(itemId, version, message);
579   }
580
581
582   @Override
583   public void sync(String itemId, Version version) {
584     versionDao.sync(itemId, version);
585   }
586
587   @Override
588   public void forceSync(String itemId, Version version) {
589     versionDao.forceSync(itemId, version);
590   }
591
592   @Override
593   public void revert(String itemId, Version version, String revisionId) {
594     versionDao.revert(itemId, version, revisionId);
595   }
596
597   @Override
598   public List<Revision> listRevisions(String itemId, Version version) {
599     return versionDao.listRevisions(itemId, version);
600
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 = versionableEntities.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 = versionableEntities.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 = versionableEntities.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 }