return Change.ofRemoval(path);
return Change.ofRemoval(path);
return Change.ofRemoval(path);
@Test public void testRecursiveRemoval() throws Exception { // A recursive removal should fail when there's no such entry. final String curDir = prefix.substring(0, prefix.length() - 1); // Remove trailing '/'. assertThatThrownBy( () -> repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, Change.ofRemoval(curDir)).join()) .isInstanceOf(CompletionException.class) .hasCauseInstanceOf(ChangeConflictException.class); // Add some files repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, jsonUpserts).join(); assertThat(repo.find(HEAD, allPattern).join()).hasSize(jsonUpserts.length); // Perform a recursive removal repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, Change.ofRemoval(curDir)).join(); assertThat(repo.find(HEAD, allPattern).join()).isEmpty(); }
@Test public void testRemoval() throws Exception { // A removal should fail when there's no such entry. assertThatThrownBy(() -> repo .commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, Change.ofRemoval(jsonPaths[0])).join()) .isInstanceOf(CompletionException.class) .hasCauseInstanceOf(ChangeConflictException.class); Revision revision = repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, jsonUpserts[0]).join(); assertThat(repo.exists(revision, jsonPaths[0]).join()).isTrue(); // A removal should succeed when there's an entry. revision = repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, Change.ofRemoval(jsonPaths[0])).join(); assertThat(repo.exists(revision, jsonPaths[0]).join()).isFalse(); // A removal should fail when there's no such entry. assertThatThrownBy(() -> repo .commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, Change.ofRemoval(jsonPaths[0])).join()) .isInstanceOf(CompletionException.class) .hasCauseInstanceOf(ChangeConflictException.class); }
@Test public void testPreviewDiff() { final Map<String, Change<?>> changeMap = repo.previewDiff(HEAD, jsonUpserts[0]).join(); assertThat(changeMap).containsEntry(jsonPaths[0], jsonUpserts[0]); // Invalid patch assertThatThrownBy(() -> repo.previewDiff(HEAD, jsonPatches[1]).join()) .isInstanceOf(CompletionException.class) .hasCauseInstanceOf(ChangeConflictException.class); // Invalid removal assertThatThrownBy(() -> repo.previewDiff(HEAD, Change.ofRemoval(jsonPaths[0])).join()) .isInstanceOf(CompletionException.class) .hasCauseInstanceOf(ChangeConflictException.class); // Apply a series of changes final List<Change<?>> changes = Arrays.asList(jsonUpserts[0], jsonPatches[1], jsonPatches[2], Change.ofRename(jsonPaths[0], jsonPaths[1]), Change.ofRemoval(jsonPaths[1])); Map<String, Change<?>> returnedChangeMap = repo.previewDiff(HEAD, changes).join(); assertThat(returnedChangeMap).isEmpty(); assertThatThrownBy(() -> repo.previewDiff(new Revision(Integer.MAX_VALUE), changes).join()) .isInstanceOf(CompletionException.class) .hasCauseInstanceOf(RevisionNotFoundException.class); assertThat(repo.previewDiff(new Revision(-1), Collections.emptyList()).join()).isEmpty(); // Test upsert on an existing path repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, jsonPatches[0], jsonPatches[1]).join(); returnedChangeMap = repo.previewDiff(HEAD, jsonUpserts[0]).join(); assertThat(returnedChangeMap.get(jsonPaths[0]).type()).isEqualTo(ChangeType.APPLY_JSON_PATCH); }
@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 Project createProjectWithoutMetadata(String projectName) { final Project project = rule.projectManager().create(projectName, Author.SYSTEM); final RepositoryManager manager = project.repos(); manager.get(REPO_DOGMA).commit(Revision.HEAD, System.currentTimeMillis(), author, "Delete /metadata.json to mimic the legacy project", "", Markup.PLAINTEXT, Change.ofRemoval(METADATA_JSON)).join(); manager.create(REPO_FOO, Author.SYSTEM); manager.create("oneMoreThing", Author.SYSTEM); return project; }
Change.ofRemoval(oldPath), Change.ofJsonUpsert(newPath, "{ \"value\": false }")).join();
@Test public void testInvalidRemoval() throws Exception { // Apply a conflict removal final Change<?> change = Change.ofRemoval("/non_existent_path.txt"); assertThatThrownByWithExpectedException(ChangeConflictException.class, "non_existent_path.txt", () -> rule.client().getPreviewDiffs(rule.project(), rule.repo1(), Revision.HEAD, change).join()) .isInstanceOf(CompletionException.class).hasCauseInstanceOf(ChangeConflictException.class); }
@Test public void testDiff_remove() throws Exception { final CentralDogma client = rule.client(); final Revision rev1 = client.push(rule.project(), rule.repo1(), HEAD, "summary1", Change.ofTextUpsert("/foo.txt", "hello")).join().revision(); final Revision rev2 = client.push(rule.project(), rule.repo1(), HEAD, "summary2", Change.ofRemoval("/foo.txt")).join().revision(); assertThat(rev1.forward(1)).isEqualTo(rev2); assertThat(client.getDiff(rule.project(), rule.repo1(), rev1, rev1, Query.ofText("/foo.txt")).join().type()) .isEqualTo(ChangeType.APPLY_TEXT_PATCH); assertThat(client.getDiff(rule.project(), rule.repo1(), rev1, rev2, Query.ofText("/foo.txt")).join()) .isEqualTo(Change.ofRemoval("/foo.txt")); }
@Test public void testDiff_rename() throws Exception { final CentralDogma client = rule.client(); final Revision rev1 = client.push(rule.project(), rule.repo1(), HEAD, "summary1", Change.ofTextUpsert("/bar.txt", "hello")).join().revision(); final Revision rev2 = client.push(rule.project(), rule.repo1(), HEAD, "summary2", Change.ofRename("/bar.txt", "/baz.txt")).join().revision(); assertThat(rev1.forward(1)).isEqualTo(rev2); assertThat(client.getDiff(rule.project(), rule.repo1(), rev1, rev2, Query.ofText("/bar.txt")).join()) .isEqualTo(Change.ofRemoval("/bar.txt")); } }
/** * Run a sequence of remove operation on the same path, valid the diff after each push. */ @Test public void testDiff_remove() throws Exception { // add all files into repository Revision lastRevision = null; for (int i = 0; i < NUM_ITERATIONS; i++) { lastRevision = repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, jsonUpserts[i], textUpserts[i]).join(); } Revision prevRevison = lastRevision; for (int i = 1; i < NUM_ITERATIONS; i++) { final String jsonPath = jsonUpserts[i].path(); final String textPath = textUpserts[i].path(); final Change<Void> jsonRemoval = Change.ofRemoval(jsonPath); final Change<Void> textRemoval = Change.ofRemoval(textPath); final Revision currRevision = repo.commit(HEAD, 0L, Author.UNKNOWN, SUMMARY, jsonRemoval, textRemoval).join(); final Map<String, Change<?>> changes = repo.diff(prevRevison, currRevision, Repository.ALL_PATH).join(); assertThat(changes).hasSize(2) .containsEntry(jsonPath, jsonRemoval) .containsEntry(textPath, textRemoval); final Map<String, Change<?>> changesRelative = repo.diff(HEAD.backward(1), HEAD, allPattern).join(); assertThat(changesRelative).isEqualTo(changes); prevRevison = currRevision; } }
/** * POST /projects/{projectName}/repositories/{repoName}/delete/revisions/{revision}{path} * Deletes a file. */ @Post("regex:/projects/(?<projectName>[^/]+)/repositories/(?<repoName>[^/]+)" + "/delete/revisions/(?<revision>[^/]+)(?<path>/.*$)") @RequiresWritePermission public HttpResponse deleteFile(@Param("projectName") String projectName, @Param("repoName") String repoName, @Param("revision") String revision, @Param("path") String path, AggregatedHttpMessage message, ServiceRequestContext ctx) { final CommitMessageDto commitMessage; try { final JsonNode node = Jackson.readTree(message.content().toStringUtf8()); commitMessage = Jackson.convertValue(node.get("commitMessage"), CommitMessageDto.class); } catch (IOException e) { throw new IllegalArgumentException("invalid data to be parsed", e); } final CompletableFuture<?> future = push(projectName, repoName, new Revision(revision), AuthUtil.currentAuthor(ctx), commitMessage.getSummary(), commitMessage.getDetail().getContent(), Markup.valueOf(commitMessage.getDetail().getMarkup()), Change.ofRemoval(path)); return HttpResponse.from(future.thenApply(unused -> HttpResponse.of(HttpStatus.OK))); }
/** * POST /projects/{projectName}/repositories/{repoName}/delete/revisions/{revision}{path} * Deletes a file. */ @Post("regex:/projects/(?<projectName>[^/]+)/repositories/(?<repoName>[^/]+)" + "/delete/revisions/(?<revision>[^/]+)(?<path>/.*$)") @RequiresWritePermission public HttpResponse deleteFile(@Param("projectName") String projectName, @Param("repoName") String repoName, @Param("revision") String revision, @Param("path") String path, AggregatedHttpMessage message, ServiceRequestContext ctx) { final CommitMessageDto commitMessage; try { final JsonNode node = Jackson.readTree(message.content().toStringUtf8()); commitMessage = Jackson.convertValue(node.get("commitMessage"), CommitMessageDto.class); } catch (IOException e) { throw new IllegalArgumentException("invalid data to be parsed", e); } final CompletableFuture<?> future = push(projectName, repoName, new Revision(revision), AuthUtil.currentAuthor(ctx), commitMessage.getSummary(), commitMessage.getDetail().getContent(), Markup.valueOf(commitMessage.getDetail().getMarkup()), Change.ofRemoval(path)); return HttpResponse.from(future.thenApply(unused -> HttpResponse.of(HttpStatus.OK))); }
/** * POST /projects/{projectName}/repositories/{repoName}/delete/revisions/{revision}{path} * Deletes a file. */ @Post("regex:/projects/(?<projectName>[^/]+)/repositories/(?<repoName>[^/]+)" + "/delete/revisions/(?<revision>[^/]+)(?<path>/.*$)") @Decorator(HasWritePermission.class) public HttpResponse deleteFile(@Param("projectName") String projectName, @Param("repoName") String repoName, @Param("revision") String revision, @Param("path") String path, AggregatedHttpMessage message, ServiceRequestContext ctx) { final CommitMessageDto commitMessage; try { final JsonNode node = Jackson.readTree(message.content().toStringUtf8()); commitMessage = Jackson.convertValue(node.get("commitMessage"), CommitMessageDto.class); } catch (IOException e) { throw new IllegalArgumentException("invalid data to be parsed", e); } final CompletableFuture<?> future = push(projectName, repoName, new Revision(revision), AuthenticationUtil.currentAuthor(ctx), commitMessage.getSummary(), commitMessage.getDetail().getContent(), Markup.valueOf(commitMessage.getDetail().getMarkup()), Change.ofRemoval(path)); return HttpResponse.from(future.thenApply(unused -> HttpResponse.of(HttpStatus.OK))); }
@Override public <T> CompletableFuture<T> execute(Command<T> command) { if (!(command instanceof CreateProjectCommand)) { return super.execute(command); } final CreateProjectCommand c = (CreateProjectCommand) command; final String projectName = c.projectName(); final long creationTimeMillis = c.timestamp(); final Author author = c.author(); // Do not generate sample files because they are not necessary for the migration test. CompletableFuture<?> f = delegate().execute(c); f = f.thenCompose(unused -> delegate().execute( Command.push(creationTimeMillis, author, projectName, REPO_DOGMA, Revision.HEAD, "Delete /metadata.json to mimic the legacy project", "", Markup.PLAINTEXT, Change.ofRemoval(METADATA_JSON)))); f = f.exceptionally(unused -> null); return f.thenCompose(unused -> delegate().execute( Command.createRepository(creationTimeMillis, author, projectName, REPO_FOO))) .thenApply(unused -> null); } }
@Override protected com.linecorp.centraldogma.common.Change<?> doBackward(Change c) { switch (c.getType()) { case UPSERT_JSON: return com.linecorp.centraldogma.common.Change.ofJsonUpsert(c.getPath(), c.getContent()); case UPSERT_TEXT: return com.linecorp.centraldogma.common.Change.ofTextUpsert(c.getPath(), c.getContent()); case REMOVE: return com.linecorp.centraldogma.common.Change.ofRemoval(c.getPath()); case RENAME: return com.linecorp.centraldogma.common.Change.ofRename(c.getPath(), c.getContent()); case APPLY_JSON_PATCH: return com.linecorp.centraldogma.common.Change.ofJsonPatch(c.getPath(), c.getContent()); case APPLY_TEXT_PATCH: return com.linecorp.centraldogma.common.Change.ofTextPatch(c.getPath(), c.getContent()); } throw new Error(); }
@Override protected com.linecorp.centraldogma.common.Change<?> doBackward(Change c) { switch (c.getType()) { case UPSERT_JSON: return com.linecorp.centraldogma.common.Change.ofJsonUpsert(c.getPath(), c.getContent()); case UPSERT_TEXT: return com.linecorp.centraldogma.common.Change.ofTextUpsert(c.getPath(), c.getContent()); case REMOVE: return com.linecorp.centraldogma.common.Change.ofRemoval(c.getPath()); case RENAME: return com.linecorp.centraldogma.common.Change.ofRename(c.getPath(), c.getContent()); case APPLY_JSON_PATCH: return com.linecorp.centraldogma.common.Change.ofJsonPatch(c.getPath(), c.getContent()); case APPLY_TEXT_PATCH: return com.linecorp.centraldogma.common.Change.ofTextPatch(c.getPath(), c.getContent()); } throw new Error(); }
break; case REMOVE: converted = unsafeCast(Change.ofRemoval(query.path())); break; case RENAME: