diff --git a/common-legacy/src/main/java/com/linecorp/centraldogma/internal/thrift/ChangeConverter.java b/common-legacy/src/main/java/com/linecorp/centraldogma/internal/thrift/ChangeConverter.java index c0c2ffbd63..92d9ee334a 100644 --- a/common-legacy/src/main/java/com/linecorp/centraldogma/internal/thrift/ChangeConverter.java +++ b/common-legacy/src/main/java/com/linecorp/centraldogma/internal/thrift/ChangeConverter.java @@ -44,6 +44,7 @@ protected Change doForward(com.linecorp.centraldogma.common.Change value) { switch (change.getType()) { case UPSERT_JSON: case APPLY_JSON_PATCH: + case APPLY_YAML_PATCH: try { change.setContent(Jackson.writeValueAsString(value.content(), EntryType.JSON)); } catch (JsonProcessingException e) { @@ -89,6 +90,9 @@ protected com.linecorp.centraldogma.common.Change doBackward(Change c) { case UPSERT_YAML: return com.linecorp.centraldogma.common.Change.ofYamlUpsert(c.getPath(), c.getContent()); + case APPLY_YAML_PATCH: + return com.linecorp.centraldogma.common.Change.ofYamlPatch(c.getPath(), + c.getContent()); } throw new Error(); diff --git a/common/src/test/java/com/linecorp/centraldogma/internal/JacksonTest.java b/common/src/test/java/com/linecorp/centraldogma/internal/JacksonTest.java index f696fb2500..9d45b418c2 100644 --- a/common/src/test/java/com/linecorp/centraldogma/internal/JacksonTest.java +++ b/common/src/test/java/com/linecorp/centraldogma/internal/JacksonTest.java @@ -24,8 +24,11 @@ import org.junit.jupiter.api.Test; +import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.dataformat.yaml.JacksonYAMLParseException; +import com.linecorp.centraldogma.common.EntryType; import com.linecorp.centraldogma.common.QueryExecutionException; class JacksonTest { @@ -156,4 +159,11 @@ void mismatchedValueNodeWhileMerging() throws IOException { .isExactlyInstanceOf(QueryExecutionException.class) .hasMessageContaining("/a/b/ type: NUMBER (expected: STRING)"); } + + @Test + void readTreeFailsOnWrongEntryType() throws JsonParseException { + assertThatThrownBy(() -> readTree("foo: 123", EntryType.JSON)) + .isExactlyInstanceOf(JsonParseException.class); + // Please add JSON string which is not interpreted as YAML + } } diff --git a/it/src/test/java/com/linecorp/centraldogma/it/WatchTest.java b/it/src/test/java/com/linecorp/centraldogma/it/WatchTest.java index be2c12fe01..f1edc87f29 100644 --- a/it/src/test/java/com/linecorp/centraldogma/it/WatchTest.java +++ b/it/src/test/java/com/linecorp/centraldogma/it/WatchTest.java @@ -213,7 +213,7 @@ void watchFile(ClientType clientType) throws Exception { @ParameterizedTest @EnumSource(ClientType.class) - void watchFileWithIdentityQuery(ClientType clientType) throws Exception { + void watchJsonFileWithIdentityQuery(ClientType clientType) throws Exception { revertTestFiles(clientType); final CentralDogma client = clientType.client(dogma); @@ -250,6 +250,45 @@ void watchFileWithIdentityQuery(ClientType clientType) throws Exception { Entry.ofJson(rev2, "/test/test1.json", "[-1,-2,-3]")); } + @ParameterizedTest + @EnumSource(ClientType.class) + void watchYamlFileWithIdentityQuery(ClientType clientType) throws Exception { + revertTestFiles(clientType); + + final CentralDogma client = clientType.client(dogma); + final Revision rev0 = client + .normalizeRevision(dogma.project(), dogma.repo1(), Revision.HEAD) + .join(); + + final CompletableFuture> future = client.watchFile( + dogma.project(), dogma.repo1(), rev0, + Query.ofYaml("/test/test1.yml"), 3000); + + assertThatThrownBy(() -> future.get(500, TimeUnit.MILLISECONDS)).isInstanceOf(TimeoutException.class); + + // An irrelevant change should not trigger a notification. + final Change change1 = Change.ofYamlUpsert("/test/test2.yml", "num: 3"); + + final PushResult res1 = client.push( + dogma.project(), dogma.repo1(), rev0, "Add test2.yml", change1).join(); + + final Revision rev1 = res1.revision(); + + assertThatThrownBy(() -> future.get(500, TimeUnit.MILLISECONDS)).isInstanceOf(TimeoutException.class); + + // Make a relevant change now. + final Change change2 = Change.ofYamlUpsert("/test/test1.yml", "foo: \"bar\""); + + final PushResult res2 = client.push( + dogma.project(), dogma.repo1(), rev1, "Update test1.yml", change2).join(); + + final Revision rev2 = res2.revision(); + + assertThat(rev2).isEqualTo(rev0.forward(2)); + assertThat(future.get(3, TimeUnit.SECONDS)).isEqualTo( + Entry.ofYaml(rev2, "/test/test1.yml", "foo: \"bar\"")); + } + @ParameterizedTest @EnumSource(ClientType.class) void watchFileWithTimeout(ClientType clientType) { diff --git a/it/src/test/java/com/linecorp/centraldogma/it/mirror/git/GitMirrorTest.java b/it/src/test/java/com/linecorp/centraldogma/it/mirror/git/GitMirrorTest.java index e19e7d4353..71f67d453e 100644 --- a/it/src/test/java/com/linecorp/centraldogma/it/mirror/git/GitMirrorTest.java +++ b/it/src/test/java/com/linecorp/centraldogma/it/mirror/git/GitMirrorTest.java @@ -173,6 +173,7 @@ void remoteToLocal() throws Exception { addToGitIndex(".gitkeep", ""); addToGitIndex("first/light.txt", "26-Aug-2014"); addToGitIndex("second/son.json", "{\"release_date\": \"21-Mar-2014\"}"); + addToGitIndex("festival/of-blood.yaml", "release_date: \"25-Oct-2011\"}"); git.commit().setMessage("Add the release dates of the 'Infamous' series").call(); mirroringService.mirror().join(); @@ -189,7 +190,10 @@ void remoteToLocal() throws Exception { Entry.ofText(rev3, "/first/light.txt", "26-Aug-2014\n"), Entry.ofDirectory(rev3, "/second"), Entry.ofJson(rev3, "/second/son.json", - "{\"release_date\": \"21-Mar-2014\"}")); + "{\"release_date\": \"21-Mar-2014\"}"), + Entry.ofDirectory(rev3, "/festival"), + Entry.ofYaml(rev3, "festival/of-blood.yaml", + "release_date: \"25-Oct-2011\"}")); // Rewrite the history of the git repository and mirror. git.reset().setMode(ResetType.HARD).setRef("HEAD^").call(); diff --git a/server/src/test/java/com/linecorp/centraldogma/server/internal/storage/repository/git/GitRepositoryTest.java b/server/src/test/java/com/linecorp/centraldogma/server/internal/storage/repository/git/GitRepositoryTest.java index 521aa2d9e3..0cc4362de3 100644 --- a/server/src/test/java/com/linecorp/centraldogma/server/internal/storage/repository/git/GitRepositoryTest.java +++ b/server/src/test/java/com/linecorp/centraldogma/server/internal/storage/repository/git/GitRepositoryTest.java @@ -458,8 +458,10 @@ void testEmptyCommitWithRedundantUpsert2() { final Change change1 = Change.ofJsonUpsert("/redundant_upsert_2.json", "{ \"foo\": 0, \"bar\": 1 }"); final Change change2 = Change.ofTextUpsert("/redundant_upsert_2.txt", "foo"); + final Change change3 = Change.ofYamlUpsert("/redundant_upsert_3.yml", "foo: 12\nbar: 34"); repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, change1).join(); repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, change2).join(); + repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, change3).join(); // Ensure redundant changes do not count as a valid change. assertThatThrownBy(() -> repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, change1).join()) @@ -468,6 +470,9 @@ void testEmptyCommitWithRedundantUpsert2() { assertThatThrownBy(() -> repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, change2).join()) .isInstanceOf(CompletionException.class) .hasCauseInstanceOf(RedundantChangeException.class); + assertThatThrownBy(() -> repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, change3).join()) + .isInstanceOf(CompletionException.class) + .hasCauseInstanceOf(RedundantChangeException.class); // Ensure a change only whose serialized form is different does not count. final Change change1a = Change.ofJsonUpsert("/redundant_upsert_2.json", @@ -475,6 +480,10 @@ void testEmptyCommitWithRedundantUpsert2() { assertThatThrownBy(() -> repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, change1a).join()) .isInstanceOf(CompletionException.class) .hasCauseInstanceOf(RedundantChangeException.class); + final Change change3a = Change.ofYamlUpsert("/redundant_upsert_3.yml", "bar: 34\nfoo: 12\n"); + assertThatThrownBy(() -> repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, change3a).join()) + .isInstanceOf(CompletionException.class) + .hasCauseInstanceOf(RedundantChangeException.class); } @Test