diff --git a/README.md b/README.md index 9a835a95..5dc520d2 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,10 @@ Liquibase turned to be the most feasible tool to extend as it allows to define c ## Release Notes +#### 4.3.1 +* Support for Liquibase 4.3.1 +* Fixed [Issue-90: Convert all statements to runCommand so it is compatible with majority JavaDriver versions](https://github.com/liquibase/liquibase-mongodb/issues/90) + #### 4.2.2.1 * Fixed [Issue-64:Support for DNS Seed List Connection Format or Atlas Cluster](https://github.com/liquibase/liquibase-mongodb/issues/66) * Fixed [Issue-69: Does it support preconditions](https://github.com/liquibase/liquibase-mongodb/issues/69) @@ -89,17 +93,17 @@ Previous releases used by default : `databaseChangeLogLock, databaseChangeLog` A couple of Changes were implemented until identified that majority of the operations can be achieved using `db.runCommand()` and `db.adminCommand()` * [createCollection](https://docs.mongodb.com/manual/reference/method/db.createCollection/#db.createCollection) - -Creates a collection with validator +Creates a collection with validator [create](https://docs.mongodb.com/manual/reference/command/create/) * [dropCollection](https://docs.mongodb.com/manual/reference/method/db.collection.drop/#db-collection-drop) - -Removes a collection or view from the database +Removes a collection or view from the database [drop](https://docs.mongodb.com/manual/reference/command/drop/) * [createIndex](https://docs.mongodb.com/manual/reference/method/db.collection.createIndex/#db.collection.createIndex) - -Creates an index for a collection +Creates an index for a collection [createIndexes](https://docs.mongodb.com/manual/reference/command/createIndexes/) * [dropIndex](https://docs.mongodb.com/manual/reference/method/db.collection.dropIndex/#db.collection.dropIndex) - -Drops index for a collection by keys +Drops index for a collection by keys [dropIndexes](https://docs.mongodb.com/manual/reference/command/dropIndexes/) * [insertMany](https://docs.mongodb.com/manual/reference/method/db.collection.insertMany/#db.collection.insertMany) - -Inserts multiple documents into a collection +Inserts multiple documents into a collection [insert](https://docs.mongodb.com/manual/reference/command/insert/) * [insertOne](https://docs.mongodb.com/manual/tutorial/insert-documents/#insert-a-single-document) - -Inserts a Single Document into a collection +Inserts a Single Document into a collection [insert](https://docs.mongodb.com/manual/reference/command/insert/) * [__runCommand__](https://docs.mongodb.com/manual/reference/method/db.runCommand/#db-runcommand) - Provides a helper to run specified database commands. This is the preferred method to issue database commands, as it provides a consistent interface between the shell and drivers * [__adminCommand__](https://docs.mongodb.com/manual/reference/method/db.adminCommand/#db.adminCommand) - diff --git a/src/main/java/liquibase/ext/mongodb/statement/CreateIndexStatement.java b/src/main/java/liquibase/ext/mongodb/statement/CreateIndexStatement.java index e32b571d..89c53ccd 100644 --- a/src/main/java/liquibase/ext/mongodb/statement/CreateIndexStatement.java +++ b/src/main/java/liquibase/ext/mongodb/statement/CreateIndexStatement.java @@ -32,7 +32,7 @@ import static liquibase.ext.mongodb.statement.BsonUtils.toCommand; /** - * Creates a index via the database runCommand method + * Creates an index via the database runCommand method * For a list of supported options see the reference page: * * @see createIndexes diff --git a/src/main/java/liquibase/ext/mongodb/statement/DropIndexStatement.java b/src/main/java/liquibase/ext/mongodb/statement/DropIndexStatement.java index 17c28672..28cfd2d9 100644 --- a/src/main/java/liquibase/ext/mongodb/statement/DropIndexStatement.java +++ b/src/main/java/liquibase/ext/mongodb/statement/DropIndexStatement.java @@ -26,22 +26,25 @@ import lombok.Getter; import org.bson.Document; -import static java.util.Optional.ofNullable; -import static liquibase.ext.mongodb.statement.AbstractRunCommandStatement.SHELL_DB_PREFIX; import static liquibase.ext.mongodb.statement.BsonUtils.orEmptyDocument; +import static liquibase.ext.mongodb.statement.BsonUtils.toCommand; +/** + * Drops an index via the database runCommand method + * For a list of supported options see the reference page: + * + * @see dropIndexes + */ @Getter @EqualsAndHashCode(callSuper = true) -public class DropIndexStatement extends AbstractCollectionStatement +public class DropIndexStatement extends AbstractRunCommandStatement implements NoSqlExecuteStatement { - public static final String COMMAND_NAME = "dropIndex"; - - private final Document keys; + public static final String RUN_COMMAND_NAME = "dropIndexes"; + private static final String INDEX = "index"; public DropIndexStatement(final String collectionName, final Document keys) { - super(collectionName); - this.keys = keys; + super(toCommand(RUN_COMMAND_NAME, collectionName, combine(keys))); } public DropIndexStatement(final String collectionName, final String keys) { @@ -49,25 +52,12 @@ public DropIndexStatement(final String collectionName, final String keys) { } @Override - public String getCommandName() { - return COMMAND_NAME; + public String getRunCommandName() { + return RUN_COMMAND_NAME; } - @Override - public String toJs() { - return - SHELL_DB_PREFIX - + getCollectionName() - + ". " - + getCommandName() - + "(" - + ofNullable(keys).map(Document::toJson).orElse(null) - + ");"; - } - - @Override - public void execute(final MongoLiquibaseDatabase database) { - getMongoDatabase(database).getCollection(collectionName).dropIndex(keys); + private static Document combine(final Document keys) { + return new Document(INDEX, keys); } } diff --git a/src/main/resources/liquibase.parser.core.xml/dbchangelog-ext.xsd b/src/main/resources/liquibase.parser.core.xml/dbchangelog-ext.xsd index c52dd6ec..cddf4330 100644 --- a/src/main/resources/liquibase.parser.core.xml/dbchangelog-ext.xsd +++ b/src/main/resources/liquibase.parser.core.xml/dbchangelog-ext.xsd @@ -65,7 +65,7 @@ - + diff --git a/src/test/java/liquibase/ext/mongodb/change/CreateIndexChangeTest.java b/src/test/java/liquibase/ext/mongodb/change/CreateIndexChangeTest.java index de1b16e8..79abe4dc 100644 --- a/src/test/java/liquibase/ext/mongodb/change/CreateIndexChangeTest.java +++ b/src/test/java/liquibase/ext/mongodb/change/CreateIndexChangeTest.java @@ -21,9 +21,13 @@ */ import liquibase.change.Change; +import liquibase.change.CheckSum; import liquibase.changelog.ChangeSet; +import liquibase.ext.mongodb.statement.AbstractRunCommandStatement; +import liquibase.ext.mongodb.statement.CreateIndexStatement; import liquibase.ext.mongodb.statement.DropIndexStatement; import lombok.SneakyThrows; +import org.bson.Document; import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -40,14 +44,32 @@ void getConfirmationMessage() { final CreateIndexChange createIndexChange = new CreateIndexChange(); createIndexChange.setCollectionName("collection1"); createIndexChange.setKeys("{ clientId: 1, type: 1}"); - assertThat(createIndexChange.getConfirmationMessage()).isEqualTo("Index created for collection collection1"); - - assertThat(Arrays.asList(createIndexChange.generateRollbackStatements(database))) - .hasSize(1) - .first() - .isInstanceOf(DropIndexStatement.class) - .returns("collection1", s -> ((DropIndexStatement)s).getCollectionName()) - .returns("{\"clientId\": 1, \"type\": 1}", s -> ((DropIndexStatement)s).getKeys().toJson()); + + assertThat(createIndexChange) + .hasFieldOrPropertyWithValue("CollectionName", "collection1") + .hasFieldOrPropertyWithValue("keys", "{ clientId: 1, type: 1}") + .hasFieldOrPropertyWithValue("options", null) + .returns(CheckSum.parse("8:e6b1630ad2b20ef41a69eee853528099"), Change::generateCheckSum) + .returns("Index created for collection collection1", Change::getConfirmationMessage) + .returns(true, c -> c.supportsRollback(database)); + + assertThat(Arrays.asList(createIndexChange.generateStatements(database))).hasSize(1) + .hasOnlyElementsOfType(CreateIndexStatement.class).first().extracting(s -> (CreateIndexStatement) s) + .returns("createIndexes", CreateIndexStatement::getRunCommandName) + .returns("runCommand", AbstractRunCommandStatement::getCommandName) + .returns("collection1", s -> s.getCommand().get("createIndexes")) + .returns(null, s -> s.getCommand().getList("indexes", Document.class).get(0).get("name")) + .returns(null, s -> s.getCommand().getList("indexes", Document.class).get(0).get("unique")) + .returns("{\"clientId\": 1, \"type\": 1}", + s -> s.getCommand().getList("indexes", Document.class).get(0).get("key", Document.class).toJson()); + + assertThat(Arrays.asList(createIndexChange.generateRollbackStatements(database))).hasSize(1) + .hasOnlyElementsOfType(DropIndexStatement.class).first().extracting(s -> (DropIndexStatement) s) + .returns("dropIndexes", DropIndexStatement::getRunCommandName) + .returns("runCommand", AbstractRunCommandStatement::getCommandName) + .returns("collection1", s -> s.getCommand().get("dropIndexes")) + .returns("{\"clientId\": 1, \"type\": 1}", + s -> s.getCommand().get("index", Document.class).toJson()); } @Test @@ -56,20 +78,60 @@ void generateStatements() { final List changeSets = getChangesets("liquibase/ext/changelog.create-index.test.xml", database); assertThat(changeSets).hasSize(1).first() - .returns("8:c2b981901db6061d2e034a4f31501ec5", s -> s.generateCheckSum().toString()); + .returns("8:c2b981901db6061d2e034a4f31501ec5", s -> s.generateCheckSum().toString()); final List changes1 = changeSets.get(0).getChanges(); assertThat(changes1).hasSize(2); assertThat(changes1).hasOnlyElementsOfType(CreateIndexChange.class); assertThat(changes1.get(0)) - .hasFieldOrPropertyWithValue("CollectionName", "createIndexTest") - .hasFieldOrPropertyWithValue("keys", "{ clientId: 1, type: 1}") - .hasFieldOrPropertyWithValue("options", "{unique: true, name: \"ui_tppClientId\"}"); + .hasFieldOrPropertyWithValue("CollectionName", "createIndexTest") + .hasFieldOrPropertyWithValue("keys", "{ clientId: 1, type: 1}") + .hasFieldOrPropertyWithValue("options", "{unique: true, name: \"ui_tppClientId\"}") + .returns(CheckSum.parse("8:2ea778164e5507ea6678158bee3f8959"), Change::generateCheckSum) + .returns(true, c -> c.supportsRollback(database)); + + assertThat(Arrays.asList(changes1.get(0).generateStatements(database))).hasSize(1) + .hasOnlyElementsOfType(CreateIndexStatement.class).first().extracting(s -> (CreateIndexStatement) s) + .returns("createIndexes", CreateIndexStatement::getRunCommandName) + .returns("runCommand", AbstractRunCommandStatement::getCommandName) + .returns("createIndexTest", s -> s.getCommand().get("createIndexes")) + .returns("ui_tppClientId", s -> s.getCommand().getList("indexes", Document.class).get(0).get("name")) + .returns(true, s -> s.getCommand().getList("indexes", Document.class).get(0).get("unique")) + .returns("{\"clientId\": 1, \"type\": 1}", + s -> s.getCommand().getList("indexes", Document.class).get(0).get("key", Document.class).toJson()); + + assertThat(Arrays.asList(changes1.get(0).generateRollbackStatements(database))).hasSize(1) + .hasOnlyElementsOfType(DropIndexStatement.class).first().extracting(s -> (DropIndexStatement) s) + .returns("dropIndexes", DropIndexStatement::getRunCommandName) + .returns("runCommand", AbstractRunCommandStatement::getCommandName) + .returns("createIndexTest", s -> s.getCommand().get("dropIndexes")) + .returns("{\"clientId\": 1, \"type\": 1}", + s -> s.getCommand().get("index", Document.class).toJson()); assertThat(changes1.get(1)) - .hasFieldOrPropertyWithValue("CollectionName", "createIndexNoOptionsTest") - .hasFieldOrPropertyWithValue("keys", "{ clientId: 1, type: 1}") - .hasFieldOrPropertyWithValue("options", null); + .hasFieldOrPropertyWithValue("CollectionName", "createIndexNoOptionsTest") + .hasFieldOrPropertyWithValue("keys", "{ clientId: 1, type: 1}") + .hasFieldOrPropertyWithValue("options", null) + .returns(CheckSum.parse("8:4499e67ade10db858b5eafa32665623f"), Change::generateCheckSum) + .returns(true, c -> c.supportsRollback(database)); + + assertThat(Arrays.asList(changes1.get(1).generateStatements(database))).hasSize(1) + .hasOnlyElementsOfType(CreateIndexStatement.class).first().extracting(s -> (CreateIndexStatement) s) + .returns("createIndexes", CreateIndexStatement::getRunCommandName) + .returns("runCommand", AbstractRunCommandStatement::getCommandName) + .returns("createIndexNoOptionsTest", s -> s.getCommand().get("createIndexes")) + .returns(null, s -> s.getCommand().getList("indexes", Document.class).get(0).get("name")) + .returns(null, s -> s.getCommand().getList("indexes", Document.class).get(0).get("unique")) + .returns("{\"clientId\": 1, \"type\": 1}", + s -> s.getCommand().getList("indexes", Document.class).get(0).get("key", Document.class).toJson()); + + assertThat(Arrays.asList(changes1.get(1).generateRollbackStatements(database))).hasSize(1) + .hasOnlyElementsOfType(DropIndexStatement.class).first().extracting(s -> (DropIndexStatement) s) + .returns("dropIndexes", DropIndexStatement::getRunCommandName) + .returns("runCommand", AbstractRunCommandStatement::getCommandName) + .returns("createIndexNoOptionsTest", s -> s.getCommand().get("dropIndexes")) + .returns("{\"clientId\": 1, \"type\": 1}", + s -> s.getCommand().get("index", Document.class).toJson()); } } diff --git a/src/test/java/liquibase/ext/mongodb/change/DropIndexChangeTest.java b/src/test/java/liquibase/ext/mongodb/change/DropIndexChangeTest.java new file mode 100644 index 00000000..df78673e --- /dev/null +++ b/src/test/java/liquibase/ext/mongodb/change/DropIndexChangeTest.java @@ -0,0 +1,60 @@ +package liquibase.ext.mongodb.change; + +/*- + * #%L + * Liquibase MongoDB Extension + * %% + * Copyright (C) 2021 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.change.Change; +import liquibase.change.CheckSum; +import liquibase.ext.mongodb.statement.AbstractRunCommandStatement; +import liquibase.ext.mongodb.statement.DropIndexStatement; +import lombok.SneakyThrows; +import org.bson.Document; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThat; + +class DropIndexChangeTest extends AbstractMongoChangeTest { + + @SneakyThrows + @Test + void getConfirmationMessage() { + final DropIndexChange dropIndexChange = new DropIndexChange(); + dropIndexChange.setCollectionName("collection1"); + dropIndexChange.setKeys("{ clientId: 1, type: 1}"); + + assertThat(dropIndexChange) + .hasFieldOrPropertyWithValue("CollectionName", "collection1") + .hasFieldOrPropertyWithValue("keys", "{ clientId: 1, type: 1}") + .returns(CheckSum.parse("8:016c274fba45cb1313dccf7866797235"), Change::generateCheckSum) + .returns("Index dropped for collection collection1", Change::getConfirmationMessage) + .returns(false, c -> c.supportsRollback(database)); + + assertThat(Arrays.asList(dropIndexChange.generateStatements(database))).hasSize(1) + .hasOnlyElementsOfType(DropIndexStatement.class).first().extracting(s -> (DropIndexStatement) s) + .returns("dropIndexes", DropIndexStatement::getRunCommandName) + .returns("runCommand", AbstractRunCommandStatement::getCommandName) + .returns("collection1", s -> s.getCommand().get("dropIndexes")) + .returns("{\"clientId\": 1, \"type\": 1}", + s -> s.getCommand().get("index", Document.class).toJson()); + } + +} diff --git a/src/test/java/liquibase/ext/mongodb/statement/DropIndexStatementIT.java b/src/test/java/liquibase/ext/mongodb/statement/DropIndexStatementIT.java new file mode 100644 index 00000000..a872f68d --- /dev/null +++ b/src/test/java/liquibase/ext/mongodb/statement/DropIndexStatementIT.java @@ -0,0 +1,69 @@ +package liquibase.ext.mongodb.statement; + +/*- + * #%L + * Liquibase MongoDB Extension + * %% + * Copyright (C) 2021 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 com.mongodb.MongoCommandException; +import liquibase.ext.AbstractMongoIntegrationTest; +import org.junit.jupiter.api.Test; + +import static java.util.stream.StreamSupport.stream; +import static liquibase.ext.mongodb.TestUtils.COLLECTION_NAME_1; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +class DropIndexStatementIT extends AbstractMongoIntegrationTest { + + @Test + void toStringJs() { + final DropIndexStatement dropIndexStatement = new DropIndexStatement(COLLECTION_NAME_1, "{ locale: 1 }"); + assertThat(dropIndexStatement.toString()) + .isEqualTo(dropIndexStatement.toJs()) + .isEqualTo("db.runCommand({\"dropIndexes\": \"collectionName\", \"index\": {\"locale\": 1}});"); + } + + @Test + void execute() { + final String indexName = "locale_indx"; + mongoDatabase.createCollection(COLLECTION_NAME_1); + new CreateIndexStatement(COLLECTION_NAME_1, "{ locale: -1 }", + "{ name: \"" + indexName + "0" + "\", unique: true, expireAfterSeconds: NumberLong(\"30\") }").execute(database); + + new CreateIndexStatement(COLLECTION_NAME_1, "{ locale: 1 }", + "{ name: \"" + indexName + "1" + "\", unique: true, expireAfterSeconds: NumberLong(\"30\") }").execute(database); + + assertThat(stream(mongoDatabase.getCollection(COLLECTION_NAME_1).listIndexes().spliterator(), false) + .filter(doc -> doc.getString("name").startsWith(indexName)).count()).isEqualTo(2); + + new DropIndexStatement(COLLECTION_NAME_1, "{ locale: 1 }").execute(database); + + assertThat(stream(mongoDatabase.getCollection(COLLECTION_NAME_1).listIndexes().spliterator(), false) + .filter(doc -> doc.getString("name").startsWith(indexName)).count()).isEqualTo(1); + + assertThat(stream(mongoDatabase.getCollection(COLLECTION_NAME_1).listIndexes().spliterator(), false) + .filter(doc -> doc.get("name").equals(indexName + "1")).count()).isEqualTo(0); + + // repeatedly attempt to delete same index or not existing + assertThatExceptionOfType(MongoCommandException.class) + .isThrownBy(() -> new DropIndexStatement(COLLECTION_NAME_1, "{ locale: 1 }").execute(database)) + .withMessageStartingWith("Command failed with error") + .withMessageContaining("can't find index with key"); + } +}