From f0c9c11f2d7f70ddf1e0085c7c91cd48de8e8283 Mon Sep 17 00:00:00 2001 From: Mohamed Karray Date: Fri, 31 Aug 2018 12:23:31 +0200 Subject: [PATCH 01/12] #9208 add group permission prefix --- .../v2/resources/PermissionRootResource.java | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionRootResource.java index 2b53192542..b3a65852a0 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionRootResource.java @@ -5,7 +5,6 @@ import com.webcohesion.enunciate.metadata.rs.ResponseHeader; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.StringUtils; import sonia.scm.AlreadyExistsException; import sonia.scm.NotFoundException; import sonia.scm.repository.NamespaceAndName; @@ -28,10 +27,12 @@ import javax.ws.rs.Produces; import javax.ws.rs.core.Response; import java.net.URI; import java.util.Optional; +import java.util.function.Predicate; @Slf4j public class PermissionRootResource { + private static final String GROUP_PREFIX = "@"; private PermissionDtoToPermissionMapper dtoToModelMapper; private PermissionToPermissionDtoMapper modelToDtoMapper; private PermissionCollectionToDtoMapper permissionCollectionToDtoMapper; @@ -101,7 +102,7 @@ public class PermissionRootResource { return Response.ok( repository.getPermissions() .stream() - .filter(permission -> permissionName.equals(permission.getName())) + .filter(filterPermission(permissionName)) .map(permission -> modelToDtoMapper.map(permission, repository)) .findFirst() .orElseThrow(NotFoundException::new) @@ -158,9 +159,9 @@ public class PermissionRootResource { RepositoryPermissions.permissionWrite(repository).check(); Permission existingPermission = repository.getPermissions() .stream() - .filter(perm -> StringUtils.isNotBlank(perm.getName()) && perm.getName().equals(permissionName)) + .filter(filterPermission(permissionName)) .findFirst() - .orElseThrow(() -> new NotFoundException()); + .orElseThrow(NotFoundException::new); dtoToModelMapper.modify(existingPermission, permission); manager.modify(repository); log.info("the permission with name: {} is updated.", permissionName); @@ -186,11 +187,11 @@ public class PermissionRootResource { @PathParam("name") String name, @PathParam("permission-name") String permissionName) throws NotFoundException { log.info("try to delete the permission with name: {}.", permissionName); - Repository repository = load(namespace, name); + Repository repository = load(namespace, name); RepositoryPermissions.modify(repository).check(); repository.getPermissions() .stream() - .filter(perm -> StringUtils.isNotBlank(perm.getName()) && perm.getName().equals(permissionName)) + .filter(filterPermission(permissionName)) .findFirst() .ifPresent(p -> repository.getPermissions().remove(p)) ; @@ -199,6 +200,16 @@ public class PermissionRootResource { return Response.noContent().build(); } + Predicate filterPermission(String permissionName) { + boolean isGroupPermission = permissionName.startsWith(GROUP_PREFIX); + return permission -> Optional.of(permissionName) + .filter(p -> !isGroupPermission) + .orElse(permissionName.substring(1)) + .equals(permission.getName()) + && + permission.isGroupPermission() == isGroupPermission; + } + /** * check if the actual user is permitted to manage the repository permissions @@ -224,7 +235,7 @@ public class PermissionRootResource { private void checkPermissionAlreadyExists(PermissionDto permission, Repository repository) throws AlreadyExistsException { boolean isPermissionAlreadyExist = repository.getPermissions() .stream() - .anyMatch(p -> p.getName().equals(permission.getName())); + .anyMatch(p -> p.getName().equals(permission.getName()) && p.isGroupPermission() == permission.isGroupPermission()); if (isPermissionAlreadyExist) { throw new AlreadyExistsException(); } From 56524fa460c09893d22fc8d4f8678aa8f67e3948 Mon Sep 17 00:00:00 2001 From: Mohamed Karray Date: Fri, 31 Aug 2018 14:23:44 +0200 Subject: [PATCH 02/12] #9208 add group permission prefix --- .../sonia/scm/AlreadyExistsException.java | 7 +++ .../java/sonia/scm/NotFoundException.java | 5 ++ .../rest/AlreadyExistsExceptionMapper.java | 4 +- .../scm/api/rest/StatusExceptionMapper.java | 4 +- .../scm/api/v2/resources/PermissionDto.java | 8 ++++ .../v2/resources/PermissionRootResource.java | 47 ++++++++++++++----- 6 files changed, 61 insertions(+), 14 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/AlreadyExistsException.java b/scm-core/src/main/java/sonia/scm/AlreadyExistsException.java index 36052d99e9..4c6a52b878 100644 --- a/scm-core/src/main/java/sonia/scm/AlreadyExistsException.java +++ b/scm-core/src/main/java/sonia/scm/AlreadyExistsException.java @@ -1,4 +1,11 @@ package sonia.scm; public class AlreadyExistsException extends Exception { + + public AlreadyExistsException(String message) { + super(message); + } + + public AlreadyExistsException() { + } } diff --git a/scm-core/src/main/java/sonia/scm/NotFoundException.java b/scm-core/src/main/java/sonia/scm/NotFoundException.java index 8a7ae642bd..0d8c14c61b 100644 --- a/scm-core/src/main/java/sonia/scm/NotFoundException.java +++ b/scm-core/src/main/java/sonia/scm/NotFoundException.java @@ -7,4 +7,9 @@ public class NotFoundException extends Exception { public NotFoundException() { } + + + public NotFoundException(String message) { + super(message); + } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/AlreadyExistsExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/rest/AlreadyExistsExceptionMapper.java index 28240482e5..13d341e1e6 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/AlreadyExistsExceptionMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/AlreadyExistsExceptionMapper.java @@ -11,6 +11,8 @@ import javax.ws.rs.ext.Provider; public class AlreadyExistsExceptionMapper implements ExceptionMapper { @Override public Response toResponse(AlreadyExistsException exception) { - return Response.status(Status.CONFLICT).build(); + return Response.status(Status.CONFLICT) + .entity(exception.getMessage()) + .build(); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/StatusExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/rest/StatusExceptionMapper.java index fef7fc96c4..6f4978637e 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/StatusExceptionMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/StatusExceptionMapper.java @@ -90,6 +90,8 @@ public class StatusExceptionMapper logger.debug(msg.toString()); } - return Response.status(status).build(); + return Response.status(status) + .entity(exception.getMessage()) + .build(); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionDto.java index b184bc3934..7fa74e9f87 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionDto.java @@ -26,6 +26,14 @@ public class PermissionDto extends HalRepresentation { private boolean groupPermission = false; + public PermissionDto() { + } + + public PermissionDto(String permissionName, boolean groupPermission) { + name = permissionName; + this.groupPermission = groupPermission; + } + @Override @SuppressWarnings("squid:S1185") // We want to have this method available in this package diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionRootResource.java index b3a65852a0..16cdf17915 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionRootResource.java @@ -136,6 +136,7 @@ public class PermissionRootResource { /** * 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 permissionName permission to modify @@ -153,10 +154,18 @@ public class PermissionRootResource { public Response update(@PathParam("namespace") String namespace, @PathParam("name") String name, @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); Repository repository = load(namespace, name); 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() .stream() .filter(filterPermission(permissionName)) @@ -201,13 +210,19 @@ public class PermissionRootResource { } Predicate filterPermission(String permissionName) { - boolean isGroupPermission = permissionName.startsWith(GROUP_PREFIX); - return permission -> Optional.of(permissionName) - .filter(p -> !isGroupPermission) - .orElse(permissionName.substring(1)) - .equals(permission.getName()) + return permission -> getPermissionName(permissionName).equals(permission.getName()) && - permission.isGroupPermission() == isGroupPermission; + 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); } @@ -230,15 +245,23 @@ public class PermissionRootResource { * * @param permission the searched permission * @param repository the repository to be inspected + * @param errorMessage error message * @throws AlreadyExistsException if the permission already exists in the repository */ - private void checkPermissionAlreadyExists(PermissionDto permission, Repository repository) throws AlreadyExistsException { - boolean isPermissionAlreadyExist = repository.getPermissions() + private void checkPermissionAlreadyExists(PermissionDto permission, Repository repository, String errorMessage) throws AlreadyExistsException { + if (isPermissionExist(permission, repository)) { + throw new AlreadyExistsException(errorMessage); + } + } + + private boolean isPermissionExist(PermissionDto permission, Repository repository) { + return repository.getPermissions() .stream() .anyMatch(p -> p.getName().equals(permission.getName()) && p.isGroupPermission() == permission.isGroupPermission()); - if (isPermissionAlreadyExist) { - throw new AlreadyExistsException(); - } + } + + private void checkPermissionAlreadyExists(PermissionDto permission, Repository repository) throws AlreadyExistsException { + checkPermissionAlreadyExists(permission, repository, "the permission " + permission.getName() + " already exist."); } } From eb270f10e4428f327a8d6996a5519b480924ec12 Mon Sep 17 00:00:00 2001 From: Mohamed Karray Date: Mon, 3 Sep 2018 12:52:33 +0200 Subject: [PATCH 03/12] #9208 add group permission prefix to the dto links --- .../sonia/scm/api/v2/resources/PermissionDto.java | 2 ++ .../scm/api/v2/resources/PermissionRootResource.java | 4 +++- .../v2/resources/PermissionToPermissionDtoMapper.java | 11 ++++++++--- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionDto.java index 7fa74e9f87..8647146f2b 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionDto.java @@ -10,6 +10,8 @@ import lombok.ToString; @Getter @Setter @ToString public class PermissionDto extends HalRepresentation { + public static final String GROUP_PREFIX = "@"; + @JsonInclude(JsonInclude.Include.NON_NULL) private String name; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionRootResource.java index 16cdf17915..90e9b3daed 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionRootResource.java @@ -29,10 +29,12 @@ import java.net.URI; import java.util.Optional; import java.util.function.Predicate; +import static sonia.scm.api.v2.resources.PermissionDto.GROUP_PREFIX; + @Slf4j public class PermissionRootResource { - private static final String GROUP_PREFIX = "@"; + private PermissionDtoToPermissionMapper dtoToModelMapper; private PermissionToPermissionDtoMapper modelToDtoMapper; private PermissionCollectionToDtoMapper permissionCollectionToDtoMapper; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionToPermissionDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionToPermissionDtoMapper.java index 6938c9059c..8ebe10eb6f 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionToPermissionDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionToPermissionDtoMapper.java @@ -12,9 +12,11 @@ import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryPermissions; import javax.inject.Inject; +import java.util.Optional; import static de.otto.edison.hal.Link.link; import static de.otto.edison.hal.Links.linkingTo; +import static sonia.scm.api.v2.resources.PermissionDto.GROUP_PREFIX; @Mapper public abstract class PermissionToPermissionDtoMapper { @@ -39,11 +41,14 @@ public abstract class PermissionToPermissionDtoMapper { */ @AfterMapping 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() - .self(resourceLinks.permission().self(repository.getNamespace(), repository.getName(), target.getName())); + .self(resourceLinks.permission().self(repository.getNamespace(), repository.getName(), permissionName)); if (RepositoryPermissions.permissionWrite(repository).isPermitted()) { - linksBuilder.single(link("update", resourceLinks.permission().update(repository.getNamespace(), repository.getName(), target.getName()))); - linksBuilder.single(link("delete", resourceLinks.permission().delete(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(), permissionName))); } target.add(linksBuilder.build()); } From 4349e2c61d76c2084dbda9ee02f116c4deb41b2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Mon, 3 Sep 2018 15:44:22 +0200 Subject: [PATCH 04/12] Remove double class --- .../v2/resources/CollectionToDtoMapper.java | 32 ------------------- 1 file changed, 32 deletions(-) delete mode 100644 scm-webapp/src/main/java/sonia/scm/api/v2/resources/CollectionToDtoMapper.java diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/CollectionToDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/CollectionToDtoMapper.java deleted file mode 100644 index 4f8c6a6f3f..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/CollectionToDtoMapper.java +++ /dev/null @@ -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 { - - private final String collectionName; - private final BaseMapper mapper; - - protected CollectionToDtoMapper(String collectionName, BaseMapper mapper) { - this.collectionName = collectionName; - this.mapper = mapper; - } - - public HalRepresentation map(Collection collection) { - List 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(); - -} From 9b7e34b08b39c812b2b97fd1e5d924249112b3b5 Mon Sep 17 00:00:00 2001 From: Mohamed Karray Date: Tue, 4 Sep 2018 17:38:28 +0200 Subject: [PATCH 05/12] add tag endpoints --- .../main/java/sonia/scm/web/VndMediaType.java | 2 + .../sonia/scm/it/RepositoryAccessITCase.java | 90 +++++++- .../java/sonia/scm/it/RepositoryUtil.java | 17 +- .../repository/client/spi/GitPushCommand.java | 19 +- .../repository/client/spi/GitTagCommand.java | 12 +- .../repository/client/spi/HgPushCommand.java | 10 +- .../repository/client/spi/HgTagCommand.java | 8 +- .../client/api/PushCommandBuilder.java | 13 +- .../client/api/TagCommandBuilder.java | 8 +- .../repository/client/spi/PushCommand.java | 10 +- .../scm/repository/client/spi/TagRequest.java | 12 ++ .../scm/api/v2/resources/ResourceLinks.java | 4 +- .../v2/resources/TagNotFoundException.java | 7 + .../scm/api/v2/resources/TagRootResource.java | 93 +++++++- .../api/v2/resources/TagToTagDtoMapper.java | 5 +- .../resources/ChangesetRootResourceTest.java | 10 +- .../api/v2/resources/TagRootResourceTest.java | 201 ++++++++++++++++++ 17 files changed, 471 insertions(+), 50 deletions(-) create mode 100644 scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagNotFoundException.java create mode 100644 scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagRootResourceTest.java diff --git a/scm-core/src/main/java/sonia/scm/web/VndMediaType.java b/scm-core/src/main/java/sonia/scm/web/VndMediaType.java index 73bcf10bd0..ecab36969c 100644 --- a/scm-core/src/main/java/sonia/scm/web/VndMediaType.java +++ b/scm-core/src/main/java/sonia/scm/web/VndMediaType.java @@ -19,6 +19,8 @@ public class VndMediaType { 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 USER_COLLECTION = PREFIX + "userCollection" + SUFFIX; public static final String GROUP_COLLECTION = PREFIX + "groupCollection" + SUFFIX; diff --git a/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java b/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java index a9139f18c8..0211647321 100644 --- a/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java +++ b/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java @@ -1,5 +1,7 @@ package sonia.scm.it; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; import org.apache.http.HttpStatus; import org.junit.Assume; import org.junit.Before; @@ -8,18 +10,22 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import sonia.scm.repository.Changeset; import sonia.scm.repository.client.api.ClientCommand; import sonia.scm.repository.client.api.RepositoryClient; +import sonia.scm.web.VndMediaType; import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.List; -import static org.assertj.core.api.Assertions.assertThat; import static java.lang.Thread.sleep; +import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.equalTo; 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.ScmTypes.availableScmTypes; @@ -74,6 +80,88 @@ public class RepositoryAccessITCase { 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 = 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 public void shouldReadContent() throws IOException, InterruptedException { RepositoryClient repositoryClient = RepositoryUtil.createRepositoryClient(repositoryType, folder); diff --git a/scm-it/src/test/java/sonia/scm/it/RepositoryUtil.java b/scm-it/src/test/java/sonia/scm/it/RepositoryUtil.java index ef2332074b..9fb1fcb236 100644 --- a/scm-it/src/test/java/sonia/scm/it/RepositoryUtil.java +++ b/scm-it/src/test/java/sonia/scm/it/RepositoryUtil.java @@ -5,6 +5,7 @@ import com.google.common.io.Files; import org.apache.http.HttpStatus; import sonia.scm.repository.Changeset; import sonia.scm.repository.Person; +import sonia.scm.repository.Tag; import sonia.scm.repository.client.api.ClientCommand; import sonia.scm.repository.client.api.RepositoryClient; import sonia.scm.repository.client.api.RepositoryClientFactory; @@ -36,11 +37,11 @@ public class RepositoryUtil { 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); Files.write(content, file, Charsets.UTF_8); addWithParentDirectories(repositoryClient, file); - commit(repositoryClient, username, "added " + fileName); + return commit(repositoryClient, username, "added " + fileName); } private static String addWithParentDirectories(RepositoryClient repositoryClient, File file) throws IOException { @@ -64,4 +65,16 @@ public class RepositoryUtil { } 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; + } } diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/client/spi/GitPushCommand.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/client/spi/GitPushCommand.java index 3b9d29abdf..8d65b54b06 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/client/spi/GitPushCommand.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/client/spi/GitPushCommand.java @@ -37,12 +37,12 @@ package sonia.scm.repository.client.spi; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.transport.CredentialsProvider; - import sonia.scm.repository.client.api.RepositoryClientException; -//~--- JDK imports ------------------------------------------------------------ - import java.io.IOException; +import java.util.function.Supplier; + +//~--- JDK imports ------------------------------------------------------------ /** * @@ -73,11 +73,20 @@ public class GitPushCommand implements PushCommand * @throws IOException */ @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 commandSupplier) throws RepositoryClientException { try { - org.eclipse.jgit.api.PushCommand cmd = git.push().setPushAll(); + org.eclipse.jgit.api.PushCommand cmd = commandSupplier.get(); if (credentialsProvider != null) { diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/client/spi/GitTagCommand.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/client/spi/GitTagCommand.java index ee03dc7458..e5e1f36155 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/client/spi/GitTagCommand.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/client/spi/GitTagCommand.java @@ -35,22 +35,20 @@ package sonia.scm.repository.client.spi; //~--- non-JDK imports -------------------------------------------------------- import com.google.common.base.Strings; - import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.revwalk.RevObject; import org.eclipse.jgit.revwalk.RevWalk; - import sonia.scm.repository.GitUtil; import sonia.scm.repository.Tag; import sonia.scm.repository.client.api.RepositoryClientException; -//~--- JDK imports ------------------------------------------------------------ - import java.io.IOException; +//~--- JDK imports ------------------------------------------------------------ + /** * * @author Sebastian Sdorra @@ -119,7 +117,11 @@ public class GitTagCommand implements TagCommand ref = git.tag().setObjectId(revObject).call(); } - tag = new Tag(request.getName(), ref.getPeeledObjectId().toString()); + if (ref.isPeeled()) { + tag = new Tag(request.getName(), ref.getPeeledObjectId().toString()); + } else { + tag = new Tag(request.getName(), ref.getObjectId().toString()); + } } catch (GitAPIException ex) diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgPushCommand.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgPushCommand.java index 73e0a1f9a4..263347ca00 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgPushCommand.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgPushCommand.java @@ -32,9 +32,10 @@ package sonia.scm.repository.client.spi; import com.aragost.javahg.Repository; import com.aragost.javahg.commands.ExecutionException; -import java.io.IOException; import sonia.scm.repository.client.api.RepositoryClientException; +import java.io.IOException; + /** * Mercurial implementation of the {@link PushCommand}. * @@ -63,5 +64,10 @@ public class HgPushCommand implements PushCommand throw new RepositoryClientException("push to repository failed", ex); } } - + + @Override + public void pushTags() throws IOException { + push(); + } + } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgTagCommand.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgTagCommand.java index 3aa448bfca..639adf9a1b 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgTagCommand.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgTagCommand.java @@ -32,7 +32,6 @@ package sonia.scm.repository.client.spi; import com.aragost.javahg.Repository; import com.google.common.base.Strings; -import java.io.IOException; import sonia.scm.repository.Tag; /** @@ -51,13 +50,16 @@ public class HgTagCommand implements TagCommand } @Override - public Tag tag(TagRequest request) throws IOException + public Tag tag(TagRequest request) { String rev = request.getRevision(); if ( Strings.isNullOrEmpty(rev) ){ 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); } diff --git a/scm-test/src/main/java/sonia/scm/repository/client/api/PushCommandBuilder.java b/scm-test/src/main/java/sonia/scm/repository/client/api/PushCommandBuilder.java index b3b7619e7b..06b59e4ba0 100644 --- a/scm-test/src/main/java/sonia/scm/repository/client/api/PushCommandBuilder.java +++ b/scm-test/src/main/java/sonia/scm/repository/client/api/PushCommandBuilder.java @@ -36,13 +36,12 @@ package sonia.scm.repository.client.api; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import sonia.scm.repository.client.spi.PushCommand; -//~--- JDK imports ------------------------------------------------------------ - import java.io.IOException; +//~--- JDK imports ------------------------------------------------------------ + /** * * @author Sebastian Sdorra @@ -88,6 +87,14 @@ public final class PushCommandBuilder command.push(); } + public void pushTags() throws IOException { + if (logger.isDebugEnabled()) { + logger.debug("push tag changes back to main repository"); + } + + command.pushTags(); + } + //~--- fields --------------------------------------------------------------- /** Field description */ diff --git a/scm-test/src/main/java/sonia/scm/repository/client/api/TagCommandBuilder.java b/scm-test/src/main/java/sonia/scm/repository/client/api/TagCommandBuilder.java index 8b42817aa8..1d153dee0e 100644 --- a/scm-test/src/main/java/sonia/scm/repository/client/api/TagCommandBuilder.java +++ b/scm-test/src/main/java/sonia/scm/repository/client/api/TagCommandBuilder.java @@ -36,15 +36,14 @@ package sonia.scm.repository.client.api; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import sonia.scm.repository.Tag; import sonia.scm.repository.client.spi.TagCommand; import sonia.scm.repository.client.spi.TagRequest; -//~--- JDK imports ------------------------------------------------------------ - import java.io.IOException; +//~--- JDK imports ------------------------------------------------------------ + /** * * @author Sebastian Sdorra @@ -84,9 +83,10 @@ public final class TagCommandBuilder * * @throws IOException */ - public Tag tag(String name) throws IOException + public Tag tag(String name, String username) throws IOException { request.setName(name); + request.setUsername(username); if (logger.isDebugEnabled()) { diff --git a/scm-test/src/main/java/sonia/scm/repository/client/spi/PushCommand.java b/scm-test/src/main/java/sonia/scm/repository/client/spi/PushCommand.java index 5e85ed0e5e..be8fd94718 100644 --- a/scm-test/src/main/java/sonia/scm/repository/client/spi/PushCommand.java +++ b/scm-test/src/main/java/sonia/scm/repository/client/spi/PushCommand.java @@ -44,11 +44,7 @@ import java.io.IOException; public interface PushCommand { - /** - * Method description - * - * - * @throws IOException - */ - public void push() throws IOException; + void push() throws IOException; + + void pushTags() throws IOException; } diff --git a/scm-test/src/main/java/sonia/scm/repository/client/spi/TagRequest.java b/scm-test/src/main/java/sonia/scm/repository/client/spi/TagRequest.java index ff34309b6d..53abf0f8f8 100644 --- a/scm-test/src/main/java/sonia/scm/repository/client/spi/TagRequest.java +++ b/scm-test/src/main/java/sonia/scm/repository/client/spi/TagRequest.java @@ -91,6 +91,7 @@ public final class TagRequest { this.name = null; this.revision = null; + this.username = null; } /** @@ -106,6 +107,7 @@ public final class TagRequest return MoreObjects.toStringHelper(this) .add("revision", revision) .add("name", name) + .add("username", username) .toString(); //J+ } @@ -134,6 +136,10 @@ public final class TagRequest this.revision = revision; } + public void setUsername(String username) { + this.username = username; + } + //~--- get methods ---------------------------------------------------------- /** @@ -158,6 +164,10 @@ public final class TagRequest return revision; } + public String getUserName() { + return username; + } + //~--- fields --------------------------------------------------------------- /** Field description */ @@ -165,4 +175,6 @@ public final class TagRequest /** Field description */ private String revision; + + private String username; } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ResourceLinks.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ResourceLinks.java index d51a462c19..1d3e5e222e 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ResourceLinks.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ResourceLinks.java @@ -218,8 +218,8 @@ class ResourceLinks { tagLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class, TagRootResource.class); } - String self(String namespace, String name, String id) { - return tagLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("tags").parameters().method("get").parameters(id).href(); + String self(String namespace, String name, String tagName) { + return tagLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("tags").parameters().method("get").parameters(tagName).href(); } String all(String namespace, String name) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagNotFoundException.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagNotFoundException.java new file mode 100644 index 0000000000..b572d20c5b --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagNotFoundException.java @@ -0,0 +1,7 @@ +package sonia.scm.api.v2.resources; + +import sonia.scm.NotFoundException; + +public class TagNotFoundException extends NotFoundException { + +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagRootResource.java index 10b4607fbc..b3601f7709 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagRootResource.java @@ -1,27 +1,100 @@ package sonia.scm.api.v2.resources; -import javax.ws.rs.DefaultValue; +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.ws.rs.GET; import javax.ws.rs.Path; -import javax.ws.rs.QueryParam; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; import javax.ws.rs.core.Response; +import java.io.IOException; public class TagRootResource { + private final RepositoryServiceFactory serviceFactory; + private final TagCollectionToDtoMapper tagCollectionToDtoMapper; + private final TagToTagDtoMapper tagToTagDtoMapper; + + @Inject + public TagRootResource(RepositoryServiceFactory serviceFactory, + TagCollectionToDtoMapper tagCollectionToDtoMapper, + TagToTagDtoMapper tagToTagDtoMapper) { + this.serviceFactory = serviceFactory; + this.tagCollectionToDtoMapper = tagCollectionToDtoMapper; + this.tagToTagDtoMapper = tagToTagDtoMapper; + } + @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(); + @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 = 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 - @Path("{id}") - public Response get(String id) { - throw new UnsupportedOperationException(); + @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(); + } + + } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagToTagDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagToTagDtoMapper.java index ada0fa2887..917b4b7789 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagToTagDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagToTagDtoMapper.java @@ -11,6 +11,7 @@ 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 @@ -25,7 +26,9 @@ public abstract class TagToTagDtoMapper { @AfterMapping void appendLinks(@MappingTarget TagDto target, @Context NamespaceAndName namespaceAndName) { Links.Builder linksBuilder = linkingTo() - .self(resourceLinks.tag().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getName())); + .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()); } } diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetRootResourceTest.java index 570830d651..40aa61852a 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetRootResourceTest.java @@ -58,7 +58,7 @@ public class ChangesetRootResourceTest { private RepositoryServiceFactory serviceFactory; @Mock - private RepositoryService service; + private RepositoryService repositoryService; @Mock private LogCommandBuilder logCommandBuilder; @@ -83,12 +83,12 @@ public class ChangesetRootResourceTest { .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(service); - when(serviceFactory.create(any(Repository.class))).thenReturn(service); - when(service.getRepository()).thenReturn(new Repository("repoId", "git", "space", "repo")); + 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(service.getLogCommand()).thenReturn(logCommandBuilder); + when(repositoryService.getLogCommand()).thenReturn(logCommandBuilder); subjectThreadState.bind(); ThreadContext.bind(subject); when(subject.isPermitted(any(String.class))).thenReturn(true); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagRootResourceTest.java new file mode 100644 index 0000000000..029184e71f --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagRootResourceTest.java @@ -0,0 +1,201 @@ +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))); + } +} From a731865e08f1be3acb7db3633f3dc4ca9b4add7a Mon Sep 17 00:00:00 2001 From: Mohamed Karray Date: Wed, 5 Sep 2018 09:35:27 +0200 Subject: [PATCH 06/12] fix unit tests for permissions and sources endpoints --- .../sonia/scm/api/v2/resources/SourceRootResource.java | 3 ++- .../scm/api/v2/resources/PermissionRootResourceTest.java | 9 +++++++-- .../scm/api/v2/resources/SourceRootResourceTest.java | 7 +++---- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java index fe12f69ecc..6eb534b465 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java @@ -1,5 +1,6 @@ package sonia.scm.api.v2.resources; +import sonia.scm.NotFoundException; import sonia.scm.repository.BrowserResult; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.RepositoryNotFoundException; @@ -46,7 +47,7 @@ public class SourceRootResource { @GET @Produces(VndMediaType.SOURCE) @Path("{revision}/{path: .*}") - public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision, @PathParam("path") String path) throws RevisionNotFoundException, RepositoryNotFoundException, IOException { + 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); } diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PermissionRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PermissionRootResourceTest.java index 227775cf16..3e0cf1d701 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PermissionRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PermissionRootResourceTest.java @@ -39,6 +39,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.function.Consumer; import java.util.stream.Collectors; 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.MockitoAnnotations.initMocks; import static sonia.scm.api.v2.resources.DispatcherMock.createDispatcher; +import static sonia.scm.api.v2.resources.PermissionDto.GROUP_PREFIX; @Slf4j @SubjectAware( @@ -253,7 +255,7 @@ public class PermissionRootResourceTest { createUserWithRepositoryAndPermissions(TEST_PERMISSIONS, PERMISSION_WRITE); Permission newPermission = TEST_PERMISSIONS.get(0); assertExpectedRequest(requestPOSTPermission - .content("{\"name\" : \"" + newPermission.getName() + "\" , \"type\" : \"WRITE\" , \"groupPermission\" : true}") + .content("{\"name\" : \"" + newPermission.getName() + "\" , \"type\" : \"WRITE\" , \"groupPermission\" : false}") .expectedResponseStatus(409) ); } @@ -358,7 +360,10 @@ public class PermissionRootResourceTest { result.setName(permission.getName()); result.setGroupPermission(permission.isGroupPermission()); 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)) { result.add(linkingTo() .self(permissionHref) diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/SourceRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/SourceRootResourceTest.java index 1e73c6dfaa..9573fb3d97 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/SourceRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/SourceRootResourceTest.java @@ -1,7 +1,6 @@ package sonia.scm.api.v2.resources; 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.Before; @@ -29,12 +28,13 @@ 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.when; +import static sonia.scm.api.v2.resources.DispatcherMock.createDispatcher; @RunWith(MockitoJUnitRunner.Silent.class) public class SourceRootResourceTest { - private final Dispatcher dispatcher = MockDispatcherFactory.createDispatcher(); + private Dispatcher dispatcher; private final URI baseUri = URI.create("/"); private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri); @@ -74,8 +74,7 @@ public class SourceRootResourceTest { null, null)), null); - - dispatcher.getRegistry().addSingletonResource(repositoryRootResource); + dispatcher = createDispatcher(repositoryRootResource); } @Test From a03a6fc8ac01d1cf8d1a2482d7c004ffd2009409 Mon Sep 17 00:00:00 2001 From: Mohamed Karray Date: Wed, 5 Sep 2018 10:44:03 +0200 Subject: [PATCH 07/12] add logs --- scm-it/src/test/java/sonia/scm/it/RepositoryUtil.java | 5 +++++ scm-it/src/test/java/sonia/scm/it/TestData.java | 9 ++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/scm-it/src/test/java/sonia/scm/it/RepositoryUtil.java b/scm-it/src/test/java/sonia/scm/it/RepositoryUtil.java index ef2332074b..e49927b1b9 100644 --- a/scm-it/src/test/java/sonia/scm/it/RepositoryUtil.java +++ b/scm-it/src/test/java/sonia/scm/it/RepositoryUtil.java @@ -3,6 +3,8 @@ package sonia.scm.it; import com.google.common.base.Charsets; import com.google.common.io.Files; import org.apache.http.HttpStatus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import sonia.scm.repository.Changeset; import sonia.scm.repository.Person; import sonia.scm.repository.client.api.ClientCommand; @@ -15,6 +17,8 @@ import java.util.UUID; public class RepositoryUtil { + private static final Logger LOG = LoggerFactory.getLogger(RepositoryUtil.class); + private static final RepositoryClientFactory REPOSITORY_CLIENT_FACTORY = new RepositoryClientFactory(); static RepositoryClient createRepositoryClient(String repositoryType, File folder) throws IOException { @@ -58,6 +62,7 @@ public class RepositoryUtil { } 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); if (repositoryClient.isCommandSupported(ClientCommand.PUSH)) { repositoryClient.getPushCommand().push(); diff --git a/scm-it/src/test/java/sonia/scm/it/TestData.java b/scm-it/src/test/java/sonia/scm/it/TestData.java index ada67d588c..ae0e35004d 100644 --- a/scm-it/src/test/java/sonia/scm/it/TestData.java +++ b/scm-it/src/test/java/sonia/scm/it/TestData.java @@ -33,6 +33,7 @@ public class TestData { } public static void cleanup() { + LOG.info("start to clean up to integration tests"); cleanupRepositories(); cleanupGroups(); cleanupUsers(); @@ -43,6 +44,7 @@ public class TestData { } public static void createUser(String username, String password) { + LOG.info("create user with username: {}", username); given(VndMediaType.USER) .when() .content(" {\n" + @@ -64,6 +66,8 @@ public class TestData { 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) .when() .content("{\n" + @@ -72,7 +76,7 @@ public class TestData { "\t\"groupPermission\": false\n" + "\t\n" + "}") - .post(TestData.getDefaultPermissionUrl(USER_SCM_ADMIN, USER_SCM_ADMIN, repositoryType)) + .post(defaultPermissionUrl) .then() .statusCode(HttpStatus.SC_CREATED) ; @@ -114,6 +118,7 @@ public class TestData { private static void cleanupRepositories() { + LOG.info("clean up repository"); List repositories = given(VndMediaType.REPOSITORY_COLLECTION) .when() .get(createResourceUrl("repositories")) @@ -160,6 +165,7 @@ public class TestData { } private static void createDefaultRepositories() { + LOG.info("create default repositories"); for (String repositoryType : availableScmTypes()) { String url = given(VndMediaType.REPOSITORY) .body(repositoryJson(repositoryType)) @@ -171,6 +177,7 @@ public class TestData { .statusCode(HttpStatus.SC_CREATED) .extract() .header("location"); + LOG.info("a {} repository is created: {}", repositoryType, url); DEFAULT_REPOSITORIES.put(repositoryType, url); } } From d70acc5fba7d19859baff1d576ccb83feff24a84 Mon Sep 17 00:00:00 2001 From: Philipp Czora Date: Wed, 5 Sep 2018 14:47:35 +0200 Subject: [PATCH 08/12] Added unit test --- .../scm/api/v2/resources/PermissionDto.java | 2 +- .../PermissionToPermissionDtoMapperTest.java | 63 +++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 scm-webapp/src/test/java/sonia/scm/api/v2/resources/PermissionToPermissionDtoMapperTest.java diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionDto.java index 8647146f2b..581b2c24cd 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionDto.java @@ -23,7 +23,7 @@ public class PermissionDto extends HalRepresentation { * **/ @JsonInclude(JsonInclude.Include.NON_NULL) - private String type ; + private String type; private boolean groupPermission = false; diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PermissionToPermissionDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PermissionToPermissionDtoMapperTest.java new file mode 100644 index 0000000000..31c2f0ec31 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PermissionToPermissionDtoMapperTest.java @@ -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"); + } +} From 6940198a88aafe22a26f6815db563ff677103c0c Mon Sep 17 00:00:00 2001 From: Philipp Czora Date: Wed, 5 Sep 2018 13:02:25 +0000 Subject: [PATCH 09/12] Close branch feature/add_permission_prefix From 753349ae18f7e91825c0d057a0bd4bd25d612f67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Wed, 5 Sep 2018 15:17:16 +0200 Subject: [PATCH 10/12] Log illegal argument exception before mapping to 400 --- .../sonia/scm/api/rest/IllegalArgumentExceptionMapper.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/IllegalArgumentExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/rest/IllegalArgumentExceptionMapper.java index 32b6a02bc6..4a8a5854f0 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/IllegalArgumentExceptionMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/IllegalArgumentExceptionMapper.java @@ -33,6 +33,8 @@ package sonia.scm.api.rest; //~--- JDK imports ------------------------------------------------------------ +import lombok.extern.slf4j.Slf4j; + import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.ext.ExceptionMapper; @@ -43,7 +45,7 @@ import javax.ws.rs.ext.Provider; * @author Sebastian Sdorra * @since 1.36 */ -@Provider +@Provider @Slf4j public class IllegalArgumentExceptionMapper implements ExceptionMapper { @@ -59,6 +61,7 @@ public class IllegalArgumentExceptionMapper @Override public Response toResponse(IllegalArgumentException exception) { + log.info("caught IllegalArgumentException -- mapping to bad request", exception); return Response.status(Status.BAD_REQUEST).build(); } } From b961a82e1536ed2d967dad72f235a56ec56908fb Mon Sep 17 00:00:00 2001 From: Philipp Czora Date: Thu, 6 Sep 2018 10:41:49 +0200 Subject: [PATCH 11/12] Minor refactoring --- .../sonia/scm/it/RepositoryAccessITCase.java | 35 +++++++++---------- .../api/v2/resources/TagRootResourceTest.java | 4 +-- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java b/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java index 0211647321..59fc70b9af 100644 --- a/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java +++ b/scm-it/src/test/java/sonia/scm/it/RepositoryAccessITCase.java @@ -109,8 +109,7 @@ public class RepositoryAccessITCase { assertThat(response.body()).isNotNull(); assertThat(response.body().asString()) .isNotNull() - .isNotBlank() - ; + .isNotBlank(); RepositoryUtil.addTag(repositoryClient, changeset.getId(), tagName); response = given(VndMediaType.TAG_COLLECTION, ADMIN_USERNAME, ADMIN_PASSWORD) @@ -118,48 +117,46 @@ public class RepositoryAccessITCase { .get(tagsUrl) .then() .statusCode(HttpStatus.SC_OK) - .extract() - ; + .extract(); assertThat(response).isNotNull(); assertThat(response.body()).isNotNull(); assertThat(response.body().asString()) .isNotNull() - .isNotBlank() - ; + .isNotBlank(); + assertThat(response.body().jsonPath().getString("_links.self.href")) .as("assert tags self link") .isNotNull() - .contains(repositoryUrl + "/tags/") - ; + .contains(repositoryUrl + "/tags/"); + assertThat(response.body().jsonPath().getList("_embedded.tags")) .as("assert tag size") .isNotNull() .size() - .isGreaterThan(0) - ; + .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()) - ; + .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)) - ; + .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())) - ; + .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())) - ; + .contains(String.format("%s/changesets/%s", repositoryUrl, changeset.getId())); } @Test @@ -200,6 +197,7 @@ public class RepositoryAccessITCase { .statusCode(HttpStatus.SC_OK) .extract() .path("files.find{it.name=='subfolder'}._links.self.href"); + String subfolderContentUrl= given() .when() .get(subfolderSourceUrl) @@ -207,6 +205,7 @@ public class RepositoryAccessITCase { .statusCode(HttpStatus.SC_OK) .extract() .path("files[0]._links.self.href"); + given() .when() .get(subfolderContentUrl) diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagRootResourceTest.java index 029184e71f..92d11b3895 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagRootResourceTest.java @@ -121,9 +121,7 @@ public class TagRootResourceTest { assertThat(response).isNotNull(); assertThat(response.getContentAsString()) .isNotBlank() - .contains("_links") - ; - + .contains("_links"); } @Test From 20f50acf4635973a241ae0f5f6e4cf5c02351cf2 Mon Sep 17 00:00:00 2001 From: Philipp Czora Date: Thu, 6 Sep 2018 08:44:04 +0000 Subject: [PATCH 12/12] Close branch feature/tag_endpoints_v2