001/*-
002 * #%L
003 * HAPI FHIR Server - SQL Migration
004 * %%
005 * Copyright (C) 2014 - 2024 Smile CDR, Inc.
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 *
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package ca.uhn.fhir.jpa.migrate;
021
022import ca.uhn.fhir.i18n.Msg;
023import ca.uhn.fhir.jpa.migrate.dao.HapiMigrationDao;
024import ca.uhn.fhir.jpa.migrate.entity.HapiMigrationEntity;
025import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask;
026import org.flywaydb.core.api.MigrationVersion;
027
028import java.util.Optional;
029import java.util.Set;
030
031public class HapiMigrationStorageSvc {
032        public static final String UNKNOWN_VERSION = "unknown";
033        public static final String LOCK_TYPE = "hapi-fhir-lock";
034
035        private final HapiMigrationDao myHapiMigrationDao;
036
037        public HapiMigrationStorageSvc(HapiMigrationDao theHapiMigrationDao) {
038                myHapiMigrationDao = theHapiMigrationDao;
039        }
040
041        public String getMigrationTablename() {
042                return myHapiMigrationDao.getMigrationTablename();
043        }
044
045        /**
046         * Returns a list of migration tasks that have not yet been successfully run against the database
047         * @param theTaskList the full list of tasks for this release
048         * @return a list of tasks that have not yet been successfully run against the database
049         */
050        public MigrationTaskList diff(MigrationTaskList theTaskList) {
051                Set<MigrationVersion> appliedMigrationVersions = fetchAppliedMigrationVersions();
052
053                return theTaskList.diff(appliedMigrationVersions);
054        }
055
056        /**
057         *
058         * @return a list of migration versions that have been successfully run against the database
059         */
060        Set<MigrationVersion> fetchAppliedMigrationVersions() {
061                return myHapiMigrationDao.fetchSuccessfulMigrationVersions();
062        }
063
064        /**
065         *
066         * @return the most recent version that was run against the database (used for logging purposes)
067         */
068        public String getLatestAppliedVersion() {
069                return fetchAppliedMigrationVersions().stream()
070                                .sorted()
071                                .map(MigrationVersion::toString)
072                                .reduce((first, second) -> second)
073                                .orElse(UNKNOWN_VERSION);
074        }
075
076        /**
077         * Save a migration task to the database
078         */
079        public void saveTask(BaseTask theBaseTask, Integer theMillis, boolean theSuccess) {
080                HapiMigrationEntity entity = HapiMigrationEntity.fromBaseTask(theBaseTask);
081                entity.setExecutionTime(theMillis);
082                entity.setSuccess(theSuccess);
083                myHapiMigrationDao.save(entity);
084        }
085
086        /**
087         * Create the migration table if it does not already exist
088         */
089        public boolean createMigrationTableIfRequired() {
090                return myHapiMigrationDao.createMigrationTableIfRequired();
091        }
092
093        /**
094         *
095         * @param  theLockDescription value of the Description for the lock record
096         * @return true if the record was successfully deleted
097         */
098        public boolean deleteLockRecord(String theLockDescription) {
099                verifyNoOtherLocksPresent(theLockDescription);
100
101                // Remove the locking row
102                return myHapiMigrationDao.deleteLockRecord(HapiMigrationLock.LOCK_PID, theLockDescription);
103        }
104
105        void verifyNoOtherLocksPresent(String theLockDescription) {
106                Optional<HapiMigrationEntity> otherLockFound =
107                                myHapiMigrationDao.findFirstByPidAndNotDescription(HapiMigrationLock.LOCK_PID, theLockDescription);
108
109                // Check that there are no other locks in place. This should not happen!
110                if (otherLockFound.isPresent()) {
111                        throw new HapiMigrationException(
112                                        Msg.code(2152) + "Internal error: on unlocking, a competing lock was found");
113                }
114        }
115
116        public boolean insertLockRecord(String theLockDescription) {
117                HapiMigrationEntity entity = new HapiMigrationEntity();
118                entity.setPid(HapiMigrationLock.LOCK_PID);
119                entity.setType(LOCK_TYPE);
120                entity.setDescription(theLockDescription);
121                entity.setExecutionTime(0);
122                entity.setSuccess(true);
123
124                return myHapiMigrationDao.save(entity);
125        }
126
127        public Optional<HapiMigrationEntity> findFirstByPidAndNotDescription(
128                        Integer theLockPid, String theLockDescription) {
129                return myHapiMigrationDao.findFirstByPidAndNotDescription(theLockPid, theLockDescription);
130        }
131}