This commit is contained in:
René Pfeuffer
2018-11-06 09:26:41 +01:00
119 changed files with 8479 additions and 5472 deletions

View File

@@ -757,7 +757,7 @@
<guice.version>4.0</guice.version> <guice.version>4.0</guice.version>
<!-- event bus --> <!-- event bus -->
<legman.version>1.3.0</legman.version> <legman.version>1.4.0</legman.version>
<!-- webserver --> <!-- webserver -->
<jetty.version>9.2.10.v20150310</jetty.version> <jetty.version>9.2.10.v20150310</jetty.version>

View File

@@ -33,33 +33,30 @@
package sonia.scm; package sonia.scm;
import java.util.Collections;
/** /**
* *
* @author Sebastian Sdorra * @author Sebastian Sdorra
* @version 1.6 * @version 1.6
*/ */
public class NotSupportedFeatureException extends Exception public class NotSupportedFeatureException extends ExceptionWithContext {
{
/** Field description */
private static final long serialVersionUID = 256498734456613496L; private static final long serialVersionUID = 256498734456613496L;
//~--- constructors --------------------------------------------------------- private static final String CODE = "9SR8G0kmU1";
/** public NotSupportedFeatureException(String feature)
* Constructs ...
*
*/
public NotSupportedFeatureException() {}
/**
* Constructs ...
*
*
* @param message
*/
public NotSupportedFeatureException(String message)
{ {
super(message); super(Collections.emptyList(),createMessage(feature));
}
@Override
public String getCode() {
return CODE;
}
private static String createMessage(String feature) {
return "feature " + feature + " is not supported by this repository";
} }
} }

View File

@@ -170,8 +170,7 @@ public abstract class AbstractRepositoryHandler<C extends RepositoryConfig>
@Override @Override
public ImportHandler getImportHandler() throws NotSupportedFeatureException public ImportHandler getImportHandler() throws NotSupportedFeatureException
{ {
throw new NotSupportedFeatureException( throw new NotSupportedFeatureException("import");
"import handler is not supported by this repository handler");
} }
/** /**

View File

@@ -80,13 +80,6 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.glassfish</groupId> <groupId>org.glassfish</groupId>
<artifactId>javax.json</artifactId> <artifactId>javax.json</artifactId>

View File

@@ -0,0 +1,265 @@
package sonia.scm.it;
import org.apache.http.HttpStatus;
import org.assertj.core.util.Lists;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import sonia.scm.it.utils.RepositoryUtil;
import sonia.scm.it.utils.ScmRequests;
import sonia.scm.it.utils.TestData;
import sonia.scm.repository.Changeset;
import sonia.scm.repository.client.api.RepositoryClient;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
import static sonia.scm.it.utils.RestUtil.ADMIN_PASSWORD;
import static sonia.scm.it.utils.RestUtil.ADMIN_USERNAME;
public class DiffITCase {
@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();
private RepositoryClient svnRepositoryClient;
private RepositoryClient gitRepositoryClient;
private RepositoryClient hgRepositoryClient;
private ScmRequests.RepositoryResponse<ScmRequests.IndexResponse> svnRepositoryResponse;
private ScmRequests.RepositoryResponse<ScmRequests.IndexResponse> hgRepositoryResponse;
private ScmRequests.RepositoryResponse<ScmRequests.IndexResponse> gitRepositoryResponse;
private File svnFolder;
private File gitFolder;
private File hgFolder;
@Before
public void init() throws IOException {
TestData.createDefault();
String namespace = ADMIN_USERNAME;
String repo = TestData.getDefaultRepoName("svn");
svnRepositoryResponse =
ScmRequests.start()
.requestIndexResource(ADMIN_USERNAME, ADMIN_PASSWORD)
.requestRepository(namespace, repo)
.assertStatusCode(HttpStatus.SC_OK);
svnFolder = tempFolder.newFolder("svn");
svnRepositoryClient = RepositoryUtil.createRepositoryClient("svn", svnFolder);
repo = TestData.getDefaultRepoName("git");
gitRepositoryResponse =
ScmRequests.start()
.requestIndexResource(ADMIN_USERNAME, ADMIN_PASSWORD)
.requestRepository(namespace, repo)
.assertStatusCode(HttpStatus.SC_OK);
gitFolder = tempFolder.newFolder("git");
gitRepositoryClient = RepositoryUtil.createRepositoryClient("git", gitFolder);
repo = TestData.getDefaultRepoName("hg");
hgRepositoryResponse =
ScmRequests.start()
.requestIndexResource(ADMIN_USERNAME, ADMIN_PASSWORD)
.requestRepository(namespace, repo)
.assertStatusCode(HttpStatus.SC_OK);
hgFolder = tempFolder.newFolder("hg");
hgRepositoryClient = RepositoryUtil.createRepositoryClient("hg", hgFolder);
}
@Test
public void shouldFindDiffsInGitFormat() throws IOException {
String svnDiff = getDiff(RepositoryUtil.createAndCommitFile(svnRepositoryClient, ADMIN_USERNAME, "a.txt", "content of a"), svnRepositoryResponse);
String gitDiff = getDiff(RepositoryUtil.createAndCommitFile(gitRepositoryClient, ADMIN_USERNAME, "a.txt", "content of a"), gitRepositoryResponse);
String hgDiff = getDiff(RepositoryUtil.createAndCommitFile(hgRepositoryClient, ADMIN_USERNAME, "a.txt", "content of a"), hgRepositoryResponse);
assertThat(Lists.newArrayList(svnDiff, gitDiff, hgDiff))
.allSatisfy(diff -> assertThat(diff)
.contains("diff --git "));
}
@Test
public void svnAddFileDiffShouldBeConvertedToGitDiff() throws IOException {
String svnDiff = getDiff(RepositoryUtil.createAndCommitFile(svnRepositoryClient, ADMIN_USERNAME, "a.txt", "content of a"), svnRepositoryResponse);
String gitDiff = getDiff(RepositoryUtil.createAndCommitFile(gitRepositoryClient, ADMIN_USERNAME, "a.txt", "content of a"), gitRepositoryResponse);
String expected = getGitDiffWithoutIndexLine(gitDiff);
assertThat(svnDiff)
.isEqualTo(expected);
}
@Test
public void svnDeleteFileDiffShouldBeConvertedToGitDiff() throws IOException {
RepositoryUtil.createAndCommitFile(svnRepositoryClient, ADMIN_USERNAME, "a.txt", "content of a");
RepositoryUtil.createAndCommitFile(gitRepositoryClient, ADMIN_USERNAME, "a.txt", "content of a");
String svnDiff = getDiff(RepositoryUtil.removeAndCommitFile(svnRepositoryClient, ADMIN_USERNAME, "a.txt"), svnRepositoryResponse);
String gitDiff = getDiff(RepositoryUtil.removeAndCommitFile(gitRepositoryClient, ADMIN_USERNAME, "a.txt"), gitRepositoryResponse);
String expected = getGitDiffWithoutIndexLine(gitDiff);
assertThat(svnDiff)
.isEqualTo(expected);
}
@Test
public void svnUpdateFileDiffShouldBeConvertedToGitDiff() throws IOException {
RepositoryUtil.createAndCommitFile(svnRepositoryClient, ADMIN_USERNAME, "a.txt", "content of a");
RepositoryUtil.createAndCommitFile(gitRepositoryClient, ADMIN_USERNAME, "a.txt", "content of a");
String svnDiff = getDiff(RepositoryUtil.updateAndCommitFile(svnRepositoryClient, ADMIN_USERNAME, "a.txt", "the updated content of a"), svnRepositoryResponse);
String gitDiff = getDiff(RepositoryUtil.updateAndCommitFile(gitRepositoryClient, ADMIN_USERNAME, "a.txt", "the updated content of a"), gitRepositoryResponse);
String expected = getGitDiffWithoutIndexLine(gitDiff);
assertThat(svnDiff)
.isEqualTo(expected);
}
@Test
public void svnMultipleChangesDiffShouldBeConvertedToGitDiff() throws IOException {
String svnDiff = getDiff(applyMultipleChanges(svnRepositoryClient, "fileToBeDeleted.txt", "fileToBeUpdated.txt", "addedFile.txt"), svnRepositoryResponse);
String gitDiff = getDiff(applyMultipleChanges(gitRepositoryClient, "fileToBeDeleted.txt", "fileToBeUpdated.txt", "addedFile.txt"), gitRepositoryResponse);
String endOfDiffPart = "\\ No newline at end of file\n";
String[] gitDiffs = gitDiff.split(endOfDiffPart);
List<String> expected = Arrays.stream(gitDiffs)
.map(this::getGitDiffWithoutIndexLine)
.collect(Collectors.toList());
assertThat(svnDiff.split(endOfDiffPart))
.containsExactlyInAnyOrderElementsOf(expected);
}
@Test
public void svnMultipleSubFolderChangesDiffShouldBeConvertedToGitDiff() throws IOException {
String svnDiff = getDiff(applyMultipleChanges(svnRepositoryClient, "a/b/fileToBeDeleted.txt", "a/c/fileToBeUpdated.txt", "a/d/addedFile.txt"), svnRepositoryResponse);
String gitDiff = getDiff(applyMultipleChanges(gitRepositoryClient, "a/b/fileToBeDeleted.txt", "a/c/fileToBeUpdated.txt", "a/d/addedFile.txt"), gitRepositoryResponse);
String endOfDiffPart = "\\ No newline at end of file\n";
String[] gitDiffs = gitDiff.split(endOfDiffPart);
List<String> expected = Arrays.stream(gitDiffs)
.map(this::getGitDiffWithoutIndexLine)
.collect(Collectors.toList());
assertThat(svnDiff.split(endOfDiffPart))
.containsExactlyInAnyOrderElementsOf(expected);
}
@Test
public void svnLargeChangesDiffShouldBeConvertedToGitDiff() throws IOException, URISyntaxException {
String fileName = "SvnDiffGenerator_forTest";
RepositoryUtil.createAndCommitFile(svnRepositoryClient, ADMIN_USERNAME, fileName, "");
RepositoryUtil.createAndCommitFile(gitRepositoryClient, ADMIN_USERNAME, fileName, "");
String fileContent = getFileContent("/diff/largefile/original/SvnDiffGenerator_forTest");
String svnDiff = getDiff(RepositoryUtil.updateAndCommitFile(svnRepositoryClient, ADMIN_USERNAME, fileName, fileContent), svnRepositoryResponse);
String gitDiff = getDiff(RepositoryUtil.updateAndCommitFile(gitRepositoryClient, ADMIN_USERNAME, fileName, fileContent), gitRepositoryResponse);
assertThat(svnDiff)
.isEqualTo(getGitDiffWithoutIndexLine(gitDiff));
fileContent = getFileContent("/diff/largefile/modified/v1/SvnDiffGenerator_forTest");
svnDiff = getDiff(RepositoryUtil.updateAndCommitFile(svnRepositoryClient, ADMIN_USERNAME, fileName, fileContent), svnRepositoryResponse);
gitDiff = getDiff(RepositoryUtil.updateAndCommitFile(gitRepositoryClient, ADMIN_USERNAME, fileName, fileContent), gitRepositoryResponse);
assertThat(svnDiff)
.isEqualTo(getGitDiffWithoutIndexLine(gitDiff));
fileContent = getFileContent("/diff/largefile/modified/v2/SvnDiffGenerator_forTest");
svnDiff = getDiff(RepositoryUtil.updateAndCommitFile(svnRepositoryClient, ADMIN_USERNAME, fileName, fileContent), svnRepositoryResponse);
gitDiff = getDiff(RepositoryUtil.updateAndCommitFile(gitRepositoryClient, ADMIN_USERNAME, fileName, fileContent), gitRepositoryResponse);
assertThat(svnDiff)
.isEqualTo(getGitDiffWithoutIndexLine(gitDiff));
}
/**
* FIXME: the binary Git Diff output is not GIT conform
*/
@Test
@Ignore
@SuppressWarnings("squid:S1607")
public void svnBinaryChangesDiffShouldBeConvertedToGitDiff() throws IOException, URISyntaxException {
String fileName = "binary";
File file = new File(svnRepositoryClient.getWorkingCopy(), fileName);
Files.copy(Paths.get(getClass().getResource("/diff/binaryfile/echo").toURI()), Paths.get(file.toURI()));
Changeset commit = RepositoryUtil.addFileAndCommit(svnRepositoryClient, fileName, ADMIN_USERNAME, "");
file = new File(gitRepositoryClient.getWorkingCopy(), fileName);
Files.copy(Paths.get(getClass().getResource("/diff/binaryfile/echo").toURI()), Paths.get(file.toURI()));
Changeset commit1 = RepositoryUtil.addFileAndCommit(gitRepositoryClient, fileName, ADMIN_USERNAME, "");
String svnDiff = getDiff(commit, svnRepositoryResponse);
String gitDiff = getDiff(commit1, gitRepositoryResponse);
assertThat(svnDiff)
.isEqualTo(getGitDiffWithoutIndexLine(gitDiff));
}
@Test
public void svnRenameChangesDiffShouldBeConvertedToGitDiff() throws IOException, URISyntaxException {
String fileName = "a.txt";
RepositoryUtil.createAndCommitFile(svnRepositoryClient, ADMIN_USERNAME, fileName, "content of a");
RepositoryUtil.createAndCommitFile(gitRepositoryClient, ADMIN_USERNAME, fileName, "content of a");
String newFileName = "renamed_a.txt";
File file = new File(svnRepositoryClient.getWorkingCopy(), fileName);
file.renameTo(new File(svnRepositoryClient.getWorkingCopy(), newFileName));
String svnDiff = getDiff(RepositoryUtil.addFileAndCommit(svnRepositoryClient, newFileName, ADMIN_USERNAME, "renamed file"), svnRepositoryResponse);
file = new File(gitRepositoryClient.getWorkingCopy(), fileName);
file.renameTo(new File(gitRepositoryClient.getWorkingCopy(), newFileName));
String gitDiff = getDiff(RepositoryUtil.addFileAndCommit(gitRepositoryClient, newFileName, ADMIN_USERNAME, "renamed file"), gitRepositoryResponse);
String expected = getGitDiffWithoutIndexLine(gitDiff);
assertThat(svnDiff)
.isEqualTo(expected);
}
public String getFileContent(String name) throws URISyntaxException, IOException {
Path path;
path = Paths.get(getClass().getResource(name).toURI());
Stream<String> lines = Files.lines(path);
String data = lines.collect(Collectors.joining("\n"));
lines.close();
return data;
}
/**
* The index line is not provided from the svn git formatter and it is not needed in the ui diff view
* for more details about the git diff format: https://git-scm.com/docs/git-diff
*
* @param gitDiff
* @return diff without the index line
*/
private String getGitDiffWithoutIndexLine(String gitDiff) {
return gitDiff.replaceAll(".*(index.*\n)", "");
}
private String getDiff(Changeset svnChangeset, ScmRequests.RepositoryResponse<ScmRequests.IndexResponse> svnRepositoryResponse) {
return svnRepositoryResponse.requestChangesets()
.requestDiffInGitFormat(svnChangeset.getId())
.getResponse()
.body()
.asString();
}
private Changeset applyMultipleChanges(RepositoryClient repositoryClient, String fileToBeDeleted, final String fileToBeUpdated, final String addedFile) throws IOException {
RepositoryUtil.createAndCommitFile(repositoryClient, ADMIN_USERNAME, fileToBeDeleted, "file to be deleted");
RepositoryUtil.createAndCommitFile(repositoryClient, ADMIN_USERNAME, fileToBeUpdated, "file to be updated");
Map<String, String> addedFiles = new HashMap<String, String>() {{
put(addedFile, "content");
}};
Map<String, String> modifiedFiles = new HashMap<String, String>() {{
put(fileToBeUpdated, "the updated content");
}};
ArrayList<String> removedFiles = Lists.newArrayList(fileToBeDeleted);
return RepositoryUtil.commitMultipleFileModifications(repositoryClient, ADMIN_USERNAME, addedFiles, modifiedFiles, removedFiles);
}
}

View File

@@ -1,5 +1,6 @@
package sonia.scm.it; package sonia.scm.it;
import groovy.util.logging.Slf4j;
import io.restassured.response.ExtractableResponse; import io.restassured.response.ExtractableResponse;
import io.restassured.response.Response; import io.restassured.response.Response;
import org.apache.http.HttpStatus; import org.apache.http.HttpStatus;
@@ -37,6 +38,7 @@ import static sonia.scm.it.utils.RestUtil.given;
import static sonia.scm.it.utils.ScmTypes.availableScmTypes; import static sonia.scm.it.utils.ScmTypes.availableScmTypes;
@RunWith(Parameterized.class) @RunWith(Parameterized.class)
@Slf4j
public class RepositoryAccessITCase { public class RepositoryAccessITCase {
@Rule @Rule
@@ -63,9 +65,9 @@ public class RepositoryAccessITCase {
String repo = TestData.getDefaultRepoName(repositoryType); String repo = TestData.getDefaultRepoName(repositoryType);
repositoryResponse = repositoryResponse =
ScmRequests.start() ScmRequests.start()
.requestIndexResource(ADMIN_USERNAME, ADMIN_PASSWORD) .requestIndexResource(ADMIN_USERNAME, ADMIN_PASSWORD)
.requestRepository(namespace, repo) .requestRepository(namespace, repo)
.assertStatusCode(HttpStatus.SC_OK); .assertStatusCode(HttpStatus.SC_OK);
} }
@Test @Test
@@ -175,6 +177,7 @@ public class RepositoryAccessITCase {
} }
@Test @Test
@SuppressWarnings("squid:S2925")
public void shouldReadContent() throws IOException, InterruptedException { public void shouldReadContent() throws IOException, InterruptedException {
RepositoryClient repositoryClient = RepositoryUtil.createRepositoryClient(repositoryType, folder); RepositoryClient repositoryClient = RepositoryUtil.createRepositoryClient(repositoryType, folder);
RepositoryUtil.createAndCommitFile(repositoryClient, "scmadmin", "a.txt", "a"); RepositoryUtil.createAndCommitFile(repositoryClient, "scmadmin", "a.txt", "a");
@@ -262,40 +265,6 @@ public class RepositoryAccessITCase {
assertThat(changesets).size().isBetween(2, 3); // svn has an implicit root revision '0' that is extra to the two commits assertThat(changesets).size().isBetween(2, 3); // svn has an implicit root revision '0' that is extra to the two commits
} }
@Test
public void shouldFindDiffs() throws IOException {
RepositoryClient repositoryClient = RepositoryUtil.createRepositoryClient(repositoryType, folder);
RepositoryUtil.createAndCommitFile(repositoryClient, "scmadmin", "a.txt", "a");
RepositoryUtil.createAndCommitFile(repositoryClient, "scmadmin", "b.txt", "b");
String changesetsUrl = given()
.when()
.get(TestData.getDefaultRepositoryUrl(repositoryType))
.then()
.statusCode(HttpStatus.SC_OK)
.extract()
.path("_links.changesets.href");
String diffUrl = given()
.when()
.get(changesetsUrl)
.then()
.statusCode(HttpStatus.SC_OK)
.extract()
.path("_embedded.changesets[0]._links.diff.href");
given()
.when()
.get(diffUrl)
.then()
.statusCode(HttpStatus.SC_OK)
.extract()
.body()
.asString()
.contains("diff");
}
@Test @Test
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@@ -393,12 +362,10 @@ public class RepositoryAccessITCase {
RepositoryUtil.createAndCommitFile(repositoryClient, ADMIN_USERNAME, "b.txt", "b"); RepositoryUtil.createAndCommitFile(repositoryClient, ADMIN_USERNAME, "b.txt", "b");
RepositoryUtil.createAndCommitFile(repositoryClient, ADMIN_USERNAME, "c.txt", "c"); RepositoryUtil.createAndCommitFile(repositoryClient, ADMIN_USERNAME, "c.txt", "c");
RepositoryUtil.createAndCommitFile(repositoryClient, ADMIN_USERNAME, "d.txt", "d"); RepositoryUtil.createAndCommitFile(repositoryClient, ADMIN_USERNAME, "d.txt", "d");
Map<String, String> addedFiles = new HashMap<String, String>() Map<String, String> addedFiles = new HashMap<String, String>() {{
{{
put("a.txt", "bla bla"); put("a.txt", "bla bla");
}}; }};
Map<String, String> modifiedFiles = new HashMap<String, String>() Map<String, String> modifiedFiles = new HashMap<String, String>() {{
{{
put("b.txt", "new content"); put("b.txt", "new content");
}}; }};
ArrayList<String> removedFiles = Lists.newArrayList("c.txt", "d.txt"); ArrayList<String> removedFiles = Lists.newArrayList("c.txt", "d.txt");
@@ -414,7 +381,7 @@ public class RepositoryAccessITCase {
.assertAdded(a -> assertThat(a) .assertAdded(a -> assertThat(a)
.hasSize(1) .hasSize(1)
.containsExactly("a.txt")) .containsExactly("a.txt"))
.assertModified(m-> assertThat(m) .assertModified(m -> assertThat(m)
.hasSize(1) .hasSize(1)
.containsExactly("b.txt")) .containsExactly("b.txt"))
.assertRemoved(r -> assertThat(r) .assertRemoved(r -> assertThat(r)

View File

@@ -80,6 +80,11 @@ public class RepositoryUtil {
return file; return file;
} }
public static Changeset updateAndCommitFile(RepositoryClient repositoryClient, String username, String fileName, String content) throws IOException {
writeAndAddFile(repositoryClient, fileName, content);
return commit(repositoryClient, username, "updated " + fileName);
}
public static Changeset removeAndCommitFile(RepositoryClient repositoryClient, String username, String fileName) throws IOException { public static Changeset removeAndCommitFile(RepositoryClient repositoryClient, String username, String fileName) throws IOException {
deleteFileAndApplyRemoveCommand(repositoryClient, fileName); deleteFileAndApplyRemoveCommand(repositoryClient, fileName);
return commit(repositoryClient, username, "removed " + fileName); return commit(repositoryClient, username, "removed " + fileName);
@@ -102,11 +107,21 @@ public class RepositoryUtil {
} else { } else {
path = thisName; path = thisName;
} }
repositoryClient.getAddCommand().add(path); addFile(repositoryClient, path);
return path; return path;
} }
static Changeset commit(RepositoryClient repositoryClient, String username, String message) throws IOException { public static Changeset addFileAndCommit(RepositoryClient repositoryClient, String path, String username, String message) throws IOException {
repositoryClient.getAddCommand().add(path);
return commit(repositoryClient, username, message);
}
public static void addFile(RepositoryClient repositoryClient, String path) throws IOException {
repositoryClient.getAddCommand().add(path);
}
public static Changeset commit(RepositoryClient repositoryClient, String username, String message) throws IOException {
LOG.info("user: {} try to commit with message: {}", username, message); LOG.info("user: {} try to commit with message: {}", username, message);
Changeset changeset = repositoryClient.getCommitCommand().commit(new Person(username, username + "@scm-manager.org"), message); Changeset changeset = repositoryClient.getCommitCommand().commit(new Person(username, username + "@scm-manager.org"), message);
if (repositoryClient.isCommandSupported(ClientCommand.PUSH)) { if (repositoryClient.isCommandSupported(ClientCommand.PUSH)) {

View File

@@ -234,8 +234,8 @@ public class ScmRequests {
return this; return this;
} }
public DiffResponse<ChangesetsResponse> requestDiff(String revision) { public DiffResponse<ChangesetsResponse> requestDiffInGitFormat(String revision) {
return new DiffResponse<>(applyGETRequestFromLink(response, "_embedded.changesets.find{it.id=='" + revision + "'}._links.diff.href"), this); return new DiffResponse<>(applyGETRequestFromLinkWithParams(response, "_embedded.changesets.find{it.id=='" + revision + "'}._links.diff.href", "?format=GIT"), this);
} }
public ModificationsResponse<ChangesetsResponse> requestModifications(String revision) { public ModificationsResponse<ChangesetsResponse> requestModifications(String revision) {
@@ -362,6 +362,10 @@ public class ScmRequests {
this.previousResponse = previousResponse; this.previousResponse = previousResponse;
} }
public Response getResponse(){
return response;
}
public PREV returnToPrevious() { public PREV returnToPrevious() {
return previousResponse; return previousResponse;
} }

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -113,92 +113,35 @@
<plugin> <plugin>
<groupId>sonia.scm.maven</groupId> <groupId>sonia.scm.maven</groupId>
<artifactId>smp-maven-plugin</artifactId> <artifactId>smp-maven-plugin</artifactId>
<version>1.0.0-alpha-2</version> <version>1.0.0-alpha-3</version>
<extensions>true</extensions> <extensions>true</extensions>
<configuration> <configuration>
<disableCompression>true</disableCompression> <links>
<link>@scm-manager/ui-types</link>
<link>@scm-manager/ui-components</link>
</links>
</configuration> </configuration>
<executions>
<execution>
<id>fix-descriptor</id>
<phase>process-resources</phase>
<goals>
<goal>fix-descriptor</goal>
</goals>
</execution>
<execution>
<id>append-dependencies</id>
<phase>process-classes</phase>
<goals>
<goal>append-dependencies</goal>
</goals>
</execution>
</executions>
</plugin> </plugin>
<plugin> <plugin>
<groupId>com.github.sdorra</groupId> <groupId>com.github.sdorra</groupId>
<artifactId>buildfrontend-maven-plugin</artifactId> <artifactId>buildfrontend-maven-plugin</artifactId>
<configuration> <configuration>
<node> <node>
<version>${nodejs.version}</version> <version>${nodejs.version}</version>
</node> </node>
<pkgManager> <pkgManager>
<type>YARN</type> <type>YARN</type>
<version>${yarn.version}</version> <version>${yarn.version}</version>
</pkgManager> </pkgManager>
<failOnMissingPackageJson>false</failOnMissingPackageJson> <failOnMissingPackageJson>false</failOnMissingPackageJson>
<script>build</script> </configuration>
</configuration> </plugin>
<executions>
<execution>
<id>install</id>
<phase>process-resources</phase>
<goals>
<goal>install</goal>
</goals>
</execution>
<execution>
<id>build</id>
<phase>compile</phase>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>
<profiles> <profiles>
<profile>
<id>release</id>
<build>
<plugins>
<plugin>
<groupId>sonia.maven</groupId>
<artifactId>web-compressor</artifactId>
<version>1.4</version>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>compress-directory</goal>
</goals>
</execution>
</executions>
<configuration>
<replace>true</replace>
<baseDirectory>${project.build.directory}/classes</baseDirectory>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile> <profile>
<id>doc</id> <id>doc</id>

View File

@@ -4,5 +4,6 @@
[include] [include]
[libs] [libs]
./node_modules/@scm-manager/ui-components/flow-typed
[options] [options]

View File

@@ -1,23 +0,0 @@
// flow-typed signature: cf86673cc32d185bdab1d2ea90578d37
// flow-typed version: 614bf49aa8/classnames_v2.x.x/flow_>=v0.25.x
type $npm$classnames$Classes =
| string
| { [className: string]: * }
| false
| void
| null;
declare module "classnames" {
declare module.exports: (
...classes: Array<$npm$classnames$Classes | $npm$classnames$Classes[]>
) => string;
}
declare module "classnames/bind" {
declare module.exports: $Exports<"classnames">;
}
declare module "classnames/dedupe" {
declare module.exports: $Exports<"classnames">;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,331 +0,0 @@
// flow-typed signature: 23b805356f90ad9384dd88489654e380
// flow-typed version: e9374c5fe9/moment_v2.3.x/flow_>=v0.25.x
type moment$MomentOptions = {
y?: number | string,
year?: number | string,
years?: number | string,
M?: number | string,
month?: number | string,
months?: number | string,
d?: number | string,
day?: number | string,
days?: number | string,
date?: number | string,
h?: number | string,
hour?: number | string,
hours?: number | string,
m?: number | string,
minute?: number | string,
minutes?: number | string,
s?: number | string,
second?: number | string,
seconds?: number | string,
ms?: number | string,
millisecond?: number | string,
milliseconds?: number | string
};
type moment$MomentObject = {
years: number,
months: number,
date: number,
hours: number,
minutes: number,
seconds: number,
milliseconds: number
};
type moment$MomentCreationData = {
input: string,
format: string,
locale: Object,
isUTC: boolean,
strict: boolean
};
type moment$CalendarFormat = string | ((moment: moment$Moment) => string);
type moment$CalendarFormats = {
sameDay?: moment$CalendarFormat,
nextDay?: moment$CalendarFormat,
nextWeek?: moment$CalendarFormat,
lastDay?: moment$CalendarFormat,
lastWeek?: moment$CalendarFormat,
sameElse?: moment$CalendarFormat
};
declare class moment$LocaleData {
months(moment: moment$Moment): string,
monthsShort(moment: moment$Moment): string,
monthsParse(month: string): number,
weekdays(moment: moment$Moment): string,
weekdaysShort(moment: moment$Moment): string,
weekdaysMin(moment: moment$Moment): string,
weekdaysParse(weekDay: string): number,
longDateFormat(dateFormat: string): string,
isPM(date: string): boolean,
meridiem(hours: number, minutes: number, isLower: boolean): string,
calendar(
key:
| "sameDay"
| "nextDay"
| "lastDay"
| "nextWeek"
| "prevWeek"
| "sameElse",
moment: moment$Moment
): string,
relativeTime(
number: number,
withoutSuffix: boolean,
key: "s" | "m" | "mm" | "h" | "hh" | "d" | "dd" | "M" | "MM" | "y" | "yy",
isFuture: boolean
): string,
pastFuture(diff: any, relTime: string): string,
ordinal(number: number): string,
preparse(str: string): any,
postformat(str: string): any,
week(moment: moment$Moment): string,
invalidDate(): string,
firstDayOfWeek(): number,
firstDayOfYear(): number
}
declare class moment$MomentDuration {
humanize(suffix?: boolean): string,
milliseconds(): number,
asMilliseconds(): number,
seconds(): number,
asSeconds(): number,
minutes(): number,
asMinutes(): number,
hours(): number,
asHours(): number,
days(): number,
asDays(): number,
months(): number,
asWeeks(): number,
weeks(): number,
asMonths(): number,
years(): number,
asYears(): number,
add(value: number | moment$MomentDuration | Object, unit?: string): this,
subtract(value: number | moment$MomentDuration | Object, unit?: string): this,
as(unit: string): number,
get(unit: string): number,
toJSON(): string,
toISOString(): string,
isValid(): boolean
}
declare class moment$Moment {
static ISO_8601: string,
static (
string?: string,
format?: string | Array<string>,
strict?: boolean
): moment$Moment,
static (
string?: string,
format?: string | Array<string>,
locale?: string,
strict?: boolean
): moment$Moment,
static (
initDate: ?Object | number | Date | Array<number> | moment$Moment | string
): moment$Moment,
static unix(seconds: number): moment$Moment,
static utc(): moment$Moment,
static utc(number: number | Array<number>): moment$Moment,
static utc(
str: string,
str2?: string | Array<string>,
str3?: string
): moment$Moment,
static utc(moment: moment$Moment): moment$Moment,
static utc(date: Date): moment$Moment,
static parseZone(): moment$Moment,
static parseZone(rawDate: string): moment$Moment,
static parseZone(
rawDate: string,
format: string | Array<string>
): moment$Moment,
static parseZone(
rawDate: string,
format: string,
strict: boolean
): moment$Moment,
static parseZone(
rawDate: string,
format: string,
locale: string,
strict: boolean
): moment$Moment,
isValid(): boolean,
invalidAt(): 0 | 1 | 2 | 3 | 4 | 5 | 6,
creationData(): moment$MomentCreationData,
millisecond(number: number): this,
milliseconds(number: number): this,
millisecond(): number,
milliseconds(): number,
second(number: number): this,
seconds(number: number): this,
second(): number,
seconds(): number,
minute(number: number): this,
minutes(number: number): this,
minute(): number,
minutes(): number,
hour(number: number): this,
hours(number: number): this,
hour(): number,
hours(): number,
date(number: number): this,
dates(number: number): this,
date(): number,
dates(): number,
day(day: number | string): this,
days(day: number | string): this,
day(): number,
days(): number,
weekday(number: number): this,
weekday(): number,
isoWeekday(number: number): this,
isoWeekday(): number,
dayOfYear(number: number): this,
dayOfYear(): number,
week(number: number): this,
weeks(number: number): this,
week(): number,
weeks(): number,
isoWeek(number: number): this,
isoWeeks(number: number): this,
isoWeek(): number,
isoWeeks(): number,
month(number: number): this,
months(number: number): this,
month(): number,
months(): number,
quarter(number: number): this,
quarter(): number,
year(number: number): this,
years(number: number): this,
year(): number,
years(): number,
weekYear(number: number): this,
weekYear(): number,
isoWeekYear(number: number): this,
isoWeekYear(): number,
weeksInYear(): number,
isoWeeksInYear(): number,
get(string: string): number,
set(unit: string, value: number): this,
set(options: { [unit: string]: number }): this,
static max(...dates: Array<moment$Moment>): moment$Moment,
static max(dates: Array<moment$Moment>): moment$Moment,
static min(...dates: Array<moment$Moment>): moment$Moment,
static min(dates: Array<moment$Moment>): moment$Moment,
add(
value: number | moment$MomentDuration | moment$Moment | Object,
unit?: string
): this,
subtract(
value: number | moment$MomentDuration | moment$Moment | string | Object,
unit?: string
): this,
startOf(unit: string): this,
endOf(unit: string): this,
local(): this,
utc(): this,
utcOffset(
offset: number | string,
keepLocalTime?: boolean,
keepMinutes?: boolean
): this,
utcOffset(): number,
format(format?: string): string,
fromNow(removeSuffix?: boolean): string,
from(
value: moment$Moment | string | number | Date | Array<number>,
removePrefix?: boolean
): string,
toNow(removePrefix?: boolean): string,
to(
value: moment$Moment | string | number | Date | Array<number>,
removePrefix?: boolean
): string,
calendar(refTime?: any, formats?: moment$CalendarFormats): string,
diff(
date: moment$Moment | string | number | Date | Array<number>,
format?: string,
floating?: boolean
): number,
valueOf(): number,
unix(): number,
daysInMonth(): number,
toDate(): Date,
toArray(): Array<number>,
toJSON(): string,
toISOString(
keepOffset?: boolean
): string,
toObject(): moment$MomentObject,
isBefore(
date?: moment$Moment | string | number | Date | Array<number>,
units?: ?string
): boolean,
isSame(
date?: moment$Moment | string | number | Date | Array<number>,
units?: ?string
): boolean,
isAfter(
date?: moment$Moment | string | number | Date | Array<number>,
units?: ?string
): boolean,
isSameOrBefore(
date?: moment$Moment | string | number | Date | Array<number>,
units?: ?string
): boolean,
isSameOrAfter(
date?: moment$Moment | string | number | Date | Array<number>,
units?: ?string
): boolean,
isBetween(
fromDate: moment$Moment | string | number | Date | Array<number>,
toDate?: ?moment$Moment | string | number | Date | Array<number>,
granularity?: ?string,
inclusion?: ?string
): boolean,
isDST(): boolean,
isDSTShifted(): boolean,
isLeapYear(): boolean,
clone(): moment$Moment,
static isMoment(obj: any): boolean,
static isDate(obj: any): boolean,
static locale(locale: string, localeData?: Object): string,
static updateLocale(locale: string, localeData?: ?Object): void,
static locale(locales: Array<string>): string,
locale(locale: string, customization?: Object | null): moment$Moment,
locale(): string,
static months(): Array<string>,
static monthsShort(): Array<string>,
static weekdays(): Array<string>,
static weekdaysShort(): Array<string>,
static weekdaysMin(): Array<string>,
static months(): string,
static monthsShort(): string,
static weekdays(): string,
static weekdaysShort(): string,
static weekdaysMin(): string,
static localeData(key?: string): moment$LocaleData,
static duration(
value: number | Object | string,
unit?: string
): moment$MomentDuration,
static isDuration(obj: any): boolean,
static normalizeUnits(unit: string): string,
static invalid(object: any): moment$Moment
}
declare module "moment" {
declare module.exports: Class<moment$Moment>;
}

View File

@@ -1,137 +0,0 @@
// flow-typed signature: ba35d02d668b0d0a3e04a63a6847974e
// flow-typed version: <<STUB>>/react-jss_v8.6.1/flow_v0.79.1
/**
* This is an autogenerated libdef stub for:
*
* 'react-jss'
*
* Fill this stub out by replacing all the `any` types.
*
* Once filled out, we encourage you to share your work with the
* community by sending a pull request to:
* https://github.com/flowtype/flow-typed
*/
declare module 'react-jss' {
declare module.exports: any;
}
/**
* We include stubs for each file inside this npm package in case you need to
* require those files directly. Feel free to delete any files that aren't
* needed.
*/
declare module 'react-jss/dist/react-jss' {
declare module.exports: any;
}
declare module 'react-jss/dist/react-jss.min' {
declare module.exports: any;
}
declare module 'react-jss/lib/compose' {
declare module.exports: any;
}
declare module 'react-jss/lib/compose.test' {
declare module.exports: any;
}
declare module 'react-jss/lib/contextTypes' {
declare module.exports: any;
}
declare module 'react-jss/lib/createHoc' {
declare module.exports: any;
}
declare module 'react-jss/lib/getDisplayName' {
declare module.exports: any;
}
declare module 'react-jss/lib/index' {
declare module.exports: any;
}
declare module 'react-jss/lib/index.test' {
declare module.exports: any;
}
declare module 'react-jss/lib/injectSheet' {
declare module.exports: any;
}
declare module 'react-jss/lib/injectSheet.test' {
declare module.exports: any;
}
declare module 'react-jss/lib/jss' {
declare module.exports: any;
}
declare module 'react-jss/lib/JssProvider' {
declare module.exports: any;
}
declare module 'react-jss/lib/JssProvider.test' {
declare module.exports: any;
}
declare module 'react-jss/lib/ns' {
declare module.exports: any;
}
declare module 'react-jss/lib/propTypes' {
declare module.exports: any;
}
// Filename aliases
declare module 'react-jss/dist/react-jss.js' {
declare module.exports: $Exports<'react-jss/dist/react-jss'>;
}
declare module 'react-jss/dist/react-jss.min.js' {
declare module.exports: $Exports<'react-jss/dist/react-jss.min'>;
}
declare module 'react-jss/lib/compose.js' {
declare module.exports: $Exports<'react-jss/lib/compose'>;
}
declare module 'react-jss/lib/compose.test.js' {
declare module.exports: $Exports<'react-jss/lib/compose.test'>;
}
declare module 'react-jss/lib/contextTypes.js' {
declare module.exports: $Exports<'react-jss/lib/contextTypes'>;
}
declare module 'react-jss/lib/createHoc.js' {
declare module.exports: $Exports<'react-jss/lib/createHoc'>;
}
declare module 'react-jss/lib/getDisplayName.js' {
declare module.exports: $Exports<'react-jss/lib/getDisplayName'>;
}
declare module 'react-jss/lib/index.js' {
declare module.exports: $Exports<'react-jss/lib/index'>;
}
declare module 'react-jss/lib/index.test.js' {
declare module.exports: $Exports<'react-jss/lib/index.test'>;
}
declare module 'react-jss/lib/injectSheet.js' {
declare module.exports: $Exports<'react-jss/lib/injectSheet'>;
}
declare module 'react-jss/lib/injectSheet.test.js' {
declare module.exports: $Exports<'react-jss/lib/injectSheet.test'>;
}
declare module 'react-jss/lib/jss.js' {
declare module.exports: $Exports<'react-jss/lib/jss'>;
}
declare module 'react-jss/lib/JssProvider.js' {
declare module.exports: $Exports<'react-jss/lib/JssProvider'>;
}
declare module 'react-jss/lib/JssProvider.test.js' {
declare module.exports: $Exports<'react-jss/lib/JssProvider.test'>;
}
declare module 'react-jss/lib/ns.js' {
declare module.exports: $Exports<'react-jss/lib/ns'>;
}
declare module 'react-jss/lib/propTypes.js' {
declare module.exports: $Exports<'react-jss/lib/propTypes'>;
}

View File

@@ -3,12 +3,15 @@
"license" : "BSD-3-Clause", "license" : "BSD-3-Clause",
"main": "src/main/js/index.js", "main": "src/main/js/index.js",
"scripts": { "scripts": {
"build": "ui-bundler plugin" "build": "ui-bundler plugin",
"watch": "ui-bundler plugin -w",
"lint": "ui-bundler lint",
"flow": "flow check"
}, },
"dependencies": { "dependencies": {
"@scm-manager/ui-extensions": "^0.0.7" "@scm-manager/ui-extensions": "^0.0.7"
}, },
"devDependencies": { "devDependencies": {
"@scm-manager/ui-bundler": "^0.0.17" "@scm-manager/ui-bundler": "^0.0.19"
} }
} }

View File

@@ -43,11 +43,20 @@
</dependencies> </dependencies>
<!-- create test jar -->
<build> <build>
<plugins> <plugins>
<plugin>
<groupId>sonia.scm.maven</groupId>
<artifactId>smp-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<corePlugin>true</corePlugin>
</configuration>
</plugin>
<!-- create test jar -->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-jar-plugin</artifactId>
@@ -61,33 +70,6 @@
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>com.github.sdorra</groupId>
<artifactId>buildfrontend-maven-plugin</artifactId>
<executions>
<execution>
<id>link-ui-types</id>
<phase>process-sources</phase>
<goals>
<goal>install-link</goal>
</goals>
<configuration>
<pkg>@scm-manager/ui-types</pkg>
</configuration>
</execution>
<execution>
<id>link-ui-components</id>
<phase>process-sources</phase>
<goals>
<goal>install-link</goal>
</goals>
<configuration>
<pkg>@scm-manager/ui-components</pkg>
</configuration>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>

View File

@@ -707,9 +707,9 @@
version "0.0.2" version "0.0.2"
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85" resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
"@scm-manager/ui-bundler@^0.0.17": "@scm-manager/ui-bundler@^0.0.19":
version "0.0.17" version "0.0.19"
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.17.tgz#949b90ca57e4268be28fcf4975bd9622f60278bb" resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.19.tgz#646ab1fa1e5389fad614542215c60678fb9816ae"
dependencies: dependencies:
"@babel/core" "^7.0.0" "@babel/core" "^7.0.0"
"@babel/plugin-proposal-class-properties" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0"

View File

@@ -4,5 +4,6 @@
[include] [include]
[libs] [libs]
./node_modules/@scm-manager/ui-components/flow-typed
[options] [options]

View File

@@ -1,23 +0,0 @@
// flow-typed signature: cf86673cc32d185bdab1d2ea90578d37
// flow-typed version: 614bf49aa8/classnames_v2.x.x/flow_>=v0.25.x
type $npm$classnames$Classes =
| string
| { [className: string]: * }
| false
| void
| null;
declare module "classnames" {
declare module.exports: (
...classes: Array<$npm$classnames$Classes | $npm$classnames$Classes[]>
) => string;
}
declare module "classnames/bind" {
declare module.exports: $Exports<"classnames">;
}
declare module "classnames/dedupe" {
declare module.exports: $Exports<"classnames">;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,331 +0,0 @@
// flow-typed signature: 23b805356f90ad9384dd88489654e380
// flow-typed version: e9374c5fe9/moment_v2.3.x/flow_>=v0.25.x
type moment$MomentOptions = {
y?: number | string,
year?: number | string,
years?: number | string,
M?: number | string,
month?: number | string,
months?: number | string,
d?: number | string,
day?: number | string,
days?: number | string,
date?: number | string,
h?: number | string,
hour?: number | string,
hours?: number | string,
m?: number | string,
minute?: number | string,
minutes?: number | string,
s?: number | string,
second?: number | string,
seconds?: number | string,
ms?: number | string,
millisecond?: number | string,
milliseconds?: number | string
};
type moment$MomentObject = {
years: number,
months: number,
date: number,
hours: number,
minutes: number,
seconds: number,
milliseconds: number
};
type moment$MomentCreationData = {
input: string,
format: string,
locale: Object,
isUTC: boolean,
strict: boolean
};
type moment$CalendarFormat = string | ((moment: moment$Moment) => string);
type moment$CalendarFormats = {
sameDay?: moment$CalendarFormat,
nextDay?: moment$CalendarFormat,
nextWeek?: moment$CalendarFormat,
lastDay?: moment$CalendarFormat,
lastWeek?: moment$CalendarFormat,
sameElse?: moment$CalendarFormat
};
declare class moment$LocaleData {
months(moment: moment$Moment): string,
monthsShort(moment: moment$Moment): string,
monthsParse(month: string): number,
weekdays(moment: moment$Moment): string,
weekdaysShort(moment: moment$Moment): string,
weekdaysMin(moment: moment$Moment): string,
weekdaysParse(weekDay: string): number,
longDateFormat(dateFormat: string): string,
isPM(date: string): boolean,
meridiem(hours: number, minutes: number, isLower: boolean): string,
calendar(
key:
| "sameDay"
| "nextDay"
| "lastDay"
| "nextWeek"
| "prevWeek"
| "sameElse",
moment: moment$Moment
): string,
relativeTime(
number: number,
withoutSuffix: boolean,
key: "s" | "m" | "mm" | "h" | "hh" | "d" | "dd" | "M" | "MM" | "y" | "yy",
isFuture: boolean
): string,
pastFuture(diff: any, relTime: string): string,
ordinal(number: number): string,
preparse(str: string): any,
postformat(str: string): any,
week(moment: moment$Moment): string,
invalidDate(): string,
firstDayOfWeek(): number,
firstDayOfYear(): number
}
declare class moment$MomentDuration {
humanize(suffix?: boolean): string,
milliseconds(): number,
asMilliseconds(): number,
seconds(): number,
asSeconds(): number,
minutes(): number,
asMinutes(): number,
hours(): number,
asHours(): number,
days(): number,
asDays(): number,
months(): number,
asWeeks(): number,
weeks(): number,
asMonths(): number,
years(): number,
asYears(): number,
add(value: number | moment$MomentDuration | Object, unit?: string): this,
subtract(value: number | moment$MomentDuration | Object, unit?: string): this,
as(unit: string): number,
get(unit: string): number,
toJSON(): string,
toISOString(): string,
isValid(): boolean
}
declare class moment$Moment {
static ISO_8601: string,
static (
string?: string,
format?: string | Array<string>,
strict?: boolean
): moment$Moment,
static (
string?: string,
format?: string | Array<string>,
locale?: string,
strict?: boolean
): moment$Moment,
static (
initDate: ?Object | number | Date | Array<number> | moment$Moment | string
): moment$Moment,
static unix(seconds: number): moment$Moment,
static utc(): moment$Moment,
static utc(number: number | Array<number>): moment$Moment,
static utc(
str: string,
str2?: string | Array<string>,
str3?: string
): moment$Moment,
static utc(moment: moment$Moment): moment$Moment,
static utc(date: Date): moment$Moment,
static parseZone(): moment$Moment,
static parseZone(rawDate: string): moment$Moment,
static parseZone(
rawDate: string,
format: string | Array<string>
): moment$Moment,
static parseZone(
rawDate: string,
format: string,
strict: boolean
): moment$Moment,
static parseZone(
rawDate: string,
format: string,
locale: string,
strict: boolean
): moment$Moment,
isValid(): boolean,
invalidAt(): 0 | 1 | 2 | 3 | 4 | 5 | 6,
creationData(): moment$MomentCreationData,
millisecond(number: number): this,
milliseconds(number: number): this,
millisecond(): number,
milliseconds(): number,
second(number: number): this,
seconds(number: number): this,
second(): number,
seconds(): number,
minute(number: number): this,
minutes(number: number): this,
minute(): number,
minutes(): number,
hour(number: number): this,
hours(number: number): this,
hour(): number,
hours(): number,
date(number: number): this,
dates(number: number): this,
date(): number,
dates(): number,
day(day: number | string): this,
days(day: number | string): this,
day(): number,
days(): number,
weekday(number: number): this,
weekday(): number,
isoWeekday(number: number): this,
isoWeekday(): number,
dayOfYear(number: number): this,
dayOfYear(): number,
week(number: number): this,
weeks(number: number): this,
week(): number,
weeks(): number,
isoWeek(number: number): this,
isoWeeks(number: number): this,
isoWeek(): number,
isoWeeks(): number,
month(number: number): this,
months(number: number): this,
month(): number,
months(): number,
quarter(number: number): this,
quarter(): number,
year(number: number): this,
years(number: number): this,
year(): number,
years(): number,
weekYear(number: number): this,
weekYear(): number,
isoWeekYear(number: number): this,
isoWeekYear(): number,
weeksInYear(): number,
isoWeeksInYear(): number,
get(string: string): number,
set(unit: string, value: number): this,
set(options: { [unit: string]: number }): this,
static max(...dates: Array<moment$Moment>): moment$Moment,
static max(dates: Array<moment$Moment>): moment$Moment,
static min(...dates: Array<moment$Moment>): moment$Moment,
static min(dates: Array<moment$Moment>): moment$Moment,
add(
value: number | moment$MomentDuration | moment$Moment | Object,
unit?: string
): this,
subtract(
value: number | moment$MomentDuration | moment$Moment | string | Object,
unit?: string
): this,
startOf(unit: string): this,
endOf(unit: string): this,
local(): this,
utc(): this,
utcOffset(
offset: number | string,
keepLocalTime?: boolean,
keepMinutes?: boolean
): this,
utcOffset(): number,
format(format?: string): string,
fromNow(removeSuffix?: boolean): string,
from(
value: moment$Moment | string | number | Date | Array<number>,
removePrefix?: boolean
): string,
toNow(removePrefix?: boolean): string,
to(
value: moment$Moment | string | number | Date | Array<number>,
removePrefix?: boolean
): string,
calendar(refTime?: any, formats?: moment$CalendarFormats): string,
diff(
date: moment$Moment | string | number | Date | Array<number>,
format?: string,
floating?: boolean
): number,
valueOf(): number,
unix(): number,
daysInMonth(): number,
toDate(): Date,
toArray(): Array<number>,
toJSON(): string,
toISOString(
keepOffset?: boolean
): string,
toObject(): moment$MomentObject,
isBefore(
date?: moment$Moment | string | number | Date | Array<number>,
units?: ?string
): boolean,
isSame(
date?: moment$Moment | string | number | Date | Array<number>,
units?: ?string
): boolean,
isAfter(
date?: moment$Moment | string | number | Date | Array<number>,
units?: ?string
): boolean,
isSameOrBefore(
date?: moment$Moment | string | number | Date | Array<number>,
units?: ?string
): boolean,
isSameOrAfter(
date?: moment$Moment | string | number | Date | Array<number>,
units?: ?string
): boolean,
isBetween(
fromDate: moment$Moment | string | number | Date | Array<number>,
toDate?: ?moment$Moment | string | number | Date | Array<number>,
granularity?: ?string,
inclusion?: ?string
): boolean,
isDST(): boolean,
isDSTShifted(): boolean,
isLeapYear(): boolean,
clone(): moment$Moment,
static isMoment(obj: any): boolean,
static isDate(obj: any): boolean,
static locale(locale: string, localeData?: Object): string,
static updateLocale(locale: string, localeData?: ?Object): void,
static locale(locales: Array<string>): string,
locale(locale: string, customization?: Object | null): moment$Moment,
locale(): string,
static months(): Array<string>,
static monthsShort(): Array<string>,
static weekdays(): Array<string>,
static weekdaysShort(): Array<string>,
static weekdaysMin(): Array<string>,
static months(): string,
static monthsShort(): string,
static weekdays(): string,
static weekdaysShort(): string,
static weekdaysMin(): string,
static localeData(key?: string): moment$LocaleData,
static duration(
value: number | Object | string,
unit?: string
): moment$MomentDuration,
static isDuration(obj: any): boolean,
static normalizeUnits(unit: string): string,
static invalid(object: any): moment$Moment
}
declare module "moment" {
declare module.exports: Class<moment$Moment>;
}

View File

@@ -1,137 +0,0 @@
// flow-typed signature: ba35d02d668b0d0a3e04a63a6847974e
// flow-typed version: <<STUB>>/react-jss_v8.6.1/flow_v0.79.1
/**
* This is an autogenerated libdef stub for:
*
* 'react-jss'
*
* Fill this stub out by replacing all the `any` types.
*
* Once filled out, we encourage you to share your work with the
* community by sending a pull request to:
* https://github.com/flowtype/flow-typed
*/
declare module 'react-jss' {
declare module.exports: any;
}
/**
* We include stubs for each file inside this npm package in case you need to
* require those files directly. Feel free to delete any files that aren't
* needed.
*/
declare module 'react-jss/dist/react-jss' {
declare module.exports: any;
}
declare module 'react-jss/dist/react-jss.min' {
declare module.exports: any;
}
declare module 'react-jss/lib/compose' {
declare module.exports: any;
}
declare module 'react-jss/lib/compose.test' {
declare module.exports: any;
}
declare module 'react-jss/lib/contextTypes' {
declare module.exports: any;
}
declare module 'react-jss/lib/createHoc' {
declare module.exports: any;
}
declare module 'react-jss/lib/getDisplayName' {
declare module.exports: any;
}
declare module 'react-jss/lib/index' {
declare module.exports: any;
}
declare module 'react-jss/lib/index.test' {
declare module.exports: any;
}
declare module 'react-jss/lib/injectSheet' {
declare module.exports: any;
}
declare module 'react-jss/lib/injectSheet.test' {
declare module.exports: any;
}
declare module 'react-jss/lib/jss' {
declare module.exports: any;
}
declare module 'react-jss/lib/JssProvider' {
declare module.exports: any;
}
declare module 'react-jss/lib/JssProvider.test' {
declare module.exports: any;
}
declare module 'react-jss/lib/ns' {
declare module.exports: any;
}
declare module 'react-jss/lib/propTypes' {
declare module.exports: any;
}
// Filename aliases
declare module 'react-jss/dist/react-jss.js' {
declare module.exports: $Exports<'react-jss/dist/react-jss'>;
}
declare module 'react-jss/dist/react-jss.min.js' {
declare module.exports: $Exports<'react-jss/dist/react-jss.min'>;
}
declare module 'react-jss/lib/compose.js' {
declare module.exports: $Exports<'react-jss/lib/compose'>;
}
declare module 'react-jss/lib/compose.test.js' {
declare module.exports: $Exports<'react-jss/lib/compose.test'>;
}
declare module 'react-jss/lib/contextTypes.js' {
declare module.exports: $Exports<'react-jss/lib/contextTypes'>;
}
declare module 'react-jss/lib/createHoc.js' {
declare module.exports: $Exports<'react-jss/lib/createHoc'>;
}
declare module 'react-jss/lib/getDisplayName.js' {
declare module.exports: $Exports<'react-jss/lib/getDisplayName'>;
}
declare module 'react-jss/lib/index.js' {
declare module.exports: $Exports<'react-jss/lib/index'>;
}
declare module 'react-jss/lib/index.test.js' {
declare module.exports: $Exports<'react-jss/lib/index.test'>;
}
declare module 'react-jss/lib/injectSheet.js' {
declare module.exports: $Exports<'react-jss/lib/injectSheet'>;
}
declare module 'react-jss/lib/injectSheet.test.js' {
declare module.exports: $Exports<'react-jss/lib/injectSheet.test'>;
}
declare module 'react-jss/lib/jss.js' {
declare module.exports: $Exports<'react-jss/lib/jss'>;
}
declare module 'react-jss/lib/JssProvider.js' {
declare module.exports: $Exports<'react-jss/lib/JssProvider'>;
}
declare module 'react-jss/lib/JssProvider.test.js' {
declare module.exports: $Exports<'react-jss/lib/JssProvider.test'>;
}
declare module 'react-jss/lib/ns.js' {
declare module.exports: $Exports<'react-jss/lib/ns'>;
}
declare module 'react-jss/lib/propTypes.js' {
declare module.exports: $Exports<'react-jss/lib/propTypes'>;
}

View File

@@ -9,6 +9,6 @@
"@scm-manager/ui-extensions": "^0.0.7" "@scm-manager/ui-extensions": "^0.0.7"
}, },
"devDependencies": { "devDependencies": {
"@scm-manager/ui-bundler": "^0.0.17" "@scm-manager/ui-bundler": "^0.0.19"
} }
} }

View File

@@ -31,8 +31,6 @@
</dependencies> </dependencies>
<!-- create test jar -->
<build> <build>
<plugins> <plugins>
@@ -55,6 +53,17 @@
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>sonia.scm.maven</groupId>
<artifactId>smp-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<corePlugin>true</corePlugin>
</configuration>
</plugin>
<!-- create test jar -->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-jar-plugin</artifactId>
@@ -68,33 +77,6 @@
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>com.github.sdorra</groupId>
<artifactId>buildfrontend-maven-plugin</artifactId>
<executions>
<execution>
<id>link-ui-types</id>
<phase>process-sources</phase>
<goals>
<goal>install-link</goal>
</goals>
<configuration>
<pkg>@scm-manager/ui-types</pkg>
</configuration>
</execution>
<execution>
<id>link-ui-components</id>
<phase>process-sources</phase>
<goals>
<goal>install-link</goal>
</goals>
<configuration>
<pkg>@scm-manager/ui-components</pkg>
</configuration>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>

View File

@@ -641,9 +641,9 @@
version "0.0.2" version "0.0.2"
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85" resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
"@scm-manager/ui-bundler@^0.0.17": "@scm-manager/ui-bundler@^0.0.19":
version "0.0.17" version "0.0.19"
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.17.tgz#949b90ca57e4268be28fcf4975bd9622f60278bb" resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.19.tgz#646ab1fa1e5389fad614542215c60678fb9816ae"
dependencies: dependencies:
"@babel/core" "^7.0.0" "@babel/core" "^7.0.0"
"@babel/plugin-proposal-class-properties" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0"
@@ -660,6 +660,7 @@
browserify-css "^0.14.0" browserify-css "^0.14.0"
colors "^1.3.1" colors "^1.3.1"
commander "^2.17.1" commander "^2.17.1"
connect-history-api-fallback "^1.5.0"
eslint "^5.4.0" eslint "^5.4.0"
eslint-config-react-app "^2.1.0" eslint-config-react-app "^2.1.0"
eslint-plugin-flowtype "^2.50.0" eslint-plugin-flowtype "^2.50.0"

View File

@@ -23,4 +23,19 @@
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>sonia.scm.maven</groupId>
<artifactId>smp-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<corePlugin>true</corePlugin>
</configuration>
</plugin>
</plugins>
</build>
</project> </project>

View File

@@ -4,5 +4,6 @@
[include] [include]
[libs] [libs]
./node_modules/@scm-manager/ui-components/flow-typed
[options] [options]

View File

@@ -1,23 +0,0 @@
// flow-typed signature: cf86673cc32d185bdab1d2ea90578d37
// flow-typed version: 614bf49aa8/classnames_v2.x.x/flow_>=v0.25.x
type $npm$classnames$Classes =
| string
| { [className: string]: * }
| false
| void
| null;
declare module "classnames" {
declare module.exports: (
...classes: Array<$npm$classnames$Classes | $npm$classnames$Classes[]>
) => string;
}
declare module "classnames/bind" {
declare module.exports: $Exports<"classnames">;
}
declare module "classnames/dedupe" {
declare module.exports: $Exports<"classnames">;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,331 +0,0 @@
// flow-typed signature: 23b805356f90ad9384dd88489654e380
// flow-typed version: e9374c5fe9/moment_v2.3.x/flow_>=v0.25.x
type moment$MomentOptions = {
y?: number | string,
year?: number | string,
years?: number | string,
M?: number | string,
month?: number | string,
months?: number | string,
d?: number | string,
day?: number | string,
days?: number | string,
date?: number | string,
h?: number | string,
hour?: number | string,
hours?: number | string,
m?: number | string,
minute?: number | string,
minutes?: number | string,
s?: number | string,
second?: number | string,
seconds?: number | string,
ms?: number | string,
millisecond?: number | string,
milliseconds?: number | string
};
type moment$MomentObject = {
years: number,
months: number,
date: number,
hours: number,
minutes: number,
seconds: number,
milliseconds: number
};
type moment$MomentCreationData = {
input: string,
format: string,
locale: Object,
isUTC: boolean,
strict: boolean
};
type moment$CalendarFormat = string | ((moment: moment$Moment) => string);
type moment$CalendarFormats = {
sameDay?: moment$CalendarFormat,
nextDay?: moment$CalendarFormat,
nextWeek?: moment$CalendarFormat,
lastDay?: moment$CalendarFormat,
lastWeek?: moment$CalendarFormat,
sameElse?: moment$CalendarFormat
};
declare class moment$LocaleData {
months(moment: moment$Moment): string,
monthsShort(moment: moment$Moment): string,
monthsParse(month: string): number,
weekdays(moment: moment$Moment): string,
weekdaysShort(moment: moment$Moment): string,
weekdaysMin(moment: moment$Moment): string,
weekdaysParse(weekDay: string): number,
longDateFormat(dateFormat: string): string,
isPM(date: string): boolean,
meridiem(hours: number, minutes: number, isLower: boolean): string,
calendar(
key:
| "sameDay"
| "nextDay"
| "lastDay"
| "nextWeek"
| "prevWeek"
| "sameElse",
moment: moment$Moment
): string,
relativeTime(
number: number,
withoutSuffix: boolean,
key: "s" | "m" | "mm" | "h" | "hh" | "d" | "dd" | "M" | "MM" | "y" | "yy",
isFuture: boolean
): string,
pastFuture(diff: any, relTime: string): string,
ordinal(number: number): string,
preparse(str: string): any,
postformat(str: string): any,
week(moment: moment$Moment): string,
invalidDate(): string,
firstDayOfWeek(): number,
firstDayOfYear(): number
}
declare class moment$MomentDuration {
humanize(suffix?: boolean): string,
milliseconds(): number,
asMilliseconds(): number,
seconds(): number,
asSeconds(): number,
minutes(): number,
asMinutes(): number,
hours(): number,
asHours(): number,
days(): number,
asDays(): number,
months(): number,
asWeeks(): number,
weeks(): number,
asMonths(): number,
years(): number,
asYears(): number,
add(value: number | moment$MomentDuration | Object, unit?: string): this,
subtract(value: number | moment$MomentDuration | Object, unit?: string): this,
as(unit: string): number,
get(unit: string): number,
toJSON(): string,
toISOString(): string,
isValid(): boolean
}
declare class moment$Moment {
static ISO_8601: string,
static (
string?: string,
format?: string | Array<string>,
strict?: boolean
): moment$Moment,
static (
string?: string,
format?: string | Array<string>,
locale?: string,
strict?: boolean
): moment$Moment,
static (
initDate: ?Object | number | Date | Array<number> | moment$Moment | string
): moment$Moment,
static unix(seconds: number): moment$Moment,
static utc(): moment$Moment,
static utc(number: number | Array<number>): moment$Moment,
static utc(
str: string,
str2?: string | Array<string>,
str3?: string
): moment$Moment,
static utc(moment: moment$Moment): moment$Moment,
static utc(date: Date): moment$Moment,
static parseZone(): moment$Moment,
static parseZone(rawDate: string): moment$Moment,
static parseZone(
rawDate: string,
format: string | Array<string>
): moment$Moment,
static parseZone(
rawDate: string,
format: string,
strict: boolean
): moment$Moment,
static parseZone(
rawDate: string,
format: string,
locale: string,
strict: boolean
): moment$Moment,
isValid(): boolean,
invalidAt(): 0 | 1 | 2 | 3 | 4 | 5 | 6,
creationData(): moment$MomentCreationData,
millisecond(number: number): this,
milliseconds(number: number): this,
millisecond(): number,
milliseconds(): number,
second(number: number): this,
seconds(number: number): this,
second(): number,
seconds(): number,
minute(number: number): this,
minutes(number: number): this,
minute(): number,
minutes(): number,
hour(number: number): this,
hours(number: number): this,
hour(): number,
hours(): number,
date(number: number): this,
dates(number: number): this,
date(): number,
dates(): number,
day(day: number | string): this,
days(day: number | string): this,
day(): number,
days(): number,
weekday(number: number): this,
weekday(): number,
isoWeekday(number: number): this,
isoWeekday(): number,
dayOfYear(number: number): this,
dayOfYear(): number,
week(number: number): this,
weeks(number: number): this,
week(): number,
weeks(): number,
isoWeek(number: number): this,
isoWeeks(number: number): this,
isoWeek(): number,
isoWeeks(): number,
month(number: number): this,
months(number: number): this,
month(): number,
months(): number,
quarter(number: number): this,
quarter(): number,
year(number: number): this,
years(number: number): this,
year(): number,
years(): number,
weekYear(number: number): this,
weekYear(): number,
isoWeekYear(number: number): this,
isoWeekYear(): number,
weeksInYear(): number,
isoWeeksInYear(): number,
get(string: string): number,
set(unit: string, value: number): this,
set(options: { [unit: string]: number }): this,
static max(...dates: Array<moment$Moment>): moment$Moment,
static max(dates: Array<moment$Moment>): moment$Moment,
static min(...dates: Array<moment$Moment>): moment$Moment,
static min(dates: Array<moment$Moment>): moment$Moment,
add(
value: number | moment$MomentDuration | moment$Moment | Object,
unit?: string
): this,
subtract(
value: number | moment$MomentDuration | moment$Moment | string | Object,
unit?: string
): this,
startOf(unit: string): this,
endOf(unit: string): this,
local(): this,
utc(): this,
utcOffset(
offset: number | string,
keepLocalTime?: boolean,
keepMinutes?: boolean
): this,
utcOffset(): number,
format(format?: string): string,
fromNow(removeSuffix?: boolean): string,
from(
value: moment$Moment | string | number | Date | Array<number>,
removePrefix?: boolean
): string,
toNow(removePrefix?: boolean): string,
to(
value: moment$Moment | string | number | Date | Array<number>,
removePrefix?: boolean
): string,
calendar(refTime?: any, formats?: moment$CalendarFormats): string,
diff(
date: moment$Moment | string | number | Date | Array<number>,
format?: string,
floating?: boolean
): number,
valueOf(): number,
unix(): number,
daysInMonth(): number,
toDate(): Date,
toArray(): Array<number>,
toJSON(): string,
toISOString(
keepOffset?: boolean
): string,
toObject(): moment$MomentObject,
isBefore(
date?: moment$Moment | string | number | Date | Array<number>,
units?: ?string
): boolean,
isSame(
date?: moment$Moment | string | number | Date | Array<number>,
units?: ?string
): boolean,
isAfter(
date?: moment$Moment | string | number | Date | Array<number>,
units?: ?string
): boolean,
isSameOrBefore(
date?: moment$Moment | string | number | Date | Array<number>,
units?: ?string
): boolean,
isSameOrAfter(
date?: moment$Moment | string | number | Date | Array<number>,
units?: ?string
): boolean,
isBetween(
fromDate: moment$Moment | string | number | Date | Array<number>,
toDate?: ?moment$Moment | string | number | Date | Array<number>,
granularity?: ?string,
inclusion?: ?string
): boolean,
isDST(): boolean,
isDSTShifted(): boolean,
isLeapYear(): boolean,
clone(): moment$Moment,
static isMoment(obj: any): boolean,
static isDate(obj: any): boolean,
static locale(locale: string, localeData?: Object): string,
static updateLocale(locale: string, localeData?: ?Object): void,
static locale(locales: Array<string>): string,
locale(locale: string, customization?: Object | null): moment$Moment,
locale(): string,
static months(): Array<string>,
static monthsShort(): Array<string>,
static weekdays(): Array<string>,
static weekdaysShort(): Array<string>,
static weekdaysMin(): Array<string>,
static months(): string,
static monthsShort(): string,
static weekdays(): string,
static weekdaysShort(): string,
static weekdaysMin(): string,
static localeData(key?: string): moment$LocaleData,
static duration(
value: number | Object | string,
unit?: string
): moment$MomentDuration,
static isDuration(obj: any): boolean,
static normalizeUnits(unit: string): string,
static invalid(object: any): moment$Moment
}
declare module "moment" {
declare module.exports: Class<moment$Moment>;
}

View File

@@ -1,137 +0,0 @@
// flow-typed signature: ba35d02d668b0d0a3e04a63a6847974e
// flow-typed version: <<STUB>>/react-jss_v8.6.1/flow_v0.79.1
/**
* This is an autogenerated libdef stub for:
*
* 'react-jss'
*
* Fill this stub out by replacing all the `any` types.
*
* Once filled out, we encourage you to share your work with the
* community by sending a pull request to:
* https://github.com/flowtype/flow-typed
*/
declare module 'react-jss' {
declare module.exports: any;
}
/**
* We include stubs for each file inside this npm package in case you need to
* require those files directly. Feel free to delete any files that aren't
* needed.
*/
declare module 'react-jss/dist/react-jss' {
declare module.exports: any;
}
declare module 'react-jss/dist/react-jss.min' {
declare module.exports: any;
}
declare module 'react-jss/lib/compose' {
declare module.exports: any;
}
declare module 'react-jss/lib/compose.test' {
declare module.exports: any;
}
declare module 'react-jss/lib/contextTypes' {
declare module.exports: any;
}
declare module 'react-jss/lib/createHoc' {
declare module.exports: any;
}
declare module 'react-jss/lib/getDisplayName' {
declare module.exports: any;
}
declare module 'react-jss/lib/index' {
declare module.exports: any;
}
declare module 'react-jss/lib/index.test' {
declare module.exports: any;
}
declare module 'react-jss/lib/injectSheet' {
declare module.exports: any;
}
declare module 'react-jss/lib/injectSheet.test' {
declare module.exports: any;
}
declare module 'react-jss/lib/jss' {
declare module.exports: any;
}
declare module 'react-jss/lib/JssProvider' {
declare module.exports: any;
}
declare module 'react-jss/lib/JssProvider.test' {
declare module.exports: any;
}
declare module 'react-jss/lib/ns' {
declare module.exports: any;
}
declare module 'react-jss/lib/propTypes' {
declare module.exports: any;
}
// Filename aliases
declare module 'react-jss/dist/react-jss.js' {
declare module.exports: $Exports<'react-jss/dist/react-jss'>;
}
declare module 'react-jss/dist/react-jss.min.js' {
declare module.exports: $Exports<'react-jss/dist/react-jss.min'>;
}
declare module 'react-jss/lib/compose.js' {
declare module.exports: $Exports<'react-jss/lib/compose'>;
}
declare module 'react-jss/lib/compose.test.js' {
declare module.exports: $Exports<'react-jss/lib/compose.test'>;
}
declare module 'react-jss/lib/contextTypes.js' {
declare module.exports: $Exports<'react-jss/lib/contextTypes'>;
}
declare module 'react-jss/lib/createHoc.js' {
declare module.exports: $Exports<'react-jss/lib/createHoc'>;
}
declare module 'react-jss/lib/getDisplayName.js' {
declare module.exports: $Exports<'react-jss/lib/getDisplayName'>;
}
declare module 'react-jss/lib/index.js' {
declare module.exports: $Exports<'react-jss/lib/index'>;
}
declare module 'react-jss/lib/index.test.js' {
declare module.exports: $Exports<'react-jss/lib/index.test'>;
}
declare module 'react-jss/lib/injectSheet.js' {
declare module.exports: $Exports<'react-jss/lib/injectSheet'>;
}
declare module 'react-jss/lib/injectSheet.test.js' {
declare module.exports: $Exports<'react-jss/lib/injectSheet.test'>;
}
declare module 'react-jss/lib/jss.js' {
declare module.exports: $Exports<'react-jss/lib/jss'>;
}
declare module 'react-jss/lib/JssProvider.js' {
declare module.exports: $Exports<'react-jss/lib/JssProvider'>;
}
declare module 'react-jss/lib/JssProvider.test.js' {
declare module.exports: $Exports<'react-jss/lib/JssProvider.test'>;
}
declare module 'react-jss/lib/ns.js' {
declare module.exports: $Exports<'react-jss/lib/ns'>;
}
declare module 'react-jss/lib/propTypes.js' {
declare module.exports: $Exports<'react-jss/lib/propTypes'>;
}

View File

@@ -9,6 +9,6 @@
"@scm-manager/ui-extensions": "^0.0.7" "@scm-manager/ui-extensions": "^0.0.7"
}, },
"devDependencies": { "devDependencies": {
"@scm-manager/ui-bundler": "^0.0.17" "@scm-manager/ui-bundler": "^0.0.19"
} }
} }

View File

@@ -37,11 +37,19 @@
</dependencies> </dependencies>
<!-- create test jar -->
<build> <build>
<plugins> <plugins>
<plugin>
<groupId>sonia.scm.maven</groupId>
<artifactId>smp-maven-plugin</artifactId>
<configuration>
<corePlugin>true</corePlugin>
</configuration>
</plugin>
<!-- create test jar -->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-jar-plugin</artifactId>
@@ -55,34 +63,6 @@
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>com.github.sdorra</groupId>
<artifactId>buildfrontend-maven-plugin</artifactId>
<executions>
<execution>
<id>link-ui-types</id>
<phase>process-sources</phase>
<goals>
<goal>install-link</goal>
</goals>
<configuration>
<pkg>@scm-manager/ui-types</pkg>
</configuration>
</execution>
<execution>
<id>link-ui-components</id>
<phase>process-sources</phase>
<goals>
<goal>install-link</goal>
</goals>
<configuration>
<pkg>@scm-manager/ui-components</pkg>
</configuration>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>

View File

@@ -1,19 +1,19 @@
/** /*
* Copyright (c) 2010, Sebastian Sdorra * Copyright (c) 2010, Sebastian Sdorra
* All rights reserved. * All rights reserved.
* * <p>
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * <p>
* 1. Redistributions of source code must retain the above copyright notice, * 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. * this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, * 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* 3. Neither the name of SCM-Manager; nor the names of its * 3. Neither the name of SCM-Manager; nor the names of its
* contributors may be used to endorse or promote products derived from this * contributors may be used to endorse or promote products derived from this
* software without specific prior written permission. * software without specific prior written permission.
* * <p>
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -24,13 +24,11 @@
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * <p>
* http://bitbucket.org/sdorra/scm-manager * http://bitbucket.org/sdorra/scm-manager
*
*/ */
package sonia.scm.repository.spi; package sonia.scm.repository.spi;
//~--- non-JDK imports -------------------------------------------------------- //~--- non-JDK imports --------------------------------------------------------
@@ -41,8 +39,7 @@ import org.slf4j.LoggerFactory;
import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.wc.DefaultSVNDiffGenerator; import org.tmatesoft.svn.core.internal.wc2.ng.SvnNewDiffGenerator;
import org.tmatesoft.svn.core.wc.ISVNDiffGenerator;
import org.tmatesoft.svn.core.wc.SVNClientManager; import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNDiffClient; import org.tmatesoft.svn.core.wc.SVNDiffClient;
import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc.SVNRevision;
@@ -60,8 +57,7 @@ import java.io.OutputStream;
* *
* @author Sebastian Sdorra * @author Sebastian Sdorra
*/ */
public class SvnDiffCommand extends AbstractSvnCommand implements DiffCommand public class SvnDiffCommand extends AbstractSvnCommand implements DiffCommand {
{
/** /**
* the logger for SvnDiffCommand * the logger for SvnDiffCommand
@@ -69,46 +65,26 @@ public class SvnDiffCommand extends AbstractSvnCommand implements DiffCommand
private static final Logger logger = private static final Logger logger =
LoggerFactory.getLogger(SvnDiffCommand.class); LoggerFactory.getLogger(SvnDiffCommand.class);
public SvnDiffCommand(SvnContext context, Repository repository) public SvnDiffCommand(SvnContext context, Repository repository) {
{
super(context, repository); super(context, repository);
} }
@Override @Override
public void getDiffResult(DiffCommandRequest request, OutputStream output) { public void getDiffResult(DiffCommandRequest request, OutputStream output) {
if (logger.isDebugEnabled()) logger.debug("create diff for {}", request);
{
logger.debug("create diff for {}", request);
}
Preconditions.checkNotNull(request, "request is required"); Preconditions.checkNotNull(request, "request is required");
Preconditions.checkNotNull(output, "outputstream is required"); Preconditions.checkNotNull(output, "outputstream is required");
String path = request.getPath(); String path = request.getPath();
SVNClientManager clientManager = null; SVNClientManager clientManager = null;
try {
try
{
SVNURL svnurl = context.createUrl(); SVNURL svnurl = context.createUrl();
if (Util.isNotEmpty(path)) {
if (Util.isNotEmpty(path))
{
svnurl = svnurl.appendPath(path, true); svnurl = svnurl.appendPath(path, true);
} }
clientManager = SVNClientManager.newInstance(); clientManager = SVNClientManager.newInstance();
SVNDiffClient diffClient = clientManager.getDiffClient(); SVNDiffClient diffClient = clientManager.getDiffClient();
ISVNDiffGenerator diffGenerator = diffClient.getDiffGenerator(); diffClient.setDiffGenerator(new SvnNewDiffGenerator(new SCMSvnDiffGenerator()));
if (diffGenerator == null)
{
diffGenerator = new DefaultSVNDiffGenerator();
}
diffGenerator.setDiffAdded(true);
diffGenerator.setDiffDeleted(true);
diffClient.setDiffGenerator(diffGenerator);
long currentRev = SvnUtil.getRevisionNumber(request.getRevision(), repository); long currentRev = SvnUtil.getRevisionNumber(request.getRevision(), repository);
@@ -117,13 +93,9 @@ public class SvnDiffCommand extends AbstractSvnCommand implements DiffCommand
diffClient.doDiff(svnurl, SVNRevision.HEAD, diffClient.doDiff(svnurl, SVNRevision.HEAD,
SVNRevision.create(currentRev - 1), SVNRevision.create(currentRev), SVNRevision.create(currentRev - 1), SVNRevision.create(currentRev),
SVNDepth.INFINITY, false, output); SVNDepth.INFINITY, false, output);
} } catch (SVNException ex) {
catch (SVNException ex)
{
throw new InternalRepositoryException(repository, "could not create diff", ex); throw new InternalRepositoryException(repository, "could not create diff", ex);
} } finally {
finally
{
SvnUtil.dispose(clientManager); SvnUtil.dispose(clientManager);
} }
} }

View File

@@ -641,9 +641,9 @@
version "0.0.2" version "0.0.2"
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85" resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
"@scm-manager/ui-bundler@^0.0.17": "@scm-manager/ui-bundler@^0.0.19":
version "0.0.17" version "0.0.19"
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.17.tgz#949b90ca57e4268be28fcf4975bd9622f60278bb" resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.19.tgz#646ab1fa1e5389fad614542215c60678fb9816ae"
dependencies: dependencies:
"@babel/core" "^7.0.0" "@babel/core" "^7.0.0"
"@babel/plugin-proposal-class-properties" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0"

View File

@@ -0,0 +1,95 @@
// flow-typed signature: 65d42f62f8de603dcc631ea5a6b00580
// flow-typed version: f3f13164e0/react-i18next_v7.x.x/flow_>=v0.64.x
declare module "react-i18next" {
declare type TFunction = (key?: ?string, data?: ?Object) => string;
declare type TranslatorProps = {|
t: TFunction,
i18nLoadedAt: Date,
i18n: Object
|};
declare type TranslatorPropsVoid = {
t: TFunction | void,
i18nLoadedAt: Date | void,
i18n: Object | void
};
declare type Translator<P: {}, Component: React$ComponentType<P>> = (
WrappedComponent: Component
) => React$ComponentType<
$Diff<React$ElementConfig<Component>, TranslatorPropsVoid>
>;
declare type TranslateOptions = $Shape<{
wait: boolean,
nsMode: "default" | "fallback",
bindi18n: false | string,
bindStore: false | string,
withRef: boolean,
translateFuncName: string,
i18n: Object,
usePureComponent: boolean
}>;
declare function translate<P: {}, Component: React$ComponentType<P>>(
namespaces?: | string
| Array<string>
| (($Diff<P, TranslatorPropsVoid>) => string | Array<string>),
options?: TranslateOptions
): Translator<P, Component>;
declare type I18nProps = {
i18n?: Object,
ns?: string | Array<string>,
children: (t: TFunction, { i18n: Object, t: TFunction }) => React$Node,
initialI18nStore?: Object,
initialLanguage?: string
};
declare var I18n: React$ComponentType<I18nProps>;
declare type InterpolateProps = {
className?: string,
dangerouslySetInnerHTMLPartElement?: string,
i18n?: Object,
i18nKey?: string,
options?: Object,
parent?: string,
style?: Object,
t?: TFunction,
useDangerouslySetInnerHTML?: boolean
};
declare var Interpolate: React$ComponentType<InterpolateProps>;
declare type TransProps = {
count?: number,
parent?: string,
i18n?: Object,
i18nKey?: string,
t?: TFunction
};
declare var Trans: React$ComponentType<TransProps>;
declare type ProviderProps = { i18n: Object, children: React$Element<*> };
declare var I18nextProvider: React$ComponentType<ProviderProps>;
declare type NamespacesProps = {
components: Array<React$ComponentType<*>>,
i18n: { loadNamespaces: Function }
};
declare function loadNamespaces(props: NamespacesProps): Promise<void>;
declare var reactI18nextModule: {
type: "3rdParty",
init: (instance: Object) => void
};
declare function setDefaults(options: TranslateOptions): void;
declare function getDefaults(): TranslateOptions;
declare function getI18n(): Object;
declare function setI18n(instance: Object): void;
}

View File

@@ -12,7 +12,7 @@
"eslint-fix": "eslint src --fix" "eslint-fix": "eslint src --fix"
}, },
"devDependencies": { "devDependencies": {
"@scm-manager/ui-bundler": "^0.0.17", "@scm-manager/ui-bundler": "^0.0.19",
"create-index": "^2.3.0", "create-index": "^2.3.0",
"enzyme": "^3.5.0", "enzyme": "^3.5.0",
"enzyme-adapter-react-16": "^1.3.1", "enzyme-adapter-react-16": "^1.3.1",

View File

@@ -1,6 +1,6 @@
//@flow //@flow
import React from "react"; import React from "react";
import { withContextPath } from "./urls"; import {withContextPath} from "./urls";
type Props = { type Props = {
src: string, src: string,

View File

@@ -48,6 +48,14 @@ class ApiClient {
return this.httpRequestWithJSONBody("PUT", url, contentType, payload); return this.httpRequestWithJSONBody("PUT", url, contentType, payload);
} }
head(url: string) {
let options: RequestOptions = {
method: "HEAD"
};
options = Object.assign(options, fetchOptions);
return fetch(createUrl(url), options).then(handleStatusCode);
}
delete(url: string): Promise<Response> { delete(url: string): Promise<Response> {
let options: RequestOptions = { let options: RequestOptions = {
method: "DELETE" method: "DELETE"

View File

@@ -0,0 +1,25 @@
//@flow
import React from "react";
import Button, { type ButtonProps } from "./Button";
import type {File} from "@scm-manager/ui-types";
type Props = {
displayName: string,
url: string
};
class DownloadButton extends React.Component<Props> {
render() {
const {displayName, url} = this.props;
return (
<a className="button is-large is-info" href={url}>
<span className="icon is-medium">
<i className="fas fa-arrow-circle-down" />
</span>
<span>{displayName}</span>
</a>
);
}
}
export default DownloadButton;

View File

@@ -7,4 +7,4 @@ export { default as DeleteButton } from "./DeleteButton.js";
export { default as EditButton } from "./EditButton.js"; export { default as EditButton } from "./EditButton.js";
export { default as RemoveEntryOfTableButton } from "./RemoveEntryOfTableButton.js"; export { default as RemoveEntryOfTableButton } from "./RemoveEntryOfTableButton.js";
export { default as SubmitButton } from "./SubmitButton.js"; export { default as SubmitButton } from "./SubmitButton.js";
export {default as DownloadButton} from "./DownloadButton.js";

View File

@@ -1,6 +1,6 @@
//@flow //@flow
import * as React from "react"; import * as React from "react";
import { Route, Link } from "react-router-dom"; import {Link, Route} from "react-router-dom";
// TODO mostly copy of PrimaryNavigationLink // TODO mostly copy of PrimaryNavigationLink

View File

@@ -1,5 +1,5 @@
// @flow // @flow
import { concat, getPageFromMatch, withEndingSlash } from "./urls"; import {concat, getPageFromMatch, withEndingSlash} from "./urls";
describe("tests for withEndingSlash", () => { describe("tests for withEndingSlash", () => {

View File

@@ -641,9 +641,9 @@
version "0.0.2" version "0.0.2"
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85" resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
"@scm-manager/ui-bundler@^0.0.17": "@scm-manager/ui-bundler@^0.0.19":
version "0.0.17" version "0.0.19"
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.17.tgz#949b90ca57e4268be28fcf4975bd9622f60278bb" resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.19.tgz#646ab1fa1e5389fad614542215c60678fb9816ae"
dependencies: dependencies:
"@babel/core" "^7.0.0" "@babel/core" "^7.0.0"
"@babel/plugin-proposal-class-properties" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0"

View File

@@ -14,7 +14,7 @@
"check": "flow check" "check": "flow check"
}, },
"devDependencies": { "devDependencies": {
"@scm-manager/ui-bundler": "^0.0.17" "@scm-manager/ui-bundler": "^0.0.19"
}, },
"browserify": { "browserify": {
"transform": [ "transform": [

View File

@@ -1,5 +1,5 @@
//@flow //@flow
import type { Links } from "./hal"; import type {Links} from "./hal";
export type Permission = PermissionCreateEntry & { export type Permission = PermissionCreateEntry & {
_links: Links _links: Links

View File

@@ -707,9 +707,9 @@
version "0.0.2" version "0.0.2"
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85" resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
"@scm-manager/ui-bundler@^0.0.17": "@scm-manager/ui-bundler@^0.0.19":
version "0.0.17" version "0.0.19"
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.17.tgz#949b90ca57e4268be28fcf4975bd9622f60278bb" resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.19.tgz#646ab1fa1e5389fad614542215c60678fb9816ae"
dependencies: dependencies:
"@babel/core" "^7.0.0" "@babel/core" "^7.0.0"
"@babel/plugin-proposal-class-properties" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0"

View File

@@ -10,19 +10,24 @@
"bulma": "^0.7.1", "bulma": "^0.7.1",
"bulma-tooltip": "^2.0.2", "bulma-tooltip": "^2.0.2",
"classnames": "^2.2.5", "classnames": "^2.2.5",
"diff2html": "^2.4.0",
"font-awesome": "^4.7.0", "font-awesome": "^4.7.0",
"history": "^4.7.2", "history": "^4.7.2",
"i18next": "^11.4.0", "i18next": "^11.4.0",
"i18next-browser-languagedetector": "^2.2.2", "i18next-browser-languagedetector": "^2.2.2",
"i18next-fetch-backend": "^0.1.0", "i18next-fetch-backend": "^0.1.0",
"moment": "^2.22.2", "moment": "^2.22.2",
"react": "^16.5.2", "node-sass": "^4.9.3",
"react-dom": "^16.5.2", "postcss-easy-import": "^3.0.0",
"react": "^16.4.2",
"react-diff-view": "^1.7.0",
"react-dom": "^16.4.2",
"react-i18next": "^7.9.0", "react-i18next": "^7.9.0",
"react-jss": "^8.6.0", "react-jss": "^8.6.0",
"react-redux": "^5.0.7", "react-redux": "^5.0.7",
"react-router-dom": "^4.3.1", "react-router-dom": "^4.3.1",
"react-router-redux": "^5.0.0-alpha.9", "react-router-redux": "^5.0.0-alpha.9",
"react-syntax-highlighter": "^9.0.1",
"redux": "^4.0.0", "redux": "^4.0.0",
"redux-devtools-extension": "^2.13.5", "redux-devtools-extension": "^2.13.5",
"redux-logger": "^3.0.6", "redux-logger": "^3.0.6",
@@ -43,7 +48,7 @@
"pre-commit": "jest && flow && eslint src" "pre-commit": "jest && flow && eslint src"
}, },
"devDependencies": { "devDependencies": {
"@scm-manager/ui-bundler": "^0.0.17", "@scm-manager/ui-bundler": "^0.0.19",
"copyfiles": "^2.0.0", "copyfiles": "^2.0.0",
"enzyme": "^3.3.0", "enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1", "enzyme-adapter-react-16": "^1.1.1",
@@ -53,6 +58,7 @@
"node-sass": "^4.9.3", "node-sass": "^4.9.3",
"node-sass-chokidar": "^1.3.0", "node-sass-chokidar": "^1.3.0",
"npm-run-all": "^4.1.3", "npm-run-all": "^4.1.3",
"postcss-easy-import": "^3.0.0",
"prettier": "^1.13.7", "prettier": "^1.13.7",
"react-router-enzyme-context": "^1.2.0", "react-router-enzyme-context": "^1.2.0",
"react-test-renderer": "^16.4.1", "react-test-renderer": "^16.4.1",

View File

@@ -36,5 +36,9 @@
</script> </script>
<script src="{{ contextPath }}/vendor.bundle.js"></script> <script src="{{ contextPath }}/vendor.bundle.js"></script>
<script src="{{ contextPath }}/scm-ui.bundle.js"></script> <script src="{{ contextPath }}/scm-ui.bundle.js"></script>
{{#liveReloadURL}}
<script src="{{liveReloadURL}}"></script>
{{/liveReloadURL}}
</body> </body>
</html> </html>

View File

@@ -51,7 +51,11 @@
"name": "Name", "name": "Name",
"length": "Length", "length": "Length",
"lastModified": "Last modified", "lastModified": "Last modified",
"description": "Description" "description": "Description",
"branch": "Branch"
},
"content": {
"downloadButton": "Download"
} }
}, },
"changesets": { "changesets": {

View File

@@ -14,7 +14,7 @@ import {
modifyConfigReset modifyConfigReset
} from "../modules/config"; } from "../modules/config";
import { connect } from "react-redux"; import { connect } from "react-redux";
import type { Config, Link } from "@scm-manager/ui-types"; import type { Config } from "@scm-manager/ui-types";
import ConfigForm from "../components/form/ConfigForm"; import ConfigForm from "../components/form/ConfigForm";
import { getConfigLink } from "../../modules/indexResource"; import { getConfigLink } from "../../modules/indexResource";

View File

@@ -22,7 +22,6 @@ import reducer, {
getConfig, getConfig,
getConfigUpdatePermission getConfigUpdatePermission
} from "./config"; } from "./config";
import { getConfigLink } from "../../modules/indexResource";
const CONFIG_URL = "/config"; const CONFIG_URL = "/config";
const URL = "/api/v2" + CONFIG_URL; const URL = "/api/v2" + CONFIG_URL;

View File

@@ -19,9 +19,8 @@ import {
Footer, Footer,
Header Header
} from "@scm-manager/ui-components"; } from "@scm-manager/ui-components";
import type { Me, Link } from "@scm-manager/ui-types"; import type { Me } from "@scm-manager/ui-types";
import { import {
fetchIndexResources,
getConfigLink, getConfigLink,
getFetchIndexResourcesFailure, getFetchIndexResourcesFailure,
getGroupsLink, getGroupsLink,

View File

@@ -11,7 +11,7 @@ import {
getLogoutFailure getLogoutFailure
} from "../modules/auth"; } from "../modules/auth";
import { Loading, ErrorPage } from "@scm-manager/ui-components"; import { Loading, ErrorPage } from "@scm-manager/ui-components";
import { fetchIndexResources, getLogoutLink } from "../modules/indexResource"; import { getLogoutLink } from "../modules/indexResource";
type Props = { type Props = {
authenticated: boolean, authenticated: boolean,

View File

@@ -9,8 +9,7 @@ import {
createGroup, createGroup,
isCreateGroupPending, isCreateGroupPending,
getCreateGroupFailure, getCreateGroupFailure,
createGroupReset, createGroupReset
getCreateGroupLink
} from "../modules/groups"; } from "../modules/groups";
import type { Group } from "@scm-manager/ui-types"; import type { Group } from "@scm-manager/ui-types";
import type { History } from "history"; import type { History } from "history";

View File

@@ -14,7 +14,6 @@ import type { BrowserHistory } from "history/createBrowserHistory";
import createReduxStore from "./createReduxStore"; import createReduxStore from "./createReduxStore";
import { ConnectedRouter } from "react-router-redux"; import { ConnectedRouter } from "react-router-redux";
import PluginLoader from "./containers/PluginLoader";
import { urls } from "@scm-manager/ui-components"; import { urls } from "@scm-manager/ui-components";
@@ -37,7 +36,7 @@ ReactDOM.render(
<I18nextProvider i18n={i18n}> <I18nextProvider i18n={i18n}>
{/* ConnectedRouter will use the store from Provider automatically */} {/* ConnectedRouter will use the store from Provider automatically */}
<ConnectedRouter history={history}> <ConnectedRouter history={history}>
<Index /> <Index />
</ConnectedRouter> </ConnectedRouter>
</I18nextProvider> </I18nextProvider>
</Provider>, </Provider>,

View File

@@ -7,8 +7,8 @@ import { isPending } from "./pending";
import { getFailure } from "./failure"; import { getFailure } from "./failure";
import { import {
callFetchIndexResources, callFetchIndexResources,
FETCH_INDEXRESOURCES_SUCCESS, fetchIndexResources,
fetchIndexResources, fetchIndexResourcesPending, fetchIndexResourcesPending,
fetchIndexResourcesSuccess fetchIndexResourcesSuccess
} from "./indexResource"; } from "./indexResource";
@@ -156,7 +156,7 @@ export const login = (
return apiClient return apiClient
.post(loginLink, login_data) .post(loginLink, login_data)
.then(response => { .then(response => {
dispatch(fetchIndexResourcesPending()) dispatch(fetchIndexResourcesPending());
return callFetchIndexResources(); return callFetchIndexResources();
}) })
.then(response => { .then(response => {

View File

@@ -1,8 +1,8 @@
//@flow //@flow
import React from "react"; import React from "react";
import { binder } from "@scm-manager/ui-extensions"; import {binder} from "@scm-manager/ui-extensions";
import type { Changeset } from "@scm-manager/ui-types"; import type {Changeset} from "@scm-manager/ui-types";
import { Image } from "@scm-manager/ui-components"; import {Image} from "@scm-manager/ui-components";
type Props = { type Props = {
changeset: Changeset changeset: Changeset

View File

@@ -1,6 +1,6 @@
//@flow //@flow
import * as React from "react"; import * as React from "react";
import { binder } from "@scm-manager/ui-extensions"; import {binder} from "@scm-manager/ui-extensions";
type Props = { type Props = {
children: React.Node children: React.Node

View File

@@ -1,20 +1,18 @@
//@flow //@flow
import React from "react"; import React from "react";
import type { import type { Changeset, Repository } from "@scm-manager/ui-types";
Changeset,
Repository
} from "../../../../../scm-ui-components/packages/ui-types/src/index";
import { Interpolate, translate } from "react-i18next"; import { Interpolate, translate } from "react-i18next";
import injectSheet from "react-jss"; import injectSheet from "react-jss";
import ChangesetTag from "./ChangesetTag"; import ChangesetTag from "./ChangesetTag";
import ChangesetAuthor from "./ChangesetAuthor"; import ChangesetAuthor from "./ChangesetAuthor";
import { parseDescription } from "./changesets"; import { parseDescription } from "./changesets";
import { DateFromNow } from "../../../../../scm-ui-components/packages/ui-components/src/index"; import { DateFromNow } from "@scm-manager/ui-components";
import AvatarWrapper from "./AvatarWrapper"; import AvatarWrapper from "./AvatarWrapper";
import AvatarImage from "./AvatarImage"; import AvatarImage from "./AvatarImage";
import classNames from "classnames"; import classNames from "classnames";
import ChangesetId from "./ChangesetId"; import ChangesetId from "./ChangesetId";
import type { Tag } from "@scm-manager/ui-types"; import type { Tag } from "@scm-manager/ui-types";
import ScmDiff from "../../containers/ScmDiff";
const styles = { const styles = {
spacing: { spacing: {
@@ -41,38 +39,43 @@ class ChangesetDetails extends React.Component<Props> {
const date = <DateFromNow date={changeset.date} />; const date = <DateFromNow date={changeset.date} />;
return ( return (
<div className="content"> <div>
<h4>{description.title}</h4> <div className="content">
<article className="media"> <h4>{description.title}</h4>
<AvatarWrapper> <article className="media">
<p className={classNames("image", "is-64x64", classes.spacing)}> <AvatarWrapper>
<AvatarImage changeset={changeset} /> <p className={classNames("image", "is-64x64", classes.spacing)}>
</p> <AvatarImage changeset={changeset} />
</AvatarWrapper> </p>
<div className="media-content"> </AvatarWrapper>
<p> <div className="media-content">
<ChangesetAuthor changeset={changeset} /> <p>
</p> <ChangesetAuthor changeset={changeset} />
<p> </p>
<Interpolate <p>
i18nKey="changesets.changeset.summary" <Interpolate
id={id} i18nKey="changesets.changeset.summary"
time={date} id={id}
/> time={date}
</p> />
</div> </p>
<div className="media-right">{this.renderTags()}</div> </div>
</article> <div className="media-right">{this.renderTags()}</div>
<p> </article>
{description.message.split("\n").map((item, key) => { <p>
return ( {description.message.split("\n").map((item, key) => {
<span key={key}> return (
{item} <span key={key}>
<br /> {item}
</span> <br />
); </span>
})} );
</p> })}
</p>
</div>
<div>
<ScmDiff changeset={changeset} sideBySide={false} />
</div>
</div> </div>
); );
} }

View File

@@ -1,8 +1,8 @@
//@flow //@flow
import { Link } from "react-router-dom"; import {Link} from "react-router-dom";
import React from "react"; import React from "react";
import type { Repository, Changeset } from "@scm-manager/ui-types"; import type {Changeset, Repository} from "@scm-manager/ui-types";
type Props = { type Props = {
repository: Repository, repository: Repository,

View File

@@ -1,15 +1,15 @@
//@flow //@flow
import React from "react"; import React from "react";
import type { Changeset, Repository, Tag } from "@scm-manager/ui-types"; import type {Changeset, Repository, Tag} from "@scm-manager/ui-types";
import classNames from "classnames"; import classNames from "classnames";
import { translate, Interpolate } from "react-i18next"; import {Interpolate, translate} from "react-i18next";
import ChangesetId from "./ChangesetId"; import ChangesetId from "./ChangesetId";
import injectSheet from "react-jss"; import injectSheet from "react-jss";
import { DateFromNow } from "@scm-manager/ui-components"; import {DateFromNow} from "@scm-manager/ui-components";
import ChangesetAuthor from "./ChangesetAuthor"; import ChangesetAuthor from "./ChangesetAuthor";
import ChangesetTag from "./ChangesetTag"; import ChangesetTag from "./ChangesetTag";
import { compose } from "redux"; import {compose} from "redux";
import { parseDescription } from "./changesets"; import {parseDescription} from "./changesets";
import AvatarWrapper from "./AvatarWrapper"; import AvatarWrapper from "./AvatarWrapper";
import AvatarImage from "./AvatarImage"; import AvatarImage from "./AvatarImage";

View File

@@ -1,6 +1,6 @@
// @flow // @flow
import { parseDescription } from "./changesets"; import {parseDescription} from "./changesets";
describe("parseDescription tests", () => { describe("parseDescription tests", () => {
it("should return a description with title and message", () => { it("should return a description with title and message", () => {

View File

@@ -1,9 +1,9 @@
//@flow //@flow
import React from "react"; import React from "react";
import {Link} from "react-router-dom"; import { Link } from "react-router-dom";
import injectSheet from "react-jss"; import injectSheet from "react-jss";
import type {Repository} from "@scm-manager/ui-types"; import type { Repository } from "@scm-manager/ui-types";
import {DateFromNow} from "@scm-manager/ui-components"; import { DateFromNow } from "@scm-manager/ui-components";
import RepositoryEntryLink from "./RepositoryEntryLink"; import RepositoryEntryLink from "./RepositoryEntryLink";
import classNames from "classnames"; import classNames from "classnames";
import RepositoryAvatar from "./RepositoryAvatar"; import RepositoryAvatar from "./RepositoryAvatar";
@@ -45,7 +45,7 @@ class RepositoryEntry extends React.Component<Props> {
return ( return (
<RepositoryEntryLink <RepositoryEntryLink
iconClass="fa-code-branch" iconClass="fa-code-branch"
to={repositoryLink + "/history"} to={repositoryLink + "/changesets"}
/> />
); );
} }

View File

@@ -11,7 +11,7 @@ import {
} from "../modules/changesets"; } from "../modules/changesets";
import ChangesetDetails from "../components/changesets/ChangesetDetails"; import ChangesetDetails from "../components/changesets/ChangesetDetails";
import { translate } from "react-i18next"; import { translate } from "react-i18next";
import { Loading, ErrorPage } from "@scm-manager/ui-components"; import { ErrorPage, Loading } from "@scm-manager/ui-components";
type Props = { type Props = {
id: string, id: string,

View File

@@ -1,13 +1,8 @@
// @flow // @flow
import React from "react"; import React from "react";
import { withRouter } from "react-router-dom"; import {withRouter} from "react-router-dom";
import type { import type {Branch, Changeset, PagedCollection, Repository} from "@scm-manager/ui-types";
Branch,
Changeset,
PagedCollection,
Repository
} from "@scm-manager/ui-types";
import { import {
fetchChangesets, fetchChangesets,
getChangesets, getChangesets,
@@ -16,15 +11,10 @@ import {
selectListAsCollection selectListAsCollection
} from "../modules/changesets"; } from "../modules/changesets";
import { connect } from "react-redux"; import {connect} from "react-redux";
import ChangesetList from "../components/changesets/ChangesetList"; import ChangesetList from "../components/changesets/ChangesetList";
import { import {ErrorNotification, getPageFromMatch, LinkPaginator, Loading} from "@scm-manager/ui-components";
ErrorNotification, import {compose} from "redux";
LinkPaginator,
Loading,
getPageFromMatch
} from "@scm-manager/ui-components";
import { compose } from "redux";
type Props = { type Props = {
repository: Repository, repository: Repository,

View File

@@ -1,32 +1,19 @@
//@flow //@flow
import React from "react"; import React from "react";
import { import {deleteRepo, fetchRepo, getFetchRepoFailure, getRepository, isFetchRepoPending} from "../modules/repos";
deleteRepo,
fetchRepo,
getFetchRepoFailure,
getRepository,
isFetchRepoPending
} from "../modules/repos";
import { connect } from "react-redux"; import {connect} from "react-redux";
import { Route, Switch } from "react-router-dom"; import {Route, Switch} from "react-router-dom";
import type { Repository } from "@scm-manager/ui-types"; import type {Repository} from "@scm-manager/ui-types";
import { import {ErrorPage, Loading, Navigation, NavLink, Page, Section} from "@scm-manager/ui-components";
ErrorPage, import {translate} from "react-i18next";
Loading,
Navigation,
NavLink,
Page,
Section
} from "@scm-manager/ui-components";
import { translate } from "react-i18next";
import RepositoryDetails from "../components/RepositoryDetails"; import RepositoryDetails from "../components/RepositoryDetails";
import DeleteNavAction from "../components/DeleteNavAction"; import DeleteNavAction from "../components/DeleteNavAction";
import Edit from "../containers/Edit"; import Edit from "../containers/Edit";
import Permissions from "../permissions/containers/Permissions"; import Permissions from "../permissions/containers/Permissions";
import type { History } from "history"; import type {History} from "history";
import EditNavLink from "../components/EditNavLink"; import EditNavLink from "../components/EditNavLink";
import BranchRoot from "./ChangesetsRoot"; import BranchRoot from "./ChangesetsRoot";
@@ -80,11 +67,6 @@ class RepositoryRoot extends React.Component<Props> {
this.props.deleteRepo(repository, this.deleted); this.props.deleteRepo(repository, this.deleted);
}; };
matchChangeset = (route: any) => {
const url = this.matchedUrl();
return route.location.pathname.match(`${url}/changeset/`);
};
matches = (route: any) => { matches = (route: any) => {
const url = this.matchedUrl(); const url = this.matchedUrl();
const regex = new RegExp(`${url}(/branches)?/?[^/]*/changesets?.*`); const regex = new RegExp(`${url}(/branches)?/?[^/]*/changesets?.*`);
@@ -125,7 +107,7 @@ class RepositoryRoot extends React.Component<Props> {
/> />
<Route <Route
path={`${url}/permissions`} path={`${url}/permissions`}
render={props => ( render={() => (
<Permissions <Permissions
namespace={this.props.repository.namespace} namespace={this.props.repository.namespace}
repoName={this.props.repository.name} repoName={this.props.repository.name}

View File

@@ -0,0 +1,51 @@
// @flow
import React from "react";
import { apiClient } from "@scm-manager/ui-components";
import type { Changeset } from "@scm-manager/ui-types";
import { Diff2Html } from "diff2html";
type Props = {
changeset: Changeset,
sideBySide: boolean
};
type State = {
diff: string,
error?: Error
};
class ScmDiff extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { diff: "" };
}
componentDidMount() {
const { changeset } = this.props;
const url = changeset._links.diff.href+"?format=GIT";
apiClient
.get(url)
.then(response => response.text())
.then(text => this.setState({ ...this.state, diff: text }))
.catch(error => this.setState({ ...this.state, error }));
}
render() {
const options = {
inputFormat: "diff",
outputFormat: this.props.sideBySide ? "side-by-side" : "line-by-line",
showFiles: false,
matching: "lines"
};
const outputHtml = Diff2Html.getPrettyHtml(this.state.diff, options);
return (
// eslint-disable-next-line react/no-danger
<div dangerouslySetInnerHTML={{ __html: outputHtml }} />
);
}
}
export default ScmDiff;

View File

@@ -1,19 +1,10 @@
// @flow // @flow
import { import {FAILURE_SUFFIX, PENDING_SUFFIX, SUCCESS_SUFFIX} from "../../modules/types";
FAILURE_SUFFIX, import {apiClient, urls} from "@scm-manager/ui-components";
PENDING_SUFFIX, import {isPending} from "../../modules/pending";
SUCCESS_SUFFIX import {getFailure} from "../../modules/failure";
} from "../../modules/types"; import type {Action, Branch, PagedCollection, Repository} from "@scm-manager/ui-types";
import { apiClient, urls } from "@scm-manager/ui-components";
import { isPending } from "../../modules/pending";
import { getFailure } from "../../modules/failure";
import type {
Action,
Branch,
PagedCollection,
Repository
} from "@scm-manager/ui-types";
export const FETCH_CHANGESETS = "scm/repos/FETCH_CHANGESETS"; export const FETCH_CHANGESETS = "scm/repos/FETCH_CHANGESETS";
export const FETCH_CHANGESETS_PENDING = `${FETCH_CHANGESETS}_${PENDING_SUFFIX}`; export const FETCH_CHANGESETS_PENDING = `${FETCH_CHANGESETS}_${PENDING_SUFFIX}`;

View File

@@ -4,27 +4,27 @@ import configureMockStore from "redux-mock-store";
import thunk from "redux-thunk"; import thunk from "redux-thunk";
import fetchMock from "fetch-mock"; import fetchMock from "fetch-mock";
import reducer, { import reducer, {
FETCH_CHANGESETS,
FETCH_CHANGESETS_FAILURE,
FETCH_CHANGESETS_PENDING,
FETCH_CHANGESETS_SUCCESS,
FETCH_CHANGESET, FETCH_CHANGESET,
FETCH_CHANGESET_FAILURE, FETCH_CHANGESET_FAILURE,
FETCH_CHANGESET_PENDING, FETCH_CHANGESET_PENDING,
FETCH_CHANGESET_SUCCESS, FETCH_CHANGESET_SUCCESS,
FETCH_CHANGESETS,
FETCH_CHANGESETS_FAILURE,
FETCH_CHANGESETS_PENDING,
FETCH_CHANGESETS_SUCCESS,
fetchChangeset,
fetchChangesetIfNeeded,
fetchChangesets, fetchChangesets,
fetchChangesetsSuccess, fetchChangesetsSuccess,
getChangesets,
getFetchChangesetsFailure,
isFetchChangesetsPending,
fetchChangeset,
getChangeset,
fetchChangesetIfNeeded,
shouldFetchChangeset,
isFetchChangesetPending,
getFetchChangesetFailure,
fetchChangesetSuccess, fetchChangesetSuccess,
selectListAsCollection getChangeset,
getChangesets,
getFetchChangesetFailure,
getFetchChangesetsFailure,
isFetchChangesetPending,
isFetchChangesetsPending,
selectListAsCollection,
shouldFetchChangeset
} from "./changesets"; } from "./changesets";
const branch = { const branch = {

View File

@@ -1,12 +1,9 @@
// @flow // @flow
import React from "react"; import React from "react";
import { translate } from "react-i18next"; import {translate} from "react-i18next";
import { Checkbox, InputField, SubmitButton } from "@scm-manager/ui-components"; import {Checkbox, InputField, SubmitButton} from "@scm-manager/ui-components";
import TypeSelector from "./TypeSelector"; import TypeSelector from "./TypeSelector";
import type { import type {PermissionCollection, PermissionCreateEntry} from "@scm-manager/ui-types";
PermissionCollection,
PermissionCreateEntry
} from "@scm-manager/ui-types";
import * as validator from "./permissionValidation"; import * as validator from "./permissionValidation";
type Props = { type Props = {

View File

@@ -1,6 +1,7 @@
// @flow // @flow
import { validation } from "@scm-manager/ui-components"; import {validation} from "@scm-manager/ui-components";
import type { PermissionCollection } from "@scm-manager/ui-types"; import type {PermissionCollection} from "@scm-manager/ui-types";
const isNameValid = validation.isNameValid; const isNameValid = validation.isNameValid;
export { isNameValid }; export { isNameValid };

View File

@@ -1,16 +1,12 @@
// @flow // @flow
import { apiClient } from "@scm-manager/ui-components"; import type {Action} from "@scm-manager/ui-components";
import {apiClient} from "@scm-manager/ui-components";
import * as types from "../../../modules/types"; import * as types from "../../../modules/types";
import type { Action } from "@scm-manager/ui-components"; import type {Permission, PermissionCollection, PermissionCreateEntry} from "@scm-manager/ui-types";
import type { import {isPending} from "../../../modules/pending";
PermissionCollection, import {getFailure} from "../../../modules/failure";
Permission, import {Dispatch} from "redux";
PermissionCreateEntry
} from "@scm-manager/ui-types";
import { isPending } from "../../../modules/pending";
import { getFailure } from "../../../modules/failure";
import { Dispatch } from "redux";
export const FETCH_PERMISSIONS = "scm/permissions/FETCH_PERMISSIONS"; export const FETCH_PERMISSIONS = "scm/permissions/FETCH_PERMISSIONS";
export const FETCH_PERMISSIONS_PENDING = `${FETCH_PERMISSIONS}_${ export const FETCH_PERMISSIONS_PENDING = `${FETCH_PERMISSIONS}_${

View File

@@ -3,44 +3,44 @@ import configureMockStore from "redux-mock-store";
import thunk from "redux-thunk"; import thunk from "redux-thunk";
import fetchMock from "fetch-mock"; import fetchMock from "fetch-mock";
import reducer, { import reducer, {
fetchPermissions, CREATE_PERMISSION,
fetchPermissionsSuccess, CREATE_PERMISSION_FAILURE,
getPermissionsOfRepo,
isFetchPermissionsPending,
getFetchPermissionsFailure,
modifyPermission,
modifyPermissionSuccess,
getModifyPermissionFailure,
isModifyPermissionPending,
createPermission,
hasCreatePermission,
deletePermission,
deletePermissionSuccess,
getDeletePermissionFailure,
isDeletePermissionPending,
getModifyPermissionsFailure,
MODIFY_PERMISSION_FAILURE,
MODIFY_PERMISSION_PENDING,
FETCH_PERMISSIONS,
FETCH_PERMISSIONS_PENDING,
FETCH_PERMISSIONS_SUCCESS,
FETCH_PERMISSIONS_FAILURE,
MODIFY_PERMISSION_SUCCESS,
MODIFY_PERMISSION,
CREATE_PERMISSION_PENDING, CREATE_PERMISSION_PENDING,
CREATE_PERMISSION_SUCCESS, CREATE_PERMISSION_SUCCESS,
CREATE_PERMISSION_FAILURE, createPermission,
createPermissionSuccess,
DELETE_PERMISSION, DELETE_PERMISSION,
DELETE_PERMISSION_FAILURE,
DELETE_PERMISSION_PENDING, DELETE_PERMISSION_PENDING,
DELETE_PERMISSION_SUCCESS, DELETE_PERMISSION_SUCCESS,
DELETE_PERMISSION_FAILURE, deletePermission,
CREATE_PERMISSION, deletePermissionSuccess,
createPermissionSuccess, FETCH_PERMISSIONS,
FETCH_PERMISSIONS_FAILURE,
FETCH_PERMISSIONS_PENDING,
FETCH_PERMISSIONS_SUCCESS,
fetchPermissions,
fetchPermissionsSuccess,
getCreatePermissionFailure, getCreatePermissionFailure,
getDeletePermissionFailure,
getDeletePermissionsFailure,
getFetchPermissionsFailure,
getModifyPermissionFailure,
getModifyPermissionsFailure,
getPermissionsOfRepo,
hasCreatePermission,
isCreatePermissionPending, isCreatePermissionPending,
getDeletePermissionsFailure isDeletePermissionPending,
isFetchPermissionsPending,
isModifyPermissionPending,
MODIFY_PERMISSION,
MODIFY_PERMISSION_FAILURE,
MODIFY_PERMISSION_PENDING,
MODIFY_PERMISSION_SUCCESS,
modifyPermission,
modifyPermissionSuccess
} from "./permissions"; } from "./permissions";
import type { Permission, PermissionCollection } from "@scm-manager/ui-types"; import type {Permission, PermissionCollection} from "@scm-manager/ui-types";
const hitchhiker_puzzle42Permission_user_eins: Permission = { const hitchhiker_puzzle42Permission_user_eins: Permission = {
name: "user_eins", name: "user_eins",

View File

@@ -7,7 +7,6 @@ import FileTreeLeaf from "./FileTreeLeaf";
import type { Repository, File } from "@scm-manager/ui-types"; import type { Repository, File } from "@scm-manager/ui-types";
import { ErrorNotification, Loading } from "@scm-manager/ui-components"; import { ErrorNotification, Loading } from "@scm-manager/ui-components";
import { import {
fetchSources,
getFetchSourcesFailure, getFetchSourcesFailure,
isFetchSourcesPending, isFetchSourcesPending,
getSources getSources
@@ -29,7 +28,6 @@ type Props = {
revision: string, revision: string,
path: string, path: string,
baseUrl: string, baseUrl: string,
fetchSources: (Repository, string, string) => void,
// context props // context props
classes: any, classes: any,
t: string => string, t: string => string,
@@ -49,19 +47,6 @@ export function findParent(path: string) {
} }
class FileTree extends React.Component<Props> { class FileTree extends React.Component<Props> {
componentDidMount() {
const { fetchSources, repository, revision, path } = this.props;
fetchSources(repository, revision, path);
}
componentDidUpdate(prevProps) {
const { fetchSources, repository, revision, path } = this.props;
if (prevProps.revision !== revision || prevProps.path !== path) {
fetchSources(repository, revision, path);
}
}
render() { render() {
const { const {
error, error,
@@ -167,18 +152,7 @@ const mapStateToProps = (state: any, ownProps: Props) => {
}; };
}; };
const mapDispatchToProps = dispatch => {
return {
fetchSources: (repository: Repository, revision: string, path: string) => {
dispatch(fetchSources(repository, revision, path));
}
};
};
export default compose( export default compose(
withRouter, withRouter,
connect( connect(mapStateToProps)
mapStateToProps,
mapDispatchToProps
)
)(injectSheet(styles)(translate("repos")(FileTree))); )(injectSheet(styles)(translate("repos")(FileTree)));

View File

@@ -49,14 +49,18 @@ class FileTreeLeaf extends React.Component<Props> {
</Link> </Link>
); );
} }
return <FileIcon file={file} />; return (
<Link to={this.createLink(file)}>
<FileIcon file={file} />
</Link>
);
}; };
createFileName = (file: File) => { createFileName = (file: File) => {
if (file.directory) { if (file.directory) {
return <Link to={this.createLink(file)}>{file.name}</Link>; return <Link to={this.createLink(file)}>{file.name}</Link>;
} }
return file.name; return <Link to={this.createLink(file)}>{file.name}</Link>;
}; };
render() { render() {

View File

@@ -0,0 +1,26 @@
// @flow
import React from "react";
import { translate } from "react-i18next";
import type { File } from "@scm-manager/ui-types";
import { DownloadButton } from "@scm-manager/ui-components";
type Props = {
t: string => string,
file: File
};
class DownloadViewer extends React.Component<Props> {
render() {
const { t, file } = this.props;
return (
<div className="has-text-centered">
<DownloadButton
url={file._links.self.href}
displayName={t("sources.content.downloadButton")}
/>
</div>
);
}
}
export default translate("repos")(DownloadViewer);

View File

@@ -0,0 +1,24 @@
// @flow
import React from "react";
import { translate } from "react-i18next";
import type { File } from "@scm-manager/ui-types";
type Props = {
t: string => string,
file: File
};
class ImageViewer extends React.Component<Props> {
render() {
const { file } = this.props;
return (
<div className="has-text-centered">
<figure>
<img src={file._links.self.href} alt={file._links.self.href} />
</figure>
</div>
);
}
}
export default translate("repos")(ImageViewer);

View File

@@ -0,0 +1,97 @@
// @flow
import React from "react";
import { translate } from "react-i18next";
import { apiClient } from "@scm-manager/ui-components";
import type { File } from "@scm-manager/ui-types";
import { ErrorNotification, Loading } from "@scm-manager/ui-components";
import SyntaxHighlighter from "react-syntax-highlighter";
import { arduinoLight } from "react-syntax-highlighter/styles/hljs";
type Props = {
t: string => string,
file: File,
language: string
};
type State = {
content: string,
error?: Error,
loaded: boolean
};
class SourcecodeViewer extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
content: "",
loaded: false
};
}
componentDidMount() {
const { file } = this.props;
getContent(file._links.self.href)
.then(result => {
if (result.error) {
this.setState({
...this.state,
error: result.error,
loaded: true
});
} else {
this.setState({
...this.state,
content: result,
loaded: true
});
}
})
.catch(err => {});
}
render() {
const { content, error, loaded } = this.state;
const language = this.props.language;
if (error) {
return <ErrorNotification error={error} />;
}
if (!loaded) {
return <Loading />;
}
if (!content) {
return null;
}
return (
<SyntaxHighlighter
showLineNumbers="true"
language={getLanguage(language)}
style={arduinoLight}
>
{content}
</SyntaxHighlighter>
);
}
}
export function getLanguage(language: string) {
return language.toLowerCase();
}
export function getContent(url: string) {
return apiClient
.get(url)
.then(response => response.text())
.then(response => {
return response;
})
.catch(err => {
return { error: err };
});
}
export default translate("repos")(SourcecodeViewer);

View File

@@ -0,0 +1,33 @@
//@flow
import fetchMock from "fetch-mock";
import {
getContent,
getLanguage
} from "./SourcecodeViewer";
describe("get content", () => {
const CONTENT_URL = "/repositories/scmadmin/TestRepo/content/testContent";
afterEach(() => {
fetchMock.reset();
fetchMock.restore();
});
it("should return content", done => {
fetchMock.getOnce("/api/v2" + CONTENT_URL, "This is a testContent");
getContent(CONTENT_URL).then(content => {
expect(content).toBe("This is a testContent");
done();
});
});
});
describe("get correct language type", () => {
it("should return javascript", () => {
expect(getLanguage("JAVASCRIPT")).toBe("javascript");
});
it("should return nothing for plain text", () => {
expect(getLanguage("")).toBe("");
});
});

View File

@@ -0,0 +1,217 @@
// @flow
import React from "react";
import { translate } from "react-i18next";
import { getSources } from "../modules/sources";
import type { Repository, File } from "@scm-manager/ui-types";
import {
ErrorNotification,
Loading,
DateFromNow
} from "@scm-manager/ui-components";
import { connect } from "react-redux";
import ImageViewer from "../components/content/ImageViewer";
import SourcecodeViewer from "../components/content/SourcecodeViewer";
import DownloadViewer from "../components/content/DownloadViewer";
import FileSize from "../components/FileSize";
import injectSheet from "react-jss";
import classNames from "classnames";
import { ExtensionPoint } from "@scm-manager/ui-extensions";
import { getContentType } from "./contentType";
type Props = {
loading: boolean,
error: Error,
file: File,
repository: Repository,
revision: string,
path: string,
classes: any,
t: string => string
};
type State = {
contentType: string,
language: string,
loaded: boolean,
collapsed: boolean,
error?: Error
};
const styles = {
toCenterContent: {
display: "block"
},
pointer: {
cursor: "pointer"
}
};
class Content extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
contentType: "",
language: "",
loaded: false,
collapsed: true
};
}
componentDidMount() {
const { file } = this.props;
getContentType(file._links.self.href)
.then(result => {
if (result.error) {
this.setState({
...this.state,
error: result.error,
loaded: true
});
} else {
this.setState({
...this.state,
contentType: result.type,
language: result.language,
loaded: true
});
}
})
.catch(err => {});
}
toggleCollapse = () => {
this.setState(prevState => ({
collapsed: !prevState.collapsed
}));
};
showHeader() {
const { file, classes } = this.props;
const collapsed = this.state.collapsed;
const icon = collapsed ? "fa-angle-right" : "fa-angle-down";
const fileSize = file.directory ? "" : <FileSize bytes={file.length} />;
return (
<span className={classes.pointer} onClick={this.toggleCollapse}>
<article className="media">
<div className="media-left">
<i className={classNames("fa", icon)} />
</div>
<div className="media-content">
<div className="content">{file.name}</div>
</div>
<p className="media-right">{fileSize}</p>
</article>
</span>
);
}
showMoreInformation() {
const collapsed = this.state.collapsed;
const { classes, file, revision } = this.props;
const date = <DateFromNow date={file.lastModified} />;
const description = file.description ? (
<p>
{file.description.split("\n").map((item, key) => {
return (
<span key={key}>
{item}
<br />
</span>
);
})}
</p>
) : null;
if (!collapsed) {
return (
<div className={classNames("panel-block", classes.toCenterContent)}>
<table className="table">
<tbody>
<tr>
<td>Path</td>
<td>{file.path}</td>
</tr>
<tr>
<td>Branch</td>
<td>{revision}</td>
</tr>
<tr>
<td>Last modified</td>
<td>{date}</td>
</tr>
<tr>
<td>Description</td>
<td>{description}</td>
</tr>
</tbody>
</table>
</div>
);
}
return null;
}
showContent() {
const { file, revision } = this.props;
const { contentType, language } = this.state;
if (contentType.startsWith("image/")) {
return <ImageViewer file={file} />;
} else if (language) {
return <SourcecodeViewer file={file} language={language} />;
} else if (contentType.startsWith("text/")) {
return <SourcecodeViewer file={file} language="none" />;
} else {
return (
<ExtensionPoint
name="repos.sources.view"
props={{ file, contentType, revision }}
>
<DownloadViewer file={file} />
</ExtensionPoint>
);
}
}
render() {
const { file, classes } = this.props;
const { loaded, error } = this.state;
if (!file || !loaded) {
return <Loading />;
}
if (error) {
return <ErrorNotification error={error} />;
}
const header = this.showHeader();
const content = this.showContent();
const moreInformation = this.showMoreInformation();
return (
<div>
<nav className="panel">
<article className="panel-heading">{header}</article>
{moreInformation}
<div className={classNames("panel-block", classes.toCenterContent)}>
{content}
</div>
</nav>
</div>
);
}
}
const mapStateToProps = (state: any, ownProps: Props) => {
const { repository, revision, path } = ownProps;
const file = getSources(state, repository, revision, path);
return {
file
};
};
export default injectSheet(styles)(
connect(mapStateToProps)(translate("repos")(Content))
);

View File

@@ -13,6 +13,8 @@ import {
isFetchBranchesPending isFetchBranchesPending
} from "../../modules/branches"; } from "../../modules/branches";
import { compose } from "redux"; import { compose } from "redux";
import Content from "./Content";
import { fetchSources, isDirectory } from "../modules/sources";
type Props = { type Props = {
repository: Repository, repository: Repository,
@@ -22,9 +24,11 @@ type Props = {
branches: Branch[], branches: Branch[],
revision: string, revision: string,
path: string, path: string,
currentFileIsDirectory: boolean,
// dispatch props // dispatch props
fetchBranches: Repository => void, fetchBranches: Repository => void,
fetchSources: (Repository, string, string) => void,
// Context props // Context props
history: any, history: any,
@@ -33,14 +37,26 @@ type Props = {
class Sources extends React.Component<Props> { class Sources extends React.Component<Props> {
componentDidMount() { componentDidMount() {
const { fetchBranches, repository } = this.props; const {
fetchBranches,
repository,
revision,
path,
fetchSources
} = this.props;
fetchBranches(repository); fetchBranches(repository);
fetchSources(repository, revision, path);
}
componentDidUpdate(prevProps) {
const { fetchSources, repository, revision, path } = this.props;
if (prevProps.revision !== revision || prevProps.path !== path) {
fetchSources(repository, revision, path);
}
} }
branchSelected = (branch?: Branch) => { branchSelected = (branch?: Branch) => {
const { baseUrl, history, path } = this.props; const { baseUrl, history, path } = this.props;
let url; let url;
if (branch) { if (branch) {
if (path) { if (path) {
@@ -55,7 +71,15 @@ class Sources extends React.Component<Props> {
}; };
render() { render() {
const { repository, baseUrl, loading, error, revision, path } = this.props; const {
repository,
baseUrl,
loading,
error,
revision,
path,
currentFileIsDirectory
} = this.props;
if (error) { if (error) {
return <ErrorNotification error={error} />; return <ErrorNotification error={error} />;
@@ -65,21 +89,28 @@ class Sources extends React.Component<Props> {
return <Loading />; return <Loading />;
} }
return ( if (currentFileIsDirectory) {
<> return (
{this.renderBranchSelector()} <>
<FileTree {this.renderBranchSelector()}
repository={repository} <FileTree
revision={revision} repository={repository}
path={path} revision={revision}
baseUrl={baseUrl} path={path}
/> baseUrl={baseUrl}
</> />
); </>
);
} else {
return (
<Content repository={repository} revision={revision} path={path} />
);
}
} }
renderBranchSelector = () => { renderBranchSelector = () => {
const { repository, branches, revision } = this.props; const { repository, branches, revision } = this.props;
if (repository._links.branches) { if (repository._links.branches) {
return ( return (
<BranchSelector <BranchSelector
@@ -99,10 +130,12 @@ const mapStateToProps = (state, ownProps) => {
const { repository, match } = ownProps; const { repository, match } = ownProps;
const { revision, path } = match.params; const { revision, path } = match.params;
const decodedRevision = revision ? decodeURIComponent(revision) : undefined; const decodedRevision = revision ? decodeURIComponent(revision) : undefined;
const loading = isFetchBranchesPending(state, repository); const loading = isFetchBranchesPending(state, repository);
const error = getFetchBranchesFailure(state, repository); const error = getFetchBranchesFailure(state, repository);
const branches = getBranches(state, repository); const branches = getBranches(state, repository);
const currentFileIsDirectory = decodedRevision
? isDirectory(state, repository, decodedRevision, path)
: isDirectory(state, repository, revision, path);
return { return {
repository, repository,
@@ -110,7 +143,8 @@ const mapStateToProps = (state, ownProps) => {
path, path,
loading, loading,
error, error,
branches branches,
currentFileIsDirectory
}; };
}; };
@@ -118,6 +152,9 @@ const mapDispatchToProps = dispatch => {
return { return {
fetchBranches: (repository: Repository) => { fetchBranches: (repository: Repository) => {
dispatch(fetchBranches(repository)); dispatch(fetchBranches(repository));
},
fetchSources: (repository: Repository, revision: string, path: string) => {
dispatch(fetchSources(repository, revision, path));
} }
}; };
}; };

View File

@@ -0,0 +1,16 @@
//@flow
import { apiClient } from "@scm-manager/ui-components";
export function getContentType(url: string) {
return apiClient
.head(url)
.then(response => {
return {
type: response.headers.get("Content-Type"),
language: response.headers.get("X-Programming-Language")
};
})
.catch(err => {
return { error: err };
});
}

View File

@@ -0,0 +1,29 @@
//@flow
import fetchMock from "fetch-mock";
import { getContentType } from "./contentType";
describe("get content type", () => {
const CONTENT_URL = "/repositories/scmadmin/TestRepo/content/testContent";
afterEach(() => {
fetchMock.reset();
fetchMock.restore();
});
it("should return content", done => {
let headers = {
"Content-Type": "application/text",
"X-Programming-Language": "JAVA"
};
fetchMock.head("/api/v2" + CONTENT_URL, {
headers
});
getContentType(CONTENT_URL).then(content => {
expect(content.type).toBe("application/text");
expect(content.language).toBe("JAVA");
done();
});
});
});

View File

@@ -102,6 +102,20 @@ export default function reducer(
// selectors // selectors
export function isDirectory(
state: any,
repository: Repository,
revision: string,
path: string
): boolean {
const currentFile = getSources(state, repository, revision, path);
if (currentFile && !currentFile.directory) {
return false;
} else {
return true; //also return true if no currentFile is found since it is the "default" path
}
}
export function getSources( export function getSources(
state: any, state: any,
repository: Repository, repository: Repository,

View File

@@ -1,6 +1,6 @@
// @flow // @flow
import type { Repository } from "@scm-manager/ui-types"; import type { Repository, File } from "@scm-manager/ui-types";
import configureMockStore from "redux-mock-store"; import configureMockStore from "redux-mock-store";
import thunk from "redux-thunk"; import thunk from "redux-thunk";
import fetchMock from "fetch-mock"; import fetchMock from "fetch-mock";
@@ -14,7 +14,8 @@ import {
isFetchSourcesPending, isFetchSourcesPending,
default as reducer, default as reducer,
getSources, getSources,
fetchSourcesSuccess fetchSourcesSuccess,
isDirectory
} from "./sources"; } from "./sources";
const sourcesUrl = const sourcesUrl =
@@ -79,6 +80,21 @@ const collection = {
} }
}; };
const noDirectory: File = {
name: "src",
path: "src",
directory: true,
length: 176,
revision: "abc",
_links: {
self: {
href:
"http://localhost:8081/scm/rest/api/v2/repositories/scm/core/sources/76aae4bb4ceacf0e88938eb5b6832738b7d537b4/src"
}
},
_embedded: collection
};
describe("sources fetch", () => { describe("sources fetch", () => {
const mockStore = configureMockStore([thunk]); const mockStore = configureMockStore([thunk]);
@@ -168,6 +184,28 @@ describe("reducer tests", () => {
}); });
describe("selector tests", () => { describe("selector tests", () => {
it("should return false if it is no directory", () => {
const state = {
sources: {
"scm/core/abc/src/main/package.json": {
noDirectory
}
}
};
expect(
isDirectory(state, repository, "abc", "src/main/package.json")
).toBeFalsy();
});
it("should return true if it is directory", () => {
const state = {
sources: {
"scm/core/abc/src": noDirectory
}
};
expect(isDirectory(state, repository, "abc", "src")).toBe(true);
});
it("should return null", () => { it("should return null", () => {
expect(getSources({}, repository)).toBeFalsy(); expect(getSources({}, repository)).toBeFalsy();
}); });
@@ -181,7 +219,7 @@ describe("selector tests", () => {
expect(getSources(state, repository)).toBe(collection); expect(getSources(state, repository)).toBe(collection);
}); });
it("should return the source collection without revision and path", () => { it("should return the source collection with revision and path", () => {
const state = { const state = {
sources: { sources: {
"scm/core/abc/src/main": collection "scm/core/abc/src/main": collection

View File

@@ -1,6 +1,7 @@
@import "bulma/sass/utilities/initial-variables"; @import "bulma/sass/utilities/initial-variables";
@import "bulma/sass/utilities/functions"; @import "bulma/sass/utilities/functions";
$blue: #33B2E8; $blue: #33B2E8;
// $footer-background-color // $footer-background-color
@@ -52,3 +53,5 @@ $blue: #33B2E8;
@import "@fortawesome/fontawesome-free/scss/fontawesome.scss"; @import "@fortawesome/fontawesome-free/scss/fontawesome.scss";
$fa-font-path: "webfonts"; $fa-font-path: "webfonts";
@import "@fortawesome/fontawesome-free/scss/solid.scss"; @import "@fortawesome/fontawesome-free/scss/solid.scss";
@import "diff2html/dist/diff2html";

File diff suppressed because it is too large Load Diff

View File

@@ -103,7 +103,6 @@
<groupId>javax</groupId> <groupId>javax</groupId>
<artifactId>javaee-api</artifactId> <artifactId>javaee-api</artifactId>
<version>7.0</version> <version>7.0</version>
<scope>test</scope>
</dependency> </dependency>
<!-- rest api --> <!-- rest api -->

View File

@@ -0,0 +1,20 @@
package sonia.scm;
import com.google.common.collect.ImmutableMap;
import com.google.inject.servlet.ServletModule;
import org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher;
import org.jboss.resteasy.plugins.server.servlet.ResteasyContextParameters;
import javax.inject.Singleton;
import java.util.Map;
public class ResteasyModule extends ServletModule {
@Override
protected void configureServlets() {
bind(HttpServletDispatcher.class).in(Singleton.class);
Map<String, String> initParams = ImmutableMap.of(ResteasyContextParameters.RESTEASY_SERVLET_MAPPING_PREFIX, "/api");
serve("/api/*").with(HttpServletDispatcher.class, initParams);
}
}

View File

@@ -126,6 +126,7 @@ public class ScmContextListener extends GuiceResteasyBootstrapServletContextList
ClassOverrides overrides = ClassOverrides.findOverrides(pluginLoader.getUberClassLoader()); ClassOverrides overrides = ClassOverrides.findOverrides(pluginLoader.getUberClassLoader());
List<Module> moduleList = Lists.newArrayList(); List<Module> moduleList = Lists.newArrayList();
moduleList.add(new ResteasyModule());
moduleList.add(new ScmInitializerModule()); moduleList.add(new ScmInitializerModule());
moduleList.add(new ScmEventBusModule()); moduleList.add(new ScmEventBusModule());
moduleList.add(new EagerSingletonModule()); moduleList.add(new EagerSingletonModule());

View File

@@ -57,5 +57,9 @@ public class TemplatingPushStateDispatcher implements PushStateDispatcher {
return request.getContextPath(); return request.getContextPath();
} }
public String getLiveReloadURL() {
return System.getProperty("livereload.url");
}
} }
} }

View File

@@ -0,0 +1,43 @@
package sonia.scm.api;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import sonia.scm.api.v2.resources.ErrorDto;
import sonia.scm.api.v2.resources.ExceptionWithContextToErrorDtoMapper;
import sonia.scm.web.VndMediaType;
import javax.inject.Inject;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import java.util.Collections;
@Provider
public class FallbackExceptionMapper implements ExceptionMapper<Exception> {
private static final Logger logger = LoggerFactory.getLogger(FallbackExceptionMapper.class);
private static final String ERROR_CODE = "CmR8GCJb31";
private final ExceptionWithContextToErrorDtoMapper mapper;
@Inject
public FallbackExceptionMapper(ExceptionWithContextToErrorDtoMapper mapper) {
this.mapper = mapper;
}
@Override
public Response toResponse(Exception exception) {
logger.debug("map {} to status code 500", exception);
ErrorDto errorDto = new ErrorDto();
errorDto.setMessage("internal server error");
errorDto.setContext(Collections.emptyList());
errorDto.setErrorCode(ERROR_CODE);
errorDto.setTransactionId(MDC.get("transaction_id"));
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(errorDto)
.type(VndMediaType.ERROR_TYPE)
.build();
}
}

View File

@@ -13,7 +13,7 @@ public class ContextualExceptionMapper<E extends ExceptionWithContext> implement
private static final Logger logger = LoggerFactory.getLogger(ContextualExceptionMapper.class); private static final Logger logger = LoggerFactory.getLogger(ContextualExceptionMapper.class);
private ExceptionWithContextToErrorDtoMapper mapper; private final ExceptionWithContextToErrorDtoMapper mapper;
private final Response.Status status; private final Response.Status status;
private final Class<E> type; private final Class<E> type;

View File

@@ -0,0 +1,17 @@
package sonia.scm.api.v2;
import sonia.scm.NotSupportedFeatureException;
import sonia.scm.api.rest.ContextualExceptionMapper;
import sonia.scm.api.v2.resources.ExceptionWithContextToErrorDtoMapper;
import javax.inject.Inject;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
@Provider
public class NotSupportedFeatureExceptionMapper extends ContextualExceptionMapper<NotSupportedFeatureException> {
@Inject
public NotSupportedFeatureExceptionMapper(ExceptionWithContextToErrorDtoMapper mapper) {
super(NotSupportedFeatureException.class, Response.Status.BAD_REQUEST, mapper);
}
}

Some files were not shown because too many files have changed in this diff Show More