2 * Copyright © 2016-2018 European Support Limited
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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 or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package org.openecomp.sdc.versioning.impl;
19 import org.openecomp.sdc.common.errors.CoreException;
20 import org.openecomp.sdc.common.errors.ErrorCategory;
21 import org.openecomp.sdc.common.errors.ErrorCode;
22 import org.openecomp.sdc.logging.api.Logger;
23 import org.openecomp.sdc.logging.api.LoggerFactory;
24 import org.openecomp.sdc.versioning.ActionVersioningManager;
25 import org.openecomp.sdc.versioning.AsdcItemManager;
26 import org.openecomp.sdc.versioning.VersionCalculator;
27 import org.openecomp.sdc.versioning.dao.VersionDao;
28 import org.openecomp.sdc.versioning.dao.VersionInfoDao;
29 import org.openecomp.sdc.versioning.dao.VersionInfoDeletedDao;
30 import org.openecomp.sdc.versioning.dao.VersionableEntityDaoFactory;
31 import org.openecomp.sdc.versioning.dao.types.*;
32 import org.openecomp.sdc.versioning.errors.*;
33 import org.openecomp.sdc.versioning.types.VersionCreationMethod;
34 import org.openecomp.sdc.versioning.types.VersionInfo;
35 import org.openecomp.sdc.versioning.types.VersionableEntityAction;
36 import org.openecomp.sdc.versioning.types.VersionableEntityMetadata;
38 import java.util.stream.Collectors;
39 import java.util.Collection;
40 import java.util.HashMap;
41 import java.util.HashSet;
42 import java.util.List;
46 public class ActionVersioningManagerImpl implements ActionVersioningManager {
47 private static final Logger LOGGER = LoggerFactory.getLogger(ActionVersioningManagerImpl.class);
48 private static final Version INITIAL_ACTIVE_VERSION = new Version(0, 0);
49 private static final Map<String, Set<VersionableEntityMetadata>> VERSIONABLE_ENTITIES =
52 private final VersionInfoDao versionInfoDao;
53 private final VersionInfoDeletedDao versionInfoDeletedDao;
54 private VersionDao versionDao;
55 private VersionCalculator versionCalculator;
56 private AsdcItemManager asdcItemManager;
58 public ActionVersioningManagerImpl(VersionInfoDao versionInfoDao,
59 VersionInfoDeletedDao versionInfoDeletedDao,
60 VersionDao versionDao,
61 VersionCalculator versionCalculator,
62 AsdcItemManager asdcItemManager) {
63 this.versionInfoDao = versionInfoDao;
64 this.versionInfoDeletedDao = versionInfoDeletedDao;
65 this.versionDao = versionDao;
66 this.versionCalculator = versionCalculator;
67 this.asdcItemManager = asdcItemManager;
70 private static VersionInfo getVersionInfo(VersionInfoEntity versionInfoEntity, String user,
71 VersionableEntityAction action) {
72 return getVersionInfo(versionInfoEntity.getEntityId(),
73 versionInfoEntity.getEntityType(),
74 versionInfoEntity.getActiveVersion(),
75 versionInfoEntity.getCandidate(),
76 versionInfoEntity.getStatus(),
77 versionInfoEntity.getLatestFinalVersion(),
78 versionInfoEntity.getViewableVersions(),
83 private static VersionInfo getVersionInfo(VersionInfoDeletedEntity versionInfoEntity, String user,
84 VersionableEntityAction action) {
85 return getVersionInfo(versionInfoEntity.getEntityId(),
86 versionInfoEntity.getEntityType(),
87 versionInfoEntity.getActiveVersion(),
88 versionInfoEntity.getCandidate(),
89 versionInfoEntity.getStatus(),
90 versionInfoEntity.getLatestFinalVersion(),
91 versionInfoEntity.getViewableVersions(),
96 private static VersionInfo getVersionInfo(String entityId, String entityType, Version activeVer,
97 UserCandidateVersion candidate, VersionStatus status,
98 Version latestFinalVersion,
99 Set<Version> viewableVersions,
100 VersionableEntityAction action, String user) {
101 Version activeVersion;
103 if (action == VersionableEntityAction.Write) {
104 if (candidate != null) {
105 if (user.equals(candidate.getUser())) {
106 activeVersion = candidate.getVersion();
108 throw new CoreException(
109 new EditOnEntityLockedByOtherErrorBuilder(entityType, entityId, candidate.getUser())
113 throw new CoreException(new EditOnUnlockedEntityErrorBuilder(entityType, entityId).build());
116 if (candidate != null && user.equals(candidate.getUser())) {
117 activeVersion = candidate.getVersion();
119 activeVersion = activeVer;
123 VersionInfo versionInfo = new VersionInfo();
124 versionInfo.setStatus(status);
125 activeVersion.setStatus(status);
126 if (latestFinalVersion != null) {
127 latestFinalVersion.setStatus(status);
129 if (viewableVersions != null) {
130 viewableVersions.forEach(version -> version.setStatus(status));
131 versionInfo.setViewableVersions(toSortedList(viewableVersions));
132 versionInfo.setFinalVersions(getFinalVersions(viewableVersions));
134 versionInfo.setActiveVersion(activeVersion);
135 versionInfo.setLatestFinalVersion(latestFinalVersion);
136 if (candidate != null) {
137 candidate.getVersion().setStatus(status);
138 versionInfo.setLockingUser(candidate.getUser());
139 if (user.equals(candidate.getUser())) {
140 versionInfo.getViewableVersions().add(candidate.getVersion());
146 private static List<Version> toSortedList(
147 Set<Version> versions) { // changing the Set to List in DB will require migration...
148 return versions.stream().sorted((o1, o2) -> o1.getMajor() > o2.getMajor() ? 1
149 : o1.getMajor() == o2.getMajor() ? (o1.getMinor() > o2.getMinor() ? 1
150 : o1.getMinor() == o2.getMinor() ? 0 : -1) : -1).collect(Collectors.toList());
153 private static List<Version> getFinalVersions(Set<Version> versions) {
154 return versions.stream().filter(Version::isFinal).collect(Collectors.toList());
158 public void register(String entityType, VersionableEntityMetadata entityMetadata) {
159 Set<VersionableEntityMetadata> entitiesMetadata =
160 VERSIONABLE_ENTITIES.computeIfAbsent(entityType, k -> new HashSet<>());
161 entitiesMetadata.add(entityMetadata);
165 public Version create(String entityType, String entityId, String user) {
167 versionInfoEntity = versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
168 if (versionInfoEntity != null) {
169 throw new CoreException(new EntityAlreadyExistErrorBuilder(entityType, entityId).build());
172 versionInfoEntity = new VersionInfoEntity(entityType, entityId);
173 versionInfoEntity.setActiveVersion(INITIAL_ACTIVE_VERSION);
174 markAsCheckedOut(versionInfoEntity, user);
175 versionInfoDao.create(versionInfoEntity);
177 return versionInfoEntity.getCandidate().getVersion();
181 public void delete(String entityType, String entityId, String user) {
182 VersionInfoEntity versionInfoEntity =
183 versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
184 if (versionInfoEntity == null) {
185 throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
188 switch (versionInfoEntity.getStatus()) {
190 throw new CoreException(new DeleteOnLockedEntityErrorBuilder(entityType, entityId,
191 versionInfoEntity.getCandidate().getUser()).build());
197 doDelete(versionInfoEntity);
201 public void undoDelete(String entityType, String entityId, String user) {
202 VersionInfoDeletedEntity versionInfoDeletedEntity =
203 versionInfoDeletedDao.get(new VersionInfoDeletedEntity(entityType, entityId));
204 if (versionInfoDeletedEntity == null) {
205 throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
208 doUndoDelete(versionInfoDeletedEntity);
212 public Version checkout(String entityType, String entityId, String user) {
213 VersionInfoEntity versionInfoEntity =
214 versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
215 if (versionInfoEntity == null) {
216 throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
219 Version checkoutVersion = null;
220 switch (versionInfoEntity.getStatus()) {
222 throw new CoreException(new CheckoutOnLockedEntityErrorBuilder(entityType, entityId,
223 versionInfoEntity.getCandidate().getUser()).build());
226 checkoutVersion = doCheckout(versionInfoEntity, user);
233 return checkoutVersion;
237 public Version undoCheckout(String entityType, String entityId, String user) {
238 VersionInfoEntity versionInfoEntity =
239 versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
240 if (versionInfoEntity == null) {
241 throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
244 Version activeVersion = null;
245 switch (versionInfoEntity.getStatus()) {
247 if (!user.equals(versionInfoEntity.getCandidate().getUser())) {
248 throw new CoreException(
249 new UndoCheckoutOnEntityLockedByOtherErrorBuilder(entityType, entityId,
250 versionInfoEntity.getCandidate().getUser()).build());
252 activeVersion = undoCheckout(versionInfoEntity);
256 throw new CoreException(
257 new UndoCheckoutOnUnlockedEntityErrorBuilder(entityType, entityId).build());
263 return activeVersion;
266 private Version undoCheckout(VersionInfoEntity versionInfoEntity) {
267 deleteVersionFromEntity(versionInfoEntity.getEntityType(), versionInfoEntity.getEntityId(),
268 versionInfoEntity.getCandidate().getVersion(), versionInfoEntity.getActiveVersion());
271 .setStatus(versionInfoEntity.getActiveVersion().isFinal() ? VersionStatus.Certified
272 : VersionStatus.Draft);
273 versionInfoEntity.setCandidate(null);
274 versionInfoDao.update(versionInfoEntity);
275 return versionInfoEntity.getActiveVersion();
279 public Version checkin(String entityType, String entityId, String user,
280 String checkinDescription) {
281 VersionInfoEntity versionInfoEntity =
282 versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
283 if (versionInfoEntity == null) {
284 throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
287 Version checkedInVersion = null;
288 switch (versionInfoEntity.getStatus()) {
291 throw new CoreException(
292 new CheckinOnUnlockedEntityErrorBuilder(entityType, entityId).build());
294 if (!user.equals(versionInfoEntity.getCandidate().getUser())) {
295 throw new CoreException(new CheckinOnEntityLockedByOtherErrorBuilder(entityType, entityId,
296 versionInfoEntity.getCandidate().getUser()).build());
298 checkedInVersion = doCheckin(versionInfoEntity, checkinDescription);
305 return checkedInVersion;
309 public Version submit(String entityType, String entityId, String user, String submitDescription) {
310 VersionInfoEntity versionInfoEntity =
311 versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
312 if (versionInfoEntity == null) {
313 throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
316 Version submitVersion = null;
317 switch (versionInfoEntity.getStatus()) {
319 throw new CoreException(
320 new EntityAlreadyFinalizedErrorBuilder(entityType, entityId).build());
322 throw new CoreException(new SubmitLockedEntityNotAllowedErrorBuilder(entityType, entityId,
323 versionInfoEntity.getCandidate().getUser()).build());
325 submitVersion = doSubmit(versionInfoEntity, user, submitDescription);
332 return submitVersion;
336 public VersionInfo getEntityVersionInfo(String entityType, String entityId, String user,
337 VersionableEntityAction action) {
338 VersionInfoEntity versionInfoEntity =
339 versionInfoDao.get(new VersionInfoEntity(entityType, entityId));
340 if (versionInfoEntity == null) {
341 throw new CoreException(new EntityNotExistErrorBuilder(entityType, entityId).build());
343 return getVersionInfo(versionInfoEntity, user, action);
347 public Map<String, VersionInfo> listEntitiesVersionInfo(String entityType, String user,
348 VersionableEntityAction action) {
349 Collection<VersionInfoEntity> versionInfoEntities =
350 versionInfoDao.list(new VersionInfoEntity(entityType, null));
351 Map<String, VersionInfo> activeVersions = new HashMap<>();
352 for (VersionInfoEntity versionInfoEntity : versionInfoEntities) {
354 .put(versionInfoEntity.getEntityId(), getVersionInfo(versionInfoEntity, user, action));
356 return activeVersions;
360 public Map<String, VersionInfo> listDeletedEntitiesVersionInfo(String entityType, String user,
361 VersionableEntityAction action) {
362 Collection<VersionInfoDeletedEntity> versionInfoDeletedEntities =
363 versionInfoDeletedDao.list(new VersionInfoDeletedEntity(entityType, null));
364 Map<String, VersionInfo> activeVersions = new HashMap<>();
367 for (VersionInfoDeletedEntity versionInfoDeletedEntity : versionInfoDeletedEntities) {
368 activeVersions.put(versionInfoDeletedEntity.getEntityId(),
369 getVersionInfo(versionInfoDeletedEntity, user, action));
371 return activeVersions;
375 public List<Version> list(String itemId) {
377 List<Version> versions = versionDao.list(itemId);
378 Set<String> versionsNames = versions.stream().map(Version::getName).collect(Collectors.toSet());
379 versions.forEach(version -> {
380 version.setAdditionalInfo(new HashMap<>());
381 versionCalculator.injectAdditionalInfo(version, versionsNames);
387 public Version get(String itemId, Version version) {
388 return versionDao.get(itemId, version)
389 .map(retrievedVersion -> getUpdateRetrievedVersion(itemId, retrievedVersion))
390 .orElseGet(() -> getSyncedVersion(itemId, version));
393 private Version getUpdateRetrievedVersion(String itemId, Version version) {
394 if (version.getStatus() == VersionStatus.Certified &&
395 (version.getState().getSynchronizationState() == SynchronizationState.OutOfSync ||
396 version.getState().isDirty())) {
397 forceSync(itemId, version);
398 LOGGER.info("Item Id {}, version Id {}: Force sync is done", itemId, version.getId());
399 version = versionDao.get(itemId, version)
400 .orElseThrow(() -> new IllegalStateException(
401 "Get version after a successful force sync must return the version"));
406 private Version getSyncedVersion(String itemId, Version version) {
407 sync(itemId, version);
408 LOGGER.info("Item Id {}, version Id {}: First time sync is done", itemId, version.getId());
409 return versionDao.get(itemId, version)
410 .orElseThrow(() -> new IllegalStateException(
411 "Get version after a successful sync must return the version"));
415 public Version create(String itemId, Version version, VersionCreationMethod creationMethod) {
416 String baseVersionName = null;
417 if (version.getBaseId() == null) {
418 version.setDescription("Initial version");
420 baseVersionName = get(itemId, new Version(version.getBaseId())).getName();
422 String versionName = versionCalculator.calculate(baseVersionName, creationMethod);
423 validateVersionName(itemId, versionName);
424 version.setName(versionName);
426 versionDao.create(itemId, version);
427 asdcItemManager.updateVersionStatus(itemId, VersionStatus.Draft, null);
429 publish(itemId, version, String.format("Create version: %s", version.getName()));
433 private void validateVersionName(String itemId, String versionName) {
434 if (versionDao.list(itemId).stream()
435 .anyMatch(version -> versionName.equals(version.getName()))) {
436 String errorDescription = String
437 .format("Item %s: create version failed, a version with the name %s already exist",
438 itemId, versionName);
439 throw new CoreException(new ErrorCode.ErrorCodeBuilder()
440 .withCategory(ErrorCategory.APPLICATION)
441 .withId("VERSION_NAME_ALREADY_EXIST")
442 .withMessage(errorDescription)
448 public void submit(String itemId, Version version, String submitDescription) {
449 version = get(itemId, version);
451 validateSubmit(itemId, version);
453 version.setStatus(VersionStatus.Certified);
454 versionDao.update(itemId, version);
456 publish(itemId, version, submitDescription);
458 asdcItemManager.updateVersionStatus(itemId, VersionStatus.Certified, VersionStatus.Draft);
461 private void validateSubmit(String itemId, Version version) {
462 if (version.getStatus() == VersionStatus.Certified) {
463 String errorDescription = String
464 .format("Item %s: submit version failed, version %s is already Certified", itemId,
466 throw new CoreException(new ErrorCode.ErrorCodeBuilder()
467 .withCategory(ErrorCategory.APPLICATION)
468 .withId("VERSION_ALREADY_CERTIFIED")
469 .withMessage(errorDescription)
475 public void publish(String itemId, Version version, String message) {
476 versionDao.publish(itemId, version, message);
481 public void sync(String itemId, Version version) {
482 versionDao.sync(itemId, version);
486 public void forceSync(String itemId, Version version) {
487 versionDao.forceSync(itemId, version);
491 public void revert(String itemId, Version version, String revisionId) {
492 versionDao.revert(itemId, version, revisionId);
496 public List<Revision> listRevisions(String itemId, Version version) {
497 return versionDao.listRevisions(itemId, version);
500 private void markAsCheckedOut(VersionInfoEntity versionInfoEntity, String checkingOutUser) {
501 versionInfoEntity.setStatus(VersionStatus.Locked);
502 versionInfoEntity.setCandidate(new UserCandidateVersion(checkingOutUser,
503 versionInfoEntity.getActiveVersion().calculateNextCandidate()));
506 private Version doCheckout(VersionInfoEntity versionInfoEntity, String user) {
507 markAsCheckedOut(versionInfoEntity, user);
508 versionInfoDao.update(versionInfoEntity);
510 initVersionOnEntity(versionInfoEntity.getEntityType(), versionInfoEntity.getEntityId(),
511 versionInfoEntity.getActiveVersion(), versionInfoEntity.getCandidate().getVersion());
513 return versionInfoEntity.getCandidate().getVersion();
516 private void doDelete(VersionInfoEntity versionInfoEntity) {
517 VersionInfoDeletedEntity versionInfoDeletedEntity = new VersionInfoDeletedEntity();
518 versionInfoDeletedEntity.setStatus(versionInfoEntity.getStatus());
519 versionInfoDeletedEntity.setViewableVersions(versionInfoEntity.getViewableVersions());
520 versionInfoDeletedEntity.setActiveVersion(versionInfoEntity.getActiveVersion());
521 versionInfoDeletedEntity.setCandidate(versionInfoEntity.getCandidate());
522 versionInfoDeletedEntity.setEntityId(versionInfoEntity.getEntityId());
523 versionInfoDeletedEntity.setEntityType(versionInfoEntity.getEntityType());
524 versionInfoDeletedEntity.setLatestFinalVersion(versionInfoEntity.getLatestFinalVersion());
525 versionInfoDeletedDao.create(versionInfoDeletedEntity);
526 versionInfoDao.delete(versionInfoEntity);
529 private void doUndoDelete(VersionInfoDeletedEntity versionInfoDeletedEntity) {
530 VersionInfoEntity versionInfoEntity = new VersionInfoEntity();
531 versionInfoEntity.setStatus(versionInfoDeletedEntity.getStatus());
532 versionInfoEntity.setViewableVersions(versionInfoDeletedEntity.getViewableVersions());
533 versionInfoEntity.setActiveVersion(versionInfoDeletedEntity.getActiveVersion());
534 versionInfoEntity.setCandidate(versionInfoDeletedEntity.getCandidate());
535 versionInfoEntity.setEntityId(versionInfoDeletedEntity.getEntityId());
536 versionInfoEntity.setEntityType(versionInfoDeletedEntity.getEntityType());
537 versionInfoEntity.setLatestFinalVersion(versionInfoDeletedEntity.getLatestFinalVersion());
538 versionInfoDao.create(versionInfoEntity);
539 versionInfoDeletedDao.delete(versionInfoDeletedEntity);
542 private Version doCheckin(VersionInfoEntity versionInfoEntity, String checkinDescription) {
543 UserCandidateVersion userCandidateVersion = versionInfoEntity.getCandidate();
544 versionInfoEntity.setCandidate(null);
545 versionInfoEntity.setActiveVersion(userCandidateVersion.getVersion());
546 versionInfoEntity.getViewableVersions().add(versionInfoEntity.getActiveVersion());
547 versionInfoEntity.setStatus(VersionStatus.Draft);
549 closeVersionOnEntity(versionInfoEntity.getEntityType(), versionInfoEntity.getEntityId(),
550 versionInfoEntity.getActiveVersion());
552 versionInfoDao.update(versionInfoEntity);
554 return versionInfoEntity.getActiveVersion();
557 private Version doSubmit(VersionInfoEntity versionInfoEntity, String submittingUser,
558 String submitDescription) {
559 Version finalVersion = versionInfoEntity.getActiveVersion().calculateNextFinal();
560 initVersionOnEntity(versionInfoEntity.getEntityType(), versionInfoEntity.getEntityId(),
561 versionInfoEntity.getActiveVersion(), finalVersion);
562 closeVersionOnEntity(versionInfoEntity.getEntityType(), versionInfoEntity.getEntityId(),
565 Set<Version> viewableVersions = new HashSet<>();
566 for (Version version : versionInfoEntity.getViewableVersions()) {
567 if (version.isFinal()) {
568 viewableVersions.add(version);
571 viewableVersions.add(finalVersion);
572 versionInfoEntity.setViewableVersions(viewableVersions);
573 versionInfoEntity.setActiveVersion(finalVersion);
574 versionInfoEntity.setLatestFinalVersion(finalVersion);
575 versionInfoEntity.setStatus(VersionStatus.Certified);
576 versionInfoDao.update(versionInfoEntity);
581 private void initVersionOnEntity(String entityType, String entityId, Version baseVersion,
582 Version newVersion) {
583 Set<VersionableEntityMetadata> entityMetadatas = VERSIONABLE_ENTITIES.get(entityType);
584 if (entityMetadatas != null) {
585 for (VersionableEntityMetadata entityMetadata : entityMetadatas) {
586 VersionableEntityDaoFactory.getInstance().createInterface(entityMetadata.getStoreType())
587 .initVersion(entityMetadata, entityId, baseVersion, newVersion);
592 private void deleteVersionFromEntity(String entityType, String entityId,
593 Version versionToDelete, Version backToVersion) {
594 Set<VersionableEntityMetadata> entityMetadatas = VERSIONABLE_ENTITIES.get(entityType);
595 if (entityMetadatas != null) {
596 for (VersionableEntityMetadata entityMetadata : entityMetadatas) {
597 VersionableEntityDaoFactory.getInstance().createInterface(entityMetadata.getStoreType())
598 .deleteVersion(entityMetadata, entityId, versionToDelete, backToVersion);
603 private void closeVersionOnEntity(String entityType, String entityId, Version versionToClose) {
604 Set<VersionableEntityMetadata> entityMetadatas = VERSIONABLE_ENTITIES.get(entityType);
605 if (entityMetadatas != null) {
606 for (VersionableEntityMetadata entityMetadata : entityMetadatas) {
607 VersionableEntityDaoFactory.getInstance().createInterface(entityMetadata.getStoreType())
608 .closeVersion(entityMetadata, entityId, versionToClose);