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