mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-03-05 11:50:58 +01:00
Merged in feature/editor_plugin_delete (pull request #307)
Feature/editor plugin delete
This commit is contained in:
@@ -8,7 +8,7 @@ public interface ModifyCommand {
|
||||
String execute(ModifyCommandRequest request);
|
||||
|
||||
interface Worker {
|
||||
void delete(String toBeDeleted);
|
||||
void delete(String toBeDeleted) throws IOException;
|
||||
|
||||
void create(String toBeCreated, File file, boolean overwrite) throws IOException;
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ public class ModifyCommandRequest implements Resetable, Validateable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(ModifyCommand.Worker worker) {
|
||||
public void execute(ModifyCommand.Worker worker) throws IOException {
|
||||
worker.delete(path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import sonia.scm.BadRequestException;
|
||||
import sonia.scm.ConcurrentModificationException;
|
||||
import sonia.scm.ContextEntry;
|
||||
import sonia.scm.ScmConstraintViolationException;
|
||||
import sonia.scm.NotFoundException;
|
||||
import sonia.scm.repository.GitWorkdirFactory;
|
||||
import sonia.scm.repository.InternalRepositoryException;
|
||||
import sonia.scm.repository.Repository;
|
||||
@@ -18,6 +18,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -83,11 +84,7 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman
|
||||
try {
|
||||
Files.copy(file.toPath(), targetFile);
|
||||
} catch (FileAlreadyExistsException e) {
|
||||
ContextEntry.ContextBuilder contextBuilder = entity("file", toBeCreated);
|
||||
if (!StringUtils.isEmpty(request.getBranch())) {
|
||||
contextBuilder.in("branch", request.getBranch());
|
||||
}
|
||||
throw alreadyExists(contextBuilder.in(context.getRepository()));
|
||||
throw alreadyExists(createFileContext(toBeCreated));
|
||||
}
|
||||
}
|
||||
try {
|
||||
@@ -98,8 +95,27 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String toBeDeleted) {
|
||||
public void delete(String toBeDeleted) throws IOException {
|
||||
Path fileToBeDeleted = new File(workDir, toBeDeleted).toPath();
|
||||
try {
|
||||
Files.delete(fileToBeDeleted);
|
||||
} catch (NoSuchFileException e) {
|
||||
throw NotFoundException.notFound(createFileContext(toBeDeleted));
|
||||
}
|
||||
try {
|
||||
getClone().rm().addFilepattern(toBeDeleted).call();
|
||||
} catch (GitAPIException e) {
|
||||
throwInternalRepositoryException("could not remove file from index", e);
|
||||
}
|
||||
}
|
||||
|
||||
private ContextEntry.ContextBuilder createFileContext(String toBeDeleted) {
|
||||
ContextEntry.ContextBuilder contextBuilder = entity("file", toBeDeleted);
|
||||
if (!StringUtils.isEmpty(request.getBranch())) {
|
||||
contextBuilder.in("branch", request.getBranch());
|
||||
}
|
||||
contextBuilder.in(context.getRepository());
|
||||
return contextBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,6 +16,7 @@ import org.junit.rules.TemporaryFolder;
|
||||
import sonia.scm.AlreadyExistsException;
|
||||
import sonia.scm.BadRequestException;
|
||||
import sonia.scm.ConcurrentModificationException;
|
||||
import sonia.scm.NotFoundException;
|
||||
import sonia.scm.ScmConstraintViolationException;
|
||||
import sonia.scm.repository.Person;
|
||||
import sonia.scm.repository.util.WorkdirProvider;
|
||||
@@ -157,6 +158,47 @@ public class GitModifyCommandTest extends AbstractGitCommandTestBase {
|
||||
command.execute(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDeleteExistingFile() throws IOException, GitAPIException {
|
||||
GitModifyCommand command = createCommand();
|
||||
|
||||
ModifyCommandRequest request = new ModifyCommandRequest();
|
||||
request.setCommitMessage("test commit");
|
||||
request.addRequest(new ModifyCommandRequest.DeleteFileRequest("a.txt"));
|
||||
request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det"));
|
||||
|
||||
command.execute(request);
|
||||
|
||||
TreeAssertions assertions = canonicalTreeParser -> assertThat(canonicalTreeParser.findFile("a.txt")).isFalse();
|
||||
|
||||
assertInTree(assertions);
|
||||
}
|
||||
|
||||
@Test(expected = NotFoundException.class)
|
||||
public void shouldThrowNotFoundExceptionWhenFileToDeleteDoesNotExist() {
|
||||
GitModifyCommand command = createCommand();
|
||||
|
||||
ModifyCommandRequest request = new ModifyCommandRequest();
|
||||
request.setCommitMessage("test commit");
|
||||
request.addRequest(new ModifyCommandRequest.DeleteFileRequest("no/such/file"));
|
||||
request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det"));
|
||||
|
||||
command.execute(request);
|
||||
}
|
||||
|
||||
@Test(expected = NotFoundException.class)
|
||||
public void shouldThrowNotFoundExceptionWhenBranchDoesNotExist() {
|
||||
GitModifyCommand command = createCommand();
|
||||
|
||||
ModifyCommandRequest request = new ModifyCommandRequest();
|
||||
request.setBranch("does-not-exist");
|
||||
request.setCommitMessage("test commit");
|
||||
request.addRequest(new ModifyCommandRequest.DeleteFileRequest("a.txt"));
|
||||
request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det"));
|
||||
|
||||
command.execute(request);
|
||||
}
|
||||
|
||||
@Test(expected = ScmConstraintViolationException.class)
|
||||
public void shouldFailWithConstraintViolationIfBranchIsNoBranch() throws IOException {
|
||||
File newFile = Files.write(temporaryFolder.newFile().toPath(), "irrelevant\n".getBytes()).toFile();
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import type { Branch } from "@scm-manager/ui-types";
|
||||
import type { Branch, Repository } from "@scm-manager/ui-types";
|
||||
import injectSheet from "react-jss";
|
||||
import { ExtensionPoint, binder } from "@scm-manager/ui-extensions";
|
||||
import {ButtonGroup} from "./buttons";
|
||||
import classNames from "classnames";
|
||||
|
||||
type Props = {
|
||||
repository: Repository,
|
||||
branch: Branch,
|
||||
defaultBranch: Branch,
|
||||
branches: Branch[],
|
||||
@@ -63,7 +64,7 @@ class Breadcrumb extends React.Component<Props> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { classes, baseUrl, branch, defaultBranch, branches, revision, path } = this.props;
|
||||
const { classes, baseUrl, branch, defaultBranch, branches, revision, path, repository } = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -82,7 +83,9 @@ class Breadcrumb extends React.Component<Props> {
|
||||
branch: branch ? branch : defaultBranch,
|
||||
path,
|
||||
isBranchUrl: branches &&
|
||||
branches.filter(b => b.name.replace("/", "%2F") === revision).length > 0 }}
|
||||
branches.filter(b => b.name.replace("/", "%2F") === revision).length > 0,
|
||||
repository
|
||||
}}
|
||||
renderAll={true}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
|
||||
@@ -2,7 +2,12 @@
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import type { File, Repository } from "@scm-manager/ui-types";
|
||||
import { DateFromNow, ButtonGroup, FileSize } from "@scm-manager/ui-components";
|
||||
import {
|
||||
DateFromNow,
|
||||
ButtonGroup,
|
||||
FileSize,
|
||||
ErrorNotification
|
||||
} from "@scm-manager/ui-components";
|
||||
import injectSheet from "react-jss";
|
||||
import classNames from "classnames";
|
||||
import FileButtonGroup from "../components/content/FileButtonGroup";
|
||||
@@ -14,7 +19,6 @@ import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
|
||||
type Props = {
|
||||
loading: boolean,
|
||||
error: Error,
|
||||
file: File,
|
||||
repository: Repository,
|
||||
revision: string,
|
||||
@@ -25,7 +29,8 @@ type Props = {
|
||||
|
||||
type State = {
|
||||
collapsed: boolean,
|
||||
showHistory: boolean
|
||||
showHistory: boolean,
|
||||
errorFromExtension?: Error
|
||||
};
|
||||
|
||||
const styles = {
|
||||
@@ -67,8 +72,12 @@ class Content extends React.Component<Props, State> {
|
||||
});
|
||||
}
|
||||
|
||||
handleExtensionError = (error: Error) => {
|
||||
this.setState({ errorFromExtension: error });
|
||||
};
|
||||
|
||||
showHeader() {
|
||||
const { file, classes } = this.props;
|
||||
const { file, revision, classes } = this.props;
|
||||
const { showHistory, collapsed } = this.state;
|
||||
const icon = collapsed ? "fa-angle-right" : "fa-angle-down";
|
||||
|
||||
@@ -100,7 +109,11 @@ class Content extends React.Component<Props, State> {
|
||||
<ButtonGroup>
|
||||
<ExtensionPoint
|
||||
name="repos.sources.content.actionbar"
|
||||
props={{ file }}
|
||||
props={{
|
||||
file,
|
||||
revision,
|
||||
handleExtensionError: this.handleExtensionError
|
||||
}}
|
||||
renderAll={true}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
@@ -167,7 +180,7 @@ class Content extends React.Component<Props, State> {
|
||||
|
||||
render() {
|
||||
const { file, revision, repository, path } = this.props;
|
||||
const { showHistory } = this.state;
|
||||
const { showHistory, errorFromExtension } = this.state;
|
||||
|
||||
const header = this.showHeader();
|
||||
const content =
|
||||
@@ -190,6 +203,7 @@ class Content extends React.Component<Props, State> {
|
||||
{moreInformation}
|
||||
{content}
|
||||
</div>
|
||||
{errorFromExtension && <ErrorNotification error={errorFromExtension} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -150,6 +150,7 @@ class Sources extends React.Component<Props, State> {
|
||||
branches && branches.filter(b => b.defaultBranch === true)[0]
|
||||
}
|
||||
branches={branches && branches}
|
||||
repository={repository}
|
||||
/>
|
||||
<FileTree
|
||||
repository={repository}
|
||||
|
||||
Reference in New Issue
Block a user