001/*-
002 * #%L
003 * HAPI FHIR Server - SQL Migration
004 * %%
005 * Copyright (C) 2014 - 2023 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
051        public MigrationTaskList diff(MigrationTaskList theTaskList) {
052                Set<MigrationVersion> appliedMigrationVersions = fetchAppliedMigrationVersions();
053
054                return theTaskList.diff(appliedMigrationVersions);
055        }
056
057        /**
058         *
059         * @return a list of migration versions that have been successfully run against the database
060         */
061        Set<MigrationVersion> fetchAppliedMigrationVersions() {
062                return myHapiMigrationDao.fetchSuccessfulMigrationVersions();
063        }
064
065        /**
066         *
067         * @return the most recent version that was run against the database (used for logging purposes)
068         */
069        public String getLatestAppliedVersion() {
070                return fetchAppliedMigrationVersions().stream()
071                        .sorted()
072                        .map(MigrationVersion::toString)
073                        .reduce((first, second) -> second)
074                        .orElse(UNKNOWN_VERSION);
075        }
076
077        /**
078         * Save a migration task to the database
079         */
080        public void saveTask(BaseTask theBaseTask, Integer theMillis, boolean theSuccess) {
081                HapiMigrationEntity entity = HapiMigrationEntity.fromBaseTask(theBaseTask);
082                entity.setExecutionTime(theMillis);
083                entity.setSuccess(theSuccess);
084                myHapiMigrationDao.save(entity);
085        }
086
087        /**
088         * Create the migration table if it does not already exist
089         */
090
091        public void createMigrationTableIfRequired() {
092                myHapiMigrationDao.createMigrationTableIfRequired();
093        }
094
095
096        /**
097         *
098         * @param  theLockDescription value of the Description for the lock record
099         * @return true if the record was successfully deleted
100         */
101        public boolean deleteLockRecord(String theLockDescription) {
102                verifyNoOtherLocksPresent(theLockDescription);
103
104                // Remove the locking row
105                return myHapiMigrationDao.deleteLockRecord(HapiMigrationLock.LOCK_PID, theLockDescription);
106        }
107
108        void verifyNoOtherLocksPresent(String theLockDescription) {
109                Optional<HapiMigrationEntity> otherLockFound = myHapiMigrationDao.findFirstByPidAndNotDescription(HapiMigrationLock.LOCK_PID, theLockDescription);
110
111                // Check that there are no other locks in place. This should not happen!
112                if (otherLockFound.isPresent()) {
113                        throw new HapiMigrationException(Msg.code(2152) + "Internal error: on unlocking, a competing lock was found");
114                }
115        }
116
117        public boolean insertLockRecord(String theLockDescription) {
118                HapiMigrationEntity entity = new HapiMigrationEntity();
119                entity.setPid(HapiMigrationLock.LOCK_PID);
120                entity.setType(LOCK_TYPE);
121                entity.setDescription(theLockDescription);
122                entity.setExecutionTime(0);
123                entity.setSuccess(true);
124
125                return myHapiMigrationDao.save(entity);
126        }
127
128        public Optional<HapiMigrationEntity> findFirstByPidAndNotDescription(Integer theLockPid, String theLockDescription) {
129                return myHapiMigrationDao.findFirstByPidAndNotDescription(theLockPid, theLockDescription);
130        }
131}