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.tasks.api;
021
022import ca.uhn.fhir.i18n.Msg;
023import ca.uhn.fhir.jpa.migrate.MigrationTaskList;
024import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask;
025import com.google.common.collect.Multimap;
026import com.google.common.collect.MultimapBuilder;
027import jakarta.annotation.Nonnull;
028import org.apache.commons.lang3.EnumUtils;
029import org.apache.commons.lang3.Validate;
030import org.flywaydb.core.api.MigrationVersion;
031
032import java.util.Collection;
033
034import static java.util.Objects.nonNull;
035
036public class BaseMigrationTasks<T extends Enum> {
037        MigrationVersion lastVersion;
038        private Multimap<T, BaseTask> myTasks =
039                        MultimapBuilder.hashKeys().arrayListValues().build();
040
041        @SuppressWarnings("unchecked")
042        public MigrationTaskList getTaskList(@Nonnull T theFrom, @Nonnull T theTo) {
043                Validate.notNull(theFrom);
044                Validate.notNull(theTo);
045                Validate.isTrue(theFrom.ordinal() < theTo.ordinal(), "From version must be lower than to version");
046
047                MigrationTaskList retVal = new MigrationTaskList();
048                for (Object nextVersion : EnumUtils.getEnumList(theFrom.getClass())) {
049                        if (((T) nextVersion).ordinal() <= theFrom.ordinal()) {
050                                continue;
051                        }
052                        if (((T) nextVersion).ordinal() > theTo.ordinal()) {
053                                continue;
054                        }
055
056                        Collection<BaseTask> nextValues = myTasks.get((T) nextVersion);
057                        retVal.addAll(nextValues);
058                }
059
060                return retVal;
061        }
062
063        public Builder forVersion(T theRelease) {
064                IAcceptsTasks sink = theTask -> {
065                        theTask.validate();
066                        myTasks.put(theRelease, theTask);
067                };
068                return new Builder(toReleaseName(theRelease), sink);
069        }
070
071        @Nonnull
072        protected String toReleaseName(T theRelease) {
073                return theRelease.name();
074        }
075
076        public MigrationTaskList getAllTasks(T... theVersionEnumValues) {
077                MigrationTaskList retval = new MigrationTaskList();
078                for (T nextVersion : theVersionEnumValues) {
079                        Collection<BaseTask> nextValues = myTasks.get(nextVersion);
080                        if (nextValues != null) {
081                                validate(nextValues);
082                                retval.addAll(nextValues);
083                        }
084                }
085
086                return retval;
087        }
088
089        public boolean hasTasksForVersion(T theRelease) {
090                Collection<BaseTask> baseTasks = myTasks.get(theRelease);
091                return nonNull(baseTasks) && !baseTasks.isEmpty();
092        }
093
094        protected BaseTask getTaskWithVersion(String theMigrationVersion) {
095                // First normalize the version number
096                String expectedVersion =
097                                MigrationVersion.fromVersion(theMigrationVersion).getVersion();
098
099                return myTasks.values().stream()
100                                .filter(task -> expectedVersion.equals(task.getMigrationVersion()))
101                                .findFirst()
102                                .get();
103        }
104
105        void validate(Collection<BaseTask> theTasks) {
106                for (BaseTask task : theTasks) {
107                        task.validateVersion();
108                        String version = task.getMigrationVersion();
109                        MigrationVersion migrationVersion = MigrationVersion.fromVersion(version);
110                        if (lastVersion != null) {
111                                if (migrationVersion.compareTo(lastVersion) <= 0) {
112                                        throw new IllegalStateException(
113                                                        Msg.code(51) + "Migration version " + migrationVersion + " found after migration version "
114                                                                        + lastVersion + ".  Migrations need to be in order by version number.");
115                                }
116                        }
117                        lastVersion = migrationVersion;
118                }
119        }
120
121        public interface IAcceptsTasks {
122                void addTask(BaseTask theTask);
123        }
124}