diff --git a/liquibase-core/src/main/java/liquibase/command/core/RegisterChangelogCommandStep.java b/liquibase-core/src/main/java/liquibase/command/core/RegisterChangelogCommandStep.java index 2e41ea8a89b..9ac553aa6f4 100644 --- a/liquibase-core/src/main/java/liquibase/command/core/RegisterChangelogCommandStep.java +++ b/liquibase-core/src/main/java/liquibase/command/core/RegisterChangelogCommandStep.java @@ -5,6 +5,7 @@ import liquibase.changelog.ChangelogRewriter; import liquibase.changelog.DatabaseChangeLog; import liquibase.command.*; +import liquibase.exception.ChangeLogAlreadyRegisteredException; import liquibase.exception.CommandExecutionException; import liquibase.exception.CommandLineParsingException; import liquibase.exception.LiquibaseException; @@ -21,6 +22,7 @@ import liquibase.util.StringUtil; import java.io.PrintWriter; +import java.nio.file.Paths; import java.util.*; public class RegisterChangelogCommandStep extends AbstractCommandStep { @@ -52,49 +54,52 @@ public String[][] defineCommandNames() { @Override public void run(CommandResultsBuilder resultsBuilder) throws Exception { - try (PrintWriter output = new PrintWriter(resultsBuilder.getOutputStream())) { + CommandScope commandScope = resultsBuilder.getCommandScope(); - final UIService ui = Scope.getCurrentScope().getUI(); - CommandScope commandScope = resultsBuilder.getCommandScope(); + // + // Access the HubService + // Stop if we do no have a key + // + final HubServiceFactory hubServiceFactory = Scope.getCurrentScope().getSingleton(HubServiceFactory.class); + if (!hubServiceFactory.isOnline()) { + throw new CommandExecutionException("The command registerChangeLog requires communication with Liquibase Hub, \nwhich is prevented by liquibase.hub.mode='off'. \nPlease set to 'all' or 'meta' and try again. \nLearn more at https://hub.liquibase.com"); + } - // - // Access the HubService - // Stop if we do no have a key - // - final HubServiceFactory hubServiceFactory = Scope.getCurrentScope().getSingleton(HubServiceFactory.class); - if (!hubServiceFactory.isOnline()) { - throw new CommandExecutionException("The command registerChangeLog requires communication with Liquibase Hub, \nwhich is prevented by liquibase.hub.mode='off'. \nPlease set to 'all' or 'meta' and try again. \nLearn more at https://hub.liquibase.com"); - } + // + // Check for existing changeLog file + // + String changeLogFile = commandScope.getArgumentValue(CHANGELOG_FILE_ARG); + UUID hubProjectId = commandScope.getArgumentValue(HUB_PROJECT_ID_ARG); + String hubProjectName = commandScope.getArgumentValue(HUB_PROJECT_NAME_ARG); - // - // Check for existing changeLog file - // - final HubService service = Scope.getCurrentScope().getSingleton(HubServiceFactory.class).getService(); - HubChangeLog hubChangeLog; - String changeLogFile = commandScope.getArgumentValue(CHANGELOG_FILE_ARG); - UUID hubProjectId = commandScope.getArgumentValue(HUB_PROJECT_ID_ARG); - String hubProjectName = commandScope.getArgumentValue(HUB_PROJECT_NAME_ARG); + doRegisterChangelog(changeLogFile, hubProjectId, hubProjectName, resultsBuilder, false); + } + + public void doRegisterChangelog(String changelogFilepath, UUID hubProjectId, String hubProjectName, CommandResultsBuilder resultsBuilder, boolean skipPromptIfOneProject) throws LiquibaseException, CommandLineParsingException { + try (PrintWriter output = new PrintWriter(resultsBuilder.getOutputStream())) { + HubChangeLog hubChangeLog; + final HubService service = Scope.getCurrentScope().getSingleton(HubServiceFactory.class).getService(); // - // CHeck for existing changeLog file + // Check for existing changeLog file using the untouched changelog filepath // - DatabaseChangeLog databaseChangeLog = parseChangeLogFile(changeLogFile); + DatabaseChangeLog databaseChangeLog = parseChangeLogFile(changelogFilepath); if (databaseChangeLog == null) { - throw new CommandExecutionException("Cannot parse "+changeLogFile); + throw new CommandExecutionException("Cannot parse "+changelogFilepath); } final String changeLogId = databaseChangeLog.getChangeLogId(); if (changeLogId != null) { hubChangeLog = service.getHubChangeLog(UUID.fromString(changeLogId)); if (hubChangeLog != null) { - throw new CommandExecutionException("Changelog '" + changeLogFile + + throw new CommandExecutionException("Changelog '" + changelogFilepath + "' is already registered with changeLogId '" + changeLogId + "' to project '" + hubChangeLog.getProject().getName() + "' with project ID '" + hubChangeLog.getProject().getId().toString() + "'.\n" + - "For more information visit https://docs.liquibase.com."); + "For more information visit https://docs.liquibase.com.", new ChangeLogAlreadyRegisteredException(hubChangeLog)); } else { - throw new CommandExecutionException("Changelog '" + changeLogFile + + throw new CommandExecutionException("Changelog '" + changelogFilepath + "' is already registered with changeLogId '" + changeLogId + "'.\n" + - "For more information visit https://docs.liquibase.com."); + "For more information visit https://docs.liquibase.com.", new ChangeLogAlreadyRegisteredException()); } } @@ -118,19 +123,20 @@ public void run(CommandResultsBuilder resultsBuilder) throws Exception { } output.print("\nProject '" + project.getName() + "' created with project ID '" + project.getId() + "'.\n\n"); } else { - project = retrieveOrCreateProject(service, commandScope); + project = retrieveOrCreateProject(service, changelogFilepath, skipPromptIfOneProject); if (project == null) { - throw new CommandExecutionException("Your changelog " + changeLogFile + " was not registered to any Liquibase Hub project. You can still run Liquibase commands, but no data will be saved in your Liquibase Hub account for monitoring or reports. Learn more at https://hub.liquibase.com."); + throw new CommandExecutionException("Your changelog " + changelogFilepath + " was not registered to any Liquibase Hub project. You can still run Liquibase commands, but no data will be saved in your Liquibase Hub account for monitoring or reports. Learn more at https://hub.liquibase.com."); } } // // Go create the Hub Changelog // + String changelogFilename = Paths.get(databaseChangeLog.getFilePath()).getFileName().toString(); HubChangeLog newChangeLog = new HubChangeLog(); newChangeLog.setProject(project); - newChangeLog.setFileName(databaseChangeLog.getFilePath()); - newChangeLog.setName(databaseChangeLog.getFilePath()); + newChangeLog.setFileName(changelogFilename); + newChangeLog.setName(changelogFilename); hubChangeLog = service.createChangeLog(newChangeLog); @@ -139,11 +145,11 @@ public void run(CommandResultsBuilder resultsBuilder) throws Exception { // Add the registered changelog ID to the results so that // the caller can use it // - ChangelogRewriter.ChangeLogRewriterResult changeLogRewriterResult = - ChangelogRewriter.addChangeLogId(changeLogFile, hubChangeLog.getId().toString(), databaseChangeLog); + ChangelogRewriter.ChangeLogRewriterResult changeLogRewriterResult = ChangelogRewriter.addChangeLogId(changelogFilepath, hubChangeLog.getId().toString(), databaseChangeLog); + if (changeLogRewriterResult.success) { Scope.getCurrentScope().getLog(RegisterChangelogCommandStep.class).info(changeLogRewriterResult.message); - output.println("* Changelog file '" + changeLogFile + "' with changelog ID '" + hubChangeLog.getId().toString() + "' has been " + + output.println("* Changelog file '" + changelogFilepath + "' with changelog ID '" + hubChangeLog.getId().toString() + "' has been " + "registered to Project "+project.getName() ); resultsBuilder.addResult("statusCode", 0); resultsBuilder.addResult(REGISTERED_CHANGELOG_ID.getName(), hubChangeLog.getId().toString()); @@ -151,16 +157,18 @@ public void run(CommandResultsBuilder resultsBuilder) throws Exception { } } - private Project retrieveOrCreateProject(HubService service, CommandScope commandScope) throws CommandLineParsingException, LiquibaseException, LiquibaseHubException { + private Project retrieveOrCreateProject(HubService service, String changeLogFile, boolean skipPromptIfOneProject) throws CommandLineParsingException, LiquibaseException, LiquibaseHubException { final UIService ui = Scope.getCurrentScope().getUI(); - String changeLogFile = commandScope.getArgumentValue(CHANGELOG_FILE_ARG); Project project = null; List projects = getProjectsFromHub(); + if (skipPromptIfOneProject && projects.size() == 1) { + return projects.get(0); + } boolean done = false; String input = null; while (!done) { - input = readProjectFromConsole(projects, commandScope); + input = readProjectFromConsole(projects, changeLogFile); try { if (input.equalsIgnoreCase("C")) { String projectName = readProjectNameFromConsole(); @@ -223,11 +231,11 @@ private String readProjectNameFromConsole() throws CommandLineParsingException { return StringUtil.trimToEmpty(input); } - private String readProjectFromConsole(List projects, CommandScope commandScope) throws CommandLineParsingException { + private String readProjectFromConsole(List projects, String changeLogFile) throws CommandLineParsingException { final UIService ui = Scope.getCurrentScope().getUI(); StringBuilder prompt = new StringBuilder("Registering a changelog connects Liquibase operations to a Project for monitoring and reporting.\n"); - prompt.append("Register changelog " + commandScope.getArgumentValue(CHANGELOG_FILE_ARG) + " to an existing Project, or create a new one.\n"); + prompt.append("Register changelog " + changeLogFile + " to an existing Project, or create a new one.\n"); prompt.append("Please make a selection:\n"); diff --git a/liquibase-core/src/main/java/liquibase/database/jvm/JdbcConnection.java b/liquibase-core/src/main/java/liquibase/database/jvm/JdbcConnection.java index a89509a548b..b87b477f4fc 100644 --- a/liquibase-core/src/main/java/liquibase/database/jvm/JdbcConnection.java +++ b/liquibase-core/src/main/java/liquibase/database/jvm/JdbcConnection.java @@ -40,13 +40,18 @@ public int getPriority() { @Override public void open(String url, Driver driverObject, Properties driverProperties) throws DatabaseException { + String driverClassName = driverObject.getClass().getName(); + String errorMessage = "Connection could not be created to " + url + " with driver " + driverClassName; try { this.con = driverObject.connect(url, driverProperties); if (this.con == null) { - throw new DatabaseException("Connection could not be created to " + url + " with driver " + driverObject.getClass().getName() + ". Possibly the wrong driver for the given database URL"); + throw new DatabaseException(errorMessage + ". Possibly the wrong driver for the given database URL"); } } catch (SQLException sqle) { - throw new DatabaseException("Connection could not be created to " + url + " with driver " + driverObject.getClass().getName() + ". " + sqle.getMessage()); + if (driverClassName.equals("org.h2.Driver")) { + errorMessage += ". Make sure your H2 database is active and accessible by opening a new terminal window, run \"liquibase init start-h2\", and then return to this terminal window to run commands"; + } + throw new DatabaseException(errorMessage + ". " + sqle.getMessage()); } } diff --git a/liquibase-core/src/main/java/liquibase/exception/ChangeLogAlreadyRegisteredException.java b/liquibase-core/src/main/java/liquibase/exception/ChangeLogAlreadyRegisteredException.java new file mode 100644 index 00000000000..e59fa0e6d87 --- /dev/null +++ b/liquibase-core/src/main/java/liquibase/exception/ChangeLogAlreadyRegisteredException.java @@ -0,0 +1,27 @@ +package liquibase.exception; + +import liquibase.hub.model.HubChangeLog; + +/** + * Exception class indicating that a particular changelog has already been registered with Hub. + */ +public class ChangeLogAlreadyRegisteredException extends Exception { + + /** + * If present, the changelog metadata from Hub. If null, it can be assumed that the changelog has been registered + * with some organization which the current API key cannot access. + */ + private final HubChangeLog hubChangeLog; + + public ChangeLogAlreadyRegisteredException() { + this(null); + } + + public ChangeLogAlreadyRegisteredException(HubChangeLog hubChangeLog) { + this.hubChangeLog = hubChangeLog; + } + + public HubChangeLog getHubChangeLog() { + return hubChangeLog; + } +} diff --git a/liquibase-core/src/main/java/liquibase/hub/HubService.java b/liquibase-core/src/main/java/liquibase/hub/HubService.java index 82fc1aa8025..bf3b1dad4a7 100644 --- a/liquibase-core/src/main/java/liquibase/hub/HubService.java +++ b/liquibase-core/src/main/java/liquibase/hub/HubService.java @@ -45,6 +45,8 @@ public interface HubService extends Plugin, PrioritizedService { Operation createOperation(String operationType, String operationCommand, HubChangeLog changeLog, Connection connection) throws LiquibaseHubException; + Operation createOperationInOrganization(String operationType, String operationCommand, UUID organizationId) throws LiquibaseHubException; + OperationEvent sendOperationEvent(Operation operation, OperationEvent operationEvent) throws LiquibaseException; /** @@ -56,7 +58,11 @@ public interface HubService extends Plugin, PrioritizedService { */ String shortenLink(String url) throws LiquibaseException; + OperationEvent sendOperationEvent(Operation operation, OperationEvent operationEvent, UUID organizationId) throws LiquibaseException; + void sendOperationChangeEvent(OperationChangeEvent operationChangeEvent) throws LiquibaseException; void sendOperationChanges(OperationChange operationChange) throws LiquibaseHubException; + + CoreInitOnboardingResponse validateOnboardingToken(String token) throws LiquibaseHubException; } \ No newline at end of file diff --git a/liquibase-core/src/main/java/liquibase/hub/HubServiceFactory.java b/liquibase-core/src/main/java/liquibase/hub/HubServiceFactory.java index 17b832bf509..71365e7904f 100644 --- a/liquibase-core/src/main/java/liquibase/hub/HubServiceFactory.java +++ b/liquibase-core/src/main/java/liquibase/hub/HubServiceFactory.java @@ -147,6 +147,11 @@ public Operation createOperation(String operationType, String operationCommand, return null; } + @Override + public Operation createOperationInOrganization(String operationType, String operationCommand, UUID organizationId) throws LiquibaseHubException { + return null; + } + @Override public OperationEvent sendOperationEvent(Operation operation, OperationEvent operationEvent) throws LiquibaseException { return null; @@ -157,6 +162,11 @@ public String shortenLink(String url) throws LiquibaseException { return null; } + @Override + public OperationEvent sendOperationEvent(Operation operation, OperationEvent operationEvent, UUID organizationId) throws LiquibaseException { + return null; + } + @Override public void sendOperationChangeEvent(OperationChangeEvent operationChangeEvent) throws LiquibaseException { @@ -166,5 +176,10 @@ public void sendOperationChangeEvent(OperationChangeEvent operationChangeEvent) public void sendOperationChanges(OperationChange operationChange) throws LiquibaseHubException { } + + @Override + public CoreInitOnboardingResponse validateOnboardingToken(String token) throws LiquibaseHubException { + return null; + } } } diff --git a/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java b/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java index e21a5bef5f8..b4b3def74e1 100644 --- a/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java +++ b/liquibase-core/src/main/java/liquibase/hub/core/MockHubService.java @@ -18,11 +18,15 @@ public class MockHubService implements HubService { public static UUID failUUID = UUID.randomUUID(); public static UUID notFoundChangeLogUUID = UUID.randomUUID(); public static Date operationCreateDate; + public static String apiKey = UUID.randomUUID().toString(); + public static UUID organizationId = UUID.randomUUID(); + public static Integer numberOfProjectsInList = null; + public static HubChangeLog lastCreatedChangelog = null; public List returnProjects = new ArrayList<>(); public List returnConnections; public List returnChangeLogs = new ArrayList<>(); - public SortedMap sentObjects = new TreeMap<>(); + public static SortedMap sentObjects = new TreeMap<>(); public boolean online = true; @Override @@ -59,21 +63,34 @@ public HubChangeLog createChangeLog(HubChangeLog hubChangeLog) throws LiquibaseE randomUUID = UUID.randomUUID(); } hubChangeLog.setId(randomUUID); + lastCreatedChangelog = hubChangeLog; return hubChangeLog; } @Override public List getProjects() throws LiquibaseHubException { - Project project1 = new Project(); - project1.setId(UUID.fromString("72e4bc5a-5404-45be-b9e1-280a80c98cbf")); - project1.setName("Project 1"); - project1.setCreateDate(new Date()); - - Project project2 = new Project(); - project2.setId(UUID.randomUUID()); - project2.setName("Project 2"); - project2.setCreateDate(new Date()); - return Arrays.asList(project1, project2); + if (numberOfProjectsInList == null) { + Project project1 = new Project(); + project1.setId(UUID.fromString("72e4bc5a-5404-45be-b9e1-280a80c98cbf")); + project1.setName("Project 1"); + project1.setCreateDate(new Date()); + + Project project2 = new Project(); + project2.setId(UUID.randomUUID()); + project2.setName("Project 2"); + project2.setCreateDate(new Date()); + return Arrays.asList(project1, project2); + } else { + List projects = new ArrayList<>(numberOfProjectsInList); + for(int i = 0; i < numberOfProjectsInList; i++) { + Project project = new Project(); + project.setId(UUID.randomUUID()); + project.setName("Project " + i + 1); + project.setCreateDate(new Date()); + projects.add(project); + } + return projects; + } } @Override @@ -156,6 +173,11 @@ public Operation createOperation(String operationType, String operationCommand, return null; } + @Override + public Operation createOperationInOrganization(String operationType, String operationCommand, UUID organizationId) throws LiquibaseHubException { + return null; + } + @Override public OperationEvent sendOperationEvent(Operation operation, OperationEvent operationEvent) throws LiquibaseException { return null; @@ -176,6 +198,25 @@ public String shortenLink(String url) throws LiquibaseException { return null; } + @Override + public OperationEvent sendOperationEvent(Operation operation, OperationEvent operationEvent, UUID organizationId) throws LiquibaseException { + sentObjects.computeIfAbsent("sendOperationEvent", k -> new ArrayList<>()).add(operationEvent); + return null; + } + + @Override + public CoreInitOnboardingResponse validateOnboardingToken(String token) throws LiquibaseHubException { + CoreInitOnboardingResponse response = new CoreInitOnboardingResponse(); + ApiKey ak = new ApiKey(); + ak.setKey(apiKey); + response.setApiKey(ak); + Organization organization = new Organization(); + organization.setId(organizationId); + organization.setName("neworg"); + response.setOrganization(organization); + return response; + } + public void reset() { randomUUID = UUID.randomUUID(); diff --git a/liquibase-core/src/main/java/liquibase/hub/core/StandardHubService.java b/liquibase-core/src/main/java/liquibase/hub/core/StandardHubService.java index db78a0afde5..acceb6eb871 100644 --- a/liquibase-core/src/main/java/liquibase/hub/core/StandardHubService.java +++ b/liquibase-core/src/main/java/liquibase/hub/core/StandardHubService.java @@ -402,7 +402,28 @@ public HubChangeLog getHubChangeLog(UUID changeLogId, String includeStatus) { @Override public Operation createOperation(String operationType, String operationCommand, HubChangeLog changeLog, Connection connection) throws LiquibaseHubException { + final IntegrationDetails integrationDetails = Scope.getCurrentScope().get("integrationDetails", IntegrationDetails.class); + + Map requestBody = new HashMap<>(); + requestBody.put("connectionId", connection.getId()); + requestBody.put("connectionJdbcUrl", connection.getJdbcUrl()); + requestBody.put("projectId", connection.getProject() == null ? null : connection.getProject().getId()); + requestBody.put("changelogId", changeLog == null ? null : changeLog.getId()); + requestBody.put("operationType", operationType); + requestBody.put("operationCommand", operationCommand); + requestBody.put("operationStatusType", "PASS"); + requestBody.put("statusMessage", operationType); + requestBody.put("clientMetadata", getClientMetadata(integrationDetails)); + if (integrationDetails!=null) { + requestBody.put("operationParameters", getCleanOperationParameters(integrationDetails.getParameters())); + } + final Operation operation = http.doPost("/api/v1/operations", requestBody, Operation.class); + operation.setConnection(connection); + return operation; + } + + private Map getClientMetadata(IntegrationDetails integrationDetails) { String hostName; try { hostName = InetAddress.getLocalHost().getHostName(); @@ -411,8 +432,6 @@ public Operation createOperation(String operationType, String operationCommand, hostName = null; } - final IntegrationDetails integrationDetails = Scope.getCurrentScope().get("integrationDetails", IntegrationDetails.class); - Map clientMetadata = new HashMap<>(); clientMetadata.put("liquibaseVersion", LiquibaseUtil.getBuildVersion()); clientMetadata.put("hostName", hostName); @@ -420,23 +439,24 @@ public Operation createOperation(String operationType, String operationCommand, if (integrationDetails != null) { clientMetadata.put("clientInterface", integrationDetails.getName()); } + return clientMetadata; + } + + @Override + public Operation createOperationInOrganization(String operationType, String operationCommand, UUID organizationId) throws LiquibaseHubException { + final IntegrationDetails integrationDetails = Scope.getCurrentScope().get("integrationDetails", IntegrationDetails.class); + Map requestBody = new HashMap<>(); - requestBody.put("connectionId", connection.getId()); - requestBody.put("connectionJdbcUrl", connection.getJdbcUrl()); - requestBody.put("projectId", connection.getProject() == null ? null : connection.getProject().getId()); - requestBody.put("changelogId", changeLog == null ? null : changeLog.getId()); requestBody.put("operationType", operationType); requestBody.put("operationCommand", operationCommand); requestBody.put("operationStatusType", "PASS"); requestBody.put("statusMessage", operationType); - requestBody.put("clientMetadata", clientMetadata); + requestBody.put("clientMetadata", getClientMetadata(integrationDetails)); if (integrationDetails!=null) { requestBody.put("operationParameters", getCleanOperationParameters(integrationDetails.getParameters())); } - final Operation operation = http.doPost("/api/v1/operations", requestBody, Operation.class); - operation.setConnection(connection); - return operation; + return http.doPost("/api/v1/organizations/" + organizationId.toString() + "/operations", requestBody, Operation.class); } protected Map getCleanOperationParameters(Map originalParams) { @@ -482,6 +502,11 @@ protected Map getCleanOperationParameters(Map or public OperationEvent sendOperationEvent(Operation operation, OperationEvent operationEvent) throws LiquibaseException { final Organization organization = getOrganization(); + return sendOperationEvent(operation, operationEvent, organization.getId()); + } + + @Override + public OperationEvent sendOperationEvent(Operation operation, OperationEvent operationEvent, UUID organizationId) throws LiquibaseException { Map requestParams = new HashMap<>(); requestParams.put("eventType", operationEvent.getEventType()); requestParams.put("startDate", operationEvent.getStartDate()); @@ -500,8 +525,7 @@ public OperationEvent sendOperationEvent(Operation operation, OperationEvent ope } } - return http.doPost("/api/v1/organizations/" + organization.getId() + "/projects/" + operation.getConnection().getProject().getId() + "/operations/" + operation.getId() + "/operation-events", requestParams, OperationEvent.class); - + return http.doPost("/api/v1/organizations/" + organizationId + ((operation.getConnection() == null || operation.getConnection().getProject() == null) ? "" : "/projects/" + operation.getConnection().getProject().getId()) + "/operations/" + operation.getId() + "/operation-events", requestParams, OperationEvent.class); } private Date convertDateToUTC(Date dateInString) { @@ -561,6 +585,18 @@ public void sendOperationChanges(OperationChange operationChange) throws Liquiba hubChangeList, ArrayList.class); } + @Override + public CoreInitOnboardingResponse validateOnboardingToken(String token) throws LiquibaseHubException { + // This call does not use authentication, so we purposefully override any existing hub API key with empty string. + Map hubApiKeyScopeValues = Collections.singletonMap(HubConfiguration.LIQUIBASE_HUB_API_KEY.getKey(), ""); + try { + return Scope.child(hubApiKeyScopeValues, () -> http.doPost("/api/v1/init", Collections.singletonMap("onboardingToken", token), CoreInitOnboardingResponse.class)); + } catch (Exception e) { + Scope.getCurrentScope().getLog(getClass()).severe("Failed to call Hub to validate onboarding token", e); + throw new LiquibaseHubException(e); + } + } + /** * Converts an object to a search string. * Any properties with non-null values are used as search arguments. diff --git a/liquibase-core/src/main/java/liquibase/hub/model/ApiKey.java b/liquibase-core/src/main/java/liquibase/hub/model/ApiKey.java new file mode 100644 index 00000000000..c6dbc977ed4 --- /dev/null +++ b/liquibase-core/src/main/java/liquibase/hub/model/ApiKey.java @@ -0,0 +1,14 @@ +package liquibase.hub.model; + +public class ApiKey { + + private String key; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } +} diff --git a/liquibase-core/src/main/java/liquibase/hub/model/CoreInitOnboardingResponse.java b/liquibase-core/src/main/java/liquibase/hub/model/CoreInitOnboardingResponse.java new file mode 100644 index 00000000000..20679dbffdc --- /dev/null +++ b/liquibase-core/src/main/java/liquibase/hub/model/CoreInitOnboardingResponse.java @@ -0,0 +1,22 @@ +package liquibase.hub.model; + +public class CoreInitOnboardingResponse { + private ApiKey apiKey; + private Organization organization; + + public ApiKey getApiKey() { + return apiKey; + } + + public void setApiKey(ApiKey apiKey) { + this.apiKey = apiKey; + } + + public Organization getOrganization() { + return organization; + } + + public void setOrganization(Organization organization) { + this.organization = organization; + } +} diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy index 09e4ef166b7..1e6345bfac3 100644 --- a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/command/CommandTests.groovy @@ -277,6 +277,9 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} (Scope.Attr.logService.name()) : logService ], { try { + if (testDef.commandTestDefinition.beforeMethodInvocation != null) { + testDef.commandTestDefinition.beforeMethodInvocation.call() + } def returnValue = commandScope.execute() assert testDef.expectedException == null : "An exception was expected but the command completed successfully" return returnValue @@ -337,6 +340,9 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} if (testDef.expectFileToNotExist != null) { assert !testDef.expectFileToNotExist.exists(): "File '${testDef.expectFileToNotExist.getAbsolutePath()}' should not exist" } + if (testDef.expectations != null) { + testDef.expectations.call() + } } finally { if (testDef.setup != null) { for (def setup : testDef.setup) { @@ -578,7 +584,13 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} * the same scope as the command that is run for the test. This method will always be called, regardless of * exceptions thrown from within the test. */ - Callable afterMethodInvocation + Closure afterMethodInvocation + /** + * An optional method that will be called before the execution of each run command. This is executed within + * the same scope as the command that is run for the test. Exceptions thrown from this method will cause the + * test to fail. + */ + Closure beforeMethodInvocation void run(@DelegatesTo(RunTestDefinition) Closure testClosure) { run(null, testClosure) @@ -627,6 +639,7 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} private Map arguments = new HashMap<>() private Map expectedFileContent = new HashMap<>() private Map expectedDatabaseContent = new HashMap<>() + private Closure expectations = null; private List setup @@ -689,6 +702,10 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} this.expectedFileContent = content } + def setExpectations(Closure expectations) { + this.expectations = expectations; + } + def setExpectedDatabaseContent(Map content) { this.expectedDatabaseContent = content } @@ -876,6 +893,10 @@ Long Description: ${commandDefinition.getLongDescription() ?: "NOT SET"} this.setups.add(new SetupCreateTempResources(originalFile, newFile, baseDir)) } + void registerValueProvider(Closure configurationValueProvider) { + this.setups.add(new SetupConfigurationValueProvider(configurationValueProvider)) + } + /** * @param fileLastModifiedDate if not null, the newly created file's last modified date will be set to this value */ diff --git a/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupConfigurationValueProvider.java b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupConfigurationValueProvider.java new file mode 100644 index 00000000000..60fbd6e1f94 --- /dev/null +++ b/liquibase-extension-testing/src/main/groovy/liquibase/extension/testing/setup/SetupConfigurationValueProvider.java @@ -0,0 +1,41 @@ +package liquibase.extension.testing.setup; + +import groovy.lang.Closure; +import liquibase.Scope; +import liquibase.configuration.ConfigurationValueProvider; +import liquibase.configuration.LiquibaseConfiguration; + +/** + * Setup class allowing registration of custom configuration value providers, and also providing automatic cleanup. + */ +public class SetupConfigurationValueProvider extends TestSetup { + + private final Closure configurationValueProvider; + /** + * Once the configuration value provider has been instantiated, save it here. + */ + private ConfigurationValueProvider actualConfigurationValueProvider = null; + + /** + * Create a new configuration value provider. + * @param configurationValueProvider This is a closure so that instantiation can be "lazy", such that you can + * create configuration value providers which depend on other TestSetup resources, + * like newly created files, for example. + */ + public SetupConfigurationValueProvider(Closure configurationValueProvider) { + this.configurationValueProvider = configurationValueProvider; + } + + @Override + public void setup(TestSetupEnvironment testSetupEnvironment) throws Exception { + final LiquibaseConfiguration liquibaseConfiguration = Scope.getCurrentScope().getSingleton(LiquibaseConfiguration.class); + actualConfigurationValueProvider = configurationValueProvider.call(); + liquibaseConfiguration.registerProvider(actualConfigurationValueProvider); + } + + @Override + public void cleanup() { + LiquibaseConfiguration lbConf = Scope.getCurrentScope().getSingleton(LiquibaseConfiguration.class); + lbConf.unregisterProvider(actualConfigurationValueProvider); + } +} diff --git a/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/MavenUtils.java b/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/MavenUtils.java index e3e93788d9f..51573d7ead1 100644 --- a/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/MavenUtils.java +++ b/liquibase-maven-plugin/src/main/java/org/liquibase/maven/plugins/MavenUtils.java @@ -171,42 +171,6 @@ private static void addFile(Set urls, File f, Log log, boolean verbose) } } - public static Connection getDatabaseConnection(ClassLoader classLoader, - String driver, - String url, - String username, - String password) - throws LiquibaseException { - Driver dbDriver = null; - try { - dbDriver = (Driver) Class.forName(driver, - true, - classLoader).getConstructor().newInstance(); - } catch (ClassNotFoundException e) { - throw new LiquibaseException("Missing Class '" + e.getMessage() + "'. Database " - + "driver may not be included in the project " - + "dependencies or with wrong scope."); - } catch (ReflectiveOperationException e) { - throw new LiquibaseException("Failed to load JDBC driver, " + driver, e); - } - - Properties info = new Properties(); - info.put("user", username); - info.put("password", password); - try { - Connection connection = dbDriver.connect(url, info); - if (connection == null) { - throw new LiquibaseException("Connection could not be created to " + url - + " with driver " + dbDriver.getClass().getName() - + ". Possibly the wrong driver for the given " - + "database URL"); - } - return connection; - } catch (SQLException e) { - throw new LiquibaseException(e); - } - } - /** * Recursively searches for the field specified by the fieldName in the class and all * the super classes until it either finds it, or runs out of parents.