diff --git a/src/main/java/liquibase/ext/opensearch/change/HttpRequestChange.java b/src/main/java/liquibase/ext/opensearch/change/HttpRequestChange.java new file mode 100644 index 0000000..b980288 --- /dev/null +++ b/src/main/java/liquibase/ext/opensearch/change/HttpRequestChange.java @@ -0,0 +1,41 @@ +package liquibase.ext.opensearch.change; + +import liquibase.change.AbstractChange; +import liquibase.change.ChangeMetaData; +import liquibase.change.DatabaseChange; +import liquibase.database.Database; +import liquibase.ext.opensearch.statement.HttpRequestStatement; +import liquibase.statement.SqlStatement; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.Optional; + +@DatabaseChange(name = "httpRequest", + description = "Execute an arbitrary HTTP request with the provided payload", + priority = ChangeMetaData.PRIORITY_DATABASE) +@NoArgsConstructor +@Getter +@Setter +public class HttpRequestChange extends AbstractChange { + + private String method; + private String path; + private String body; + + @Override + public String getConfirmationMessage() { + return String.format("executed the %s HTTP request against %s (with a body of size %d)", + this.getMethod(), + this.getPath(), + Optional.ofNullable(this.getBody()).map(String::length).orElse(0)); + } + + @Override + public SqlStatement[] generateStatements(final Database database) { + return new SqlStatement[] { + new HttpRequestStatement(this.getMethod(), this.getPath(), this.getBody()) + }; + } +} diff --git a/src/main/java/liquibase/ext/opensearch/executor/OpenSearchExecutor.java b/src/main/java/liquibase/ext/opensearch/executor/OpenSearchExecutor.java index 7a76fe6..200f6ef 100644 --- a/src/main/java/liquibase/ext/opensearch/executor/OpenSearchExecutor.java +++ b/src/main/java/liquibase/ext/opensearch/executor/OpenSearchExecutor.java @@ -25,6 +25,8 @@ import liquibase.exception.DatabaseException; import liquibase.executor.AbstractExecutor; import liquibase.ext.opensearch.database.OpenSearchLiquibaseDatabase; +import liquibase.ext.opensearch.statement.OpenSearchExecuteStatement; +import liquibase.ext.opensearch.statement.OpenSearchUpdateStatement; import liquibase.logging.Logger; import liquibase.servicelocator.LiquibaseService; import liquibase.sql.visitor.SqlVisitor; @@ -125,13 +127,21 @@ public void execute(final SqlStatement sql) throws DatabaseException { @Override public void execute(final SqlStatement sql, final List sqlVisitors) throws DatabaseException { - throw new DatabaseException("liquibase-opensearch extension cannot execute changeset \n" + - "Unknown type: " + sql.getClass().getName() + - "\nPlease check the following common causes:\n" + - "- Verify change set definitions for common error such as: changeType name, changeSet attributes spelling " + - "(such as runWith, context, etc.), and punctuation.\n" + - "- Verify that changesets have all the required changeset attributes and do not have invalid attributes for the designated change type.\n" + - "- Double-check to make sure your basic setup includes all needed extensions in your Java classpath"); + if (sql instanceof OpenSearchExecuteStatement) { + try { + ((OpenSearchExecuteStatement) sql).execute(getDatabase()); + } catch (final Exception e) { + throw new DatabaseException("Could not execute", e); + } + } else { + throw new DatabaseException("liquibase-opensearch extension cannot execute changeset \n" + + "Unknown type: " + sql.getClass().getName() + + "\nPlease check the following common causes:\n" + + "- Verify change set definitions for common error such as: changeType name, changeSet attributes spelling " + + "(such as runWith, context, etc.), and punctuation.\n" + + "- Verify that changesets have all the required changeset attributes and do not have invalid attributes for the designated change type.\n" + + "- Double-check to make sure your basic setup includes all needed extensions in your Java classpath"); + } } @Override @@ -141,7 +151,15 @@ public int update(final SqlStatement sql) throws DatabaseException { @Override public int update(final SqlStatement sql, final List sqlVisitors) throws DatabaseException { - throw new IllegalArgumentException(); + if (sql instanceof OpenSearchUpdateStatement) { + try { + return ((OpenSearchUpdateStatement) sql).update(getDatabase()); + } catch (final Exception e) { + throw new DatabaseException("Could not execute", e); + } + } else { + throw new IllegalArgumentException(); + } } @Override diff --git a/src/main/java/liquibase/ext/opensearch/statement/HttpRequestStatement.java b/src/main/java/liquibase/ext/opensearch/statement/HttpRequestStatement.java new file mode 100644 index 0000000..6758ad0 --- /dev/null +++ b/src/main/java/liquibase/ext/opensearch/statement/HttpRequestStatement.java @@ -0,0 +1,58 @@ +package liquibase.ext.opensearch.statement; + +import liquibase.Scope; +import liquibase.exception.DatabaseException; +import liquibase.ext.opensearch.database.OpenSearchLiquibaseDatabase; +import liquibase.logging.Logger; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import org.opensearch.client.opensearch.generic.Body; +import org.opensearch.client.opensearch.generic.OpenSearchGenericClient.ClientOptions; +import org.opensearch.client.opensearch.generic.Requests; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Optional; + +@AllArgsConstructor +@Getter +@EqualsAndHashCode(callSuper = true) +public class HttpRequestStatement extends AbstractOpenSearchStatement implements OpenSearchExecuteStatement { + + private final Logger log = Scope.getCurrentScope().getLog(getClass()); + + private String method; + private String path; + private String body; + + @Override + public String toString() { + return String.format("%s HTTP request against %s (with a body of size %d)", + this.getMethod(), + this.getPath(), + Optional.ofNullable(this.getBody()).map(String::length).orElse(0)); + } + + @Override + public void execute(final OpenSearchLiquibaseDatabase database) throws DatabaseException { + log.info(this.toString()); + + final var httpClient = this.getOpenSearchClient(database).generic().withClientOptions(ClientOptions.throwOnHttpErrors()); + + final var request = Requests.builder() + .endpoint(this.getPath()) + .method(this.getMethod()) + .body(Body.from(this.getBody().getBytes(StandardCharsets.UTF_8), "application/json")) + .build(); + + try (final var response = httpClient.execute(request)) { + if (response.getStatus() >= 400) { + throw new DatabaseException(String.format("HTTP request failed with code %d: %s", response.getStatus(), response)); + } + } catch (final IOException e) { + throw new DatabaseException("failed to execute the HTTP request", e); + } + } + +} diff --git a/src/main/java/liquibase/ext/opensearch/statement/OpenSearchExecuteStatement.java b/src/main/java/liquibase/ext/opensearch/statement/OpenSearchExecuteStatement.java new file mode 100644 index 0000000..5661566 --- /dev/null +++ b/src/main/java/liquibase/ext/opensearch/statement/OpenSearchExecuteStatement.java @@ -0,0 +1,30 @@ +package liquibase.ext.opensearch.statement; + +/*- + * #%L + * Liquibase NoSql Extension + * %% + * Copyright (C) 2020 Mastercard + * %% + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import liquibase.exception.DatabaseException; +import liquibase.ext.opensearch.database.OpenSearchLiquibaseDatabase; + +public interface OpenSearchExecuteStatement { + + void execute(final OpenSearchLiquibaseDatabase database) throws DatabaseException; + +} diff --git a/src/main/java/liquibase/ext/opensearch/statement/OpenSearchUpdateStatement.java b/src/main/java/liquibase/ext/opensearch/statement/OpenSearchUpdateStatement.java new file mode 100644 index 0000000..c66fb26 --- /dev/null +++ b/src/main/java/liquibase/ext/opensearch/statement/OpenSearchUpdateStatement.java @@ -0,0 +1,29 @@ +package liquibase.ext.opensearch.statement; + +/*- + * #%L + * Liquibase NoSql Extension + * %% + * Copyright (C) 2020 Mastercard + * %% + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import liquibase.ext.opensearch.database.OpenSearchLiquibaseDatabase; + +public interface OpenSearchUpdateStatement { + + int update(final OpenSearchLiquibaseDatabase database); + +} diff --git a/src/main/resources/META-INF/services/liquibase.change.Change b/src/main/resources/META-INF/services/liquibase.change.Change new file mode 100644 index 0000000..512ff5c --- /dev/null +++ b/src/main/resources/META-INF/services/liquibase.change.Change @@ -0,0 +1 @@ +liquibase.ext.opensearch.change.HttpRequestChange diff --git a/src/test/java/liquibase/ext/opensearch/OpenSearchLiquibaseIT.java b/src/test/java/liquibase/ext/opensearch/OpenSearchLiquibaseIT.java index d7563d1..47d3ec5 100644 --- a/src/test/java/liquibase/ext/opensearch/OpenSearchLiquibaseIT.java +++ b/src/test/java/liquibase/ext/opensearch/OpenSearchLiquibaseIT.java @@ -24,4 +24,11 @@ public void itCreatesTheChangelogAndLockIndices() { assertThat(this.indexExists(this.database.getDatabaseChangeLogTableName())).isTrue(); } + @SneakyThrows + @Test + public void itExecutesAHttpRequestAndCreatesTheIndex() { + this.doLiquibaseUpdate("liquibase/ext/changelog.httprequest.yaml"); + assertThat(this.indexExists("testindex")).isTrue(); + } + } diff --git a/src/test/resources/liquibase/ext/changelog.httprequest.yaml b/src/test/resources/liquibase/ext/changelog.httprequest.yaml new file mode 100644 index 0000000..aa1b4b9 --- /dev/null +++ b/src/test/resources/liquibase/ext/changelog.httprequest.yaml @@ -0,0 +1,21 @@ +databaseChangeLog: + - changeSet: + id: 1 + author: test + labels: httpRequestLabel + context: httpRequestContext + comment: httpRequestComment + changes: + - httpRequest: + method: PUT + path: /testindex + body: > + { + "mappings": { + "properties": { + "testfield": { + "type": "text" + } + } + } + }