mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-07 05:55:44 +01:00
Merged 2.0.0-m3
This commit is contained in:
2
Jenkinsfile
vendored
2
Jenkinsfile
vendored
@@ -74,7 +74,7 @@ Maven setupMavenBuild() {
|
|||||||
|
|
||||||
void analyzeWith(Maven mvn) {
|
void analyzeWith(Maven mvn) {
|
||||||
|
|
||||||
withSonarQubeEnv('sonarcloud.io') {
|
withSonarQubeEnv('sonarcloud.io-scm') {
|
||||||
|
|
||||||
String mvnArgs = "${env.SONAR_MAVEN_GOAL} " +
|
String mvnArgs = "${env.SONAR_MAVEN_GOAL} " +
|
||||||
"-Dsonar.host.url=${env.SONAR_HOST_URL} " +
|
"-Dsonar.host.url=${env.SONAR_HOST_URL} " +
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
package sonia.scm;
|
package sonia.scm;
|
||||||
|
|
||||||
public class AlreadyExistsException extends Exception {
|
public class AlreadyExistsException extends Exception {
|
||||||
|
|
||||||
|
public AlreadyExistsException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AlreadyExistsException() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,4 +7,9 @@ public class NotFoundException extends Exception {
|
|||||||
|
|
||||||
public NotFoundException() {
|
public NotFoundException() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public NotFoundException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,11 +33,9 @@
|
|||||||
|
|
||||||
package sonia.scm.repository;
|
package sonia.scm.repository;
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import sonia.scm.BasicPropertiesAware;
|
import sonia.scm.BasicPropertiesAware;
|
||||||
import sonia.scm.Validateable;
|
import sonia.scm.ModelObject;
|
||||||
import sonia.scm.util.Util;
|
import sonia.scm.util.Util;
|
||||||
import sonia.scm.util.ValidationUtil;
|
import sonia.scm.util.ValidationUtil;
|
||||||
|
|
||||||
@@ -45,12 +43,10 @@ import javax.xml.bind.annotation.XmlAccessType;
|
|||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
import javax.xml.bind.annotation.XmlElement;
|
import javax.xml.bind.annotation.XmlElement;
|
||||||
import javax.xml.bind.annotation.XmlRootElement;
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a changeset/commit of a repository.
|
* Represents a changeset/commit of a repository.
|
||||||
@@ -59,43 +55,58 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
@XmlRootElement(name = "changeset")
|
@XmlRootElement(name = "changeset")
|
||||||
@XmlAccessorType(XmlAccessType.FIELD)
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
public class Changeset extends BasicPropertiesAware
|
public class Changeset extends BasicPropertiesAware implements ModelObject {
|
||||||
implements Validateable, Serializable
|
|
||||||
{
|
|
||||||
|
|
||||||
/** Field description */
|
|
||||||
private static final long serialVersionUID = -8373308448928993039L;
|
private static final long serialVersionUID = -8373308448928993039L;
|
||||||
|
|
||||||
//~--- constructors ---------------------------------------------------------
|
/**
|
||||||
|
* The author of the changeset
|
||||||
|
*/
|
||||||
|
private Person author;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new instance of changeset.
|
* The name of the branches on which the changeset was committed.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
private List<String> branches;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The date when the changeset was committed
|
||||||
|
*/
|
||||||
|
private Long date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The text of the changeset description
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The changeset identification string
|
||||||
|
*/
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of files changed by this changeset
|
||||||
|
*/
|
||||||
|
@XmlElement(name = "modifications")
|
||||||
|
private Modifications modifications;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* parent changeset ids
|
||||||
|
*/
|
||||||
|
private List<String> parents;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The tags associated with the changeset
|
||||||
|
*/
|
||||||
|
private List<String> tags;
|
||||||
|
|
||||||
public Changeset() {}
|
public Changeset() {}
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new instance of changeset.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param id id of the changeset
|
|
||||||
* @param date date of the changeset
|
|
||||||
* @param author author of the changeset
|
|
||||||
*/
|
|
||||||
public Changeset(String id, Long date, Person author)
|
public Changeset(String id, Long date, Person author)
|
||||||
{
|
{
|
||||||
this(id, date, author, null);
|
this(id, date, author, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new instance of changeset.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param id id of the changeset
|
|
||||||
* @param date date of the changeset
|
|
||||||
* @param author author of the changeset
|
|
||||||
* @param description description of the changeset
|
|
||||||
*/
|
|
||||||
public Changeset(String id, Long date, Person author, String description)
|
public Changeset(String id, Long date, Person author, String description)
|
||||||
{
|
{
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@@ -104,16 +115,6 @@ public class Changeset extends BasicPropertiesAware
|
|||||||
this.description = description;
|
this.description = description;
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- methods --------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param obj
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj)
|
public boolean equals(Object obj)
|
||||||
{
|
{
|
||||||
@@ -122,8 +123,7 @@ public class Changeset extends BasicPropertiesAware
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getClass() != obj.getClass())
|
if (getClass() != obj.getClass()) {
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,7 +192,21 @@ public class Changeset extends BasicPropertiesAware
|
|||||||
return out.toString();
|
return out.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- get methods ----------------------------------------------------------
|
|
||||||
|
/**
|
||||||
|
* Returns a timestamp of the creation date of the {@link Changeset}.
|
||||||
|
*
|
||||||
|
* @return a timestamp of the creation date of the {@link Changeset}
|
||||||
|
*/
|
||||||
|
public Long getCreationDate() {
|
||||||
|
return getDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCreationDate(Long timestamp) {
|
||||||
|
this.setDate(timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the author of the changeset.
|
* Returns the author of the changeset.
|
||||||
@@ -200,8 +214,7 @@ public class Changeset extends BasicPropertiesAware
|
|||||||
*
|
*
|
||||||
* @return author of the changeset
|
* @return author of the changeset
|
||||||
*/
|
*/
|
||||||
public Person getAuthor()
|
public Person getAuthor() {
|
||||||
{
|
|
||||||
return author;
|
return author;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,11 +264,27 @@ public class Changeset extends BasicPropertiesAware
|
|||||||
*
|
*
|
||||||
* @return id of the changeset
|
* @return id of the changeset
|
||||||
*/
|
*/
|
||||||
public String getId()
|
@Override
|
||||||
{
|
public String getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLastModified(Long timestamp) {
|
||||||
|
throw new UnsupportedOperationException("changesets are immutable");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getLastModified() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return "Changeset";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the file modifications, which was done with this changeset.
|
* Returns the file modifications, which was done with this changeset.
|
||||||
*
|
*
|
||||||
@@ -318,8 +347,6 @@ public class Changeset extends BasicPropertiesAware
|
|||||||
&& (date != null);
|
&& (date != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- set methods ----------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the author of the changeset.
|
* Sets the author of the changeset.
|
||||||
*
|
*
|
||||||
@@ -409,30 +436,4 @@ public class Changeset extends BasicPropertiesAware
|
|||||||
this.tags = tags;
|
this.tags = tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- fields ---------------------------------------------------------------
|
|
||||||
|
|
||||||
/** The author of the changeset */
|
|
||||||
private Person author;
|
|
||||||
|
|
||||||
/** The name of the branches on which the changeset was committed. */
|
|
||||||
private List<String> branches;
|
|
||||||
|
|
||||||
/** The date when the changeset was committed */
|
|
||||||
private Long date;
|
|
||||||
|
|
||||||
/** The text of the changeset description */
|
|
||||||
private String description;
|
|
||||||
|
|
||||||
/** The changeset identification string */
|
|
||||||
private String id;
|
|
||||||
|
|
||||||
/** List of files changed by this changeset */
|
|
||||||
@XmlElement(name = "modifications")
|
|
||||||
private Modifications modifications;
|
|
||||||
|
|
||||||
/** parent changeset ids */
|
|
||||||
private List<String> parents;
|
|
||||||
|
|
||||||
/** The tags associated with the changeset */
|
|
||||||
private List<String> tags;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -249,9 +249,11 @@ public class Person implements Validateable, Serializable
|
|||||||
|
|
||||||
//~--- fields ---------------------------------------------------------------
|
//~--- fields ---------------------------------------------------------------
|
||||||
|
|
||||||
/** name of the person */
|
/** mail address of the person */
|
||||||
private String mail;
|
private String mail;
|
||||||
|
|
||||||
/** mail address of the person */
|
/**
|
||||||
|
* name of the person
|
||||||
|
*/
|
||||||
private String name;
|
private String name;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ public final class BundleCommandBuilder
|
|||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
checkNotNull(sink, "byte sink is required");
|
checkNotNull(sink, "byte sink is required");
|
||||||
logger.info("bundle {} to byte sink");
|
logger.info("bundle {} to byte sink", sink);
|
||||||
|
|
||||||
return bundleCommand.bundle(new BundleCommandRequest(sink));
|
return bundleCommand.bundle(new BundleCommandRequest(sink));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ public class VndMediaType {
|
|||||||
public static final String GROUP = PREFIX + "group" + SUFFIX;
|
public static final String GROUP = PREFIX + "group" + SUFFIX;
|
||||||
public static final String REPOSITORY = PREFIX + "repository" + SUFFIX;
|
public static final String REPOSITORY = PREFIX + "repository" + SUFFIX;
|
||||||
public static final String PERMISSION = PREFIX + "permission" + SUFFIX;
|
public static final String PERMISSION = PREFIX + "permission" + SUFFIX;
|
||||||
|
public static final String CHANGESET = PREFIX + "changeset" + SUFFIX;
|
||||||
|
public static final String CHANGESET_COLLECTION = PREFIX + "changesetCollection" + SUFFIX;
|
||||||
|
public static final String TAG = PREFIX + "tag" + SUFFIX;
|
||||||
|
public static final String TAG_COLLECTION = PREFIX + "tagCollection" + SUFFIX;
|
||||||
public static final String BRANCH = PREFIX + "branch" + SUFFIX;
|
public static final String BRANCH = PREFIX + "branch" + SUFFIX;
|
||||||
public static final String USER_COLLECTION = PREFIX + "userCollection" + SUFFIX;
|
public static final String USER_COLLECTION = PREFIX + "userCollection" + SUFFIX;
|
||||||
public static final String GROUP_COLLECTION = PREFIX + "groupCollection" + SUFFIX;
|
public static final String GROUP_COLLECTION = PREFIX + "groupCollection" + SUFFIX;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package sonia.scm.it;
|
package sonia.scm.it;
|
||||||
|
|
||||||
|
import io.restassured.response.ExtractableResponse;
|
||||||
|
import io.restassured.response.Response;
|
||||||
import org.apache.http.HttpStatus;
|
import org.apache.http.HttpStatus;
|
||||||
import org.junit.Assume;
|
import org.junit.Assume;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -8,16 +10,22 @@ import org.junit.Test;
|
|||||||
import org.junit.rules.TemporaryFolder;
|
import org.junit.rules.TemporaryFolder;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.Parameterized;
|
import org.junit.runners.Parameterized;
|
||||||
|
import sonia.scm.repository.Changeset;
|
||||||
import sonia.scm.repository.client.api.ClientCommand;
|
import sonia.scm.repository.client.api.ClientCommand;
|
||||||
import sonia.scm.repository.client.api.RepositoryClient;
|
import sonia.scm.repository.client.api.RepositoryClient;
|
||||||
|
import sonia.scm.web.VndMediaType;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static java.lang.Thread.sleep;
|
import static java.lang.Thread.sleep;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static sonia.scm.it.RestUtil.ADMIN_PASSWORD;
|
||||||
|
import static sonia.scm.it.RestUtil.ADMIN_USERNAME;
|
||||||
import static sonia.scm.it.RestUtil.given;
|
import static sonia.scm.it.RestUtil.given;
|
||||||
import static sonia.scm.it.ScmTypes.availableScmTypes;
|
import static sonia.scm.it.ScmTypes.availableScmTypes;
|
||||||
|
|
||||||
@@ -72,6 +80,85 @@ public class RepositoryAccessITCase {
|
|||||||
assertNotNull(branchName);
|
assertNotNull(branchName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldFindTags() throws IOException {
|
||||||
|
RepositoryClient repositoryClient = RepositoryUtil.createRepositoryClient(repositoryType, folder);
|
||||||
|
|
||||||
|
Assume.assumeTrue("There are no tags for " + repositoryType, repositoryClient.isCommandSupported(ClientCommand.TAG));
|
||||||
|
|
||||||
|
Changeset changeset = RepositoryUtil.createAndCommitFile(repositoryClient, ADMIN_USERNAME, "a.txt", "a");
|
||||||
|
|
||||||
|
String tagName = "v1.0";
|
||||||
|
String repositoryUrl = TestData.getDefaultRepositoryUrl(repositoryType);
|
||||||
|
String tagsUrl = given()
|
||||||
|
.when()
|
||||||
|
.get(repositoryUrl)
|
||||||
|
.then()
|
||||||
|
.statusCode(HttpStatus.SC_OK)
|
||||||
|
.extract()
|
||||||
|
.path("_links.tags.href");
|
||||||
|
|
||||||
|
ExtractableResponse<Response> response = given(VndMediaType.TAG_COLLECTION, ADMIN_USERNAME, ADMIN_PASSWORD)
|
||||||
|
.when()
|
||||||
|
.get(tagsUrl)
|
||||||
|
.then()
|
||||||
|
.statusCode(HttpStatus.SC_OK)
|
||||||
|
.extract();
|
||||||
|
|
||||||
|
assertThat(response).isNotNull();
|
||||||
|
assertThat(response.body()).isNotNull();
|
||||||
|
assertThat(response.body().asString())
|
||||||
|
.isNotNull()
|
||||||
|
.isNotBlank();
|
||||||
|
|
||||||
|
RepositoryUtil.addTag(repositoryClient, changeset.getId(), tagName);
|
||||||
|
response = given(VndMediaType.TAG_COLLECTION, ADMIN_USERNAME, ADMIN_PASSWORD)
|
||||||
|
.when()
|
||||||
|
.get(tagsUrl)
|
||||||
|
.then()
|
||||||
|
.statusCode(HttpStatus.SC_OK)
|
||||||
|
.extract();
|
||||||
|
|
||||||
|
assertThat(response).isNotNull();
|
||||||
|
assertThat(response.body()).isNotNull();
|
||||||
|
assertThat(response.body().asString())
|
||||||
|
.isNotNull()
|
||||||
|
.isNotBlank();
|
||||||
|
|
||||||
|
assertThat(response.body().jsonPath().getString("_links.self.href"))
|
||||||
|
.as("assert tags self link")
|
||||||
|
.isNotNull()
|
||||||
|
.contains(repositoryUrl + "/tags/");
|
||||||
|
|
||||||
|
assertThat(response.body().jsonPath().getList("_embedded.tags"))
|
||||||
|
.as("assert tag size")
|
||||||
|
.isNotNull()
|
||||||
|
.size()
|
||||||
|
.isGreaterThan(0);
|
||||||
|
|
||||||
|
assertThat(response.body().jsonPath().getMap("_embedded.tags.find{it.name=='" + tagName + "'}"))
|
||||||
|
.as("assert tag name and revision")
|
||||||
|
.isNotNull()
|
||||||
|
.hasSize(3)
|
||||||
|
.containsEntry("name", tagName)
|
||||||
|
.containsEntry("revision", changeset.getId());
|
||||||
|
|
||||||
|
assertThat(response.body().jsonPath().getString("_embedded.tags.find{it.name=='" + tagName + "'}._links.self.href"))
|
||||||
|
.as("assert single tag self link")
|
||||||
|
.isNotNull()
|
||||||
|
.contains(String.format("%s/tags/%s", repositoryUrl, tagName));
|
||||||
|
|
||||||
|
assertThat(response.body().jsonPath().getString("_embedded.tags.find{it.name=='" + tagName + "'}._links.sources.href"))
|
||||||
|
.as("assert single tag source link")
|
||||||
|
.isNotNull()
|
||||||
|
.contains(String.format("%s/sources/%s", repositoryUrl, changeset.getId()));
|
||||||
|
|
||||||
|
assertThat(response.body().jsonPath().getString("_embedded.tags.find{it.name=='" + tagName + "'}._links.changesets.href"))
|
||||||
|
.as("assert single tag changesets link")
|
||||||
|
.isNotNull()
|
||||||
|
.contains(String.format("%s/changesets/%s", repositoryUrl, changeset.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldReadContent() throws IOException, InterruptedException {
|
public void shouldReadContent() throws IOException, InterruptedException {
|
||||||
RepositoryClient repositoryClient = RepositoryUtil.createRepositoryClient(repositoryType, folder);
|
RepositoryClient repositoryClient = RepositoryUtil.createRepositoryClient(repositoryType, folder);
|
||||||
@@ -95,7 +182,8 @@ public class RepositoryAccessITCase {
|
|||||||
.then()
|
.then()
|
||||||
.statusCode(HttpStatus.SC_OK)
|
.statusCode(HttpStatus.SC_OK)
|
||||||
.extract()
|
.extract()
|
||||||
.path("files.find{it.name=='a.txt'}._links.self.href");
|
.path("_embedded.files.find{it.name=='a.txt'}._links.self.href");
|
||||||
|
|
||||||
given()
|
given()
|
||||||
.when()
|
.when()
|
||||||
.get(rootContentUrl)
|
.get(rootContentUrl)
|
||||||
@@ -109,14 +197,22 @@ public class RepositoryAccessITCase {
|
|||||||
.then()
|
.then()
|
||||||
.statusCode(HttpStatus.SC_OK)
|
.statusCode(HttpStatus.SC_OK)
|
||||||
.extract()
|
.extract()
|
||||||
.path("files.find{it.name=='subfolder'}._links.self.href");
|
.path("_embedded.files.find{it.name=='subfolder'}._links.self.href");
|
||||||
String subfolderContentUrl= given()
|
String selfOfSubfolderUrl = given()
|
||||||
.when()
|
.when()
|
||||||
.get(subfolderSourceUrl)
|
.get(subfolderSourceUrl)
|
||||||
.then()
|
.then()
|
||||||
.statusCode(HttpStatus.SC_OK)
|
.statusCode(HttpStatus.SC_OK)
|
||||||
.extract()
|
.extract()
|
||||||
.path("files[0]._links.self.href");
|
.path("_links.self.href");
|
||||||
|
assertThat(subfolderSourceUrl).isEqualTo(selfOfSubfolderUrl);
|
||||||
|
String subfolderContentUrl = given()
|
||||||
|
.when()
|
||||||
|
.get(subfolderSourceUrl)
|
||||||
|
.then()
|
||||||
|
.statusCode(HttpStatus.SC_OK)
|
||||||
|
.extract()
|
||||||
|
.path("_embedded.files[0]._links.self.href");
|
||||||
given()
|
given()
|
||||||
.when()
|
.when()
|
||||||
.get(subfolderContentUrl)
|
.get(subfolderContentUrl)
|
||||||
@@ -124,4 +220,31 @@ public class RepositoryAccessITCase {
|
|||||||
.statusCode(HttpStatus.SC_OK)
|
.statusCode(HttpStatus.SC_OK)
|
||||||
.body(equalTo("sub-a"));
|
.body(equalTo("sub-a"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldFindChangesets() 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");
|
||||||
|
|
||||||
|
List changesets = given()
|
||||||
|
.when()
|
||||||
|
.get(changesetsUrl)
|
||||||
|
.then()
|
||||||
|
.statusCode(HttpStatus.SC_OK)
|
||||||
|
.extract()
|
||||||
|
.path("_embedded.changesets.id");
|
||||||
|
|
||||||
|
assertThat(changesets).size().isBetween(2, 3); // svn has an implicit root revision '0' that is extra to the two commits
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,11 @@ package sonia.scm.it;
|
|||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
import org.apache.http.HttpStatus;
|
import org.apache.http.HttpStatus;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import sonia.scm.repository.Changeset;
|
import sonia.scm.repository.Changeset;
|
||||||
import sonia.scm.repository.Person;
|
import sonia.scm.repository.Person;
|
||||||
|
import sonia.scm.repository.Tag;
|
||||||
import sonia.scm.repository.client.api.ClientCommand;
|
import sonia.scm.repository.client.api.ClientCommand;
|
||||||
import sonia.scm.repository.client.api.RepositoryClient;
|
import sonia.scm.repository.client.api.RepositoryClient;
|
||||||
import sonia.scm.repository.client.api.RepositoryClientFactory;
|
import sonia.scm.repository.client.api.RepositoryClientFactory;
|
||||||
@@ -15,6 +18,8 @@ import java.util.UUID;
|
|||||||
|
|
||||||
public class RepositoryUtil {
|
public class RepositoryUtil {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(RepositoryUtil.class);
|
||||||
|
|
||||||
private static final RepositoryClientFactory REPOSITORY_CLIENT_FACTORY = new RepositoryClientFactory();
|
private static final RepositoryClientFactory REPOSITORY_CLIENT_FACTORY = new RepositoryClientFactory();
|
||||||
|
|
||||||
static RepositoryClient createRepositoryClient(String repositoryType, File folder) throws IOException {
|
static RepositoryClient createRepositoryClient(String repositoryType, File folder) throws IOException {
|
||||||
@@ -36,11 +41,11 @@ public class RepositoryUtil {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void createAndCommitFile(RepositoryClient repositoryClient, String username, String fileName, String content) throws IOException {
|
static Changeset createAndCommitFile(RepositoryClient repositoryClient, String username, String fileName, String content) throws IOException {
|
||||||
File file = new File(repositoryClient.getWorkingCopy(), fileName);
|
File file = new File(repositoryClient.getWorkingCopy(), fileName);
|
||||||
Files.write(content, file, Charsets.UTF_8);
|
Files.write(content, file, Charsets.UTF_8);
|
||||||
addWithParentDirectories(repositoryClient, file);
|
addWithParentDirectories(repositoryClient, file);
|
||||||
commit(repositoryClient, username, "added " + fileName);
|
return commit(repositoryClient, username, "added " + fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String addWithParentDirectories(RepositoryClient repositoryClient, File file) throws IOException {
|
private static String addWithParentDirectories(RepositoryClient repositoryClient, File file) throws IOException {
|
||||||
@@ -58,10 +63,23 @@ public class RepositoryUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Changeset commit(RepositoryClient repositoryClient, String username, String message) throws IOException {
|
static Changeset commit(RepositoryClient repositoryClient, String username, String message) throws IOException {
|
||||||
|
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)) {
|
||||||
repositoryClient.getPushCommand().push();
|
repositoryClient.getPushCommand().push();
|
||||||
}
|
}
|
||||||
return changeset;
|
return changeset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Tag addTag(RepositoryClient repositoryClient, String revision, String tagName) throws IOException {
|
||||||
|
if (repositoryClient.isCommandSupported(ClientCommand.TAG)) {
|
||||||
|
Tag tag = repositoryClient.getTagCommand().setRevision(revision).tag(tagName, TestData.USER_SCM_ADMIN);
|
||||||
|
if (repositoryClient.isCommandSupported(ClientCommand.PUSH)) {
|
||||||
|
repositoryClient.getPushCommand().pushTags();
|
||||||
|
}
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ public class TestData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void cleanup() {
|
public static void cleanup() {
|
||||||
|
LOG.info("start to clean up to integration tests");
|
||||||
cleanupRepositories();
|
cleanupRepositories();
|
||||||
cleanupGroups();
|
cleanupGroups();
|
||||||
cleanupUsers();
|
cleanupUsers();
|
||||||
@@ -43,6 +44,7 @@ public class TestData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void createUser(String username, String password) {
|
public static void createUser(String username, String password) {
|
||||||
|
LOG.info("create user with username: {}", username);
|
||||||
given(VndMediaType.USER)
|
given(VndMediaType.USER)
|
||||||
.when()
|
.when()
|
||||||
.content(" {\n" +
|
.content(" {\n" +
|
||||||
@@ -64,6 +66,8 @@ public class TestData {
|
|||||||
|
|
||||||
|
|
||||||
public static void createUserPermission(String name, PermissionType permissionType, String repositoryType) {
|
public static void createUserPermission(String name, PermissionType permissionType, String repositoryType) {
|
||||||
|
String defaultPermissionUrl = TestData.getDefaultPermissionUrl(USER_SCM_ADMIN, USER_SCM_ADMIN, repositoryType);
|
||||||
|
LOG.info("create permission with name {} and type: {} using the endpoint: {}", name, permissionType, defaultPermissionUrl);
|
||||||
given(VndMediaType.PERMISSION)
|
given(VndMediaType.PERMISSION)
|
||||||
.when()
|
.when()
|
||||||
.content("{\n" +
|
.content("{\n" +
|
||||||
@@ -72,7 +76,7 @@ public class TestData {
|
|||||||
"\t\"groupPermission\": false\n" +
|
"\t\"groupPermission\": false\n" +
|
||||||
"\t\n" +
|
"\t\n" +
|
||||||
"}")
|
"}")
|
||||||
.post(TestData.getDefaultPermissionUrl(USER_SCM_ADMIN, USER_SCM_ADMIN, repositoryType))
|
.post(defaultPermissionUrl)
|
||||||
.then()
|
.then()
|
||||||
.statusCode(HttpStatus.SC_CREATED)
|
.statusCode(HttpStatus.SC_CREATED)
|
||||||
;
|
;
|
||||||
@@ -114,6 +118,7 @@ public class TestData {
|
|||||||
|
|
||||||
|
|
||||||
private static void cleanupRepositories() {
|
private static void cleanupRepositories() {
|
||||||
|
LOG.info("clean up repository");
|
||||||
List<String> repositories = given(VndMediaType.REPOSITORY_COLLECTION)
|
List<String> repositories = given(VndMediaType.REPOSITORY_COLLECTION)
|
||||||
.when()
|
.when()
|
||||||
.get(createResourceUrl("repositories"))
|
.get(createResourceUrl("repositories"))
|
||||||
@@ -160,6 +165,7 @@ public class TestData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void createDefaultRepositories() {
|
private static void createDefaultRepositories() {
|
||||||
|
LOG.info("create default repositories");
|
||||||
for (String repositoryType : availableScmTypes()) {
|
for (String repositoryType : availableScmTypes()) {
|
||||||
String url = given(VndMediaType.REPOSITORY)
|
String url = given(VndMediaType.REPOSITORY)
|
||||||
.body(repositoryJson(repositoryType))
|
.body(repositoryJson(repositoryType))
|
||||||
@@ -171,6 +177,7 @@ public class TestData {
|
|||||||
.statusCode(HttpStatus.SC_CREATED)
|
.statusCode(HttpStatus.SC_CREATED)
|
||||||
.extract()
|
.extract()
|
||||||
.header("location");
|
.header("location");
|
||||||
|
LOG.info("a {} repository is created: {}", repositoryType, url);
|
||||||
DEFAULT_REPOSITORIES.put(repositoryType, url);
|
DEFAULT_REPOSITORIES.put(repositoryType, url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
/**
|
/**
|
||||||
* 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,
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
* 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,96 +24,49 @@
|
|||||||
* 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;
|
package sonia.scm.repository;
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
|
||||||
|
|
||||||
import sonia.scm.util.Util;
|
import sonia.scm.util.Util;
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
*/
|
*/
|
||||||
public final class GitSubModuleParser
|
public final class GitSubModuleParser {
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
private GitSubModuleParser() {
|
||||||
* Constructs ...
|
}
|
||||||
*
|
|
||||||
*/
|
|
||||||
private GitSubModuleParser() {}
|
|
||||||
|
|
||||||
//~--- methods --------------------------------------------------------------
|
public static Map<String, SubRepository> parse(String content) {
|
||||||
|
Map<String, SubRepository> subRepositories = new HashMap<>();
|
||||||
/**
|
try (Scanner scanner = new Scanner(content)) {
|
||||||
* //~--- methods --------------------------------------------------------------
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param content
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static Map<String, SubRepository> parse(String content)
|
|
||||||
{
|
|
||||||
Map<String, SubRepository> subRepositories = new HashMap<String,
|
|
||||||
SubRepository>();
|
|
||||||
Scanner scanner = new Scanner(content);
|
|
||||||
SubRepository repository = null;
|
SubRepository repository = null;
|
||||||
|
while (scanner.hasNextLine()) {
|
||||||
while (scanner.hasNextLine())
|
|
||||||
{
|
|
||||||
String line = scanner.nextLine();
|
String line = scanner.nextLine();
|
||||||
|
if (Util.isNotEmpty(line)) {
|
||||||
if (Util.isNotEmpty(line))
|
|
||||||
{
|
|
||||||
line = line.trim();
|
line = line.trim();
|
||||||
|
if (line.startsWith("[") && line.endsWith("]")) {
|
||||||
if (line.startsWith("[") && line.endsWith("]"))
|
|
||||||
{
|
|
||||||
repository = new SubRepository();
|
repository = new SubRepository();
|
||||||
}
|
} else if (line.startsWith("path")) {
|
||||||
else if (line.startsWith("path"))
|
|
||||||
{
|
|
||||||
subRepositories.put(getValue(line), repository);
|
subRepositories.put(getValue(line), repository);
|
||||||
}
|
} else if (line.startsWith("url")) {
|
||||||
else if (line.startsWith("url"))
|
|
||||||
{
|
|
||||||
repository.setRepositoryUrl(getValue(line));
|
repository.setRepositoryUrl(getValue(line));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return subRepositories;
|
return subRepositories;
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- get methods ----------------------------------------------------------
|
private static String getValue(String line) {
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param line
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private static String getValue(String line)
|
|
||||||
{
|
|
||||||
return line.split("=")[1].trim();
|
return line.split("=")[1].trim();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ package sonia.scm.repository.spi;
|
|||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
import org.eclipse.jgit.errors.MissingObjectException;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
import org.eclipse.jgit.revwalk.RevCommit;
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
@@ -51,6 +52,7 @@ import sonia.scm.repository.ChangesetPagingResult;
|
|||||||
import sonia.scm.repository.GitChangesetConverter;
|
import sonia.scm.repository.GitChangesetConverter;
|
||||||
import sonia.scm.repository.GitUtil;
|
import sonia.scm.repository.GitUtil;
|
||||||
import sonia.scm.repository.InternalRepositoryException;
|
import sonia.scm.repository.InternalRepositoryException;
|
||||||
|
import sonia.scm.repository.RevisionNotFoundException;
|
||||||
import sonia.scm.util.IOUtil;
|
import sonia.scm.util.IOUtil;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -156,15 +158,11 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand
|
|||||||
* @return
|
* @return
|
||||||
*
|
*
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* @throws RepositoryException
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public ChangesetPagingResult getChangesets(LogCommandRequest request)
|
public ChangesetPagingResult getChangesets(LogCommandRequest request) throws RevisionNotFoundException {
|
||||||
throws IOException
|
if (logger.isDebugEnabled()) {
|
||||||
{
|
|
||||||
if (logger.isDebugEnabled())
|
|
||||||
{
|
|
||||||
logger.debug("fetch changesets for request: {}", request);
|
logger.debug("fetch changesets for request: {}", request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,17 +170,13 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand
|
|||||||
GitChangesetConverter converter = null;
|
GitChangesetConverter converter = null;
|
||||||
RevWalk revWalk = null;
|
RevWalk revWalk = null;
|
||||||
|
|
||||||
try (org.eclipse.jgit.lib.Repository gr = open())
|
try (org.eclipse.jgit.lib.Repository gr = open()) {
|
||||||
{
|
if (!gr.getAllRefs().isEmpty()) {
|
||||||
if (!gr.getAllRefs().isEmpty())
|
|
||||||
{
|
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
int start = request.getPagingStart();
|
int start = request.getPagingStart();
|
||||||
|
|
||||||
if (start < 0)
|
if (start < 0) {
|
||||||
{
|
if (logger.isErrorEnabled()) {
|
||||||
if (logger.isErrorEnabled())
|
|
||||||
{
|
|
||||||
logger.error("start parameter is negative, reset to 0");
|
logger.error("start parameter is negative, reset to 0");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,15 +187,13 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand
|
|||||||
int limit = request.getPagingLimit();
|
int limit = request.getPagingLimit();
|
||||||
ObjectId startId = null;
|
ObjectId startId = null;
|
||||||
|
|
||||||
if (!Strings.isNullOrEmpty(request.getStartChangeset()))
|
if (!Strings.isNullOrEmpty(request.getStartChangeset())) {
|
||||||
{
|
|
||||||
startId = gr.resolve(request.getStartChangeset());
|
startId = gr.resolve(request.getStartChangeset());
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectId endId = null;
|
ObjectId endId = null;
|
||||||
|
|
||||||
if (!Strings.isNullOrEmpty(request.getEndChangeset()))
|
if (!Strings.isNullOrEmpty(request.getEndChangeset())) {
|
||||||
{
|
|
||||||
endId = gr.resolve(request.getEndChangeset());
|
endId = gr.resolve(request.getEndChangeset());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,8 +201,7 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand
|
|||||||
|
|
||||||
converter = new GitChangesetConverter(gr, revWalk);
|
converter = new GitChangesetConverter(gr, revWalk);
|
||||||
|
|
||||||
if (!Strings.isNullOrEmpty(request.getPath()))
|
if (!Strings.isNullOrEmpty(request.getPath())) {
|
||||||
{
|
|
||||||
revWalk.setTreeFilter(
|
revWalk.setTreeFilter(
|
||||||
AndTreeFilter.create(
|
AndTreeFilter.create(
|
||||||
PathFilter.create(request.getPath()), TreeFilter.ANY_DIFF));
|
PathFilter.create(request.getPath()), TreeFilter.ANY_DIFF));
|
||||||
@@ -218,48 +209,43 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand
|
|||||||
|
|
||||||
ObjectId head = getBranchOrDefault(gr, request.getBranch());
|
ObjectId head = getBranchOrDefault(gr, request.getBranch());
|
||||||
|
|
||||||
if (head != null)
|
if (head != null) {
|
||||||
{
|
if (startId != null) {
|
||||||
if (startId != null)
|
|
||||||
{
|
|
||||||
revWalk.markStart(revWalk.lookupCommit(startId));
|
revWalk.markStart(revWalk.lookupCommit(startId));
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
revWalk.markStart(revWalk.lookupCommit(head));
|
revWalk.markStart(revWalk.lookupCommit(head));
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator<RevCommit> iterator = revWalk.iterator();
|
Iterator<RevCommit> iterator = revWalk.iterator();
|
||||||
|
|
||||||
while (iterator.hasNext())
|
while (iterator.hasNext()) {
|
||||||
{
|
|
||||||
RevCommit commit = iterator.next();
|
RevCommit commit = iterator.next();
|
||||||
|
|
||||||
if ((counter >= start)
|
if ((counter >= start)
|
||||||
&& ((limit < 0) || (counter < start + limit)))
|
&& ((limit < 0) || (counter < start + limit))) {
|
||||||
{
|
|
||||||
changesetList.add(converter.createChangeset(commit));
|
changesetList.add(converter.createChangeset(commit));
|
||||||
}
|
}
|
||||||
|
|
||||||
counter++;
|
counter++;
|
||||||
|
|
||||||
if ((endId != null) && commit.getId().equals(endId))
|
if ((endId != null) && commit.getId().equals(endId)) {
|
||||||
{
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
changesets = new ChangesetPagingResult(counter, changesetList);
|
changesets = new ChangesetPagingResult(counter, changesetList);
|
||||||
}
|
} else if (logger.isWarnEnabled()) {
|
||||||
else if (logger.isWarnEnabled())
|
|
||||||
{
|
|
||||||
logger.warn("the repository {} seems to be empty",
|
logger.warn("the repository {} seems to be empty",
|
||||||
repository.getName());
|
repository.getName());
|
||||||
|
|
||||||
changesets = new ChangesetPagingResult(0, Collections.EMPTY_LIST);
|
changesets = new ChangesetPagingResult(0, Collections.EMPTY_LIST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (MissingObjectException e)
|
||||||
|
{
|
||||||
|
throw new RevisionNotFoundException(e.getObjectId().name());
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
throw new InternalRepositoryException("could not create change log", ex);
|
throw new InternalRepositoryException("could not create change log", ex);
|
||||||
|
|||||||
@@ -37,12 +37,12 @@ package sonia.scm.repository.client.spi;
|
|||||||
import org.eclipse.jgit.api.Git;
|
import org.eclipse.jgit.api.Git;
|
||||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||||
import org.eclipse.jgit.transport.CredentialsProvider;
|
import org.eclipse.jgit.transport.CredentialsProvider;
|
||||||
|
|
||||||
import sonia.scm.repository.client.api.RepositoryClientException;
|
import sonia.scm.repository.client.api.RepositoryClientException;
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
//~--- JDK imports ------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -73,11 +73,20 @@ public class GitPushCommand implements PushCommand
|
|||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void push() throws IOException
|
public void push() throws IOException {
|
||||||
|
push(() -> git.push().setPushAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pushTags() throws IOException {
|
||||||
|
push(() -> git.push().setPushTags());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void push(Supplier<org.eclipse.jgit.api.PushCommand> commandSupplier) throws RepositoryClientException
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
org.eclipse.jgit.api.PushCommand cmd = git.push().setPushAll();
|
org.eclipse.jgit.api.PushCommand cmd = commandSupplier.get();
|
||||||
|
|
||||||
if (credentialsProvider != null)
|
if (credentialsProvider != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -35,22 +35,20 @@ package sonia.scm.repository.client.spi;
|
|||||||
//~--- non-JDK imports --------------------------------------------------------
|
//~--- non-JDK imports --------------------------------------------------------
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
|
|
||||||
import org.eclipse.jgit.api.Git;
|
import org.eclipse.jgit.api.Git;
|
||||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.Ref;
|
import org.eclipse.jgit.lib.Ref;
|
||||||
import org.eclipse.jgit.revwalk.RevObject;
|
import org.eclipse.jgit.revwalk.RevObject;
|
||||||
import org.eclipse.jgit.revwalk.RevWalk;
|
import org.eclipse.jgit.revwalk.RevWalk;
|
||||||
|
|
||||||
import sonia.scm.repository.GitUtil;
|
import sonia.scm.repository.GitUtil;
|
||||||
import sonia.scm.repository.Tag;
|
import sonia.scm.repository.Tag;
|
||||||
import sonia.scm.repository.client.api.RepositoryClientException;
|
import sonia.scm.repository.client.api.RepositoryClientException;
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
//~--- JDK imports ------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
@@ -119,7 +117,11 @@ public class GitTagCommand implements TagCommand
|
|||||||
ref = git.tag().setObjectId(revObject).call();
|
ref = git.tag().setObjectId(revObject).call();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ref.isPeeled()) {
|
||||||
tag = new Tag(request.getName(), ref.getPeeledObjectId().toString());
|
tag = new Tag(request.getName(), ref.getPeeledObjectId().toString());
|
||||||
|
} else {
|
||||||
|
tag = new Tag(request.getName(), ref.getObjectId().toString());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (GitAPIException ex)
|
catch (GitAPIException ex)
|
||||||
|
|||||||
@@ -35,15 +35,12 @@ package sonia.scm.repository.spi;
|
|||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
//~--- non-JDK imports --------------------------------------------------------
|
||||||
|
|
||||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import sonia.scm.repository.Changeset;
|
import sonia.scm.repository.Changeset;
|
||||||
import sonia.scm.repository.ChangesetPagingResult;
|
import sonia.scm.repository.ChangesetPagingResult;
|
||||||
import sonia.scm.repository.GitConstants;
|
import sonia.scm.repository.GitConstants;
|
||||||
import sonia.scm.repository.Modifications;
|
import sonia.scm.repository.Modifications;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.contains;
|
import static org.hamcrest.Matchers.contains;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
@@ -63,13 +60,9 @@ public class GitLogCommandTest extends AbstractGitCommandTestBase
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests log command with the usage of a default branch.
|
* Tests log command with the usage of a default branch.
|
||||||
*
|
|
||||||
* @throws IOException
|
|
||||||
* @throws GitAPIException
|
|
||||||
* @
|
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testGetDefaultBranch() throws IOException, GitAPIException {
|
public void testGetDefaultBranch() throws Exception {
|
||||||
// without default branch, the repository head should be used
|
// without default branch, the repository head should be used
|
||||||
ChangesetPagingResult result = createCommand().getChangesets(new LogCommandRequest());
|
ChangesetPagingResult result = createCommand().getChangesets(new LogCommandRequest());
|
||||||
|
|
||||||
@@ -92,15 +85,8 @@ public class GitLogCommandTest extends AbstractGitCommandTestBase
|
|||||||
assertEquals("435df2f061add3589cb326cc64be9b9c3897ceca", result.getChangesets().get(2).getId());
|
assertEquals("435df2f061add3589cb326cc64be9b9c3897ceca", result.getChangesets().get(2).getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @throws IOException
|
|
||||||
* @throws RepositoryException
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetAll() throws IOException
|
public void testGetAll() throws Exception
|
||||||
{
|
{
|
||||||
ChangesetPagingResult result =
|
ChangesetPagingResult result =
|
||||||
createCommand().getChangesets(new LogCommandRequest());
|
createCommand().getChangesets(new LogCommandRequest());
|
||||||
@@ -110,15 +96,8 @@ public class GitLogCommandTest extends AbstractGitCommandTestBase
|
|||||||
assertEquals(4, result.getChangesets().size());
|
assertEquals(4, result.getChangesets().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @throws IOException
|
|
||||||
* @throws RepositoryException
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetAllByPath() throws IOException
|
public void testGetAllByPath() throws Exception
|
||||||
{
|
{
|
||||||
LogCommandRequest request = new LogCommandRequest();
|
LogCommandRequest request = new LogCommandRequest();
|
||||||
|
|
||||||
@@ -133,15 +112,8 @@ public class GitLogCommandTest extends AbstractGitCommandTestBase
|
|||||||
assertEquals("435df2f061add3589cb326cc64be9b9c3897ceca", result.getChangesets().get(1).getId());
|
assertEquals("435df2f061add3589cb326cc64be9b9c3897ceca", result.getChangesets().get(1).getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @throws IOException
|
|
||||||
* @throws RepositoryException
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetAllWithLimit() throws IOException
|
public void testGetAllWithLimit() throws Exception
|
||||||
{
|
{
|
||||||
LogCommandRequest request = new LogCommandRequest();
|
LogCommandRequest request = new LogCommandRequest();
|
||||||
|
|
||||||
@@ -164,15 +136,8 @@ public class GitLogCommandTest extends AbstractGitCommandTestBase
|
|||||||
assertEquals("86a6645eceefe8b9a247db5eb16e3d89a7e6e6d1", c2.getId());
|
assertEquals("86a6645eceefe8b9a247db5eb16e3d89a7e6e6d1", c2.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @throws IOException
|
|
||||||
* @throws RepositoryException
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetAllWithPaging() throws IOException
|
public void testGetAllWithPaging() throws Exception
|
||||||
{
|
{
|
||||||
LogCommandRequest request = new LogCommandRequest();
|
LogCommandRequest request = new LogCommandRequest();
|
||||||
|
|
||||||
@@ -196,10 +161,6 @@ public class GitLogCommandTest extends AbstractGitCommandTestBase
|
|||||||
assertEquals("592d797cd36432e591416e8b2b98154f4f163411", c2.getId());
|
assertEquals("592d797cd36432e591416e8b2b98154f4f163411", c2.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetCommit()
|
public void testGetCommit()
|
||||||
{
|
{
|
||||||
@@ -224,15 +185,8 @@ public class GitLogCommandTest extends AbstractGitCommandTestBase
|
|||||||
assertThat(mods.getAdded(), contains("a.txt", "b.txt"));
|
assertThat(mods.getAdded(), contains("a.txt", "b.txt"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @throws IOException
|
|
||||||
* @throws RepositoryException
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetRange() throws IOException
|
public void testGetRange() throws Exception
|
||||||
{
|
{
|
||||||
LogCommandRequest request = new LogCommandRequest();
|
LogCommandRequest request = new LogCommandRequest();
|
||||||
|
|
||||||
@@ -254,12 +208,6 @@ public class GitLogCommandTest extends AbstractGitCommandTestBase
|
|||||||
assertEquals("435df2f061add3589cb326cc64be9b9c3897ceca", c2.getId());
|
assertEquals("435df2f061add3589cb326cc64be9b9c3897ceca", c2.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private GitLogCommand createCommand()
|
private GitLogCommand createCommand()
|
||||||
{
|
{
|
||||||
return new GitLogCommand(createContext(), repository);
|
return new GitLogCommand(createContext(), repository);
|
||||||
|
|||||||
@@ -32,9 +32,10 @@ package sonia.scm.repository.client.spi;
|
|||||||
|
|
||||||
import com.aragost.javahg.Repository;
|
import com.aragost.javahg.Repository;
|
||||||
import com.aragost.javahg.commands.ExecutionException;
|
import com.aragost.javahg.commands.ExecutionException;
|
||||||
import java.io.IOException;
|
|
||||||
import sonia.scm.repository.client.api.RepositoryClientException;
|
import sonia.scm.repository.client.api.RepositoryClientException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mercurial implementation of the {@link PushCommand}.
|
* Mercurial implementation of the {@link PushCommand}.
|
||||||
*
|
*
|
||||||
@@ -64,4 +65,9 @@ public class HgPushCommand implements PushCommand
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pushTags() throws IOException {
|
||||||
|
push();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ package sonia.scm.repository.client.spi;
|
|||||||
|
|
||||||
import com.aragost.javahg.Repository;
|
import com.aragost.javahg.Repository;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import java.io.IOException;
|
|
||||||
import sonia.scm.repository.Tag;
|
import sonia.scm.repository.Tag;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -51,13 +50,16 @@ public class HgTagCommand implements TagCommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Tag tag(TagRequest request) throws IOException
|
public Tag tag(TagRequest request)
|
||||||
{
|
{
|
||||||
String rev = request.getRevision();
|
String rev = request.getRevision();
|
||||||
if ( Strings.isNullOrEmpty(rev) ){
|
if ( Strings.isNullOrEmpty(rev) ){
|
||||||
rev = repository.tip().getNode();
|
rev = repository.tip().getNode();
|
||||||
}
|
}
|
||||||
com.aragost.javahg.commands.TagCommand.on(repository).rev(rev).execute(request.getName());
|
com.aragost.javahg.commands.TagCommand.on(repository)
|
||||||
|
.rev(rev)
|
||||||
|
.user(request.getUserName())
|
||||||
|
.execute(request.getName());
|
||||||
return new Tag(request.getName(), rev);
|
return new Tag(request.getName(), rev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,13 +36,12 @@ package sonia.scm.repository.client.api;
|
|||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import sonia.scm.repository.client.spi.PushCommand;
|
import sonia.scm.repository.client.spi.PushCommand;
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
//~--- JDK imports ------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
@@ -88,6 +87,14 @@ public final class PushCommandBuilder
|
|||||||
command.push();
|
command.push();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void pushTags() throws IOException {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("push tag changes back to main repository");
|
||||||
|
}
|
||||||
|
|
||||||
|
command.pushTags();
|
||||||
|
}
|
||||||
|
|
||||||
//~--- fields ---------------------------------------------------------------
|
//~--- fields ---------------------------------------------------------------
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
|
|||||||
@@ -36,15 +36,14 @@ package sonia.scm.repository.client.api;
|
|||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import sonia.scm.repository.Tag;
|
import sonia.scm.repository.Tag;
|
||||||
import sonia.scm.repository.client.spi.TagCommand;
|
import sonia.scm.repository.client.spi.TagCommand;
|
||||||
import sonia.scm.repository.client.spi.TagRequest;
|
import sonia.scm.repository.client.spi.TagRequest;
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
//~--- JDK imports ------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
@@ -84,9 +83,10 @@ public final class TagCommandBuilder
|
|||||||
*
|
*
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public Tag tag(String name) throws IOException
|
public Tag tag(String name, String username) throws IOException
|
||||||
{
|
{
|
||||||
request.setName(name);
|
request.setName(name);
|
||||||
|
request.setUsername(username);
|
||||||
|
|
||||||
if (logger.isDebugEnabled())
|
if (logger.isDebugEnabled())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -44,11 +44,7 @@ import java.io.IOException;
|
|||||||
public interface PushCommand
|
public interface PushCommand
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
void push() throws IOException;
|
||||||
* Method description
|
|
||||||
*
|
void pushTags() throws IOException;
|
||||||
*
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public void push() throws IOException;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ public final class TagRequest
|
|||||||
{
|
{
|
||||||
this.name = null;
|
this.name = null;
|
||||||
this.revision = null;
|
this.revision = null;
|
||||||
|
this.username = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -106,6 +107,7 @@ public final class TagRequest
|
|||||||
return MoreObjects.toStringHelper(this)
|
return MoreObjects.toStringHelper(this)
|
||||||
.add("revision", revision)
|
.add("revision", revision)
|
||||||
.add("name", name)
|
.add("name", name)
|
||||||
|
.add("username", username)
|
||||||
.toString();
|
.toString();
|
||||||
//J+
|
//J+
|
||||||
}
|
}
|
||||||
@@ -134,6 +136,10 @@ public final class TagRequest
|
|||||||
this.revision = revision;
|
this.revision = revision;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
//~--- get methods ----------------------------------------------------------
|
//~--- get methods ----------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -158,6 +164,10 @@ public final class TagRequest
|
|||||||
return revision;
|
return revision;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getUserName() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
//~--- fields ---------------------------------------------------------------
|
//~--- fields ---------------------------------------------------------------
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
@@ -165,4 +175,6 @@ public final class TagRequest
|
|||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
private String revision;
|
private String revision;
|
||||||
|
|
||||||
|
private String username;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import javax.ws.rs.ext.Provider;
|
|||||||
public class AlreadyExistsExceptionMapper implements ExceptionMapper<AlreadyExistsException> {
|
public class AlreadyExistsExceptionMapper implements ExceptionMapper<AlreadyExistsException> {
|
||||||
@Override
|
@Override
|
||||||
public Response toResponse(AlreadyExistsException exception) {
|
public Response toResponse(AlreadyExistsException exception) {
|
||||||
return Response.status(Status.CONFLICT).build();
|
return Response.status(Status.CONFLICT)
|
||||||
|
.entity(exception.getMessage())
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ package sonia.scm.api.rest;
|
|||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
//~--- JDK imports ------------------------------------------------------------
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
import javax.ws.rs.ext.ExceptionMapper;
|
import javax.ws.rs.ext.ExceptionMapper;
|
||||||
@@ -43,7 +45,7 @@ import javax.ws.rs.ext.Provider;
|
|||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
* @since 1.36
|
* @since 1.36
|
||||||
*/
|
*/
|
||||||
@Provider
|
@Provider @Slf4j
|
||||||
public class IllegalArgumentExceptionMapper
|
public class IllegalArgumentExceptionMapper
|
||||||
implements ExceptionMapper<IllegalArgumentException>
|
implements ExceptionMapper<IllegalArgumentException>
|
||||||
{
|
{
|
||||||
@@ -59,6 +61,7 @@ public class IllegalArgumentExceptionMapper
|
|||||||
@Override
|
@Override
|
||||||
public Response toResponse(IllegalArgumentException exception)
|
public Response toResponse(IllegalArgumentException exception)
|
||||||
{
|
{
|
||||||
|
log.info("caught IllegalArgumentException -- mapping to bad request", exception);
|
||||||
return Response.status(Status.BAD_REQUEST).build();
|
return Response.status(Status.BAD_REQUEST).build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,6 +90,8 @@ public class StatusExceptionMapper<E extends Throwable>
|
|||||||
logger.debug(msg.toString());
|
logger.debug(msg.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return Response.status(status).build();
|
return Response.status(status)
|
||||||
|
.entity(exception.getMessage())
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import sonia.scm.PageResult;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
import static com.damnhandy.uri.template.UriTemplate.fromTemplate;
|
import static com.damnhandy.uri.template.UriTemplate.fromTemplate;
|
||||||
import static de.otto.edison.hal.Embedded.embeddedBuilder;
|
import static de.otto.edison.hal.Embedded.embeddedBuilder;
|
||||||
@@ -19,25 +21,28 @@ import static de.otto.edison.hal.Links.linkingTo;
|
|||||||
import static de.otto.edison.hal.paging.NumberedPaging.zeroBasedNumberedPaging;
|
import static de.otto.edison.hal.paging.NumberedPaging.zeroBasedNumberedPaging;
|
||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
abstract class BasicCollectionToDtoMapper<E extends ModelObject, D extends HalRepresentation> {
|
abstract class BasicCollectionToDtoMapper<E extends ModelObject, D extends HalRepresentation, M extends BaseMapper<E, D>> {
|
||||||
|
|
||||||
private final String collectionName;
|
private final String collectionName;
|
||||||
private final BaseMapper<E, D> entityToDtoMapper;
|
|
||||||
|
private final M entityToDtoMapper;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public BasicCollectionToDtoMapper(String collectionName, BaseMapper<E, D> entityToDtoMapper) {
|
public BasicCollectionToDtoMapper(String collectionName, M entityToDtoMapper) {
|
||||||
this.collectionName = collectionName;
|
this.collectionName = collectionName;
|
||||||
this.entityToDtoMapper = entityToDtoMapper;
|
this.entityToDtoMapper = entityToDtoMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CollectionDto map(int pageNumber, int pageSize, PageResult<E> pageResult) {
|
CollectionDto map(int pageNumber, int pageSize, PageResult<E> pageResult, String selfLink, Optional<String> createLink) {
|
||||||
NumberedPaging paging = zeroBasedNumberedPaging(pageNumber, pageSize, pageResult.getOverallCount());
|
return map(pageNumber, pageSize, pageResult, selfLink, createLink, entityToDtoMapper::map);
|
||||||
List<D> dtos = pageResult.getEntities().stream().map(entityToDtoMapper::map).collect(toList());
|
}
|
||||||
|
|
||||||
|
CollectionDto map(int pageNumber, int pageSize, PageResult<E> pageResult, String selfLink, Optional<String> createLink, Function<E, ? extends HalRepresentation> mapper) {
|
||||||
|
NumberedPaging paging = zeroBasedNumberedPaging(pageNumber, pageSize, pageResult.getOverallCount());
|
||||||
|
List<HalRepresentation> dtos = pageResult.getEntities().stream().map(mapper).collect(toList());
|
||||||
CollectionDto collectionDto = new CollectionDto(
|
CollectionDto collectionDto = new CollectionDto(
|
||||||
createLinks(paging),
|
createLinks(paging, selfLink, createLink),
|
||||||
embedDtos(dtos)
|
embedDtos(dtos));
|
||||||
);
|
|
||||||
collectionDto.setPage(pageNumber);
|
collectionDto.setPage(pageNumber);
|
||||||
collectionDto.setPageTotal(computePageTotal(pageSize, pageResult));
|
collectionDto.setPageTotal(computePageTotal(pageSize, pageResult));
|
||||||
return collectionDto;
|
return collectionDto;
|
||||||
@@ -51,26 +56,16 @@ abstract class BasicCollectionToDtoMapper<E extends ModelObject, D extends HalRe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Links createLinks(NumberedPaging page) {
|
private Links createLinks(NumberedPaging page, String selfLink, Optional<String> createLink) {
|
||||||
String baseUrl = createSelfLink();
|
|
||||||
|
|
||||||
Links.Builder linksBuilder = linkingTo()
|
Links.Builder linksBuilder = linkingTo()
|
||||||
.with(page.links(
|
.with(page.links(
|
||||||
fromTemplate(baseUrl + "{?page,pageSize}"),
|
fromTemplate(selfLink + "{?page,pageSize}"),
|
||||||
EnumSet.allOf(PagingRel.class)));
|
EnumSet.allOf(PagingRel.class)));
|
||||||
if (isCreatePermitted()) {
|
createLink.ifPresent(link -> linksBuilder.single(link("create", link)));
|
||||||
linksBuilder.single(link("create", createCreateLink()));
|
|
||||||
}
|
|
||||||
return linksBuilder.build();
|
return linksBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract boolean isCreatePermitted();
|
private Embedded embedDtos(List<HalRepresentation> dtos) {
|
||||||
|
|
||||||
abstract String createCreateLink();
|
|
||||||
|
|
||||||
abstract String createSelfLink();
|
|
||||||
|
|
||||||
private Embedded embedDtos(List<D> dtos) {
|
|
||||||
return embeddedBuilder()
|
return embeddedBuilder()
|
||||||
.with(collectionName, dtos)
|
.with(collectionName, dtos)
|
||||||
.build();
|
.build();
|
||||||
|
|||||||
@@ -26,8 +26,11 @@ public class BranchCollectionToDtoMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public HalRepresentation map(String namespace, String name, Collection<Branch> branches) {
|
public HalRepresentation map(String namespace, String name, Collection<Branch> branches) {
|
||||||
List<BranchDto> dtos = branches.stream().map(branch -> branchToDtoMapper.map(branch, new NamespaceAndName(namespace, name))).collect(toList());
|
return new HalRepresentation(createLinks(namespace, name), embedDtos(getBranchDtoList(namespace, name, branches)));
|
||||||
return new HalRepresentation(createLinks(namespace, name), embedDtos(dtos));
|
}
|
||||||
|
|
||||||
|
public List<BranchDto> getBranchDtoList(String namespace, String name, Collection<Branch> branches) {
|
||||||
|
return branches.stream().map(branch -> branchToDtoMapper.map(branch, new NamespaceAndName(namespace, name))).collect(toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Links createLinks(String namespace, String name) {
|
private Links createLinks(String namespace, String name) {
|
||||||
|
|||||||
@@ -3,33 +3,43 @@ package sonia.scm.api.v2.resources;
|
|||||||
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
||||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||||
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
||||||
|
import sonia.scm.PageResult;
|
||||||
import sonia.scm.repository.Branches;
|
import sonia.scm.repository.Branches;
|
||||||
|
import sonia.scm.repository.Changeset;
|
||||||
|
import sonia.scm.repository.ChangesetPagingResult;
|
||||||
import sonia.scm.repository.NamespaceAndName;
|
import sonia.scm.repository.NamespaceAndName;
|
||||||
|
import sonia.scm.repository.Repository;
|
||||||
import sonia.scm.repository.RepositoryNotFoundException;
|
import sonia.scm.repository.RepositoryNotFoundException;
|
||||||
|
import sonia.scm.repository.RepositoryPermissions;
|
||||||
import sonia.scm.repository.api.CommandNotSupportedException;
|
import sonia.scm.repository.api.CommandNotSupportedException;
|
||||||
import sonia.scm.repository.api.RepositoryService;
|
import sonia.scm.repository.api.RepositoryService;
|
||||||
import sonia.scm.repository.api.RepositoryServiceFactory;
|
import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||||
import sonia.scm.web.VndMediaType;
|
import sonia.scm.web.VndMediaType;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.ws.rs.DefaultValue;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.QueryParam;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class BranchRootResource {
|
public class BranchRootResource {
|
||||||
|
|
||||||
private final RepositoryServiceFactory servicefactory;
|
private final RepositoryServiceFactory serviceFactory;
|
||||||
private final BranchToBranchDtoMapper branchToDtoMapper;
|
private final BranchToBranchDtoMapper branchToDtoMapper;
|
||||||
private final BranchCollectionToDtoMapper branchCollectionToDtoMapper;
|
private final BranchCollectionToDtoMapper branchCollectionToDtoMapper;
|
||||||
|
|
||||||
|
private final ChangesetCollectionToDtoMapper changesetCollectionToDtoMapper;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public BranchRootResource(RepositoryServiceFactory servicefactory, BranchToBranchDtoMapper branchToDtoMapper, BranchCollectionToDtoMapper branchCollectionToDtoMapper) {
|
public BranchRootResource(RepositoryServiceFactory serviceFactory, BranchToBranchDtoMapper branchToDtoMapper, BranchCollectionToDtoMapper branchCollectionToDtoMapper, ChangesetCollectionToDtoMapper changesetCollectionToDtoMapper) {
|
||||||
this.servicefactory = servicefactory;
|
this.serviceFactory = serviceFactory;
|
||||||
this.branchToDtoMapper = branchToDtoMapper;
|
this.branchToDtoMapper = branchToDtoMapper;
|
||||||
this.branchCollectionToDtoMapper = branchCollectionToDtoMapper;
|
this.branchCollectionToDtoMapper = branchCollectionToDtoMapper;
|
||||||
|
this.changesetCollectionToDtoMapper = changesetCollectionToDtoMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,7 +50,6 @@ public class BranchRootResource {
|
|||||||
* @param namespace the namespace of the repository
|
* @param namespace the namespace of the repository
|
||||||
* @param name the name of the repository
|
* @param name the name of the repository
|
||||||
* @param branchName the name of the branch
|
* @param branchName the name of the branch
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@GET
|
@GET
|
||||||
@Path("{branch}")
|
@Path("{branch}")
|
||||||
@@ -55,7 +64,7 @@ public class BranchRootResource {
|
|||||||
@ResponseCode(code = 500, condition = "internal server error")
|
@ResponseCode(code = 500, condition = "internal server error")
|
||||||
})
|
})
|
||||||
public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("branch") String branchName) throws IOException {
|
public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("branch") String branchName) throws IOException {
|
||||||
try (RepositoryService repositoryService = servicefactory.create(new NamespaceAndName(namespace, name))) {
|
try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) {
|
||||||
Branches branches = repositoryService.getBranchesCommand().getBranches();
|
Branches branches = repositoryService.getBranchesCommand().getBranches();
|
||||||
return branches.getBranches()
|
return branches.getBranches()
|
||||||
.stream()
|
.stream()
|
||||||
@@ -74,8 +83,35 @@ public class BranchRootResource {
|
|||||||
|
|
||||||
@Path("{branch}/changesets/")
|
@Path("{branch}/changesets/")
|
||||||
@GET
|
@GET
|
||||||
public Response history(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("branch") String branchName) {
|
@StatusCodes({
|
||||||
throw new UnsupportedOperationException();
|
@ResponseCode(code = 200, condition = "success"),
|
||||||
|
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
||||||
|
@ResponseCode(code = 403, condition = "not authorized, the current user has no privileges to read the changeset"),
|
||||||
|
@ResponseCode(code = 404, condition = "not found, no changesets available in the repository"),
|
||||||
|
@ResponseCode(code = 500, condition = "internal server error")
|
||||||
|
})
|
||||||
|
@Produces(VndMediaType.CHANGESET_COLLECTION)
|
||||||
|
@TypeHint(CollectionDto.class)
|
||||||
|
public Response history(@PathParam("namespace") String namespace,
|
||||||
|
@PathParam("name") String name,
|
||||||
|
@PathParam("branch") String branchName,
|
||||||
|
@DefaultValue("0") @QueryParam("page") int page,
|
||||||
|
@DefaultValue("10") @QueryParam("pageSize") int pageSize) throws Exception {
|
||||||
|
try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) {
|
||||||
|
Repository repository = repositoryService.getRepository();
|
||||||
|
RepositoryPermissions.read(repository).check();
|
||||||
|
ChangesetPagingResult changesets = repositoryService.getLogCommand()
|
||||||
|
.setPagingStart(page)
|
||||||
|
.setPagingLimit(pageSize)
|
||||||
|
.setBranch(branchName)
|
||||||
|
.getChangesets();
|
||||||
|
if (changesets != null && changesets.getChangesets() != null) {
|
||||||
|
PageResult<Changeset> pageResult = new PageResult<>(changesets.getChangesets(), changesets.getTotal());
|
||||||
|
return Response.ok(changesetCollectionToDtoMapper.map(page, pageSize, pageResult, repository)).build();
|
||||||
|
} else {
|
||||||
|
return Response.ok().build();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -85,7 +121,6 @@ public class BranchRootResource {
|
|||||||
*
|
*
|
||||||
* @param namespace the namespace of the repository
|
* @param namespace the namespace of the repository
|
||||||
* @param name the name of the repository
|
* @param name the name of the repository
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@GET
|
@GET
|
||||||
@Path("")
|
@Path("")
|
||||||
@@ -100,7 +135,7 @@ public class BranchRootResource {
|
|||||||
@ResponseCode(code = 500, condition = "internal server error")
|
@ResponseCode(code = 500, condition = "internal server error")
|
||||||
})
|
})
|
||||||
public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name) throws IOException {
|
public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name) throws IOException {
|
||||||
try (RepositoryService repositoryService = servicefactory.create(new NamespaceAndName(namespace, name))) {
|
try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) {
|
||||||
Branches branches = repositoryService.getBranchesCommand().getBranches();
|
Branches branches = repositoryService.getBranchesCommand().getBranches();
|
||||||
return Response.ok(branchCollectionToDtoMapper.map(namespace, name, branches.getBranches())).build();
|
return Response.ok(branchCollectionToDtoMapper.map(namespace, name, branches.getBranches())).build();
|
||||||
} catch (CommandNotSupportedException ex) {
|
} catch (CommandNotSupportedException ex) {
|
||||||
|
|||||||
@@ -6,18 +6,13 @@ import lombok.Getter;
|
|||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public class BrowserResultDto extends HalRepresentation implements Iterable<FileObjectDto> {
|
public class BrowserResultDto extends HalRepresentation {
|
||||||
private String revision;
|
private String revision;
|
||||||
private String tag;
|
|
||||||
private String branch;
|
|
||||||
// REVIEW files nicht embedded?
|
|
||||||
private List<FileObjectDto> files;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("squid:S1185") // We want to have this method available in this package
|
@SuppressWarnings("squid:S1185") // We want to have this method available in this package
|
||||||
@@ -25,16 +20,7 @@ public class BrowserResultDto extends HalRepresentation implements Iterable<File
|
|||||||
return super.add(links);
|
return super.add(links);
|
||||||
}
|
}
|
||||||
|
|
||||||
// REVIEW return null?
|
public void setFiles(List<FileObjectDto> files) {
|
||||||
@Override
|
this.withEmbedded("files", files);
|
||||||
public Iterator<FileObjectDto> iterator() {
|
|
||||||
Iterator<FileObjectDto> it = null;
|
|
||||||
|
|
||||||
if (files != null)
|
|
||||||
{
|
|
||||||
it = files.iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
return it;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,11 +17,9 @@ public class BrowserResultToBrowserResultDtoMapper {
|
|||||||
@Inject
|
@Inject
|
||||||
private ResourceLinks resourceLinks;
|
private ResourceLinks resourceLinks;
|
||||||
|
|
||||||
public BrowserResultDto map(BrowserResult browserResult, NamespaceAndName namespaceAndName) {
|
public BrowserResultDto map(BrowserResult browserResult, NamespaceAndName namespaceAndName, String path) {
|
||||||
BrowserResultDto browserResultDto = new BrowserResultDto();
|
BrowserResultDto browserResultDto = new BrowserResultDto();
|
||||||
|
|
||||||
browserResultDto.setTag(browserResult.getTag());
|
|
||||||
browserResultDto.setBranch(browserResult.getBranch());
|
|
||||||
browserResultDto.setRevision(browserResult.getRevision());
|
browserResultDto.setRevision(browserResult.getRevision());
|
||||||
|
|
||||||
List<FileObjectDto> fileObjectDtoList = new ArrayList<>();
|
List<FileObjectDto> fileObjectDtoList = new ArrayList<>();
|
||||||
@@ -30,7 +28,7 @@ public class BrowserResultToBrowserResultDtoMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
browserResultDto.setFiles(fileObjectDtoList);
|
browserResultDto.setFiles(fileObjectDtoList);
|
||||||
this.addLinks(browserResult, browserResultDto, namespaceAndName);
|
this.addLinks(browserResult, browserResultDto, namespaceAndName, path);
|
||||||
return browserResultDto;
|
return browserResultDto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,13 +36,14 @@ public class BrowserResultToBrowserResultDtoMapper {
|
|||||||
return fileObjectToFileObjectDtoMapper.map(fileObject, namespaceAndName, revision);
|
return fileObjectToFileObjectDtoMapper.map(fileObject, namespaceAndName, revision);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addLinks(BrowserResult browserResult, BrowserResultDto dto, NamespaceAndName namespaceAndName) {
|
private void addLinks(BrowserResult browserResult, BrowserResultDto dto, NamespaceAndName namespaceAndName, String path) {
|
||||||
|
if (path.equals("/")) {
|
||||||
|
path = "";
|
||||||
|
}
|
||||||
if (browserResult.getRevision() == null) {
|
if (browserResult.getRevision() == null) {
|
||||||
dto.add(Links.linkingTo().self(resourceLinks.source().selfWithoutRevision(namespaceAndName.getNamespace(), namespaceAndName.getName())).build());
|
throw new IllegalStateException("missing revision in browser result for repository " + namespaceAndName + " and path " + path);
|
||||||
} else {
|
} else {
|
||||||
dto.add(Links.linkingTo().self(resourceLinks.source().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), browserResult.getRevision())).build());
|
dto.add(Links.linkingTo().self(resourceLinks.source().sourceWithPath(namespaceAndName.getNamespace(), namespaceAndName.getName(), browserResult.getRevision(), path)).build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import sonia.scm.PageResult;
|
||||||
|
import sonia.scm.repository.Changeset;
|
||||||
|
import sonia.scm.repository.Repository;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class ChangesetCollectionToDtoMapper extends BasicCollectionToDtoMapper<Changeset, ChangesetDto, ChangesetToChangesetDtoMapper> {
|
||||||
|
|
||||||
|
private final ChangesetToChangesetDtoMapper changesetToChangesetDtoMapper;
|
||||||
|
private final ResourceLinks resourceLinks;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ChangesetCollectionToDtoMapper(ChangesetToChangesetDtoMapper changesetToChangesetDtoMapper, ResourceLinks resourceLinks) {
|
||||||
|
super("changesets", changesetToChangesetDtoMapper);
|
||||||
|
this.changesetToChangesetDtoMapper = changesetToChangesetDtoMapper;
|
||||||
|
this.resourceLinks = resourceLinks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CollectionDto map(int pageNumber, int pageSize, PageResult<Changeset> pageResult, Repository repository) {
|
||||||
|
return super.map(pageNumber, pageSize, pageResult, createSelfLink(repository), Optional.empty(), changeset -> changesetToChangesetDtoMapper.map(changeset, repository));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String createSelfLink(Repository repository) {
|
||||||
|
return resourceLinks.changeset().all(repository.getNamespace(), repository.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import de.otto.edison.hal.HalRepresentation;
|
||||||
|
import de.otto.edison.hal.Links;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class ChangesetDto extends HalRepresentation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The changeset identification string
|
||||||
|
*/
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The author of the changeset
|
||||||
|
*/
|
||||||
|
private PersonDto author;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The date when the changeset was committed
|
||||||
|
*/
|
||||||
|
private Instant date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The text of the changeset description
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("squid:S1185") // We want to have this method available in this package
|
||||||
|
protected HalRepresentation add(Links links) {
|
||||||
|
return super.add(links);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("squid:S1185") // We want to have this method available in this package
|
||||||
|
protected HalRepresentation withEmbedded(String rel, List<? extends HalRepresentation> halRepresentations) {
|
||||||
|
return super.withEmbedded(rel, halRepresentations);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,25 +1,101 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import sonia.scm.PageResult;
|
||||||
|
import sonia.scm.repository.Changeset;
|
||||||
|
import sonia.scm.repository.ChangesetPagingResult;
|
||||||
|
import sonia.scm.repository.NamespaceAndName;
|
||||||
|
import sonia.scm.repository.Repository;
|
||||||
|
import sonia.scm.repository.RepositoryNotFoundException;
|
||||||
|
import sonia.scm.repository.RepositoryPermissions;
|
||||||
|
import sonia.scm.repository.RevisionNotFoundException;
|
||||||
|
import sonia.scm.repository.api.RepositoryService;
|
||||||
|
import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||||
|
import sonia.scm.web.VndMediaType;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
import javax.ws.rs.DefaultValue;
|
import javax.ws.rs.DefaultValue;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.QueryParam;
|
import javax.ws.rs.QueryParam;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
public class ChangesetRootResource {
|
public class ChangesetRootResource {
|
||||||
|
|
||||||
|
private final RepositoryServiceFactory serviceFactory;
|
||||||
|
|
||||||
|
private final ChangesetCollectionToDtoMapper changesetCollectionToDtoMapper;
|
||||||
|
|
||||||
|
private final ChangesetToChangesetDtoMapper changesetToChangesetDtoMapper;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ChangesetRootResource(RepositoryServiceFactory serviceFactory, ChangesetCollectionToDtoMapper changesetCollectionToDtoMapper, ChangesetToChangesetDtoMapper changesetToChangesetDtoMapper) {
|
||||||
|
this.serviceFactory = serviceFactory;
|
||||||
|
this.changesetCollectionToDtoMapper = changesetCollectionToDtoMapper;
|
||||||
|
this.changesetToChangesetDtoMapper = changesetToChangesetDtoMapper;
|
||||||
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("")
|
@Path("")
|
||||||
public Response getAll(@DefaultValue("0") @QueryParam("page") int page,
|
@StatusCodes({
|
||||||
@DefaultValue("10") @QueryParam("pageSize") int pageSize,
|
@ResponseCode(code = 200, condition = "success"),
|
||||||
@QueryParam("sortBy") String sortBy,
|
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
||||||
@DefaultValue("false") @QueryParam("desc") boolean desc) {
|
@ResponseCode(code = 403, condition = "not authorized, the current user has no privileges to read the changeset"),
|
||||||
throw new UnsupportedOperationException();
|
@ResponseCode(code = 404, condition = "not found, no changesets available in the repository"),
|
||||||
|
@ResponseCode(code = 500, condition = "internal server error")
|
||||||
|
})
|
||||||
|
@Produces(VndMediaType.CHANGESET_COLLECTION)
|
||||||
|
@TypeHint(CollectionDto.class)
|
||||||
|
public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name, @DefaultValue("0") @QueryParam("page") int page,
|
||||||
|
@DefaultValue("10") @QueryParam("pageSize") int pageSize) throws IOException, RevisionNotFoundException, RepositoryNotFoundException {
|
||||||
|
try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) {
|
||||||
|
Repository repository = repositoryService.getRepository();
|
||||||
|
RepositoryPermissions.read(repository).check();
|
||||||
|
ChangesetPagingResult changesets = repositoryService.getLogCommand()
|
||||||
|
.setPagingStart(page)
|
||||||
|
.setPagingLimit(pageSize)
|
||||||
|
.getChangesets();
|
||||||
|
if (changesets != null && changesets.getChangesets() != null) {
|
||||||
|
PageResult<Changeset> pageResult = new PageResult<>(changesets.getChangesets(), changesets.getTotal());
|
||||||
|
return Response.ok(changesetCollectionToDtoMapper.map(page, pageSize, pageResult, repository)).build();
|
||||||
|
} else {
|
||||||
|
return Response.ok().build();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("{revision}")
|
@StatusCodes({
|
||||||
public Response get() {
|
@ResponseCode(code = 200, condition = "success"),
|
||||||
throw new UnsupportedOperationException();
|
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
||||||
|
@ResponseCode(code = 403, condition = "not authorized, the current user has no privileges to read the changeset"),
|
||||||
|
@ResponseCode(code = 404, condition = "not found, no changeset with the specified id is available in the repository"),
|
||||||
|
@ResponseCode(code = 500, condition = "internal server error")
|
||||||
|
})
|
||||||
|
@Produces(VndMediaType.CHANGESET)
|
||||||
|
@TypeHint(ChangesetDto.class)
|
||||||
|
@Path("{id}")
|
||||||
|
public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("id") String id) throws IOException, RevisionNotFoundException, RepositoryNotFoundException {
|
||||||
|
try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) {
|
||||||
|
Repository repository = repositoryService.getRepository();
|
||||||
|
RepositoryPermissions.read(repository).check();
|
||||||
|
ChangesetPagingResult changesets = repositoryService.getLogCommand()
|
||||||
|
.setStartChangeset(id)
|
||||||
|
.setEndChangeset(id)
|
||||||
|
.getChangesets();
|
||||||
|
if (changesets != null && changesets.getChangesets() != null && changesets.getChangesets().size() == 1) {
|
||||||
|
return Response.ok(changesetToChangesetDtoMapper.map(changesets.getChangesets().get(0), repository)).build();
|
||||||
|
} else {
|
||||||
|
return Response.status(Response.Status.NOT_FOUND).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import de.otto.edison.hal.Links;
|
||||||
|
import org.mapstruct.AfterMapping;
|
||||||
|
import org.mapstruct.Context;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.MappingTarget;
|
||||||
|
import sonia.scm.repository.Branch;
|
||||||
|
import sonia.scm.repository.Changeset;
|
||||||
|
import sonia.scm.repository.Repository;
|
||||||
|
import sonia.scm.repository.Tag;
|
||||||
|
import sonia.scm.repository.api.Command;
|
||||||
|
import sonia.scm.repository.api.RepositoryService;
|
||||||
|
import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static de.otto.edison.hal.Link.link;
|
||||||
|
import static de.otto.edison.hal.Links.linkingTo;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public abstract class ChangesetToChangesetDtoMapper extends BaseMapper<Changeset, ChangesetDto> {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private RepositoryServiceFactory serviceFactory;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ResourceLinks resourceLinks;
|
||||||
|
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private BranchCollectionToDtoMapper branchCollectionToDtoMapper;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ChangesetToParentDtoMapper changesetToParentDtoMapper;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private TagCollectionToDtoMapper tagCollectionToDtoMapper;
|
||||||
|
|
||||||
|
|
||||||
|
@Mapping(target = "attributes", ignore = true) // We do not map HAL attributes
|
||||||
|
public abstract ChangesetDto map(Changeset changeset, @Context Repository repository);
|
||||||
|
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
void appendLinks(Changeset source, @MappingTarget ChangesetDto target, @Context Repository repository) {
|
||||||
|
String namespace = repository.getNamespace();
|
||||||
|
String name = repository.getName();
|
||||||
|
|
||||||
|
try (RepositoryService repositoryService = serviceFactory.create(repository)) {
|
||||||
|
if (repositoryService.isSupported(Command.TAGS)) {
|
||||||
|
target.withEmbedded("tags", tagCollectionToDtoMapper.getTagDtoList(namespace, name,
|
||||||
|
getListOfObjects(source.getTags(), tagName -> new Tag(tagName, source.getId()))));
|
||||||
|
}
|
||||||
|
if (repositoryService.isSupported(Command.BRANCHES)) {
|
||||||
|
target.withEmbedded("branches", branchCollectionToDtoMapper.getBranchDtoList(namespace, name,
|
||||||
|
getListOfObjects(source.getBranches(), branchName -> new Branch(branchName, source.getId()))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target.withEmbedded("parents", getListOfObjects(source.getParents(), parent -> changesetToParentDtoMapper.map(new Changeset(parent, 0L, null), repository)));
|
||||||
|
|
||||||
|
Links.Builder linksBuilder = linkingTo()
|
||||||
|
.self(resourceLinks.changeset().self(repository.getNamespace(), repository.getName(), target.getId()))
|
||||||
|
.single(link("diff", resourceLinks.diff().self(namespace, name, target.getId())));
|
||||||
|
target.add(linksBuilder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> List<T> getListOfObjects(List<String> list, Function<String, T> mapFunction) {
|
||||||
|
return list
|
||||||
|
.stream()
|
||||||
|
.map(mapFunction)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
|
||||||
|
import de.otto.edison.hal.Links;
|
||||||
|
import org.mapstruct.AfterMapping;
|
||||||
|
import org.mapstruct.Context;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.MappingTarget;
|
||||||
|
import sonia.scm.repository.Changeset;
|
||||||
|
import sonia.scm.repository.Repository;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static de.otto.edison.hal.Link.link;
|
||||||
|
import static de.otto.edison.hal.Links.linkingTo;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public abstract class ChangesetToParentDtoMapper extends BaseMapper<Changeset, ParentChangesetDto> {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ResourceLinks resourceLinks;
|
||||||
|
|
||||||
|
@Mapping(target = "attributes", ignore = true) // We do not map HAL attributes
|
||||||
|
public abstract ParentChangesetDto map(Changeset changeset, @Context Repository repository);
|
||||||
|
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
void appendLinks(@MappingTarget ParentChangesetDto target, @Context Repository repository) {
|
||||||
|
String namespace = repository.getNamespace();
|
||||||
|
String name = repository.getName();
|
||||||
|
Links.Builder linksBuilder = linkingTo()
|
||||||
|
.self(resourceLinks.changeset().self(repository.getNamespace(), repository.getName(), target.getId()))
|
||||||
|
.single(link("diff", resourceLinks.diff().self(namespace, name, target.getId())));
|
||||||
|
target.add(linksBuilder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
|
||||||
|
|
||||||
import de.otto.edison.hal.HalRepresentation;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static de.otto.edison.hal.Embedded.embeddedBuilder;
|
|
||||||
import static de.otto.edison.hal.Links.linkingTo;
|
|
||||||
|
|
||||||
abstract class CollectionToDtoMapper<E, D extends HalRepresentation> {
|
|
||||||
|
|
||||||
private final String collectionName;
|
|
||||||
private final BaseMapper<E, D> mapper;
|
|
||||||
|
|
||||||
protected CollectionToDtoMapper(String collectionName, BaseMapper<E, D> mapper) {
|
|
||||||
this.collectionName = collectionName;
|
|
||||||
this.mapper = mapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HalRepresentation map(Collection<E> collection) {
|
|
||||||
List<D> dtos = collection.stream().map(mapper::map).collect(Collectors.toList());
|
|
||||||
return new HalRepresentation(
|
|
||||||
linkingTo().self(createSelfLink()).build(),
|
|
||||||
embeddedBuilder().with(collectionName, dtos).build()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract String createSelfLink();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import javax.ws.rs.DefaultValue;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.QueryParam;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
public class DiffRootResource {
|
||||||
|
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("")
|
||||||
|
public Response getAll(@DefaultValue("0") @QueryParam("page") int page,
|
||||||
|
@DefaultValue("10") @QueryParam("pageSize") int pageSize,
|
||||||
|
@QueryParam("sortBy") String sortBy,
|
||||||
|
@DefaultValue("false") @QueryParam("desc") boolean desc) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{id}")
|
||||||
|
public Response get(String id) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -10,7 +10,6 @@ import sonia.scm.repository.NamespaceAndName;
|
|||||||
import sonia.scm.repository.SubRepository;
|
import sonia.scm.repository.SubRepository;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import java.net.URI;
|
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public abstract class FileObjectToFileObjectDtoMapper extends BaseMapper<FileObject, FileObjectDto> {
|
public abstract class FileObjectToFileObjectDtoMapper extends BaseMapper<FileObject, FileObjectDto> {
|
||||||
@@ -27,19 +26,14 @@ public abstract class FileObjectToFileObjectDtoMapper extends BaseMapper<FileObj
|
|||||||
String path = removeFirstSlash(fileObject.getPath());
|
String path = removeFirstSlash(fileObject.getPath());
|
||||||
Links.Builder links = Links.linkingTo();
|
Links.Builder links = Links.linkingTo();
|
||||||
if (dto.isDirectory()) {
|
if (dto.isDirectory()) {
|
||||||
links.self(addPath(resourceLinks.source().sourceWithPath(namespaceAndName.getNamespace(), namespaceAndName.getName(), revision, ""), path));
|
links.self(resourceLinks.source().sourceWithPath(namespaceAndName.getNamespace(), namespaceAndName.getName(), revision, path));
|
||||||
} else {
|
} else {
|
||||||
links.self(addPath(resourceLinks.source().content(namespaceAndName.getNamespace(), namespaceAndName.getName(), revision, ""), path));
|
links.self(resourceLinks.source().content(namespaceAndName.getNamespace(), namespaceAndName.getName(), revision, path));
|
||||||
}
|
}
|
||||||
|
|
||||||
dto.add(links.build());
|
dto.add(links.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
// we have to add the file path using URI, so that path separators (aka '/') will not be encoded as '%2F'
|
|
||||||
private String addPath(String sourceWithPath, String path) {
|
|
||||||
return URI.create(sourceWithPath).resolve(path).toASCIIString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String removeFirstSlash(String source) {
|
private String removeFirstSlash(String source) {
|
||||||
return source.startsWith("/") ? source.substring(1) : source;
|
return source.startsWith("/") ? source.substring(1) : source;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import sonia.scm.PageResult;
|
||||||
import sonia.scm.group.Group;
|
import sonia.scm.group.Group;
|
||||||
import sonia.scm.group.GroupPermissions;
|
import sonia.scm.group.GroupPermissions;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public class GroupCollectionToDtoMapper extends BasicCollectionToDtoMapper<Group, GroupDto> {
|
import static java.util.Optional.empty;
|
||||||
|
import static java.util.Optional.of;
|
||||||
|
|
||||||
|
public class GroupCollectionToDtoMapper extends BasicCollectionToDtoMapper<Group, GroupDto, GroupToGroupDtoMapper> {
|
||||||
|
|
||||||
private final ResourceLinks resourceLinks;
|
private final ResourceLinks resourceLinks;
|
||||||
|
|
||||||
@@ -15,18 +20,15 @@ public class GroupCollectionToDtoMapper extends BasicCollectionToDtoMapper<Group
|
|||||||
this.resourceLinks = resourceLinks;
|
this.resourceLinks = resourceLinks;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public CollectionDto map(int pageNumber, int pageSize, PageResult<Group> pageResult) {
|
||||||
String createCreateLink() {
|
return map(pageNumber, pageSize, pageResult, this.createSelfLink(), this.createCreateLink());
|
||||||
return resourceLinks.groupCollection().create();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private Optional<String> createCreateLink() {
|
||||||
String createSelfLink() {
|
return GroupPermissions.create().isPermitted() ? of(resourceLinks.groupCollection().create()): empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String createSelfLink() {
|
||||||
return resourceLinks.groupCollection().self();
|
return resourceLinks.groupCollection().self();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean isCreatePermitted() {
|
|
||||||
return GroupPermissions.create().isPermitted();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,11 @@ public class MapperModule extends AbstractModule {
|
|||||||
bind(PermissionDtoToPermissionMapper.class).to(Mappers.getMapper(PermissionDtoToPermissionMapper.class).getClass());
|
bind(PermissionDtoToPermissionMapper.class).to(Mappers.getMapper(PermissionDtoToPermissionMapper.class).getClass());
|
||||||
bind(PermissionToPermissionDtoMapper.class).to(Mappers.getMapper(PermissionToPermissionDtoMapper.class).getClass());
|
bind(PermissionToPermissionDtoMapper.class).to(Mappers.getMapper(PermissionToPermissionDtoMapper.class).getClass());
|
||||||
|
|
||||||
|
bind(ChangesetToChangesetDtoMapper.class).to(Mappers.getMapper(ChangesetToChangesetDtoMapper.class).getClass());
|
||||||
|
bind(ChangesetToParentDtoMapper.class).to(Mappers.getMapper(ChangesetToParentDtoMapper.class).getClass());
|
||||||
|
|
||||||
|
bind(TagToTagDtoMapper.class).to(Mappers.getMapper(TagToTagDtoMapper.class).getClass());
|
||||||
|
|
||||||
bind(FileObjectToFileObjectDtoMapper.class).to(Mappers.getMapper(FileObjectToFileObjectDtoMapper.class).getClass());
|
bind(FileObjectToFileObjectDtoMapper.class).to(Mappers.getMapper(FileObjectToFileObjectDtoMapper.class).getClass());
|
||||||
|
|
||||||
// no mapstruct required
|
// no mapstruct required
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import de.otto.edison.hal.HalRepresentation;
|
||||||
|
import de.otto.edison.hal.Links;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@ToString
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class ParentChangesetDto extends HalRepresentation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the id of the parent changeset
|
||||||
|
*/
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("squid:S1185") // We want to have this method available in this package
|
||||||
|
protected HalRepresentation add(Links links) {
|
||||||
|
return super.add(links);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -10,6 +10,8 @@ import lombok.ToString;
|
|||||||
@Getter @Setter @ToString
|
@Getter @Setter @ToString
|
||||||
public class PermissionDto extends HalRepresentation {
|
public class PermissionDto extends HalRepresentation {
|
||||||
|
|
||||||
|
public static final String GROUP_PREFIX = "@";
|
||||||
|
|
||||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
@@ -21,11 +23,19 @@ public class PermissionDto extends HalRepresentation {
|
|||||||
*
|
*
|
||||||
**/
|
**/
|
||||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
private String type ;
|
private String type;
|
||||||
|
|
||||||
|
|
||||||
private boolean groupPermission = false;
|
private boolean groupPermission = false;
|
||||||
|
|
||||||
|
public PermissionDto() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public PermissionDto(String permissionName, boolean groupPermission) {
|
||||||
|
name = permissionName;
|
||||||
|
this.groupPermission = groupPermission;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("squid:S1185") // We want to have this method available in this package
|
@SuppressWarnings("squid:S1185") // We want to have this method available in this package
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import com.webcohesion.enunciate.metadata.rs.ResponseHeader;
|
|||||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||||
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang.StringUtils;
|
|
||||||
import sonia.scm.AlreadyExistsException;
|
import sonia.scm.AlreadyExistsException;
|
||||||
import sonia.scm.NotFoundException;
|
import sonia.scm.NotFoundException;
|
||||||
import sonia.scm.repository.NamespaceAndName;
|
import sonia.scm.repository.NamespaceAndName;
|
||||||
@@ -28,10 +27,14 @@ import javax.ws.rs.Produces;
|
|||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import static sonia.scm.api.v2.resources.PermissionDto.GROUP_PREFIX;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class PermissionRootResource {
|
public class PermissionRootResource {
|
||||||
|
|
||||||
|
|
||||||
private PermissionDtoToPermissionMapper dtoToModelMapper;
|
private PermissionDtoToPermissionMapper dtoToModelMapper;
|
||||||
private PermissionToPermissionDtoMapper modelToDtoMapper;
|
private PermissionToPermissionDtoMapper modelToDtoMapper;
|
||||||
private PermissionCollectionToDtoMapper permissionCollectionToDtoMapper;
|
private PermissionCollectionToDtoMapper permissionCollectionToDtoMapper;
|
||||||
@@ -101,7 +104,7 @@ public class PermissionRootResource {
|
|||||||
return Response.ok(
|
return Response.ok(
|
||||||
repository.getPermissions()
|
repository.getPermissions()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(permission -> permissionName.equals(permission.getName()))
|
.filter(filterPermission(permissionName))
|
||||||
.map(permission -> modelToDtoMapper.map(permission, repository))
|
.map(permission -> modelToDtoMapper.map(permission, repository))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow(NotFoundException::new)
|
.orElseThrow(NotFoundException::new)
|
||||||
@@ -135,6 +138,7 @@ public class PermissionRootResource {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a permission to the user or group managed by the repository
|
* Update a permission to the user or group managed by the repository
|
||||||
|
* ignore the user input for groupPermission and take it from the path parameter (if the group prefix (@) exists it is a group permission)
|
||||||
*
|
*
|
||||||
* @param permission permission to modify
|
* @param permission permission to modify
|
||||||
* @param permissionName permission to modify
|
* @param permissionName permission to modify
|
||||||
@@ -152,15 +156,23 @@ public class PermissionRootResource {
|
|||||||
public Response update(@PathParam("namespace") String namespace,
|
public Response update(@PathParam("namespace") String namespace,
|
||||||
@PathParam("name") String name,
|
@PathParam("name") String name,
|
||||||
@PathParam("permission-name") String permissionName,
|
@PathParam("permission-name") String permissionName,
|
||||||
PermissionDto permission) throws NotFoundException {
|
PermissionDto permission) throws NotFoundException, AlreadyExistsException {
|
||||||
log.info("try to update the permission with name: {}. the modified permission is: {}", permissionName, permission);
|
log.info("try to update the permission with name: {}. the modified permission is: {}", permissionName, permission);
|
||||||
Repository repository = load(namespace, name);
|
Repository repository = load(namespace, name);
|
||||||
RepositoryPermissions.permissionWrite(repository).check();
|
RepositoryPermissions.permissionWrite(repository).check();
|
||||||
|
String extractedPermissionName = getPermissionName(permissionName);
|
||||||
|
if (!isPermissionExist(new PermissionDto(extractedPermissionName, isGroupPermission(permissionName)), repository)) {
|
||||||
|
throw new NotFoundException("the permission " + extractedPermissionName + " does not exist");
|
||||||
|
}
|
||||||
|
permission.setGroupPermission(isGroupPermission(permissionName));
|
||||||
|
if (!extractedPermissionName.equals(permission.getName())) {
|
||||||
|
checkPermissionAlreadyExists(permission, repository, "target permission " + permission.getName() + " already exists");
|
||||||
|
}
|
||||||
Permission existingPermission = repository.getPermissions()
|
Permission existingPermission = repository.getPermissions()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(perm -> StringUtils.isNotBlank(perm.getName()) && perm.getName().equals(permissionName))
|
.filter(filterPermission(permissionName))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow(() -> new NotFoundException());
|
.orElseThrow(NotFoundException::new);
|
||||||
dtoToModelMapper.modify(existingPermission, permission);
|
dtoToModelMapper.modify(existingPermission, permission);
|
||||||
manager.modify(repository);
|
manager.modify(repository);
|
||||||
log.info("the permission with name: {} is updated.", permissionName);
|
log.info("the permission with name: {} is updated.", permissionName);
|
||||||
@@ -190,7 +202,7 @@ public class PermissionRootResource {
|
|||||||
RepositoryPermissions.modify(repository).check();
|
RepositoryPermissions.modify(repository).check();
|
||||||
repository.getPermissions()
|
repository.getPermissions()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(perm -> StringUtils.isNotBlank(perm.getName()) && perm.getName().equals(permissionName))
|
.filter(filterPermission(permissionName))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.ifPresent(p -> repository.getPermissions().remove(p))
|
.ifPresent(p -> repository.getPermissions().remove(p))
|
||||||
;
|
;
|
||||||
@@ -199,6 +211,22 @@ public class PermissionRootResource {
|
|||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Predicate<Permission> filterPermission(String permissionName) {
|
||||||
|
return permission -> getPermissionName(permissionName).equals(permission.getName())
|
||||||
|
&&
|
||||||
|
permission.isGroupPermission() == isGroupPermission(permissionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPermissionName(String permissionName) {
|
||||||
|
return Optional.of(permissionName)
|
||||||
|
.filter(p -> !isGroupPermission(permissionName))
|
||||||
|
.orElse(permissionName.substring(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isGroupPermission(String permissionName) {
|
||||||
|
return permissionName.startsWith(GROUP_PREFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* check if the actual user is permitted to manage the repository permissions
|
* check if the actual user is permitted to manage the repository permissions
|
||||||
@@ -219,16 +247,24 @@ public class PermissionRootResource {
|
|||||||
*
|
*
|
||||||
* @param permission the searched permission
|
* @param permission the searched permission
|
||||||
* @param repository the repository to be inspected
|
* @param repository the repository to be inspected
|
||||||
|
* @param errorMessage error message
|
||||||
* @throws AlreadyExistsException if the permission already exists in the repository
|
* @throws AlreadyExistsException if the permission already exists in the repository
|
||||||
*/
|
*/
|
||||||
private void checkPermissionAlreadyExists(PermissionDto permission, Repository repository) throws AlreadyExistsException {
|
private void checkPermissionAlreadyExists(PermissionDto permission, Repository repository, String errorMessage) throws AlreadyExistsException {
|
||||||
boolean isPermissionAlreadyExist = repository.getPermissions()
|
if (isPermissionExist(permission, repository)) {
|
||||||
.stream()
|
throw new AlreadyExistsException(errorMessage);
|
||||||
.anyMatch(p -> p.getName().equals(permission.getName()));
|
|
||||||
if (isPermissionAlreadyExist) {
|
|
||||||
throw new AlreadyExistsException();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isPermissionExist(PermissionDto permission, Repository repository) {
|
||||||
|
return repository.getPermissions()
|
||||||
|
.stream()
|
||||||
|
.anyMatch(p -> p.getName().equals(permission.getName()) && p.isGroupPermission() == permission.isGroupPermission());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkPermissionAlreadyExists(PermissionDto permission, Repository repository) throws AlreadyExistsException {
|
||||||
|
checkPermissionAlreadyExists(permission, repository, "the permission " + permission.getName() + " already exist.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,9 +12,11 @@ import sonia.scm.repository.Repository;
|
|||||||
import sonia.scm.repository.RepositoryPermissions;
|
import sonia.scm.repository.RepositoryPermissions;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import static de.otto.edison.hal.Link.link;
|
import static de.otto.edison.hal.Link.link;
|
||||||
import static de.otto.edison.hal.Links.linkingTo;
|
import static de.otto.edison.hal.Links.linkingTo;
|
||||||
|
import static sonia.scm.api.v2.resources.PermissionDto.GROUP_PREFIX;
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public abstract class PermissionToPermissionDtoMapper {
|
public abstract class PermissionToPermissionDtoMapper {
|
||||||
@@ -39,11 +41,14 @@ public abstract class PermissionToPermissionDtoMapper {
|
|||||||
*/
|
*/
|
||||||
@AfterMapping
|
@AfterMapping
|
||||||
void appendLinks(@MappingTarget PermissionDto target, @Context Repository repository) {
|
void appendLinks(@MappingTarget PermissionDto target, @Context Repository repository) {
|
||||||
|
String permissionName = Optional.of(target.getName())
|
||||||
|
.filter(p -> !target.isGroupPermission())
|
||||||
|
.orElse(GROUP_PREFIX + target.getName());
|
||||||
Links.Builder linksBuilder = linkingTo()
|
Links.Builder linksBuilder = linkingTo()
|
||||||
.self(resourceLinks.permission().self(repository.getNamespace(), repository.getName(), target.getName()));
|
.self(resourceLinks.permission().self(repository.getNamespace(), repository.getName(), permissionName));
|
||||||
if (RepositoryPermissions.permissionWrite(repository).isPermitted()) {
|
if (RepositoryPermissions.permissionWrite(repository).isPermitted()) {
|
||||||
linksBuilder.single(link("update", resourceLinks.permission().update(repository.getNamespace(), repository.getName(), target.getName())));
|
linksBuilder.single(link("update", resourceLinks.permission().update(repository.getNamespace(), repository.getName(), permissionName)));
|
||||||
linksBuilder.single(link("delete", resourceLinks.permission().delete(repository.getNamespace(), repository.getName(), target.getName())));
|
linksBuilder.single(link("delete", resourceLinks.permission().delete(repository.getNamespace(), repository.getName(), permissionName)));
|
||||||
}
|
}
|
||||||
target.add(linksBuilder.build());
|
target.add(linksBuilder.build());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class PersonDto {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mail address of the person
|
||||||
|
*/
|
||||||
|
private String mail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* name of the person
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,13 +1,18 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import sonia.scm.PageResult;
|
||||||
import sonia.scm.repository.Repository;
|
import sonia.scm.repository.Repository;
|
||||||
import sonia.scm.repository.RepositoryPermissions;
|
import sonia.scm.repository.RepositoryPermissions;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static java.util.Optional.empty;
|
||||||
|
import static java.util.Optional.of;
|
||||||
|
|
||||||
// Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection.
|
// Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection.
|
||||||
@SuppressWarnings("squid:S3306")
|
@SuppressWarnings("squid:S3306")
|
||||||
public class RepositoryCollectionToDtoMapper extends BasicCollectionToDtoMapper<Repository, RepositoryDto> {
|
public class RepositoryCollectionToDtoMapper extends BasicCollectionToDtoMapper<Repository, RepositoryDto, RepositoryToRepositoryDtoMapper> {
|
||||||
|
|
||||||
private final ResourceLinks resourceLinks;
|
private final ResourceLinks resourceLinks;
|
||||||
|
|
||||||
@@ -17,18 +22,15 @@ public class RepositoryCollectionToDtoMapper extends BasicCollectionToDtoMapper<
|
|||||||
this.resourceLinks = resourceLinks;
|
this.resourceLinks = resourceLinks;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public CollectionDto map(int pageNumber, int pageSize, PageResult<Repository> pageResult) {
|
||||||
String createCreateLink() {
|
return map(pageNumber, pageSize, pageResult, this.createSelfLink(), this.createCreateLink());
|
||||||
return resourceLinks.repositoryCollection().create();
|
}
|
||||||
|
|
||||||
|
Optional<String> createCreateLink() {
|
||||||
|
return RepositoryPermissions.create().isPermitted() ? of(resourceLinks.repositoryCollection().create()): empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
String createSelfLink() {
|
String createSelfLink() {
|
||||||
return resourceLinks.repositoryCollection().self();
|
return resourceLinks.repositoryCollection().self();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean isCreatePermitted() {
|
|
||||||
return RepositoryPermissions.create().isPermitted();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ public class RepositoryResource {
|
|||||||
private final Provider<SourceRootResource> sourceRootResource;
|
private final Provider<SourceRootResource> sourceRootResource;
|
||||||
private final Provider<ContentResource> contentResource;
|
private final Provider<ContentResource> contentResource;
|
||||||
private final Provider<PermissionRootResource> permissionRootResource;
|
private final Provider<PermissionRootResource> permissionRootResource;
|
||||||
|
private final Provider<DiffRootResource> diffRootResource;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public RepositoryResource(
|
public RepositoryResource(
|
||||||
@@ -47,7 +48,9 @@ public class RepositoryResource {
|
|||||||
Provider<TagRootResource> tagRootResource,
|
Provider<TagRootResource> tagRootResource,
|
||||||
Provider<BranchRootResource> branchRootResource,
|
Provider<BranchRootResource> branchRootResource,
|
||||||
Provider<ChangesetRootResource> changesetRootResource,
|
Provider<ChangesetRootResource> changesetRootResource,
|
||||||
Provider<SourceRootResource> sourceRootResource, Provider<ContentResource> contentResource, Provider<PermissionRootResource> permissionRootResource) {
|
Provider<SourceRootResource> sourceRootResource, Provider<ContentResource> contentResource,
|
||||||
|
Provider<PermissionRootResource> permissionRootResource,
|
||||||
|
Provider<DiffRootResource> diffRootResource) {
|
||||||
this.dtoToRepositoryMapper = dtoToRepositoryMapper;
|
this.dtoToRepositoryMapper = dtoToRepositoryMapper;
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
this.repositoryToDtoMapper = repositoryToDtoMapper;
|
this.repositoryToDtoMapper = repositoryToDtoMapper;
|
||||||
@@ -58,6 +61,7 @@ public class RepositoryResource {
|
|||||||
this.sourceRootResource = sourceRootResource;
|
this.sourceRootResource = sourceRootResource;
|
||||||
this.contentResource = contentResource;
|
this.contentResource = contentResource;
|
||||||
this.permissionRootResource = permissionRootResource;
|
this.permissionRootResource = permissionRootResource;
|
||||||
|
this.diffRootResource = diffRootResource;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -146,6 +150,11 @@ public class RepositoryResource {
|
|||||||
return tagRootResource.get();
|
return tagRootResource.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Path("diff/")
|
||||||
|
public DiffRootResource diff() {
|
||||||
|
return diffRootResource.get();
|
||||||
|
}
|
||||||
|
|
||||||
@Path("branches/")
|
@Path("branches/")
|
||||||
public BranchRootResource branches(@PathParam("namespace") String namespace, @PathParam("name") String name) {
|
public BranchRootResource branches(@PathParam("namespace") String namespace, @PathParam("name") String name) {
|
||||||
return branchRootResource.get();
|
return branchRootResource.get();
|
||||||
|
|||||||
@@ -40,13 +40,13 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper<Reposit
|
|||||||
}
|
}
|
||||||
try (RepositoryService repositoryService = serviceFactory.create(repository)) {
|
try (RepositoryService repositoryService = serviceFactory.create(repository)) {
|
||||||
if (repositoryService.isSupported(Command.TAGS)) {
|
if (repositoryService.isSupported(Command.TAGS)) {
|
||||||
linksBuilder.single(link("tags", resourceLinks.tagCollection().self(target.getNamespace(), target.getName())));
|
linksBuilder.single(link("tags", resourceLinks.tag().all(target.getNamespace(), target.getName())));
|
||||||
}
|
}
|
||||||
if (repositoryService.isSupported(Command.BRANCHES)) {
|
if (repositoryService.isSupported(Command.BRANCHES)) {
|
||||||
linksBuilder.single(link("branches", resourceLinks.branchCollection().self(target.getNamespace(), target.getName())));
|
linksBuilder.single(link("branches", resourceLinks.branchCollection().self(target.getNamespace(), target.getName())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
linksBuilder.single(link("changesets", resourceLinks.changeset().self(target.getNamespace(), target.getName())));
|
linksBuilder.single(link("changesets", resourceLinks.changeset().all(target.getNamespace(), target.getName())));
|
||||||
linksBuilder.single(link("sources", resourceLinks.source().selfWithoutRevision(target.getNamespace(), target.getName())));
|
linksBuilder.single(link("sources", resourceLinks.source().selfWithoutRevision(target.getNamespace(), target.getName())));
|
||||||
target.add(linksBuilder.build());
|
target.add(linksBuilder.build());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ class ResourceLinks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public TagCollectionLinks tagCollection() {
|
public TagCollectionLinks tag() {
|
||||||
return new TagCollectionLinks(uriInfoStore.get());
|
return new TagCollectionLinks(uriInfoStore.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,11 +215,35 @@ class ResourceLinks {
|
|||||||
private final LinkBuilder tagLinkBuilder;
|
private final LinkBuilder tagLinkBuilder;
|
||||||
|
|
||||||
TagCollectionLinks(UriInfo uriInfo) {
|
TagCollectionLinks(UriInfo uriInfo) {
|
||||||
tagLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class, TagRootResource.class, TagCollectionResource.class);
|
tagLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class, TagRootResource.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
String self(String namespace, String name) {
|
String self(String namespace, String name, String tagName) {
|
||||||
return tagLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("tags").parameters().method("getTagCollectionResource").parameters().method("getAll").parameters().href();
|
return tagLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("tags").parameters().method("get").parameters(tagName).href();
|
||||||
|
}
|
||||||
|
|
||||||
|
String all(String namespace, String name) {
|
||||||
|
return tagLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("tags").parameters().method("getAll").parameters().href();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DiffLinks diff() {
|
||||||
|
return new DiffLinks(uriInfoStore.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DiffLinks {
|
||||||
|
private final LinkBuilder diffLinkBuilder;
|
||||||
|
|
||||||
|
DiffLinks(UriInfo uriInfo) {
|
||||||
|
diffLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class, DiffRootResource.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
String self(String namespace, String name, String id) {
|
||||||
|
return diffLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("diff").parameters().method("get").parameters(id).href();
|
||||||
|
}
|
||||||
|
|
||||||
|
String all(String namespace, String name) {
|
||||||
|
return diffLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("diff").parameters().method("getAll").parameters().href();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +294,11 @@ class ResourceLinks {
|
|||||||
changesetLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class, ChangesetRootResource.class);
|
changesetLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class, ChangesetRootResource.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
String self(String namespace, String name) {
|
String self(String namespace, String name, String changesetId) {
|
||||||
|
return changesetLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("changesets").parameters().method("get").parameters(changesetId).href();
|
||||||
|
}
|
||||||
|
|
||||||
|
String all(String namespace, String name) {
|
||||||
return changesetLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("changesets").parameters().method("getAll").parameters().href();
|
return changesetLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("changesets").parameters().method("getAll").parameters().href();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,15 +331,16 @@ class ResourceLinks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String sourceWithPath(String namespace, String name, String revision, String path) {
|
public String sourceWithPath(String namespace, String name, String revision, String path) {
|
||||||
if (revision == null) {
|
return addPath(sourceLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("sources").parameters().method("get").parameters(revision, "").href(), path);
|
||||||
return sourceLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("sources").parameters().method("get").parameters(null, path).href();
|
|
||||||
} else {
|
|
||||||
return sourceLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("sources").parameters().method("get").parameters(revision, path).href();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String content(String namespace, String name, String revision, String path) {
|
public String content(String namespace, String name, String revision, String path) {
|
||||||
return sourceLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("content").parameters().method("get").parameters(revision, path).href();
|
return addPath(sourceLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("content").parameters().method("get").parameters(revision, "").href(), path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have to add the file path using URI, so that path separators (aka '/') will not be encoded as '%2F'
|
||||||
|
private String addPath(String sourceWithPath, String path) {
|
||||||
|
return URI.create(sourceWithPath).resolve(path).toASCIIString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public PermissionLinks permission() {
|
public PermissionLinks permission() {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import sonia.scm.NotFoundException;
|
||||||
import sonia.scm.repository.BrowserResult;
|
import sonia.scm.repository.BrowserResult;
|
||||||
import sonia.scm.repository.NamespaceAndName;
|
import sonia.scm.repository.NamespaceAndName;
|
||||||
import sonia.scm.repository.RepositoryNotFoundException;
|
import sonia.scm.repository.RepositoryNotFoundException;
|
||||||
@@ -32,25 +33,25 @@ public class SourceRootResource {
|
|||||||
@GET
|
@GET
|
||||||
@Produces(VndMediaType.SOURCE)
|
@Produces(VndMediaType.SOURCE)
|
||||||
@Path("")
|
@Path("")
|
||||||
public Response getAllWithoutRevision(@PathParam("namespace") String namespace, @PathParam("name") String name) {
|
public Response getAllWithoutRevision(@PathParam("namespace") String namespace, @PathParam("name") String name) throws RevisionNotFoundException, RepositoryNotFoundException, IOException {
|
||||||
return getSource(namespace, name, "/", null);
|
return getSource(namespace, name, "/", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Produces(VndMediaType.SOURCE)
|
@Produces(VndMediaType.SOURCE)
|
||||||
@Path("{revision}")
|
@Path("{revision}")
|
||||||
public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision) {
|
public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision) throws RevisionNotFoundException, RepositoryNotFoundException, IOException {
|
||||||
return getSource(namespace, name, "/", revision);
|
return getSource(namespace, name, "/", revision);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Produces(VndMediaType.SOURCE)
|
@Produces(VndMediaType.SOURCE)
|
||||||
@Path("{revision}/{path: .*}")
|
@Path("{revision}/{path: .*}")
|
||||||
public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision, @PathParam("path") String path) {
|
public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision, @PathParam("path") String path) throws NotFoundException, IOException {
|
||||||
return getSource(namespace, name, path, revision);
|
return getSource(namespace, name, path, revision);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Response getSource(String namespace, String repoName, String path, String revision) {
|
private Response getSource(String namespace, String repoName, String path, String revision) throws IOException, RevisionNotFoundException, RepositoryNotFoundException {
|
||||||
NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, repoName);
|
NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, repoName);
|
||||||
try (RepositoryService repositoryService = serviceFactory.create(namespaceAndName)) {
|
try (RepositoryService repositoryService = serviceFactory.create(namespaceAndName)) {
|
||||||
BrowseCommandBuilder browseCommand = repositoryService.getBrowseCommand();
|
BrowseCommandBuilder browseCommand = repositoryService.getBrowseCommand();
|
||||||
@@ -61,15 +62,10 @@ public class SourceRootResource {
|
|||||||
BrowserResult browserResult = browseCommand.getBrowserResult();
|
BrowserResult browserResult = browseCommand.getBrowserResult();
|
||||||
|
|
||||||
if (browserResult != null) {
|
if (browserResult != null) {
|
||||||
return Response.ok(browserResultToBrowserResultDtoMapper.map(browserResult, namespaceAndName)).build();
|
return Response.ok(browserResultToBrowserResultDtoMapper.map(browserResult, namespaceAndName, path)).build();
|
||||||
} else {
|
} else {
|
||||||
return Response.status(Response.Status.NOT_FOUND).build();
|
return Response.status(Response.Status.NOT_FOUND).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (RepositoryNotFoundException | RevisionNotFoundException e) {
|
|
||||||
return Response.status(Response.Status.NOT_FOUND).build();
|
|
||||||
} catch (IOException e) {
|
|
||||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
|
||||||
|
|
||||||
import javax.ws.rs.DefaultValue;
|
|
||||||
import javax.ws.rs.GET;
|
|
||||||
import javax.ws.rs.Path;
|
|
||||||
import javax.ws.rs.QueryParam;
|
|
||||||
import javax.ws.rs.core.Response;
|
|
||||||
|
|
||||||
public class TagCollectionResource {
|
|
||||||
@GET
|
|
||||||
@Path("")
|
|
||||||
public Response getAll(@DefaultValue("0") @QueryParam("page") int page,
|
|
||||||
@DefaultValue("10") @QueryParam("pageSize") int pageSize,
|
|
||||||
@QueryParam("sortBy") String sortBy,
|
|
||||||
@DefaultValue("false") @QueryParam("desc") boolean desc) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import de.otto.edison.hal.Embedded;
|
||||||
|
import de.otto.edison.hal.HalRepresentation;
|
||||||
|
import de.otto.edison.hal.Links;
|
||||||
|
import sonia.scm.repository.NamespaceAndName;
|
||||||
|
import sonia.scm.repository.Tag;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static de.otto.edison.hal.Embedded.embeddedBuilder;
|
||||||
|
import static de.otto.edison.hal.Links.linkingTo;
|
||||||
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
|
public class TagCollectionToDtoMapper {
|
||||||
|
|
||||||
|
|
||||||
|
private final ResourceLinks resourceLinks;
|
||||||
|
private final TagToTagDtoMapper tagToTagDtoMapper;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public TagCollectionToDtoMapper(ResourceLinks resourceLinks, TagToTagDtoMapper tagToTagDtoMapper) {
|
||||||
|
this.resourceLinks = resourceLinks;
|
||||||
|
this.tagToTagDtoMapper = tagToTagDtoMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HalRepresentation map(String namespace, String name, Collection<Tag> tags) {
|
||||||
|
return new HalRepresentation(createLinks(namespace, name), embedDtos(getTagDtoList(namespace, name, tags)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TagDto> getTagDtoList(String namespace, String name, Collection<Tag> tags) {
|
||||||
|
return tags.stream().map(tag -> tagToTagDtoMapper.map(tag, new NamespaceAndName(namespace, name))).collect(toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Links createLinks(String namespace, String name) {
|
||||||
|
return
|
||||||
|
linkingTo()
|
||||||
|
.self(resourceLinks.tag().all(namespace, name))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Embedded embedDtos(List<TagDto> dtos) {
|
||||||
|
return embeddedBuilder()
|
||||||
|
.with("tags", dtos)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import de.otto.edison.hal.HalRepresentation;
|
||||||
|
import de.otto.edison.hal.Links;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class TagDto extends HalRepresentation {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private String revision;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("squid:S1185") // We want to have this method available in this package
|
||||||
|
protected HalRepresentation add(Links links) {
|
||||||
|
return super.add(links);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import sonia.scm.NotFoundException;
|
||||||
|
|
||||||
|
public class TagNotFoundException extends NotFoundException {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,20 +1,100 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
||||||
|
import sonia.scm.repository.NamespaceAndName;
|
||||||
|
import sonia.scm.repository.Repository;
|
||||||
|
import sonia.scm.repository.RepositoryNotFoundException;
|
||||||
|
import sonia.scm.repository.RepositoryPermissions;
|
||||||
|
import sonia.scm.repository.Tag;
|
||||||
|
import sonia.scm.repository.Tags;
|
||||||
|
import sonia.scm.repository.api.RepositoryService;
|
||||||
|
import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||||
|
import sonia.scm.web.VndMediaType;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Provider;
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
public class TagRootResource {
|
public class TagRootResource {
|
||||||
|
|
||||||
private final Provider<TagCollectionResource> tagCollectionResource;
|
private final RepositoryServiceFactory serviceFactory;
|
||||||
|
private final TagCollectionToDtoMapper tagCollectionToDtoMapper;
|
||||||
|
private final TagToTagDtoMapper tagToTagDtoMapper;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public TagRootResource(Provider<TagCollectionResource> tagCollectionResource) {
|
public TagRootResource(RepositoryServiceFactory serviceFactory,
|
||||||
this.tagCollectionResource = tagCollectionResource;
|
TagCollectionToDtoMapper tagCollectionToDtoMapper,
|
||||||
|
TagToTagDtoMapper tagToTagDtoMapper) {
|
||||||
|
this.serviceFactory = serviceFactory;
|
||||||
|
this.tagCollectionToDtoMapper = tagCollectionToDtoMapper;
|
||||||
|
this.tagToTagDtoMapper = tagToTagDtoMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@GET
|
||||||
@Path("")
|
@Path("")
|
||||||
public TagCollectionResource getTagCollectionResource() {
|
@StatusCodes({
|
||||||
return tagCollectionResource.get();
|
@ResponseCode(code = 200, condition = "success"),
|
||||||
|
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
||||||
|
@ResponseCode(code = 403, condition = "not authorized, the current user has no privileges to read the tags"),
|
||||||
|
@ResponseCode(code = 500, condition = "internal server error")
|
||||||
|
})
|
||||||
|
@Produces(VndMediaType.TAG_COLLECTION)
|
||||||
|
@TypeHint(CollectionDto.class)
|
||||||
|
public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name) throws IOException, RepositoryNotFoundException {
|
||||||
|
try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) {
|
||||||
|
Tags tags = getTags(repositoryService);
|
||||||
|
if (tags != null && tags.getTags() != null) {
|
||||||
|
return Response.ok(tagCollectionToDtoMapper.map(namespace, name, tags.getTags())).build();
|
||||||
|
} else {
|
||||||
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||||
|
.entity("Error on getting tag from repository.")
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@StatusCodes({
|
||||||
|
@ResponseCode(code = 200, condition = "success"),
|
||||||
|
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
||||||
|
@ResponseCode(code = 403, condition = "not authorized, the current user has no privileges to read the tags"),
|
||||||
|
@ResponseCode(code = 404, condition = "not found, no tag available in the repository"),
|
||||||
|
@ResponseCode(code = 500, condition = "internal server error")
|
||||||
|
})
|
||||||
|
@Produces(VndMediaType.TAG)
|
||||||
|
@TypeHint(TagDto.class)
|
||||||
|
@Path("{tagName}")
|
||||||
|
public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("tagName") String tagName) throws IOException, RepositoryNotFoundException, TagNotFoundException {
|
||||||
|
NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name);
|
||||||
|
try (RepositoryService repositoryService = serviceFactory.create(namespaceAndName)) {
|
||||||
|
Tags tags = getTags(repositoryService);
|
||||||
|
if (tags != null && tags.getTags() != null) {
|
||||||
|
Tag tag = tags.getTags().stream()
|
||||||
|
.filter(t -> tagName.equals(t.getName()))
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow(TagNotFoundException::new);
|
||||||
|
return Response.ok(tagToTagDtoMapper.map(tag, namespaceAndName)).build();
|
||||||
|
} else {
|
||||||
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||||
|
.entity("Error on getting tag from repository.")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tags getTags(RepositoryService repositoryService) throws IOException {
|
||||||
|
Repository repository = repositoryService.getRepository();
|
||||||
|
RepositoryPermissions.read(repository).check();
|
||||||
|
return repositoryService.getTagsCommand().getTags();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import de.otto.edison.hal.Links;
|
||||||
|
import org.mapstruct.AfterMapping;
|
||||||
|
import org.mapstruct.Context;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.MappingTarget;
|
||||||
|
import sonia.scm.repository.NamespaceAndName;
|
||||||
|
import sonia.scm.repository.Tag;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static de.otto.edison.hal.Link.link;
|
||||||
|
import static de.otto.edison.hal.Links.linkingTo;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public abstract class TagToTagDtoMapper {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ResourceLinks resourceLinks;
|
||||||
|
|
||||||
|
@Mapping(target = "attributes", ignore = true) // We do not map HAL attributes
|
||||||
|
public abstract TagDto map(Tag tag, @Context NamespaceAndName namespaceAndName);
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
void appendLinks(@MappingTarget TagDto target, @Context NamespaceAndName namespaceAndName) {
|
||||||
|
Links.Builder linksBuilder = linkingTo()
|
||||||
|
.self(resourceLinks.tag().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getName()))
|
||||||
|
.single(link("sources", resourceLinks.source().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getRevision())))
|
||||||
|
.single(link("changesets", resourceLinks.changeset().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getRevision())));
|
||||||
|
target.add(linksBuilder.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,18 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import sonia.scm.PageResult;
|
||||||
import sonia.scm.user.User;
|
import sonia.scm.user.User;
|
||||||
import sonia.scm.user.UserPermissions;
|
import sonia.scm.user.UserPermissions;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static java.util.Optional.empty;
|
||||||
|
import static java.util.Optional.of;
|
||||||
|
|
||||||
// Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection.
|
// Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection.
|
||||||
@SuppressWarnings("squid:S3306")
|
@SuppressWarnings("squid:S3306")
|
||||||
public class UserCollectionToDtoMapper extends BasicCollectionToDtoMapper<User, UserDto> {
|
public class UserCollectionToDtoMapper extends BasicCollectionToDtoMapper<User, UserDto, UserToUserDtoMapper> {
|
||||||
|
|
||||||
private final ResourceLinks resourceLinks;
|
private final ResourceLinks resourceLinks;
|
||||||
|
|
||||||
@@ -17,18 +22,15 @@ public class UserCollectionToDtoMapper extends BasicCollectionToDtoMapper<User,
|
|||||||
this.resourceLinks = resourceLinks;
|
this.resourceLinks = resourceLinks;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public CollectionDto map(int pageNumber, int pageSize, PageResult<User> pageResult) {
|
||||||
String createCreateLink() {
|
return map(pageNumber, pageSize, pageResult, this.createSelfLink(), this.createCreateLink());
|
||||||
return resourceLinks.userCollection().create();
|
}
|
||||||
|
|
||||||
|
Optional<String> createCreateLink() {
|
||||||
|
return UserPermissions.create().isPermitted() ? of(resourceLinks.userCollection().create()): empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
String createSelfLink() {
|
String createSelfLink() {
|
||||||
return resourceLinks.userCollection().self();
|
return resourceLinks.userCollection().self();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean isCreatePermitted() {
|
|
||||||
return UserPermissions.create().isPermitted();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,34 +39,27 @@ import com.google.common.collect.ImmutableSet;
|
|||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import com.google.common.hash.Hashing;
|
import com.google.common.hash.Hashing;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import sonia.scm.plugin.ExplodedSmp.PathTransformer;
|
import sonia.scm.plugin.ExplodedSmp.PathTransformer;
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
import javax.xml.bind.JAXBContext;
|
||||||
|
import javax.xml.bind.JAXBException;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
import java.nio.file.DirectoryStream;
|
import java.nio.file.DirectoryStream;
|
||||||
import java.nio.file.DirectoryStream.Filter;
|
import java.nio.file.DirectoryStream.Filter;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.xml.bind.JAXBContext;
|
//~--- JDK imports ------------------------------------------------------------
|
||||||
import javax.xml.bind.JAXBException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -370,11 +363,12 @@ public final class PluginProcessor
|
|||||||
|
|
||||||
if (Files.exists(libDir))
|
if (Files.exists(libDir))
|
||||||
{
|
{
|
||||||
for (Path f : Files.newDirectoryStream(libDir, GLOB_JAR))
|
try (DirectoryStream<Path> pathDirectoryStream = Files.newDirectoryStream(libDir, GLOB_JAR)) {
|
||||||
{
|
for (Path f : pathDirectoryStream) {
|
||||||
urls.add(f.toUri().toURL());
|
urls.add(f.toUri().toURL());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ClassLoader classLoader;
|
ClassLoader classLoader;
|
||||||
URL[] urlArray = urls.toArray(new URL[urls.size()]);
|
URL[] urlArray = urls.toArray(new URL[urls.size()]);
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.shiro.subject.Subject;
|
||||||
|
import org.apache.shiro.subject.support.SubjectThreadState;
|
||||||
|
import org.apache.shiro.util.ThreadContext;
|
||||||
|
import org.apache.shiro.util.ThreadState;
|
||||||
|
import org.assertj.core.util.Lists;
|
||||||
import org.jboss.resteasy.core.Dispatcher;
|
import org.jboss.resteasy.core.Dispatcher;
|
||||||
import org.jboss.resteasy.mock.MockDispatcherFactory;
|
import org.jboss.resteasy.mock.MockDispatcherFactory;
|
||||||
import org.jboss.resteasy.mock.MockHttpRequest;
|
import org.jboss.resteasy.mock.MockHttpRequest;
|
||||||
import org.jboss.resteasy.mock.MockHttpResponse;
|
import org.jboss.resteasy.mock.MockHttpResponse;
|
||||||
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@@ -12,20 +19,35 @@ import org.mockito.Mock;
|
|||||||
import org.mockito.junit.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
import sonia.scm.repository.Branch;
|
import sonia.scm.repository.Branch;
|
||||||
import sonia.scm.repository.Branches;
|
import sonia.scm.repository.Branches;
|
||||||
|
import sonia.scm.repository.Changeset;
|
||||||
|
import sonia.scm.repository.ChangesetPagingResult;
|
||||||
import sonia.scm.repository.NamespaceAndName;
|
import sonia.scm.repository.NamespaceAndName;
|
||||||
|
import sonia.scm.repository.Person;
|
||||||
|
import sonia.scm.repository.Repository;
|
||||||
import sonia.scm.repository.api.BranchesCommandBuilder;
|
import sonia.scm.repository.api.BranchesCommandBuilder;
|
||||||
|
import sonia.scm.repository.api.LogCommandBuilder;
|
||||||
import sonia.scm.repository.api.RepositoryService;
|
import sonia.scm.repository.api.RepositoryService;
|
||||||
import sonia.scm.repository.api.RepositoryServiceFactory;
|
import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.Silent.class)
|
@RunWith(MockitoJUnitRunner.Silent.class)
|
||||||
|
@Slf4j
|
||||||
public class BranchRootResourceTest {
|
public class BranchRootResourceTest {
|
||||||
|
|
||||||
|
public static final String BRANCH_PATH = "space/repo/branches/master";
|
||||||
|
public static final String BRANCH_URL = "/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + BRANCH_PATH;
|
||||||
private final Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
|
private final Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
|
||||||
|
|
||||||
private final URI baseUri = URI.create("/");
|
private final URI baseUri = URI.create("/");
|
||||||
@@ -38,25 +60,62 @@ public class BranchRootResourceTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private BranchesCommandBuilder branchesCommandBuilder;
|
private BranchesCommandBuilder branchesCommandBuilder;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private LogCommandBuilder logCommandBuilder;
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private BranchToBranchDtoMapperImpl branchToDtoMapper;
|
private BranchToBranchDtoMapperImpl branchToDtoMapper;
|
||||||
|
|
||||||
|
private ChangesetCollectionToDtoMapper changesetCollectionToDtoMapper;
|
||||||
|
|
||||||
|
private BranchRootResource branchRootResource;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private BranchCollectionToDtoMapper branchCollectionToDtoMapper;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ChangesetToParentDtoMapper changesetToParentDtoMapper;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private TagCollectionToDtoMapper tagCollectionToDtoMapper;
|
||||||
|
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private ChangesetToChangesetDtoMapperImpl changesetToChangesetDtoMapper;
|
||||||
|
|
||||||
|
private final Subject subject = mock(Subject.class);
|
||||||
|
private final ThreadState subjectThreadState = new SubjectThreadState(subject);
|
||||||
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void prepareEnvironment() throws Exception {
|
public void prepareEnvironment() throws Exception {
|
||||||
|
changesetCollectionToDtoMapper = new ChangesetCollectionToDtoMapper(changesetToChangesetDtoMapper, resourceLinks);
|
||||||
BranchCollectionToDtoMapper branchCollectionToDtoMapper = new BranchCollectionToDtoMapper(branchToDtoMapper, resourceLinks);
|
BranchCollectionToDtoMapper branchCollectionToDtoMapper = new BranchCollectionToDtoMapper(branchToDtoMapper, resourceLinks);
|
||||||
BranchRootResource branchRootResource = new BranchRootResource(serviceFactory, branchToDtoMapper, branchCollectionToDtoMapper);
|
branchRootResource = new BranchRootResource(serviceFactory, branchToDtoMapper, branchCollectionToDtoMapper, changesetCollectionToDtoMapper);
|
||||||
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider.of(new RepositoryResource(null, null, null, null, MockProvider.of(branchRootResource), null, null, null, null)), null);
|
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider.of(new RepositoryResource(null, null, null, null, MockProvider.of(branchRootResource), null, null, null, null, null)), null);
|
||||||
dispatcher.getRegistry().addSingletonResource(repositoryRootResource);
|
dispatcher.getRegistry().addSingletonResource(repositoryRootResource);
|
||||||
|
|
||||||
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(service);
|
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(service);
|
||||||
|
when(serviceFactory.create(any(Repository.class))).thenReturn(service);
|
||||||
|
when(service.getRepository()).thenReturn(new Repository("repoId", "git", "space", "repo"));
|
||||||
|
|
||||||
when(service.getBranchesCommand()).thenReturn(branchesCommandBuilder);
|
when(service.getBranchesCommand()).thenReturn(branchesCommandBuilder);
|
||||||
|
when(service.getLogCommand()).thenReturn(logCommandBuilder);
|
||||||
|
subjectThreadState.bind();
|
||||||
|
ThreadContext.bind(subject);
|
||||||
|
when(subject.isPermitted(any(String.class))).thenReturn(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void cleanupContext() {
|
||||||
|
ThreadContext.unbindSubject();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldHandleMissingBranch() throws Exception {
|
public void shouldHandleMissingBranch() throws Exception {
|
||||||
when(branchesCommandBuilder.getBranches()).thenReturn(new Branches());
|
when(branchesCommandBuilder.getBranches()).thenReturn(new Branches());
|
||||||
|
|
||||||
MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/branches/master");
|
MockHttpRequest request = MockHttpRequest.get(BRANCH_URL);
|
||||||
MockHttpResponse response = new MockHttpResponse();
|
MockHttpResponse response = new MockHttpResponse();
|
||||||
|
|
||||||
dispatcher.invoke(request, response);
|
dispatcher.invoke(request, response);
|
||||||
@@ -68,13 +127,40 @@ public class BranchRootResourceTest {
|
|||||||
public void shouldFindExistingBranch() throws Exception {
|
public void shouldFindExistingBranch() throws Exception {
|
||||||
when(branchesCommandBuilder.getBranches()).thenReturn(new Branches(new Branch("master", "revision")));
|
when(branchesCommandBuilder.getBranches()).thenReturn(new Branches(new Branch("master", "revision")));
|
||||||
|
|
||||||
MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/branches/master");
|
MockHttpRequest request = MockHttpRequest.get(BRANCH_URL);
|
||||||
MockHttpResponse response = new MockHttpResponse();
|
MockHttpResponse response = new MockHttpResponse();
|
||||||
|
|
||||||
dispatcher.invoke(request, response);
|
dispatcher.invoke(request, response);
|
||||||
|
|
||||||
assertEquals(200, response.getStatus());
|
assertEquals(200, response.getStatus());
|
||||||
System.out.println(response.getContentAsString());
|
log.info("Response :{}", response.getContentAsString());
|
||||||
assertTrue(response.getContentAsString().contains("\"revision\":\"revision\""));
|
assertTrue(response.getContentAsString().contains("\"revision\":\"revision\""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldFindHistory() throws Exception {
|
||||||
|
String id = "revision_123";
|
||||||
|
Instant creationDate = Instant.now();
|
||||||
|
String authorName = "name";
|
||||||
|
String authorEmail = "em@i.l";
|
||||||
|
String commit = "my branch commit";
|
||||||
|
ChangesetPagingResult changesetPagingResult = mock(ChangesetPagingResult.class);
|
||||||
|
List<Changeset> changesetList = Lists.newArrayList(new Changeset(id, Date.from(creationDate).getTime(), new Person(authorName, authorEmail), commit));
|
||||||
|
when(changesetPagingResult.getChangesets()).thenReturn(changesetList);
|
||||||
|
when(changesetPagingResult.getTotal()).thenReturn(1);
|
||||||
|
when(logCommandBuilder.setPagingStart(anyInt())).thenReturn(logCommandBuilder);
|
||||||
|
when(logCommandBuilder.setPagingLimit(anyInt())).thenReturn(logCommandBuilder);
|
||||||
|
when(logCommandBuilder.setBranch(anyString())).thenReturn(logCommandBuilder);
|
||||||
|
when(logCommandBuilder.getChangesets()).thenReturn(changesetPagingResult);
|
||||||
|
MockHttpRequest request = MockHttpRequest.get(BRANCH_URL + "/changesets/");
|
||||||
|
MockHttpResponse response = new MockHttpResponse();
|
||||||
|
dispatcher.invoke(request, response);
|
||||||
|
assertEquals(200, response.getStatus());
|
||||||
|
log.info("Response :{}", response.getContentAsString());
|
||||||
|
assertTrue(response.getContentAsString().contains(String.format("\"id\":\"%s\"", id)));
|
||||||
|
assertTrue(response.getContentAsString().contains(String.format("\"name\":\"%s\"", authorName)));
|
||||||
|
assertTrue(response.getContentAsString().contains(String.format("\"mail\":\"%s\"", authorEmail)));
|
||||||
|
assertTrue(response.getContentAsString().contains(String.format("\"description\":\"%s\"", commit)));
|
||||||
|
assertTrue(response.getContentAsString().contains(String.format("\"description\":\"%s\"", commit)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,11 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
import static org.mockito.MockitoAnnotations.initMocks;
|
import static org.mockito.MockitoAnnotations.initMocks;
|
||||||
|
|
||||||
public class BrowserResultToBrowserResultDtoMapperTest {
|
public class BrowserResultToBrowserResultDtoMapperTest {
|
||||||
@@ -60,6 +63,9 @@ public class BrowserResultToBrowserResultDtoMapperTest {
|
|||||||
fileObject2.setPath("/path/object/2");
|
fileObject2.setPath("/path/object/2");
|
||||||
fileObject2.setDescription("description of file object 2");
|
fileObject2.setDescription("description of file object 2");
|
||||||
fileObject2.setDirectory(true);
|
fileObject2.setDirectory(true);
|
||||||
|
|
||||||
|
when(fileObjectToFileObjectDtoMapper.map(any(FileObject.class), any(NamespaceAndName.class), anyString()))
|
||||||
|
.thenReturn(new FileObjectDto());
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@@ -71,7 +77,7 @@ public class BrowserResultToBrowserResultDtoMapperTest {
|
|||||||
public void shouldMapAttributesCorrectly() {
|
public void shouldMapAttributesCorrectly() {
|
||||||
BrowserResult browserResult = createBrowserResult();
|
BrowserResult browserResult = createBrowserResult();
|
||||||
|
|
||||||
BrowserResultDto dto = mapper.map(browserResult, new NamespaceAndName("foo", "bar"));
|
BrowserResultDto dto = mapper.map(browserResult, new NamespaceAndName("foo", "bar"), "path");
|
||||||
|
|
||||||
assertEqualAttributes(browserResult, dto);
|
assertEqualAttributes(browserResult, dto);
|
||||||
}
|
}
|
||||||
@@ -81,17 +87,25 @@ public class BrowserResultToBrowserResultDtoMapperTest {
|
|||||||
BrowserResult browserResult = createBrowserResult();
|
BrowserResult browserResult = createBrowserResult();
|
||||||
NamespaceAndName namespaceAndName = new NamespaceAndName("foo", "bar");
|
NamespaceAndName namespaceAndName = new NamespaceAndName("foo", "bar");
|
||||||
|
|
||||||
BrowserResultDto dto = mapper.map(browserResult, namespaceAndName);
|
BrowserResultDto dto = mapper.map(browserResult, namespaceAndName, "path");
|
||||||
|
|
||||||
verify(fileObjectToFileObjectDtoMapper).map(fileObject1, namespaceAndName, "Revision");
|
verify(fileObjectToFileObjectDtoMapper).map(fileObject1, namespaceAndName, "Revision");
|
||||||
verify(fileObjectToFileObjectDtoMapper).map(fileObject2, namespaceAndName, "Revision");
|
verify(fileObjectToFileObjectDtoMapper).map(fileObject2, namespaceAndName, "Revision");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldSetLinksCorrectly() {
|
||||||
|
BrowserResult browserResult = createBrowserResult();
|
||||||
|
NamespaceAndName namespaceAndName = new NamespaceAndName("foo", "bar");
|
||||||
|
|
||||||
|
BrowserResultDto dto = mapper.map(browserResult, namespaceAndName, "path");
|
||||||
|
|
||||||
|
assertThat(dto.getLinks().getLinkBy("self").get().getHref()).contains("path");
|
||||||
|
}
|
||||||
|
|
||||||
private BrowserResult createBrowserResult() {
|
private BrowserResult createBrowserResult() {
|
||||||
BrowserResult browserResult = new BrowserResult();
|
BrowserResult browserResult = new BrowserResult();
|
||||||
browserResult.setTag("Tag");
|
|
||||||
browserResult.setRevision("Revision");
|
browserResult.setRevision("Revision");
|
||||||
browserResult.setBranch("Branch");
|
|
||||||
browserResult.setFiles(createFileObjects());
|
browserResult.setFiles(createFileObjects());
|
||||||
|
|
||||||
return browserResult;
|
return browserResult;
|
||||||
@@ -106,8 +120,6 @@ public class BrowserResultToBrowserResultDtoMapperTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void assertEqualAttributes(BrowserResult browserResult, BrowserResultDto dto) {
|
private void assertEqualAttributes(BrowserResult browserResult, BrowserResultDto dto) {
|
||||||
assertThat(dto.getTag()).isEqualTo(browserResult.getTag());
|
|
||||||
assertThat(dto.getBranch()).isEqualTo(browserResult.getBranch());
|
|
||||||
assertThat(dto.getRevision()).isEqualTo(browserResult.getRevision());
|
assertThat(dto.getRevision()).isEqualTo(browserResult.getRevision());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.shiro.subject.Subject;
|
||||||
|
import org.apache.shiro.subject.support.SubjectThreadState;
|
||||||
|
import org.apache.shiro.util.ThreadContext;
|
||||||
|
import org.apache.shiro.util.ThreadState;
|
||||||
|
import org.assertj.core.util.Lists;
|
||||||
|
import org.jboss.resteasy.core.Dispatcher;
|
||||||
|
import org.jboss.resteasy.mock.MockDispatcherFactory;
|
||||||
|
import org.jboss.resteasy.mock.MockHttpRequest;
|
||||||
|
import org.jboss.resteasy.mock.MockHttpResponse;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
import sonia.scm.api.rest.AuthorizationExceptionMapper;
|
||||||
|
import sonia.scm.repository.Changeset;
|
||||||
|
import sonia.scm.repository.ChangesetPagingResult;
|
||||||
|
import sonia.scm.repository.NamespaceAndName;
|
||||||
|
import sonia.scm.repository.Person;
|
||||||
|
import sonia.scm.repository.Repository;
|
||||||
|
import sonia.scm.repository.api.LogCommandBuilder;
|
||||||
|
import sonia.scm.repository.api.RepositoryService;
|
||||||
|
import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||||
|
import sonia.scm.web.VndMediaType;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.Silent.class)
|
||||||
|
@Slf4j
|
||||||
|
public class ChangesetRootResourceTest {
|
||||||
|
|
||||||
|
|
||||||
|
public static final String CHANGESET_PATH = "space/repo/changesets/";
|
||||||
|
public static final String CHANGESET_URL = "/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + CHANGESET_PATH;
|
||||||
|
private final Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
|
||||||
|
|
||||||
|
private final URI baseUri = URI.create("/");
|
||||||
|
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private RepositoryServiceFactory serviceFactory;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private RepositoryService repositoryService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private LogCommandBuilder logCommandBuilder;
|
||||||
|
|
||||||
|
private ChangesetCollectionToDtoMapper changesetCollectionToDtoMapper;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private ChangesetToChangesetDtoMapperImpl changesetToChangesetDtoMapper;
|
||||||
|
|
||||||
|
private ChangesetRootResource changesetRootResource;
|
||||||
|
|
||||||
|
|
||||||
|
private final Subject subject = mock(Subject.class);
|
||||||
|
private final ThreadState subjectThreadState = new SubjectThreadState(subject);
|
||||||
|
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void prepareEnvironment() throws Exception {
|
||||||
|
changesetCollectionToDtoMapper = new ChangesetCollectionToDtoMapper(changesetToChangesetDtoMapper, resourceLinks);
|
||||||
|
changesetRootResource = new ChangesetRootResource(serviceFactory, changesetCollectionToDtoMapper, changesetToChangesetDtoMapper);
|
||||||
|
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider
|
||||||
|
.of(new RepositoryResource(null, null, null, null, null,
|
||||||
|
MockProvider.of(changesetRootResource), null, null, null, null)), null);
|
||||||
|
dispatcher.getRegistry().addSingletonResource(repositoryRootResource);
|
||||||
|
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(repositoryService);
|
||||||
|
when(serviceFactory.create(any(Repository.class))).thenReturn(repositoryService);
|
||||||
|
when(repositoryService.getRepository()).thenReturn(new Repository("repoId", "git", "space", "repo"));
|
||||||
|
dispatcher.getProviderFactory().registerProvider(NotFoundExceptionMapper.class);
|
||||||
|
dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class);
|
||||||
|
when(repositoryService.getLogCommand()).thenReturn(logCommandBuilder);
|
||||||
|
subjectThreadState.bind();
|
||||||
|
ThreadContext.bind(subject);
|
||||||
|
when(subject.isPermitted(any(String.class))).thenReturn(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void cleanupContext() {
|
||||||
|
ThreadContext.unbindSubject();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldGetChangeSets() throws Exception {
|
||||||
|
String id = "revision_123";
|
||||||
|
Instant creationDate = Instant.now();
|
||||||
|
String authorName = "name";
|
||||||
|
String authorEmail = "em@i.l";
|
||||||
|
String commit = "my branch commit";
|
||||||
|
ChangesetPagingResult changesetPagingResult = mock(ChangesetPagingResult.class);
|
||||||
|
List<Changeset> changesetList = Lists.newArrayList(new Changeset(id, Date.from(creationDate).getTime(), new Person(authorName, authorEmail), commit));
|
||||||
|
when(changesetPagingResult.getChangesets()).thenReturn(changesetList);
|
||||||
|
when(changesetPagingResult.getTotal()).thenReturn(1);
|
||||||
|
when(logCommandBuilder.setPagingStart(anyInt())).thenReturn(logCommandBuilder);
|
||||||
|
when(logCommandBuilder.setPagingLimit(anyInt())).thenReturn(logCommandBuilder);
|
||||||
|
when(logCommandBuilder.setBranch(anyString())).thenReturn(logCommandBuilder);
|
||||||
|
when(logCommandBuilder.getChangesets()).thenReturn(changesetPagingResult);
|
||||||
|
MockHttpRequest request = MockHttpRequest
|
||||||
|
.get(CHANGESET_URL)
|
||||||
|
.accept(VndMediaType.CHANGESET_COLLECTION);
|
||||||
|
MockHttpResponse response = new MockHttpResponse();
|
||||||
|
dispatcher.invoke(request, response);
|
||||||
|
assertEquals(200, response.getStatus());
|
||||||
|
log.info("Response :{}", response.getContentAsString());
|
||||||
|
assertTrue(response.getContentAsString().contains(String.format("\"id\":\"%s\"", id)));
|
||||||
|
assertTrue(response.getContentAsString().contains(String.format("\"name\":\"%s\"", authorName)));
|
||||||
|
assertTrue(response.getContentAsString().contains(String.format("\"mail\":\"%s\"", authorEmail)));
|
||||||
|
assertTrue(response.getContentAsString().contains(String.format("\"description\":\"%s\"", commit)));
|
||||||
|
assertTrue(response.getContentAsString().contains(String.format("\"description\":\"%s\"", commit)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldGetChangeSet() throws Exception {
|
||||||
|
String id = "revision_123";
|
||||||
|
Instant creationDate = Instant.now();
|
||||||
|
String authorName = "name";
|
||||||
|
String authorEmail = "em@i.l";
|
||||||
|
String commit = "my branch commit";
|
||||||
|
ChangesetPagingResult changesetPagingResult = mock(ChangesetPagingResult.class);
|
||||||
|
List<Changeset> changesetList = Lists.newArrayList(new Changeset(id, Date.from(creationDate).getTime(), new Person(authorName, authorEmail), commit));
|
||||||
|
when(changesetPagingResult.getChangesets()).thenReturn(changesetList);
|
||||||
|
when(changesetPagingResult.getTotal()).thenReturn(1);
|
||||||
|
when(logCommandBuilder.setPagingStart(anyInt())).thenReturn(logCommandBuilder);
|
||||||
|
when(logCommandBuilder.setPagingLimit(anyInt())).thenReturn(logCommandBuilder);
|
||||||
|
when(logCommandBuilder.setEndChangeset(anyString())).thenReturn(logCommandBuilder);
|
||||||
|
when(logCommandBuilder.setStartChangeset(anyString())).thenReturn(logCommandBuilder);
|
||||||
|
when(logCommandBuilder.getChangesets()).thenReturn(changesetPagingResult);
|
||||||
|
MockHttpRequest request = MockHttpRequest
|
||||||
|
.get(CHANGESET_URL + "id")
|
||||||
|
.accept(VndMediaType.CHANGESET);
|
||||||
|
MockHttpResponse response = new MockHttpResponse();
|
||||||
|
dispatcher.invoke(request, response);
|
||||||
|
assertEquals(200, response.getStatus());
|
||||||
|
log.info("Response :{}", response.getContentAsString());
|
||||||
|
assertTrue(response.getContentAsString().contains(String.format("\"id\":\"%s\"", id)));
|
||||||
|
assertTrue(response.getContentAsString().contains(String.format("\"name\":\"%s\"", authorName)));
|
||||||
|
assertTrue(response.getContentAsString().contains(String.format("\"mail\":\"%s\"", authorEmail)));
|
||||||
|
assertTrue(response.getContentAsString().contains(String.format("\"description\":\"%s\"", commit)));
|
||||||
|
assertTrue(response.getContentAsString().contains(String.format("\"description\":\"%s\"", commit)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -39,6 +39,7 @@ import java.net.URI;
|
|||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
@@ -55,6 +56,7 @@ import static org.mockito.Mockito.mock;
|
|||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static org.mockito.MockitoAnnotations.initMocks;
|
import static org.mockito.MockitoAnnotations.initMocks;
|
||||||
import static sonia.scm.api.v2.resources.DispatcherMock.createDispatcher;
|
import static sonia.scm.api.v2.resources.DispatcherMock.createDispatcher;
|
||||||
|
import static sonia.scm.api.v2.resources.PermissionDto.GROUP_PREFIX;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@SubjectAware(
|
@SubjectAware(
|
||||||
@@ -136,7 +138,7 @@ public class PermissionRootResourceTest {
|
|||||||
permissionCollectionToDtoMapper = new PermissionCollectionToDtoMapper(permissionToPermissionDtoMapper, resourceLinks);
|
permissionCollectionToDtoMapper = new PermissionCollectionToDtoMapper(permissionToPermissionDtoMapper, resourceLinks);
|
||||||
permissionRootResource = new PermissionRootResource(permissionDtoToPermissionMapper, permissionToPermissionDtoMapper, permissionCollectionToDtoMapper, resourceLinks, repositoryManager);
|
permissionRootResource = new PermissionRootResource(permissionDtoToPermissionMapper, permissionToPermissionDtoMapper, permissionCollectionToDtoMapper, resourceLinks, repositoryManager);
|
||||||
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider
|
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider
|
||||||
.of(new RepositoryResource(null, null, null, null, null, null, null, null, MockProvider.of(permissionRootResource))), null);
|
.of(new RepositoryResource(null, null, null, null, null, null, null, null, MockProvider.of(permissionRootResource), null)), null);
|
||||||
dispatcher = createDispatcher(repositoryRootResource);
|
dispatcher = createDispatcher(repositoryRootResource);
|
||||||
subjectThreadState.bind();
|
subjectThreadState.bind();
|
||||||
ThreadContext.bind(subject);
|
ThreadContext.bind(subject);
|
||||||
@@ -253,7 +255,7 @@ public class PermissionRootResourceTest {
|
|||||||
createUserWithRepositoryAndPermissions(TEST_PERMISSIONS, PERMISSION_WRITE);
|
createUserWithRepositoryAndPermissions(TEST_PERMISSIONS, PERMISSION_WRITE);
|
||||||
Permission newPermission = TEST_PERMISSIONS.get(0);
|
Permission newPermission = TEST_PERMISSIONS.get(0);
|
||||||
assertExpectedRequest(requestPOSTPermission
|
assertExpectedRequest(requestPOSTPermission
|
||||||
.content("{\"name\" : \"" + newPermission.getName() + "\" , \"type\" : \"WRITE\" , \"groupPermission\" : true}")
|
.content("{\"name\" : \"" + newPermission.getName() + "\" , \"type\" : \"WRITE\" , \"groupPermission\" : false}")
|
||||||
.expectedResponseStatus(409)
|
.expectedResponseStatus(409)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -358,7 +360,10 @@ public class PermissionRootResourceTest {
|
|||||||
result.setName(permission.getName());
|
result.setName(permission.getName());
|
||||||
result.setGroupPermission(permission.isGroupPermission());
|
result.setGroupPermission(permission.isGroupPermission());
|
||||||
result.setType(permission.getType().name());
|
result.setType(permission.getType().name());
|
||||||
String permissionHref = "/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + PATH_OF_ALL_PERMISSIONS + permission.getName();
|
String permissionName = Optional.of(permission.getName())
|
||||||
|
.filter(p -> !permission.isGroupPermission())
|
||||||
|
.orElse(GROUP_PREFIX + permission.getName());
|
||||||
|
String permissionHref = "/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + PATH_OF_ALL_PERMISSIONS + permissionName;
|
||||||
if (PERMISSION_READ.equals(userPermission)) {
|
if (PERMISSION_READ.equals(userPermission)) {
|
||||||
result.add(linkingTo()
|
result.add(linkingTo()
|
||||||
.self(permissionHref)
|
.self(permissionHref)
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import com.github.sdorra.shiro.ShiroRule;
|
||||||
|
import com.github.sdorra.shiro.SubjectAware;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
import sonia.scm.repository.Permission;
|
||||||
|
import sonia.scm.repository.PermissionType;
|
||||||
|
import sonia.scm.repository.Repository;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.Silent.class)
|
||||||
|
@SubjectAware(
|
||||||
|
configuration = "classpath:sonia/scm/repository/shiro.ini"
|
||||||
|
)
|
||||||
|
public class PermissionToPermissionDtoMapperTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public ShiroRule shiro = new ShiroRule();
|
||||||
|
|
||||||
|
private final URI baseUri = URI.create("http://example.com/base/");
|
||||||
|
|
||||||
|
@SuppressWarnings("unused") // Is injected
|
||||||
|
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
PermissionToPermissionDtoMapperImpl mapper;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SubjectAware(username = "trillian", password = "secret")
|
||||||
|
public void shouldMapGroupPermissionCorrectly() {
|
||||||
|
Repository repository = getDummyRepository();
|
||||||
|
Permission permission = new Permission("42", PermissionType.OWNER, true);
|
||||||
|
|
||||||
|
PermissionDto permissionDto = mapper.map(permission, repository);
|
||||||
|
|
||||||
|
assertThat(permissionDto.getLinks().getLinkBy("self").isPresent()).isTrue();
|
||||||
|
assertThat(permissionDto.getLinks().getLinkBy("self").get().getHref()).contains("@42");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SubjectAware(username = "trillian", password = "secret")
|
||||||
|
public void shouldMapNonGroupPermissionCorrectly() {
|
||||||
|
Repository repository = getDummyRepository();
|
||||||
|
Permission permission = new Permission("42", PermissionType.OWNER, false);
|
||||||
|
|
||||||
|
PermissionDto permissionDto = mapper.map(permission, repository);
|
||||||
|
|
||||||
|
assertThat(permissionDto.getLinks().getLinkBy("self").isPresent()).isTrue();
|
||||||
|
assertThat(permissionDto.getLinks().getLinkBy("self").get().getHref()).contains("42");
|
||||||
|
assertThat(permissionDto.getLinks().getLinkBy("self").get().getHref()).doesNotContain("@");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Repository getDummyRepository() {
|
||||||
|
return new Repository("repo", "git", "foo", "bar");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -79,7 +79,7 @@ public class RepositoryRootResourceTest {
|
|||||||
@Before
|
@Before
|
||||||
public void prepareEnvironment() {
|
public void prepareEnvironment() {
|
||||||
initMocks(this);
|
initMocks(this);
|
||||||
RepositoryResource repositoryResource = new RepositoryResource(repositoryToDtoMapper, dtoToRepositoryMapper, repositoryManager, null, null, null, null, null, null);
|
RepositoryResource repositoryResource = new RepositoryResource(repositoryToDtoMapper, dtoToRepositoryMapper, repositoryManager, null, null, null, null, null, null, null);
|
||||||
RepositoryCollectionToDtoMapper repositoryCollectionToDtoMapper = new RepositoryCollectionToDtoMapper(repositoryToDtoMapper, resourceLinks);
|
RepositoryCollectionToDtoMapper repositoryCollectionToDtoMapper = new RepositoryCollectionToDtoMapper(repositoryToDtoMapper, resourceLinks);
|
||||||
RepositoryCollectionResource repositoryCollectionResource = new RepositoryCollectionResource(repositoryManager, repositoryCollectionToDtoMapper, dtoToRepositoryMapper, resourceLinks);
|
RepositoryCollectionResource repositoryCollectionResource = new RepositoryCollectionResource(repositoryManager, repositoryCollectionToDtoMapper, dtoToRepositoryMapper, resourceLinks);
|
||||||
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider.of(repositoryResource), MockProvider.of(repositoryCollectionResource));
|
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider.of(repositoryResource), MockProvider.of(repositoryCollectionResource));
|
||||||
|
|||||||
@@ -19,13 +19,14 @@ public class ResourceLinksMock {
|
|||||||
when(resourceLinks.groupCollection()).thenReturn(new ResourceLinks.GroupCollectionLinks(uriInfo));
|
when(resourceLinks.groupCollection()).thenReturn(new ResourceLinks.GroupCollectionLinks(uriInfo));
|
||||||
when(resourceLinks.repository()).thenReturn(new ResourceLinks.RepositoryLinks(uriInfo));
|
when(resourceLinks.repository()).thenReturn(new ResourceLinks.RepositoryLinks(uriInfo));
|
||||||
when(resourceLinks.repositoryCollection()).thenReturn(new ResourceLinks.RepositoryCollectionLinks(uriInfo));
|
when(resourceLinks.repositoryCollection()).thenReturn(new ResourceLinks.RepositoryCollectionLinks(uriInfo));
|
||||||
when(resourceLinks.tagCollection()).thenReturn(new ResourceLinks.TagCollectionLinks(uriInfo));
|
when(resourceLinks.tag()).thenReturn(new ResourceLinks.TagCollectionLinks(uriInfo));
|
||||||
when(resourceLinks.branchCollection()).thenReturn(new ResourceLinks.BranchCollectionLinks(uriInfo));
|
when(resourceLinks.branchCollection()).thenReturn(new ResourceLinks.BranchCollectionLinks(uriInfo));
|
||||||
when(resourceLinks.changeset()).thenReturn(new ResourceLinks.ChangesetLinks(uriInfo));
|
when(resourceLinks.changeset()).thenReturn(new ResourceLinks.ChangesetLinks(uriInfo));
|
||||||
when(resourceLinks.source()).thenReturn(new ResourceLinks.SourceLinks(uriInfo));
|
when(resourceLinks.source()).thenReturn(new ResourceLinks.SourceLinks(uriInfo));
|
||||||
when(resourceLinks.permission()).thenReturn(new ResourceLinks.PermissionLinks(uriInfo));
|
when(resourceLinks.permission()).thenReturn(new ResourceLinks.PermissionLinks(uriInfo));
|
||||||
when(resourceLinks.config()).thenReturn(new ResourceLinks.ConfigLinks(uriInfo));
|
when(resourceLinks.config()).thenReturn(new ResourceLinks.ConfigLinks(uriInfo));
|
||||||
when(resourceLinks.branch()).thenReturn(new ResourceLinks.BranchLinks(uriInfo));
|
when(resourceLinks.branch()).thenReturn(new ResourceLinks.BranchLinks(uriInfo));
|
||||||
|
when(resourceLinks.diff()).thenReturn(new ResourceLinks.DiffLinks(uriInfo));
|
||||||
when(resourceLinks.repositoryType()).thenReturn(new ResourceLinks.RepositoryTypeLinks(uriInfo));
|
when(resourceLinks.repositoryType()).thenReturn(new ResourceLinks.RepositoryTypeLinks(uriInfo));
|
||||||
when(resourceLinks.repositoryTypeCollection()).thenReturn(new ResourceLinks.RepositoryTypeCollectionLinks(uriInfo));
|
when(resourceLinks.repositoryTypeCollection()).thenReturn(new ResourceLinks.RepositoryTypeCollectionLinks(uriInfo));
|
||||||
when(resourceLinks.uiPluginCollection()).thenReturn(new ResourceLinks.UIPluginCollectionLinks(uriInfo));
|
when(resourceLinks.uiPluginCollection()).thenReturn(new ResourceLinks.UIPluginCollectionLinks(uriInfo));
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ public class ResourceLinksTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldCreateCorrectTagCollectionUrl() {
|
public void shouldCreateCorrectTagCollectionUrl() {
|
||||||
String url = resourceLinks.tagCollection().self("space", "repo");
|
String url = resourceLinks.tag().all("space", "repo");
|
||||||
assertEquals(BASE_URL + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/tags/", url);
|
assertEquals(BASE_URL + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/tags/", url);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ public class ResourceLinksTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldCreateCorrectChangesetCollectionUrl() {
|
public void shouldCreateCorrectChangesetCollectionUrl() {
|
||||||
String url = resourceLinks.changeset().self("space", "repo");
|
String url = resourceLinks.changeset().all("space", "repo");
|
||||||
assertEquals(BASE_URL + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/changesets/", url);
|
assertEquals(BASE_URL + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/changesets/", url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,12 +29,13 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
import static sonia.scm.api.v2.resources.DispatcherMock.createDispatcher;
|
||||||
|
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.Silent.class)
|
@RunWith(MockitoJUnitRunner.Silent.class)
|
||||||
public class SourceRootResourceTest {
|
public class SourceRootResourceTest {
|
||||||
|
|
||||||
private final Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
|
private Dispatcher dispatcher;
|
||||||
private final URI baseUri = URI.create("/");
|
private final URI baseUri = URI.create("/");
|
||||||
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
|
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
|
||||||
|
|
||||||
@@ -72,10 +73,10 @@ public class SourceRootResourceTest {
|
|||||||
null,
|
null,
|
||||||
MockProvider.of(sourceRootResource),
|
MockProvider.of(sourceRootResource),
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
null)),
|
null)),
|
||||||
null);
|
null);
|
||||||
|
dispatcher = createDispatcher(repositoryRootResource);
|
||||||
dispatcher.getRegistry().addSingletonResource(repositoryRootResource);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -88,8 +89,6 @@ public class SourceRootResourceTest {
|
|||||||
dispatcher.invoke(request, response);
|
dispatcher.invoke(request, response);
|
||||||
assertThat(response.getStatus()).isEqualTo(200);
|
assertThat(response.getStatus()).isEqualTo(200);
|
||||||
assertThat(response.getContentAsString()).contains("\"revision\":\"revision\"");
|
assertThat(response.getContentAsString()).contains("\"revision\":\"revision\"");
|
||||||
assertThat(response.getContentAsString()).contains("\"tag\":\"tag\"");
|
|
||||||
assertThat(response.getContentAsString()).contains("\"branch\":\"branch\"");
|
|
||||||
assertThat(response.getContentAsString()).contains("\"files\":");
|
assertThat(response.getContentAsString()).contains("\"files\":");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,9 +105,7 @@ public class SourceRootResourceTest {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldGetResultForSingleFile() throws URISyntaxException, IOException, RevisionNotFoundException {
|
public void shouldGetResultForSingleFile() throws URISyntaxException, IOException, RevisionNotFoundException {
|
||||||
BrowserResult browserResult = new BrowserResult();
|
BrowserResult browserResult = new BrowserResult();
|
||||||
browserResult.setBranch("abc");
|
|
||||||
browserResult.setRevision("revision");
|
browserResult.setRevision("revision");
|
||||||
browserResult.setTag("tag");
|
|
||||||
FileObject fileObject = new FileObject();
|
FileObject fileObject = new FileObject();
|
||||||
fileObject.setName("File Object!");
|
fileObject.setName("File Object!");
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,199 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.shiro.subject.Subject;
|
||||||
|
import org.apache.shiro.subject.support.SubjectThreadState;
|
||||||
|
import org.apache.shiro.util.ThreadContext;
|
||||||
|
import org.apache.shiro.util.ThreadState;
|
||||||
|
import org.assertj.core.util.Lists;
|
||||||
|
import org.jboss.resteasy.core.Dispatcher;
|
||||||
|
import org.jboss.resteasy.mock.MockDispatcherFactory;
|
||||||
|
import org.jboss.resteasy.mock.MockHttpRequest;
|
||||||
|
import org.jboss.resteasy.mock.MockHttpResponse;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
import sonia.scm.api.rest.AuthorizationExceptionMapper;
|
||||||
|
import sonia.scm.repository.NamespaceAndName;
|
||||||
|
import sonia.scm.repository.Repository;
|
||||||
|
import sonia.scm.repository.Tag;
|
||||||
|
import sonia.scm.repository.Tags;
|
||||||
|
import sonia.scm.repository.api.RepositoryService;
|
||||||
|
import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||||
|
import sonia.scm.repository.api.TagsCommandBuilder;
|
||||||
|
import sonia.scm.web.VndMediaType;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RunWith(MockitoJUnitRunner.Silent.class)
|
||||||
|
public class TagRootResourceTest {
|
||||||
|
|
||||||
|
public static final String TAG_PATH = "space/repo/tags/";
|
||||||
|
public static final String TAG_URL = "/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + TAG_PATH;
|
||||||
|
private final Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
|
||||||
|
|
||||||
|
private final URI baseUri = URI.create("/");
|
||||||
|
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private RepositoryServiceFactory serviceFactory;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private RepositoryService repositoryService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private TagsCommandBuilder tagsCommandBuilder;
|
||||||
|
|
||||||
|
private TagCollectionToDtoMapper tagCollectionToDtoMapper;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private TagToTagDtoMapperImpl tagToTagDtoMapper;
|
||||||
|
|
||||||
|
private TagRootResource tagRootResource;
|
||||||
|
|
||||||
|
|
||||||
|
private final Subject subject = mock(Subject.class);
|
||||||
|
private final ThreadState subjectThreadState = new SubjectThreadState(subject);
|
||||||
|
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void prepareEnvironment() throws Exception {
|
||||||
|
tagCollectionToDtoMapper = new TagCollectionToDtoMapper(resourceLinks, tagToTagDtoMapper);
|
||||||
|
tagRootResource = new TagRootResource(serviceFactory, tagCollectionToDtoMapper, tagToTagDtoMapper);
|
||||||
|
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider
|
||||||
|
.of(new RepositoryResource(null, null, null, MockProvider.of(tagRootResource), null,
|
||||||
|
null, null, null, null, null)), null);
|
||||||
|
dispatcher.getRegistry().addSingletonResource(repositoryRootResource);
|
||||||
|
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(repositoryService);
|
||||||
|
when(serviceFactory.create(any(Repository.class))).thenReturn(repositoryService);
|
||||||
|
when(repositoryService.getRepository()).thenReturn(new Repository("repoId", "git", "space", "repo"));
|
||||||
|
dispatcher.getProviderFactory().registerProvider(NotFoundExceptionMapper.class);
|
||||||
|
dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class);
|
||||||
|
when(repositoryService.getTagsCommand()).thenReturn(tagsCommandBuilder);
|
||||||
|
subjectThreadState.bind();
|
||||||
|
ThreadContext.bind(subject);
|
||||||
|
when(subject.isPermitted(any(String.class))).thenReturn(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void cleanupContext() {
|
||||||
|
ThreadContext.unbindSubject();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldGet404OnMissingTag() throws Exception {
|
||||||
|
Tags tags = new Tags();
|
||||||
|
tags.setTags(Lists.emptyList());
|
||||||
|
when(tagsCommandBuilder.getTags()).thenReturn(tags);
|
||||||
|
|
||||||
|
MockHttpRequest request = MockHttpRequest
|
||||||
|
.get(TAG_URL + "not_existing_tag")
|
||||||
|
.accept(VndMediaType.TAG);
|
||||||
|
MockHttpResponse response = new MockHttpResponse();
|
||||||
|
dispatcher.invoke(request, response);
|
||||||
|
assertEquals(404, response.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldGetEmptyTagListOnMissingTags() throws Exception {
|
||||||
|
Tags tags = new Tags();
|
||||||
|
tags.setTags(Lists.emptyList());
|
||||||
|
when(tagsCommandBuilder.getTags()).thenReturn(tags);
|
||||||
|
|
||||||
|
MockHttpRequest request = MockHttpRequest
|
||||||
|
.get(TAG_URL)
|
||||||
|
.accept(VndMediaType.TAG_COLLECTION);
|
||||||
|
MockHttpResponse response = new MockHttpResponse();
|
||||||
|
dispatcher.invoke(request, response);
|
||||||
|
assertEquals(200, response.getStatus());
|
||||||
|
assertThat(response).isNotNull();
|
||||||
|
assertThat(response.getContentAsString())
|
||||||
|
.isNotBlank()
|
||||||
|
.contains("_links");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldGet500OnTagCommandError() throws Exception {
|
||||||
|
when(tagsCommandBuilder.getTags()).thenReturn(null);
|
||||||
|
|
||||||
|
MockHttpRequest request = MockHttpRequest
|
||||||
|
.get(TAG_URL + "not_existing_tag")
|
||||||
|
.accept(VndMediaType.TAG);
|
||||||
|
MockHttpResponse response = new MockHttpResponse();
|
||||||
|
dispatcher.invoke(request, response);
|
||||||
|
assertEquals(500, response.getStatus());
|
||||||
|
|
||||||
|
request = MockHttpRequest
|
||||||
|
.get(TAG_URL)
|
||||||
|
.accept(VndMediaType.TAG_COLLECTION);
|
||||||
|
response = new MockHttpResponse();
|
||||||
|
dispatcher.invoke(request, response);
|
||||||
|
assertEquals(500, response.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldGetTags() throws Exception {
|
||||||
|
Tags tags = new Tags();
|
||||||
|
String tag1 = "v1.0";
|
||||||
|
String revision1 = "revision_1234";
|
||||||
|
String tag2 = "v2.0";
|
||||||
|
String revision2 = "revision_12345";
|
||||||
|
tags.setTags(Lists.newArrayList(new Tag(tag1, revision1), new Tag(tag2, revision2)));
|
||||||
|
when(tagsCommandBuilder.getTags()).thenReturn(tags);
|
||||||
|
|
||||||
|
MockHttpRequest request = MockHttpRequest
|
||||||
|
.get(TAG_URL)
|
||||||
|
.accept(VndMediaType.TAG_COLLECTION);
|
||||||
|
MockHttpResponse response = new MockHttpResponse();
|
||||||
|
dispatcher.invoke(request, response);
|
||||||
|
assertEquals(200, response.getStatus());
|
||||||
|
log.info("the content: ", response.getContentAsString());
|
||||||
|
assertTrue(response.getContentAsString().contains(String.format("\"name\":\"%s\"", tag1)));
|
||||||
|
assertTrue(response.getContentAsString().contains(String.format("\"revision\":\"%s\"", revision1)));
|
||||||
|
assertTrue(response.getContentAsString().contains(String.format("\"name\":\"%s\"", tag2)));
|
||||||
|
assertTrue(response.getContentAsString().contains(String.format("\"revision\":\"%s\"", revision2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldGetTag() throws Exception {
|
||||||
|
Tags tags = new Tags();
|
||||||
|
String tag1 = "v1.0";
|
||||||
|
String revision1 = "revision_1234";
|
||||||
|
String tag2 = "v2.0";
|
||||||
|
String revision2 = "revision_12345";
|
||||||
|
tags.setTags(Lists.newArrayList(new Tag(tag1, revision1), new Tag(tag2, revision2)));
|
||||||
|
when(tagsCommandBuilder.getTags()).thenReturn(tags);
|
||||||
|
|
||||||
|
MockHttpRequest request = MockHttpRequest
|
||||||
|
.get(TAG_URL + tag1)
|
||||||
|
.accept(VndMediaType.TAG);
|
||||||
|
MockHttpResponse response = new MockHttpResponse();
|
||||||
|
dispatcher.invoke(request, response);
|
||||||
|
assertEquals(200, response.getStatus());
|
||||||
|
assertTrue(response.getContentAsString().contains(String.format("\"name\":\"%s\"", tag1)));
|
||||||
|
assertTrue(response.getContentAsString().contains(String.format("\"revision\":\"%s\"", revision1)));
|
||||||
|
|
||||||
|
request = MockHttpRequest
|
||||||
|
.get(TAG_URL + tag2)
|
||||||
|
.accept(VndMediaType.TAG);
|
||||||
|
response = new MockHttpResponse();
|
||||||
|
dispatcher.invoke(request, response);
|
||||||
|
assertEquals(200, response.getStatus());
|
||||||
|
log.info("the content: ", response.getContentAsString());
|
||||||
|
assertTrue(response.getContentAsString().contains(String.format("\"name\":\"%s\"", tag2)));
|
||||||
|
assertTrue(response.getContentAsString().contains(String.format("\"revision\":\"%s\"", revision2)));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user