@Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof Change)) { return false; } final Change<?> that = (Change<?>) o; if (type != that.type()) { return false; } if (!path.equals(that.path())) { return false; } return Objects.equals(content, that.content()); }
case UPSERT_JSON: { final JsonNode oldJsonNode = oldContent != null ? Jackson.readTree(oldContent) : null; final JsonNode newJsonNode = (JsonNode) change.content(); final String newPath = ((String) change.content()).substring(1); // Strip the leading '/'. newJsonNode = JsonPatch.fromJson((JsonNode) change.content()).apply(oldJsonNode); } catch (Exception e) { throw new ChangeConflictException("failed to apply JSON patch: " + change, e); Util.stringToLines(sanitizeText((String) change.content())));
@Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof Change)) { return false; } final Change<?> that = (Change<?>) o; if (type != that.type()) { return false; } if (!path.equals(that.path())) { return false; } return Objects.equals(content, that.content()); }
case UPSERT_JSON: { final JsonNode oldJsonNode = oldContent != null ? Jackson.readTree(oldContent) : null; final JsonNode newJsonNode = (JsonNode) change.content(); final String newPath = ((String) change.content()).substring(1); // Strip the leading '/'. newJsonNode = JsonPatch.fromJson((JsonNode) change.content()).apply(oldJsonNode); } catch (Exception e) { throw new ChangeConflictException("failed to apply JSON patch: " + change, e); Util.stringToLines(sanitizeText((String) change.content())));
@Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof Change)) { return false; } final Change<?> that = (Change<?>) o; if (type != that.type()) { return false; } if (!path.equals(that.path())) { return false; } return Objects.equals(content, that.content()); }
public static <T> ChangeDto<T> convert(Change<T> change) { requireNonNull(change, "change"); return new ChangeDto<>(change.path(), change.type(), change.content()); }
public static <T> ChangeDto<T> convert(Change<T> change) { requireNonNull(change, "change"); return new ChangeDto<>(change.path(), change.type(), change.content()); }
public static <T> ChangeDto<T> convert(Change<T> change) { requireNonNull(change, "change"); return new ChangeDto<>(change.path(), change.type(), change.content()); }
@Before public void setUp() throws Exception { prefix = '/' + testName.getMethodName() + '/'; allPattern = prefix + "**"; for (int i = 0; i < NUM_ITERATIONS; i++) { final String jsonPath = prefix + i + ".json"; final String textPath = prefix + i + ".txt"; jsonPaths[i] = jsonPath; textPaths[i] = textPath; jsonUpserts[i] = Change.ofJsonUpsert(jsonPath, "{ \"" + i + "\": " + i + " }"); textUpserts[i] = Change.ofTextUpsert(textPath, "value:\n" + i); } jsonPatches[0] = Change.ofJsonPatch(jsonPaths[0], null, jsonUpserts[0].content()); textPatches[0] = Change.ofTextPatch(textPaths[0], null, textUpserts[0].content()); for (int i = 1; i < NUM_ITERATIONS; i++) { jsonPatches[i] = Change.ofJsonPatch(jsonPaths[0], jsonUpserts[i - 1].content(), jsonUpserts[i].content()); textPatches[i] = Change.ofTextPatch(textPaths[0], textUpserts[i - 1].content(), textUpserts[i].content()); } watchConsumer = null; }
@Test(timeout = 10000) public void testWatchWithIdentityQuery() throws Exception { final Revision rev1 = repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, textUpserts[0]).join(); final CompletableFuture<Entry<String>> f = repo.watch(rev1, Query.ofText(textPaths[0])); final Revision rev2 = repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, textPatches[1]).join(); final Entry<String> res = f.get(3, TimeUnit.SECONDS); assertThat(res.revision()).isEqualTo(rev2); assertThat(res.type()).isEqualTo(EntryType.TEXT); // Text must be sanitized so that the last line ends with \n. assertThat(res.content()).isEqualTo(textUpserts[1].content() + '\n'); }
@Override protected Change doForward(com.linecorp.centraldogma.common.Change<?> value) { final Change change = new Change(value.path(), convertChangeType(value.type())); switch (change.getType()) { case UPSERT_JSON: case APPLY_JSON_PATCH: try { change.setContent(Jackson.writeValueAsString(value.content())); } catch (JsonProcessingException e) { throw new ChangeFormatException("failed to read a JSON tree", e); } break; case UPSERT_TEXT: case APPLY_TEXT_PATCH: case RENAME: change.setContent((String) value.content()); break; case REMOVE: break; } return change; }
@Override protected Change doForward(com.linecorp.centraldogma.common.Change<?> value) { final Change change = new Change(value.path(), convertChangeType(value.type())); switch (change.getType()) { case UPSERT_JSON: case APPLY_JSON_PATCH: try { change.setContent(Jackson.writeValueAsString(value.content())); } catch (JsonProcessingException e) { throw new ChangeFormatException("failed to read a JSON tree", e); } break; case UPSERT_TEXT: case APPLY_TEXT_PATCH: case RENAME: change.setContent((String) value.content()); break; case REMOVE: break; } return change; }
@Test public void testRename() throws Exception { repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, jsonUpserts[0]).join(); // Rename without content modification. repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, Change.ofRename(jsonPaths[0], jsonPaths[1])).join(); assertThat(repo.exists(HEAD, jsonPaths[0]).join()).isFalse(); assertThat(repo.exists(HEAD, jsonPaths[1]).join()).isTrue(); assertThat(repo.exists(HEAD, jsonPaths[2]).join()).isFalse(); assertThatJson(repo.get(HEAD, jsonPaths[1]).join().content()) .isEqualTo(jsonUpserts[0].content()); // Rename with content modification. repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, Change.ofRename(jsonPaths[1], jsonPaths[2]), Change.ofJsonPatch(jsonPaths[2], jsonPatches[1].content()), Change.ofJsonPatch(jsonPaths[2], jsonPatches[2].content())).join(); assertThat(repo.exists(HEAD, jsonPaths[0]).join()).isFalse(); assertThat(repo.exists(HEAD, jsonPaths[1]).join()).isFalse(); assertThat(repo.exists(HEAD, jsonPaths[2]).join()).isTrue(); assertThatJson(repo.get(HEAD, jsonPaths[2]).join().content()) .isEqualTo(jsonUpserts[2].content()); }
assertThatJson(diff.get(newPath).content()).isEqualTo("{ \"value\": true }");
@Test public void testMultipleChanges() throws Exception { final List<Change<?>> changes = new ArrayList<>(); Collections.addAll(changes, jsonUpserts); for (int i = 1; i < jsonPatches.length; i++) { changes.add(jsonPatches[i]); } repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, changes).join(); final Map<String, Entry<?>> entries = repo.find(HEAD, allPattern).join(); assertThat(entries).hasSize(jsonUpserts.length); for (int i = 0; i < jsonUpserts.length; i++) { final Change<?> c = jsonUpserts[i]; assertThat(entries).containsKey(c.path()); if (i == 0) { // We have patched the first upsert to make it identical to the last upsert. assertThatJson(entries.get(c.path()).content()) .isEqualTo(jsonUpserts[jsonUpserts.length - 1].content()); } else { assertThatJson(entries.get(c.path()).content()).isEqualTo(c.content()); } } }
@Test public void testWatchRemoval() throws Exception { final String path = jsonPaths[0]; final Change<JsonNode> upsert1 = Change.ofJsonUpsert(path, "1"); final Change<JsonNode> upsert2 = Change.ofJsonUpsert(path, "2"); final Revision rev1 = repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, upsert1).join(); final CompletableFuture<Entry<JsonNode>> f = repo.watch(rev1, Query.ofJson(path)); // Remove the file being watched. repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, Change.ofRemoval(path)).join(); // Should wait patiently until the file reappears. assertThatThrownBy(() -> f.get(1, TimeUnit.SECONDS)).isInstanceOf(TimeoutException.class); // Add the file back again without changing the content. repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, upsert1).join(); // Should wait patiently until the file changes really. assertThatThrownBy(() -> f.get(1, TimeUnit.SECONDS)).isInstanceOf(TimeoutException.class); // Remove the file being watched again. repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, Change.ofRemoval(path)).join(); // Should wait patiently until the file reappears. assertThatThrownBy(() -> f.get(1, TimeUnit.SECONDS)).isInstanceOf(TimeoutException.class); // Add the file back again with different content. final Revision rev2 = repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, upsert2).join(); // Should be notified this time because the content has changed really. final Entry<JsonNode> res = f.get(3, TimeUnit.SECONDS); assertThat(res.revision()).isEqualTo(rev2); assertThat(res.type()).isEqualTo(EntryType.JSON); assertThatJson(res.content()).isEqualTo(upsert2.content()); }
private static void testPatch(Change<?>[] patches, Change<?>[] upserts) { final String path = patches[0].path(); for (int i = 0; i < NUM_ITERATIONS; i++) { assert path.equals(patches[i].path()); final Revision rev = repo.normalizeNow(HEAD); // Ensure that we cannot apply patched in an incorrect order. for (int j = i + 1; j < NUM_ITERATIONS; j++) { final int finalJ = j; assertThatThrownBy( () -> repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, patches[finalJ]).join()) .isInstanceOf(CompletionException.class) .hasCauseInstanceOf(ChangeConflictException.class); } // Ensure that the failed commit does not change the revision. assertThat(repo.normalizeNow(HEAD)).isEqualTo(rev); // Ensure that the successful commit changes the revision. final Revision newRev = repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, patches[i]).join(); assertThat(repo.normalizeNow(HEAD)).isEqualTo(newRev); assertThat(newRev).isEqualTo(new Revision(rev.major() + 1)); // Ensure the entry has been patched as expected. final Entry<?> e = repo.get(HEAD, path).join(); if (e.type() == EntryType.JSON) { assertThatJson(e.content()).isEqualTo(upserts[i].content()); } else { // Text must be sanitized so that the last line ends with \n. assertThat(e.content()).isEqualTo(upserts[i].content() + "\n"); } } }
private void testUpsert(Change<?>[] upserts, EntryType entryType) { final Revision oldHeadRev = repo.normalizeNow(HEAD); for (int i = 0; i < upserts.length; i++) { final Change<?> change = upserts[i]; final Revision revision = repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, change).join(); // Ensure the revision is incremented. assertThat(revision.major()).isEqualTo(oldHeadRev.major() + i + 1); assertThat(repo.normalizeNow(HEAD)).isEqualTo(revision); // Ensure that the entries which were created in the previous revisions are retrieved // as well as the entry in the latest revision. final Map<String, Entry<?>> entries = repo.find(revision, allPattern).join(); assertThat(entries).hasSize(i + 1); for (int j = 0; j <= i; j++) { assertThat(entries).containsKey(upserts[j].path()); } } // Check the content of all entries. final Revision headRev = repo.normalizeNow(HEAD); final Map<String, Entry<?>> entries = Util.unsafeCast(repo.find(headRev, allPattern).join()); for (Change<?> c : upserts) { final String path = c.path(); if (entryType == EntryType.TEXT) { // Text must be sanitized so that the last line ends with \n. assertThat(entries).containsEntry(path, Entry.of(headRev, path, EntryType.TEXT, c.content() + "\n")); } else { assertThat(entries).containsEntry(path, Entry.of(headRev, path, entryType, c.content())); } } }
@Test public void testTextPatches() throws PatchFailedException { final String oriStr = "1\n2\n3\n4\n5\n6\n7\n8\n9"; final String newStr = "1a\n2\n3\n4\n5\n6\n7\n8\n9a"; final String expectedUnifiedDiff = "--- /text_file.txt\n" + "+++ /text_file.txt\n" + "@@ -1,4 +1,4 @@\n" + "-1\n" + "+1a\n" + " 2\n" + " 3\n" + " 4\n" + "@@ -6,4 +6,4 @@\n" + " 6\n" + " 7\n" + " 8\n" + "-9\n" + "+9a"; final Change<String> change = Change.ofTextPatch("/text_file.txt", oriStr, newStr); assertEquals(expectedUnifiedDiff, change.content()); final Patch<String> patch = DiffUtils.parseUnifiedDiff(Util.stringToLines(change.content())); final String patchedStr = String.join("\n", patch.applyTo(Util.stringToLines(oriStr))); assertEquals(newStr, patchedStr); }
@Test public void testQueryByRange() throws Exception { final String path = "/test_json_file.json"; for (int i = 0; i < 5; i++) { final Change<JsonNode> change = Change.ofJsonUpsert(path, String.format("{ \"key\" : \"%d\"}", i)); rule.client().push( rule.project(), rule.repo1(), HEAD, TestConstants.randomText(), change).join(); } final Change<JsonNode> res = rule.client().getDiff( rule.project(), rule.repo1(), new Revision(-4), new Revision(-1), Query.ofJsonPath(path, "$.key")).join(); assertThat(res.type()).isEqualTo(ChangeType.APPLY_JSON_PATCH); assertThatJson(res.content()).isEqualTo( "[{" + " \"op\": \"safeReplace\"," + " \"path\": \"\"," + " \"oldValue\": \"1\"," + " \"value\": \"4\"" + "}]"); }