stub out all migrations if an empty schema was initialized (#1975)
This commit is contained in:
parent
1449b9cb0a
commit
50ae07acaa
|
@ -35,6 +35,7 @@ public abstract class BaseMigrator implements IMigrator {
|
||||||
private boolean myDryRun;
|
private boolean myDryRun;
|
||||||
private boolean myNoColumnShrink;
|
private boolean myNoColumnShrink;
|
||||||
private boolean myOutOfOrderPermitted;
|
private boolean myOutOfOrderPermitted;
|
||||||
|
private boolean mySchemaWasInitialized;
|
||||||
private DriverTypeEnum myDriverType;
|
private DriverTypeEnum myDriverType;
|
||||||
private DataSource myDataSource;
|
private DataSource myDataSource;
|
||||||
private List<BaseTask.ExecutedStatement> myExecutedStatements = new ArrayList<>();
|
private List<BaseTask.ExecutedStatement> myExecutedStatements = new ArrayList<>();
|
||||||
|
@ -111,4 +112,13 @@ public abstract class BaseMigrator implements IMigrator {
|
||||||
}
|
}
|
||||||
return statementBuilder;
|
return statementBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isSchemaWasInitialized() {
|
||||||
|
return mySchemaWasInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseMigrator setSchemaWasInitialized(boolean theSchemaWasInitialized) {
|
||||||
|
mySchemaWasInitialized = theSchemaWasInitialized;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.migrate;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask;
|
import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask;
|
||||||
|
import ca.uhn.fhir.jpa.migrate.taskdef.InitializeSchemaTask;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import org.flywaydb.core.api.MigrationVersion;
|
import org.flywaydb.core.api.MigrationVersion;
|
||||||
import org.flywaydb.core.api.migration.Context;
|
import org.flywaydb.core.api.migration.Context;
|
||||||
|
@ -32,14 +33,14 @@ import java.sql.SQLException;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
|
||||||
public class FlywayMigration implements JavaMigration {
|
public class FlywayMigrationTask implements JavaMigration {
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(FlywayMigration.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(FlywayMigrationTask.class);
|
||||||
|
|
||||||
private final BaseTask myTask;
|
private final BaseTask myTask;
|
||||||
private final FlywayMigrator myFlywayMigrator;
|
private final FlywayMigrator myFlywayMigrator;
|
||||||
private DriverTypeEnum.ConnectionProperties myConnectionProperties;
|
private DriverTypeEnum.ConnectionProperties myConnectionProperties;
|
||||||
|
|
||||||
public FlywayMigration(BaseTask theTask, FlywayMigrator theFlywayMigrator) {
|
public FlywayMigrationTask(BaseTask theTask, FlywayMigrator theFlywayMigrator) {
|
||||||
myTask = theTask;
|
myTask = theTask;
|
||||||
myFlywayMigrator = theFlywayMigrator;
|
myFlywayMigrator = theFlywayMigrator;
|
||||||
}
|
}
|
||||||
|
@ -76,8 +77,7 @@ public class FlywayMigration implements JavaMigration {
|
||||||
myTask.setNoColumnShrink(myFlywayMigrator.isNoColumnShrink());
|
myTask.setNoColumnShrink(myFlywayMigrator.isNoColumnShrink());
|
||||||
myTask.setConnectionProperties(myConnectionProperties);
|
myTask.setConnectionProperties(myConnectionProperties);
|
||||||
try {
|
try {
|
||||||
myTask.execute();
|
executeTask();
|
||||||
myFlywayMigrator.addExecutedStatements(myTask.getExecutedStatements());
|
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
String description = myTask.getDescription();
|
String description = myTask.getDescription();
|
||||||
if (isBlank(description)) {
|
if (isBlank(description)) {
|
||||||
|
@ -88,6 +88,20 @@ public class FlywayMigration implements JavaMigration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void executeTask() throws SQLException {
|
||||||
|
if (myFlywayMigrator.isSchemaWasInitialized() && !(myTask instanceof InitializeSchemaTask)) {
|
||||||
|
// Empty schema was initialized, stub out this non-schema-init task since we're starting with a fully migrated schema
|
||||||
|
myTask.setDoNothing(true);
|
||||||
|
}
|
||||||
|
myTask.execute();
|
||||||
|
if (myTask.initializedSchema()) {
|
||||||
|
ourLog.info("Empty schema was Initialized. Stubbing out all following migration tasks that are not Schema Initializations.");
|
||||||
|
myFlywayMigrator.setSchemaWasInitialized(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
myFlywayMigrator.addExecutedStatements(myTask.getExecutedStatements());
|
||||||
|
}
|
||||||
|
|
||||||
public void setConnectionProperties(DriverTypeEnum.ConnectionProperties theConnectionProperties) {
|
public void setConnectionProperties(DriverTypeEnum.ConnectionProperties theConnectionProperties) {
|
||||||
myConnectionProperties = theConnectionProperties;
|
myConnectionProperties = theConnectionProperties;
|
||||||
}
|
}
|
|
@ -40,7 +40,7 @@ public class FlywayMigrator extends BaseMigrator {
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(FlywayMigrator.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(FlywayMigrator.class);
|
||||||
|
|
||||||
private final String myMigrationTableName;
|
private final String myMigrationTableName;
|
||||||
private List<FlywayMigration> myTasks = new ArrayList<>();
|
private List<FlywayMigrationTask> myTasks = new ArrayList<>();
|
||||||
|
|
||||||
public FlywayMigrator(String theMigrationTableName, DataSource theDataSource, DriverTypeEnum theDriverType) {
|
public FlywayMigrator(String theMigrationTableName, DataSource theDataSource, DriverTypeEnum theDriverType) {
|
||||||
this(theMigrationTableName);
|
this(theMigrationTableName);
|
||||||
|
@ -53,7 +53,7 @@ public class FlywayMigrator extends BaseMigrator {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addTask(BaseTask theTask) {
|
public void addTask(BaseTask theTask) {
|
||||||
myTasks.add(new FlywayMigration(theTask, this));
|
myTasks.add(new FlywayMigrationTask(theTask, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -80,7 +80,7 @@ public class FlywayMigrator extends BaseMigrator {
|
||||||
.javaMigrations(myTasks.toArray(new JavaMigration[0]))
|
.javaMigrations(myTasks.toArray(new JavaMigration[0]))
|
||||||
.callbacks(getCallbacks().toArray(new Callback[0]))
|
.callbacks(getCallbacks().toArray(new Callback[0]))
|
||||||
.load();
|
.load();
|
||||||
for (FlywayMigration task : myTasks) {
|
for (FlywayMigrationTask task : myTasks) {
|
||||||
task.setConnectionProperties(theConnectionProperties);
|
task.setConnectionProperties(theConnectionProperties);
|
||||||
}
|
}
|
||||||
return flyway;
|
return flyway;
|
||||||
|
|
|
@ -230,6 +230,10 @@ public abstract class BaseTask {
|
||||||
|
|
||||||
protected abstract void generateEquals(EqualsBuilder theBuilder, BaseTask theOtherObject);
|
protected abstract void generateEquals(EqualsBuilder theBuilder, BaseTask theOtherObject);
|
||||||
|
|
||||||
|
public boolean initializedSchema() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static class ExecutedStatement {
|
public static class ExecutedStatement {
|
||||||
private final String mySql;
|
private final String mySql;
|
||||||
private final List<Object> myArguments;
|
private final List<Object> myArguments;
|
||||||
|
|
|
@ -37,6 +37,7 @@ public class InitializeSchemaTask extends BaseTask {
|
||||||
public static final String DESCRIPTION_PREFIX = "Initialize schema for ";
|
public static final String DESCRIPTION_PREFIX = "Initialize schema for ";
|
||||||
|
|
||||||
private final ISchemaInitializationProvider mySchemaInitializationProvider;
|
private final ISchemaInitializationProvider mySchemaInitializationProvider;
|
||||||
|
private boolean myInitializedSchema;
|
||||||
|
|
||||||
public InitializeSchemaTask(String theProductVersion, String theSchemaVersion, ISchemaInitializationProvider theSchemaInitializationProvider) {
|
public InitializeSchemaTask(String theProductVersion, String theSchemaVersion, ISchemaInitializationProvider theSchemaInitializationProvider) {
|
||||||
super(theProductVersion, theSchemaVersion);
|
super(theProductVersion, theSchemaVersion);
|
||||||
|
@ -68,9 +69,18 @@ public class InitializeSchemaTask extends BaseTask {
|
||||||
executeSql(null, nextSql);
|
executeSql(null, nextSql);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mySchemaInitializationProvider.canInitializeSchema()) {
|
||||||
|
myInitializedSchema = true;
|
||||||
|
}
|
||||||
|
|
||||||
logInfo(ourLog, "{} schema for {} initialized successfully", driverType, mySchemaInitializationProvider.getSchemaDescription());
|
logInfo(ourLog, "{} schema for {} initialized successfully", driverType, mySchemaInitializationProvider.getSchemaDescription());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean initializedSchema() {
|
||||||
|
return myInitializedSchema;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void generateEquals(EqualsBuilder theBuilder, BaseTask theOtherObject) {
|
protected void generateEquals(EqualsBuilder theBuilder, BaseTask theOtherObject) {
|
||||||
InitializeSchemaTask otherObject = (InitializeSchemaTask) theOtherObject;
|
InitializeSchemaTask otherObject = (InitializeSchemaTask) theOtherObject;
|
||||||
|
|
|
@ -1139,7 +1139,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
||||||
protected void init330() { // 20180114 - 20180329
|
protected void init330() { // 20180114 - 20180329
|
||||||
Builder version = forVersion(VersionEnum.V3_3_0);
|
Builder version = forVersion(VersionEnum.V3_3_0);
|
||||||
|
|
||||||
version.initializeSchema("20180115.0", new SchemaInitializationProvider("HAPI FHIR", "/ca/uhn/hapi/fhir/jpa/docs/database", "HFJ_RESOURCE"));
|
version.initializeSchema("20180115.0", new SchemaInitializationProvider("HAPI FHIR", "/ca/uhn/hapi/fhir/jpa/docs/database", "HFJ_RESOURCE", true));
|
||||||
|
|
||||||
Builder.BuilderWithTableName hfjResource = version.onTable("HFJ_RESOURCE");
|
Builder.BuilderWithTableName hfjResource = version.onTable("HFJ_RESOURCE");
|
||||||
version.startSectionWithMessage("Starting work on table: " + hfjResource.getTableName());
|
version.startSectionWithMessage("Starting work on table: " + hfjResource.getTableName());
|
||||||
|
|
|
@ -41,15 +41,18 @@ public class SchemaInitializationProvider implements ISchemaInitializationProvid
|
||||||
|
|
||||||
private String mySchemaDescription;
|
private String mySchemaDescription;
|
||||||
private final String mySchemaExistsIndicatorTable;
|
private final String mySchemaExistsIndicatorTable;
|
||||||
|
private final boolean myCanInitializeSchema;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param theSchemaFileClassPath pathname to script used to initialize schema
|
* @param theSchemaFileClassPath pathname to script used to initialize schema
|
||||||
* @param theSchemaExistsIndicatorTable a table name we can use to determine if this schema has already been initialized
|
* @param theSchemaExistsIndicatorTable a table name we can use to determine if this schema has already been initialized
|
||||||
|
* @param theCanInitializeSchema this is a "root" schema initializer that creates the primary tables used by this app
|
||||||
*/
|
*/
|
||||||
public SchemaInitializationProvider(String theSchemaDescription, String theSchemaFileClassPath, String theSchemaExistsIndicatorTable) {
|
public SchemaInitializationProvider(String theSchemaDescription, String theSchemaFileClassPath, String theSchemaExistsIndicatorTable, boolean theCanInitializeSchema) {
|
||||||
mySchemaDescription = theSchemaDescription;
|
mySchemaDescription = theSchemaDescription;
|
||||||
mySchemaFileClassPath = theSchemaFileClassPath;
|
mySchemaFileClassPath = theSchemaFileClassPath;
|
||||||
mySchemaExistsIndicatorTable = theSchemaExistsIndicatorTable;
|
mySchemaExistsIndicatorTable = theSchemaExistsIndicatorTable;
|
||||||
|
myCanInitializeSchema = theCanInitializeSchema;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -129,5 +132,10 @@ public class SchemaInitializationProvider implements ISchemaInitializationProvid
|
||||||
mySchemaDescription = theSchemaDescription;
|
mySchemaDescription = theSchemaDescription;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canInitializeSchema() {
|
||||||
|
return myCanInitializeSchema;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,9 @@ public interface ISchemaInitializationProvider {
|
||||||
|
|
||||||
String getSchemaExistsIndicatorTable();
|
String getSchemaExistsIndicatorTable();
|
||||||
|
|
||||||
String getSchemaDescription();
|
String getSchemaDescription();
|
||||||
|
|
||||||
ISchemaInitializationProvider setSchemaDescription(String theSchemaDescription);
|
ISchemaInitializationProvider setSchemaDescription(String theSchemaDescription);
|
||||||
|
|
||||||
|
boolean canInitializeSchema();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
package ca.uhn.fhir.jpa.migrate;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask;
|
||||||
|
import ca.uhn.fhir.jpa.migrate.taskdef.InitializeSchemaTask;
|
||||||
|
import ca.uhn.fhir.jpa.migrate.tasks.api.ISchemaInitializationProvider;
|
||||||
|
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||||
|
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||||
|
import org.flywaydb.core.api.migration.Context;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class FlywayMigrationTaskTest {
|
||||||
|
@Mock
|
||||||
|
private FlywayMigrator myFlywayMigrator;
|
||||||
|
@Mock
|
||||||
|
private Context myContext;
|
||||||
|
|
||||||
|
TestTask myTestTask = new TestTask();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void schemaInitializedStubsFollowingMigration() {
|
||||||
|
when(myFlywayMigrator.isSchemaWasInitialized()).thenReturn(true);
|
||||||
|
FlywayMigrationTask task = new FlywayMigrationTask(myTestTask, myFlywayMigrator);
|
||||||
|
task.migrate(myContext);
|
||||||
|
assertTrue(myTestTask.isDoNothing());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void schemaNotInitializedStubsFollowingMigration() {
|
||||||
|
when(myFlywayMigrator.isSchemaWasInitialized()).thenReturn(false);
|
||||||
|
FlywayMigrationTask task = new FlywayMigrationTask(myTestTask, myFlywayMigrator);
|
||||||
|
task.migrate(myContext);
|
||||||
|
assertFalse(myTestTask.isDoNothing());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void schemaInitializedStubsFollowingMigrationExceptInitSchemaTask() {
|
||||||
|
when(myFlywayMigrator.isSchemaWasInitialized()).thenReturn(true);
|
||||||
|
InitializeSchemaTask initSchemaTask = new TestInitializeSchemaTask(false);
|
||||||
|
FlywayMigrationTask task = new FlywayMigrationTask(initSchemaTask, myFlywayMigrator);
|
||||||
|
task.migrate(myContext);
|
||||||
|
assertFalse(myTestTask.isDoNothing());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void schemaInitializedSetsInitializedFlag() {
|
||||||
|
InitializeSchemaTask initSchemaTask = new TestInitializeSchemaTask(true);
|
||||||
|
FlywayMigrationTask task = new FlywayMigrationTask(initSchemaTask, myFlywayMigrator);
|
||||||
|
task.migrate(myContext);
|
||||||
|
verify(myFlywayMigrator, times(1)).setSchemaWasInitialized(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void nonInitSchemaInitializedSetsInitializedFlag() {
|
||||||
|
InitializeSchemaTask initSchemaTask = new TestInitializeSchemaTask(false);
|
||||||
|
FlywayMigrationTask task = new FlywayMigrationTask(initSchemaTask, myFlywayMigrator);
|
||||||
|
task.migrate(myContext);
|
||||||
|
verify(myFlywayMigrator, never()).setSchemaWasInitialized(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can't use @Mock since BaseTask.equals is final
|
||||||
|
private class TestTask extends BaseTask {
|
||||||
|
protected TestTask() {
|
||||||
|
super("1", "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doExecute() throws SQLException {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void generateHashCode(HashCodeBuilder theBuilder) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void generateEquals(EqualsBuilder theBuilder, BaseTask theOtherObject) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestInitializeSchemaTask extends InitializeSchemaTask {
|
||||||
|
private final boolean myInitializedSchema;
|
||||||
|
|
||||||
|
public TestInitializeSchemaTask(boolean theInitializedSchema) {
|
||||||
|
super("1", "1", mock(ISchemaInitializationProvider.class));
|
||||||
|
myInitializedSchema = theInitializedSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws SQLException {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean initializedSchema() {
|
||||||
|
return myInitializedSchema;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -56,6 +56,11 @@ public class InitializeSchemaTaskTest extends BaseTest {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canInitializeSchema() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object theO) {
|
public boolean equals(Object theO) {
|
||||||
if (this == theO) return true;
|
if (this == theO) return true;
|
||||||
|
|
Loading…
Reference in New Issue