Merged in feature/embedded_enricher (pull request #179)

Feature/embedded enricher
This commit is contained in:
Philipp Czora
2019-02-04 16:26:00 +00:00
43 changed files with 394 additions and 364 deletions

View File

@@ -3,7 +3,7 @@ package sonia.scm.api.v2.resources;
import de.otto.edison.hal.HalRepresentation;
import org.mapstruct.Mapping;
public abstract class BaseMapper<T, D extends HalRepresentation> extends LinkAppenderMapper implements InstantAttributeMapper {
public abstract class BaseMapper<T, D extends HalRepresentation> extends HalAppenderMapper implements InstantAttributeMapper {
@Mapping(target = "attributes", ignore = true) // We do not map HAL attributes
public abstract D map(T modelObject);

View File

@@ -1,12 +1,14 @@
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.HalRepresentation;
/**
* The {@link LinkAppender} can be used within an {@link LinkEnricher} to append hateoas links to a json response.
* The {@link HalAppender} can be used within an {@link HalEnricher} to append hateoas links to a json response.
*
* @author Sebastian Sdorra
* @since 2.0.0
*/
public interface LinkAppender {
public interface HalAppender {
/**
* Appends one link to the json response.
@@ -14,7 +16,7 @@ public interface LinkAppender {
* @param rel name of relation
* @param href link uri
*/
void appendOne(String rel, String href);
void appendLink(String rel, String href);
/**
* Returns a builder which is able to append an array of links to the resource.
@@ -22,8 +24,15 @@ public interface LinkAppender {
* @param rel name of link relation
* @return multi link builder
*/
LinkArrayBuilder arrayBuilder(String rel);
LinkArrayBuilder linkArrayBuilder(String rel);
/**
* Appends one embedded to the json response.
*
* @param rel name of relation
* @param embeddedItem embedded object
*/
void appendEmbedded(String rel, HalRepresentation embeddedItem);
/**
* Builder for link arrays.

View File

@@ -4,17 +4,17 @@ import com.google.common.annotations.VisibleForTesting;
import javax.inject.Inject;
public class LinkAppenderMapper {
public class HalAppenderMapper {
@Inject
private LinkEnricherRegistry registry;
private HalEnricherRegistry registry;
@VisibleForTesting
void setRegistry(LinkEnricherRegistry registry) {
void setRegistry(HalEnricherRegistry registry) {
this.registry = registry;
}
protected void appendLinks(LinkAppender appender, Object source, Object... contextEntries) {
protected void applyEnrichers(HalAppender appender, Object source, Object... contextEntries) {
// null check is only their to not break existing tests
if (registry != null) {
@@ -24,10 +24,10 @@ public class LinkAppenderMapper {
ctx[i + 1] = contextEntries[i];
}
LinkEnricherContext context = LinkEnricherContext.of(ctx);
HalEnricherContext context = HalEnricherContext.of(ctx);
Iterable<LinkEnricher> enrichers = registry.allByType(source.getClass());
for (LinkEnricher enricher : enrichers) {
Iterable<HalEnricher> enrichers = registry.allByType(source.getClass());
for (HalEnricher enricher : enrichers) {
enricher.enrich(context, appender);
}
}

View File

@@ -3,8 +3,8 @@ package sonia.scm.api.v2.resources;
import sonia.scm.plugin.ExtensionPoint;
/**
* A {@link LinkEnricher} can be used to append hateoas links to a specific json response.
* To register an enricher use the {@link Enrich} annotation or the {@link LinkEnricherRegistry} which is available
* A {@link HalEnricher} can be used to append hal specific attributes, such as links, to the json response.
* To register an enricher use the {@link Enrich} annotation or the {@link HalEnricherRegistry} which is available
* via injection.
*
* <b>Warning:</b> enrichers are always registered as singletons.
@@ -14,13 +14,13 @@ import sonia.scm.plugin.ExtensionPoint;
*/
@ExtensionPoint
@FunctionalInterface
public interface LinkEnricher {
public interface HalEnricher {
/**
* Enriches the response with hateoas links.
* Enriches the response with hal specific attributes.
*
* @param context contains the source for the json mapping and related objects
* @param appender can be used to append links to the json response
* @param appender can be used to append links or embedded objects to the json response
*/
void enrich(LinkEnricherContext context, LinkAppender appender);
void enrich(HalEnricherContext context, HalAppender appender);
}

View File

@@ -7,17 +7,17 @@ import java.util.NoSuchElementException;
import java.util.Optional;
/**
* Context object for the {@link LinkEnricher}. The context holds the source object for the json and all related
* objects, which can be useful for the link creation.
* Context object for the {@link HalEnricher}. The context holds the source object for the json and all related
* objects, which can be useful for the enrichment.
*
* @author Sebastian Sdorra
* @since 2.0.0
*/
public final class LinkEnricherContext {
public final class HalEnricherContext {
private final Map<Class, Object> instanceMap;
private LinkEnricherContext(Map<Class,Object> instanceMap) {
private HalEnricherContext(Map<Class,Object> instanceMap) {
this.instanceMap = instanceMap;
}
@@ -28,12 +28,12 @@ public final class LinkEnricherContext {
*
* @return context of given entries
*/
public static LinkEnricherContext of(Object... instances) {
public static HalEnricherContext of(Object... instances) {
ImmutableMap.Builder<Class, Object> builder = ImmutableMap.builder();
for (Object instance : instances) {
builder.put(instance.getClass(), instance);
}
return new LinkEnricherContext(builder.build());
return new HalEnricherContext(builder.build());
}
/**

View File

@@ -7,34 +7,34 @@ import sonia.scm.plugin.Extension;
import javax.inject.Singleton;
/**
* The {@link LinkEnricherRegistry} is responsible for binding {@link LinkEnricher} instances to their source types.
* The {@link HalEnricherRegistry} is responsible for binding {@link HalEnricher} instances to their source types.
*
* @author Sebastian Sdorra
* @since 2.0.0
*/
@Extension
@Singleton
public final class LinkEnricherRegistry {
public final class HalEnricherRegistry {
private final Multimap<Class, LinkEnricher> enrichers = HashMultimap.create();
private final Multimap<Class, HalEnricher> enrichers = HashMultimap.create();
/**
* Registers a new {@link LinkEnricher} for the given source type.
* Registers a new {@link HalEnricher} for the given source type.
*
* @param sourceType type of json mapping source
* @param enricher link enricher instance
*/
public void register(Class sourceType, LinkEnricher enricher) {
public void register(Class sourceType, HalEnricher enricher) {
enrichers.put(sourceType, enricher);
}
/**
* Returns all registered {@link LinkEnricher} for the given type.
* Returns all registered {@link HalEnricher} for the given type.
*
* @param sourceType type of json mapping source
* @return all registered enrichers
*/
public Iterable<LinkEnricher> allByType(Class sourceType) {
public Iterable<HalEnricher> allByType(Class sourceType) {
return enrichers.get(sourceType);
}
}

View File

@@ -1,7 +1,7 @@
package sonia.scm.api.v2.resources;
/**
* The {@link Index} object can be used to register a {@link LinkEnricher} for the index resource.
* The {@link Index} object can be used to register a {@link HalEnricher} for the index resource.
*
* @author Sebastian Sdorra
* @since 2.0.0

View File

@@ -1,7 +1,7 @@
package sonia.scm.api.v2.resources;
/**
* The {@link Me} object can be used to register a {@link LinkEnricher} for the me resource.
* The {@link Me} object can be used to register a {@link HalEnricher} for the me resource.
*
* @author Sebastian Sdorra
* @since 2.0.0

View File

@@ -11,51 +11,51 @@ import java.util.Optional;
import static org.mockito.Mockito.verify;
@ExtendWith(MockitoExtension.class)
class LinkAppenderMapperTest {
class HalAppenderMapperTest {
@Mock
private LinkAppender appender;
private HalAppender appender;
private LinkEnricherRegistry registry;
private LinkAppenderMapper mapper;
private HalEnricherRegistry registry;
private HalAppenderMapper mapper;
@BeforeEach
void beforeEach() {
registry = new LinkEnricherRegistry();
mapper = new LinkAppenderMapper();
registry = new HalEnricherRegistry();
mapper = new HalAppenderMapper();
mapper.setRegistry(registry);
}
@Test
void shouldAppendSimpleLink() {
registry.register(String.class, (ctx, appender) -> appender.appendOne("42", "https://hitchhiker.com"));
registry.register(String.class, (ctx, appender) -> appender.appendLink("42", "https://hitchhiker.com"));
mapper.appendLinks(appender, "hello");
mapper.applyEnrichers(appender, "hello");
verify(appender).appendOne("42", "https://hitchhiker.com");
verify(appender).appendLink("42", "https://hitchhiker.com");
}
@Test
void shouldCallMultipleEnrichers() {
registry.register(String.class, (ctx, appender) -> appender.appendOne("42", "https://hitchhiker.com"));
registry.register(String.class, (ctx, appender) -> appender.appendOne("21", "https://scm.hitchhiker.com"));
registry.register(String.class, (ctx, appender) -> appender.appendLink("42", "https://hitchhiker.com"));
registry.register(String.class, (ctx, appender) -> appender.appendLink("21", "https://scm.hitchhiker.com"));
mapper.appendLinks(appender, "hello");
mapper.applyEnrichers(appender, "hello");
verify(appender).appendOne("42", "https://hitchhiker.com");
verify(appender).appendOne("21", "https://scm.hitchhiker.com");
verify(appender).appendLink("42", "https://hitchhiker.com");
verify(appender).appendLink("21", "https://scm.hitchhiker.com");
}
@Test
void shouldAppendLinkByUsingSourceFromContext() {
registry.register(String.class, (ctx, appender) -> {
Optional<String> rel = ctx.oneByType(String.class);
appender.appendOne(rel.get(), "https://hitchhiker.com");
appender.appendLink(rel.get(), "https://hitchhiker.com");
});
mapper.appendLinks(appender, "42");
mapper.applyEnrichers(appender, "42");
verify(appender).appendOne("42", "https://hitchhiker.com");
verify(appender).appendLink("42", "https://hitchhiker.com");
}
@Test
@@ -63,12 +63,12 @@ class LinkAppenderMapperTest {
registry.register(Integer.class, (ctx, appender) -> {
Optional<Integer> rel = ctx.oneByType(Integer.class);
Optional<String> href = ctx.oneByType(String.class);
appender.appendOne(String.valueOf(rel.get()), href.get());
appender.appendLink(String.valueOf(rel.get()), href.get());
});
mapper.appendLinks(appender, Integer.valueOf(42), "https://hitchhiker.com");
mapper.applyEnrichers(appender, Integer.valueOf(42), "https://hitchhiker.com");
verify(appender).appendOne("42", "https://hitchhiker.com");
verify(appender).appendLink("42", "https://hitchhiker.com");
}
}

View File

@@ -7,17 +7,17 @@ import org.junit.jupiter.api.Test;
import java.util.NoSuchElementException;
class LinkEnricherContextTest {
class HalEnricherContextTest {
@Test
void shouldCreateContextFromSingleObject() {
LinkEnricherContext context = LinkEnricherContext.of("hello");
HalEnricherContext context = HalEnricherContext.of("hello");
assertThat(context.oneByType(String.class)).contains("hello");
}
@Test
void shouldCreateContextFromMultipleObjects() {
LinkEnricherContext context = LinkEnricherContext.of("hello", Integer.valueOf(42), Long.valueOf(21L));
HalEnricherContext context = HalEnricherContext.of("hello", Integer.valueOf(42), Long.valueOf(21L));
assertThat(context.oneByType(String.class)).contains("hello");
assertThat(context.oneByType(Integer.class)).contains(42);
assertThat(context.oneByType(Long.class)).contains(21L);
@@ -25,19 +25,19 @@ class LinkEnricherContextTest {
@Test
void shouldReturnEmptyOptionalForUnknownTypes() {
LinkEnricherContext context = LinkEnricherContext.of();
HalEnricherContext context = HalEnricherContext.of();
assertThat(context.oneByType(String.class)).isNotPresent();
}
@Test
void shouldReturnRequiredObject() {
LinkEnricherContext context = LinkEnricherContext.of("hello");
HalEnricherContext context = HalEnricherContext.of("hello");
assertThat(context.oneRequireByType(String.class)).isEqualTo("hello");
}
@Test
void shouldThrowAnNoSuchElementExceptionForUnknownTypes() {
LinkEnricherContext context = LinkEnricherContext.of();
HalEnricherContext context = HalEnricherContext.of();
assertThrows(NoSuchElementException.class, () -> context.oneRequireByType(String.class));
}

View File

@@ -5,54 +5,54 @@ import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class LinkEnricherRegistryTest {
class HalEnricherRegistryTest {
private LinkEnricherRegistry registry;
private HalEnricherRegistry registry;
@BeforeEach
void setUpObjectUnderTest() {
registry = new LinkEnricherRegistry();
registry = new HalEnricherRegistry();
}
@Test
void shouldRegisterTheEnricher() {
SampleLinkEnricher enricher = new SampleLinkEnricher();
SampleHalEnricher enricher = new SampleHalEnricher();
registry.register(String.class, enricher);
Iterable<LinkEnricher> enrichers = registry.allByType(String.class);
Iterable<HalEnricher> enrichers = registry.allByType(String.class);
assertThat(enrichers).containsOnly(enricher);
}
@Test
void shouldRegisterMultipleEnrichers() {
SampleLinkEnricher one = new SampleLinkEnricher();
SampleHalEnricher one = new SampleHalEnricher();
registry.register(String.class, one);
SampleLinkEnricher two = new SampleLinkEnricher();
SampleHalEnricher two = new SampleHalEnricher();
registry.register(String.class, two);
Iterable<LinkEnricher> enrichers = registry.allByType(String.class);
Iterable<HalEnricher> enrichers = registry.allByType(String.class);
assertThat(enrichers).containsOnly(one, two);
}
@Test
void shouldRegisterEnrichersForDifferentTypes() {
SampleLinkEnricher one = new SampleLinkEnricher();
SampleHalEnricher one = new SampleHalEnricher();
registry.register(String.class, one);
SampleLinkEnricher two = new SampleLinkEnricher();
SampleHalEnricher two = new SampleHalEnricher();
registry.register(Integer.class, two);
Iterable<LinkEnricher> enrichers = registry.allByType(String.class);
Iterable<HalEnricher> enrichers = registry.allByType(String.class);
assertThat(enrichers).containsOnly(one);
enrichers = registry.allByType(Integer.class);
assertThat(enrichers).containsOnly(two);
}
private static class SampleLinkEnricher implements LinkEnricher {
private static class SampleHalEnricher implements HalEnricher {
@Override
public void enrich(LinkEnricherContext context, LinkAppender appender) {
public void enrich(HalEnricherContext context, HalAppender appender) {
}
}

View File

@@ -1,5 +1,6 @@
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.Embedded;
import de.otto.edison.hal.HalRepresentation;
import de.otto.edison.hal.Links;
import lombok.Getter;
@@ -12,8 +13,7 @@ public class BranchDto extends HalRepresentation {
private String name;
private String revision;
@Override
protected HalRepresentation add(Links links) {
return super.add(links);
BranchDto(Links links, Embedded embedded) {
super(links, embedded);
}
}

View File

@@ -1,11 +1,11 @@
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.Embedded;
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 org.mapstruct.ObjectFactory;
import sonia.scm.repository.Branch;
import sonia.scm.repository.NamespaceAndName;
@@ -15,7 +15,7 @@ import static de.otto.edison.hal.Link.linkBuilder;
import static de.otto.edison.hal.Links.linkingTo;
@Mapper
public abstract class BranchToBranchDtoMapper extends LinkAppenderMapper {
public abstract class BranchToBranchDtoMapper extends HalAppenderMapper {
@Inject
private ResourceLinks resourceLinks;
@@ -23,16 +23,17 @@ public abstract class BranchToBranchDtoMapper extends LinkAppenderMapper {
@Mapping(target = "attributes", ignore = true) // We do not map HAL attributes
public abstract BranchDto map(Branch branch, @Context NamespaceAndName namespaceAndName);
@AfterMapping
void appendLinks(Branch source, @MappingTarget BranchDto target, @Context NamespaceAndName namespaceAndName) {
@ObjectFactory
BranchDto createDto(@Context NamespaceAndName namespaceAndName, Branch branch) {
Links.Builder linksBuilder = linkingTo()
.self(resourceLinks.branch().self(namespaceAndName, target.getName()))
.single(linkBuilder("history", resourceLinks.branch().history(namespaceAndName, target.getName())).build())
.single(linkBuilder("changeset", resourceLinks.changeset().changeset(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getRevision())).build())
.single(linkBuilder("source", resourceLinks.source().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getRevision())).build());
.self(resourceLinks.branch().self(namespaceAndName, branch.getName()))
.single(linkBuilder("history", resourceLinks.branch().history(namespaceAndName, branch.getName())).build())
.single(linkBuilder("changeset", resourceLinks.changeset().changeset(namespaceAndName.getNamespace(), namespaceAndName.getName(), branch.getRevision())).build())
.single(linkBuilder("source", resourceLinks.source().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), branch.getRevision())).build());
appendLinks(new EdisonLinkAppender(linksBuilder), source, namespaceAndName);
Embedded.Builder embeddedBuilder = Embedded.embeddedBuilder();
applyEnrichers(new EdisonHalAppender(linksBuilder, embeddedBuilder), branch, namespaceAndName);
target.add(linksBuilder.build());
return new BranchDto(linksBuilder.build(), embeddedBuilder.build());
}
}

View File

@@ -1,5 +1,6 @@
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.Embedded;
import de.otto.edison.hal.HalRepresentation;
import de.otto.edison.hal.Links;
import lombok.Getter;
@@ -7,7 +8,6 @@ import lombok.NoArgsConstructor;
import lombok.Setter;
import java.time.Instant;
import java.util.List;
@Getter
@Setter
@@ -34,16 +34,7 @@ public class ChangesetDto extends HalRepresentation {
*/
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);
public ChangesetDto(Links links, Embedded embedded) {
super(links, embedded);
}
@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);
}
}

View File

@@ -1,11 +1,11 @@
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.Embedded;
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 org.mapstruct.ObjectFactory;
import sonia.scm.repository.Branch;
import sonia.scm.repository.Changeset;
import sonia.scm.repository.Repository;
@@ -19,11 +19,12 @@ import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import static de.otto.edison.hal.Embedded.embeddedBuilder;
import static de.otto.edison.hal.Link.link;
import static de.otto.edison.hal.Links.linkingTo;
@Mapper
public abstract class ChangesetToChangesetDtoMapper extends LinkAppenderMapper implements InstantAttributeMapper {
public abstract class ChangesetToChangesetDtoMapper extends HalAppenderMapper implements InstantAttributeMapper {
@Inject
private RepositoryServiceFactory serviceFactory;
@@ -31,7 +32,6 @@ public abstract class ChangesetToChangesetDtoMapper extends LinkAppenderMapper i
@Inject
private ResourceLinks resourceLinks;
@Inject
private BranchCollectionToDtoMapper branchCollectionToDtoMapper;
@@ -46,31 +46,34 @@ public abstract class ChangesetToChangesetDtoMapper extends LinkAppenderMapper i
public abstract ChangesetDto map(Changeset changeset, @Context Repository repository);
@AfterMapping
void appendLinks(Changeset source, @MappingTarget ChangesetDto target, @Context Repository repository) {
@ObjectFactory
ChangesetDto createDto(@Context Repository repository, Changeset source) {
String namespace = repository.getNamespace();
String name = repository.getName();
Embedded.Builder embeddedBuilder = embeddedBuilder();
try (RepositoryService repositoryService = serviceFactory.create(repository)) {
if (repositoryService.isSupported(Command.TAGS)) {
target.withEmbedded("tags", tagCollectionToDtoMapper.getTagDtoList(namespace, name,
embeddedBuilder.with("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,
embeddedBuilder.with("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)));
embeddedBuilder.with("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())))
.single(link("modifications", resourceLinks.modifications().self(namespace, name, target.getId())));
.self(resourceLinks.changeset().self(repository.getNamespace(), repository.getName(), source.getId()))
.single(link("diff", resourceLinks.diff().self(namespace, name, source.getId())))
.single(link("modifications", resourceLinks.modifications().self(namespace, name, source.getId())));
appendLinks(new EdisonLinkAppender(linksBuilder), source, repository);
target.add(linksBuilder.build());
applyEnrichers(new EdisonHalAppender(linksBuilder, embeddedBuilder), source, repository);
return new ChangesetDto(linksBuilder.build(), embeddedBuilder.build());
}
private <T> List<T> getListOfObjects(List<String> list, Function<String, T> mapFunction) {

View File

@@ -1,27 +1,36 @@
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.Embedded;
import de.otto.edison.hal.HalRepresentation;
import de.otto.edison.hal.Link;
import de.otto.edison.hal.Links;
import java.util.ArrayList;
import java.util.List;
class EdisonLinkAppender implements LinkAppender {
class EdisonHalAppender implements HalAppender {
private final Links.Builder builder;
private final Links.Builder linkBuilder;
private final Embedded.Builder embeddedBuilder;
EdisonLinkAppender(Links.Builder builder) {
this.builder = builder;
EdisonHalAppender(Links.Builder linkBuilder, Embedded.Builder embeddedBuilder) {
this.linkBuilder = linkBuilder;
this.embeddedBuilder = embeddedBuilder;
}
@Override
public void appendOne(String rel, String href) {
builder.single(Link.link(rel, href));
public void appendLink(String rel, String href) {
linkBuilder.single(Link.link(rel, href));
}
@Override
public LinkArrayBuilder arrayBuilder(String rel) {
return new EdisonLinkArrayBuilder(builder, rel);
public LinkArrayBuilder linkArrayBuilder(String rel) {
return new EdisonLinkArrayBuilder(linkBuilder, rel);
}
@Override
public void appendEmbedded(String rel, HalRepresentation embedded) {
embeddedBuilder.with(rel, embedded);
}
private static class EdisonLinkArrayBuilder implements LinkArrayBuilder {

View File

@@ -1,6 +1,7 @@
package sonia.scm.api.v2.resources;
import com.fasterxml.jackson.annotation.JsonInclude;
import de.otto.edison.hal.Embedded;
import de.otto.edison.hal.HalRepresentation;
import de.otto.edison.hal.Links;
import lombok.Getter;
@@ -27,10 +28,8 @@ public class FileObjectDto extends HalRepresentation {
@JsonInclude(JsonInclude.Include.NON_EMPTY)
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);
public FileObjectDto(Links links, Embedded embedded) {
super(links, embedded);
}
public void setChildren(List<FileObjectDto> children) {

View File

@@ -1,24 +1,22 @@
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.Embedded;
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 org.mapstruct.ObjectFactory;
import sonia.scm.repository.FileObject;
import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.SubRepository;
import javax.inject.Inject;
import java.util.List;
import java.util.stream.Collectors;
import static de.otto.edison.hal.Embedded.embeddedBuilder;
import static de.otto.edison.hal.Link.link;
@Mapper
public abstract class FileObjectToFileObjectDtoMapper extends LinkAppenderMapper implements InstantAttributeMapper {
public abstract class FileObjectToFileObjectDtoMapper extends HalAppenderMapper implements InstantAttributeMapper {
@Inject
private ResourceLinks resourceLinks;
@@ -28,20 +26,21 @@ public abstract class FileObjectToFileObjectDtoMapper extends LinkAppenderMapper
abstract SubRepositoryDto mapSubrepository(SubRepository subRepository);
@AfterMapping
void addLinks(FileObject fileObject, @MappingTarget FileObjectDto dto, @Context NamespaceAndName namespaceAndName, @Context String revision) {
@ObjectFactory
FileObjectDto createDto(@Context NamespaceAndName namespaceAndName, @Context String revision, FileObject fileObject) {
String path = removeFirstSlash(fileObject.getPath());
Links.Builder links = Links.linkingTo();
if (dto.isDirectory()) {
if (fileObject.isDirectory()) {
links.self(resourceLinks.source().sourceWithPath(namespaceAndName.getNamespace(), namespaceAndName.getName(), revision, path));
} else {
links.self(resourceLinks.source().content(namespaceAndName.getNamespace(), namespaceAndName.getName(), revision, path));
links.single(link("history", resourceLinks.fileHistory().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), revision, path)));
}
appendLinks(new EdisonLinkAppender(links), fileObject, namespaceAndName, revision);
Embedded.Builder embeddedBuilder = embeddedBuilder();
applyEnrichers(new EdisonHalAppender(links, embeddedBuilder), fileObject, namespaceAndName, revision);
dto.add(links.build());
return new FileObjectDto(links.build(), embeddedBuilder.build());
}
private String removeFirstSlash(String source) {

View File

@@ -1,6 +1,7 @@
package sonia.scm.api.v2.resources;
import com.fasterxml.jackson.annotation.JsonInclude;
import de.otto.edison.hal.Embedded;
import de.otto.edison.hal.HalRepresentation;
import de.otto.edison.hal.Links;
import lombok.Getter;
@@ -28,13 +29,7 @@ public class GroupDto extends HalRepresentation {
private Map<String, String> properties;
private List<String> members;
@Override
@SuppressWarnings("squid:S1185") // We want to have this method available in this package
protected HalRepresentation add(Links links) {
return super.add(links);
}
public HalRepresentation withMembers(List<MemberDto> members) {
return super.withEmbedded("members", members);
GroupDto(Links links, Embedded embedded) {
super(links, embedded);
}
}

View File

@@ -1,9 +1,9 @@
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.Embedded;
import de.otto.edison.hal.Links;
import org.mapstruct.AfterMapping;
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
import org.mapstruct.ObjectFactory;
import sonia.scm.group.Group;
import sonia.scm.group.GroupPermissions;
import sonia.scm.security.PermissionPermissions;
@@ -12,6 +12,7 @@ import javax.inject.Inject;
import java.util.List;
import java.util.stream.Collectors;
import static de.otto.edison.hal.Embedded.embeddedBuilder;
import static de.otto.edison.hal.Link.link;
import static de.otto.edison.hal.Links.linkingTo;
@@ -23,28 +24,26 @@ public abstract class GroupToGroupDtoMapper extends BaseMapper<Group, GroupDto>
@Inject
private ResourceLinks resourceLinks;
@AfterMapping
void appendLinks(Group group, @MappingTarget GroupDto target) {
Links.Builder linksBuilder = linkingTo().self(resourceLinks.group().self(target.getName()));
@ObjectFactory
GroupDto createDto(Group group) {
Links.Builder linksBuilder = linkingTo().self(resourceLinks.group().self(group.getName()));
if (GroupPermissions.delete(group).isPermitted()) {
linksBuilder.single(link("delete", resourceLinks.group().delete(target.getName())));
linksBuilder.single(link("delete", resourceLinks.group().delete(group.getName())));
}
if (GroupPermissions.modify(group).isPermitted()) {
linksBuilder.single(link("update", resourceLinks.group().update(target.getName())));
linksBuilder.single(link("update", resourceLinks.group().update(group.getName())));
}
if (PermissionPermissions.read().isPermitted()) {
linksBuilder.single(link("permissions", resourceLinks.groupPermissions().permissions(target.getName())));
linksBuilder.single(link("permissions", resourceLinks.groupPermissions().permissions(group.getName())));
}
appendLinks(new EdisonLinkAppender(linksBuilder), group);
target.add(linksBuilder.build());
}
@AfterMapping
void mapMembers(Group group, @MappingTarget GroupDto target) {
Embedded.Builder embeddedBuilder = embeddedBuilder();
List<MemberDto> memberDtos = group.getMembers().stream().map(this::createMember).collect(Collectors.toList());
target.withMembers(memberDtos);
embeddedBuilder.with("members", memberDtos);
applyEnrichers(new EdisonHalAppender(linksBuilder, embeddedBuilder), group);
return new GroupDto(linksBuilder.build(), embeddedBuilder.build());
}
private MemberDto createMember(String name) {

View File

@@ -1,5 +1,6 @@
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.Embedded;
import de.otto.edison.hal.HalRepresentation;
import de.otto.edison.hal.Links;
import lombok.Getter;
@@ -9,8 +10,8 @@ public class IndexDto extends HalRepresentation {
private final String version;
IndexDto(String version, Links links) {
super(links);
IndexDto(Links links, Embedded embedded, String version) {
super(links, embedded);
this.version = version;
}
}

View File

@@ -1,6 +1,7 @@
package sonia.scm.api.v2.resources;
import com.google.common.collect.Lists;
import de.otto.edison.hal.Embedded;
import de.otto.edison.hal.Link;
import de.otto.edison.hal.Links;
import org.apache.shiro.SecurityUtils;
@@ -13,9 +14,10 @@ import sonia.scm.user.UserPermissions;
import javax.inject.Inject;
import java.util.List;
import static de.otto.edison.hal.Embedded.embeddedBuilder;
import static de.otto.edison.hal.Link.link;
public class IndexDtoGenerator extends LinkAppenderMapper {
public class IndexDtoGenerator extends HalAppenderMapper {
private final ResourceLinks resourceLinks;
private final SCMContextProvider scmContextProvider;
@@ -61,8 +63,9 @@ public class IndexDtoGenerator extends LinkAppenderMapper {
builder.single(link("login", resourceLinks.authentication().jsonLogin()));
}
appendLinks(new EdisonLinkAppender(builder), new Index());
Embedded.Builder embeddedBuilder = embeddedBuilder();
applyEnrichers(new EdisonHalAppender(builder, embeddedBuilder), new Index());
return new IndexDto(scmContextProvider.getVersion(), builder.build());
return new IndexDto(builder.build(), embeddedBuilder.build(), scmContextProvider.getVersion());
}
}

View File

@@ -10,30 +10,30 @@ import javax.servlet.ServletContextListener;
import java.util.Set;
/**
* Registers every {@link LinkEnricher} which is annotated with an {@link Enrich} annotation.
* Registers every {@link HalEnricher} which is annotated with an {@link Enrich} annotation.
*/
@Extension
public class LinkEnricherAutoRegistration implements ServletContextListener {
private static final Logger LOG = LoggerFactory.getLogger(LinkEnricherAutoRegistration.class);
private final LinkEnricherRegistry registry;
private final Set<LinkEnricher> enrichers;
private final HalEnricherRegistry registry;
private final Set<HalEnricher> enrichers;
@Inject
public LinkEnricherAutoRegistration(LinkEnricherRegistry registry, Set<LinkEnricher> enrichers) {
public LinkEnricherAutoRegistration(HalEnricherRegistry registry, Set<HalEnricher> enrichers) {
this.registry = registry;
this.enrichers = enrichers;
}
@Override
public void contextInitialized(ServletContextEvent sce) {
for (LinkEnricher enricher : enrichers) {
for (HalEnricher enricher : enrichers) {
Enrich annotation = enricher.getClass().getAnnotation(Enrich.class);
if (annotation != null) {
registry.register(annotation.value(), enricher);
} else {
LOG.warn("found LinkEnricher extension {} without Enrich annotation", enricher.getClass());
LOG.warn("found HalEnricher extension {} without Enrich annotation", enricher.getClass());
}
}
}

View File

@@ -1,5 +1,6 @@
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.Embedded;
import de.otto.edison.hal.HalRepresentation;
import de.otto.edison.hal.Links;
import lombok.Getter;
@@ -18,9 +19,7 @@ public class MeDto extends HalRepresentation {
private String mail;
private List<String> groups;
@Override
@SuppressWarnings("squid:S1185") // We want to have this method available in this package
protected HalRepresentation add(Links links) {
return super.add(links);
MeDto(Links links, Embedded embedded) {
super(links, embedded);
}
}

View File

@@ -1,6 +1,7 @@
package sonia.scm.api.v2.resources;
import com.google.common.collect.ImmutableList;
import de.otto.edison.hal.Embedded;
import de.otto.edison.hal.Links;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.PrincipalCollection;
@@ -13,10 +14,11 @@ import sonia.scm.user.UserPermissions;
import javax.inject.Inject;
import java.util.Collections;
import static de.otto.edison.hal.Embedded.embeddedBuilder;
import static de.otto.edison.hal.Link.link;
import static de.otto.edison.hal.Links.linkingTo;
public class MeDtoFactory extends LinkAppenderMapper {
public class MeDtoFactory extends HalAppenderMapper {
private final ResourceLinks resourceLinks;
private final UserManager userManager;
@@ -29,15 +31,11 @@ public class MeDtoFactory extends LinkAppenderMapper {
public MeDto create() {
PrincipalCollection principals = getPrincipalCollection();
MeDto dto = new MeDto();
User user = principals.oneByType(User.class);
MeDto dto = createDto(user);
mapUserProperties(user, dto);
mapGroups(principals, dto);
appendLinks(user, dto);
return dto;
}
@@ -61,21 +59,22 @@ public class MeDtoFactory extends LinkAppenderMapper {
}
private void appendLinks(User user, MeDto target) {
private MeDto createDto(User user) {
Links.Builder linksBuilder = linkingTo().self(resourceLinks.me().self());
if (UserPermissions.delete(user).isPermitted()) {
linksBuilder.single(link("delete", resourceLinks.me().delete(target.getName())));
linksBuilder.single(link("delete", resourceLinks.me().delete(user.getName())));
}
if (UserPermissions.modify(user).isPermitted()) {
linksBuilder.single(link("update", resourceLinks.me().update(target.getName())));
linksBuilder.single(link("update", resourceLinks.me().update(user.getName())));
}
if (userManager.isTypeDefault(user) && UserPermissions.changePassword(user).isPermitted()) {
linksBuilder.single(link("password", resourceLinks.me().passwordChange()));
}
appendLinks(new EdisonLinkAppender(linksBuilder), new Me(), user);
Embedded.Builder embeddedBuilder = embeddedBuilder();
applyEnrichers(new EdisonHalAppender(linksBuilder, embeddedBuilder), new Me(), user);
target.add(linksBuilder.build());
return new MeDto(linksBuilder.build(), embeddedBuilder.build());
}
}

View File

@@ -1,9 +1,11 @@
package sonia.scm.api.v2.resources;
import com.fasterxml.jackson.annotation.JsonInclude;
import de.otto.edison.hal.Embedded;
import de.otto.edison.hal.HalRepresentation;
import de.otto.edison.hal.Links;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;
@@ -13,7 +15,7 @@ import java.time.Instant;
import java.util.List;
import java.util.Map;
@Getter @Setter
@Getter @Setter @NoArgsConstructor
public class RepositoryDto extends HalRepresentation {
@Email
@@ -31,9 +33,7 @@ public class RepositoryDto extends HalRepresentation {
private String type;
protected Map<String, String> properties;
@Override
@SuppressWarnings("squid:S1185") // We want to have this method available in this package
protected HalRepresentation add(Links links) {
return super.add(links);
RepositoryDto(Links links, Embedded embedded) {
super(links, embedded);
}
}

View File

@@ -1,11 +1,11 @@
package sonia.scm.api.v2.resources;
import com.google.inject.Inject;
import de.otto.edison.hal.Embedded;
import de.otto.edison.hal.Link;
import de.otto.edison.hal.Links;
import org.mapstruct.AfterMapping;
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
import org.mapstruct.ObjectFactory;
import sonia.scm.repository.Feature;
import sonia.scm.repository.HealthCheckFailure;
import sonia.scm.repository.Repository;
@@ -17,6 +17,7 @@ import sonia.scm.repository.api.ScmProtocol;
import java.util.List;
import static de.otto.edison.hal.Embedded.embeddedBuilder;
import static de.otto.edison.hal.Link.link;
import static de.otto.edison.hal.Links.linkingTo;
import static java.util.stream.Collectors.toList;
@@ -33,17 +34,17 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper<Reposit
abstract HealthCheckFailureDto toDto(HealthCheckFailure failure);
@AfterMapping
void appendLinks(Repository repository, @MappingTarget RepositoryDto target) {
Links.Builder linksBuilder = linkingTo().self(resourceLinks.repository().self(target.getNamespace(), target.getName()));
@ObjectFactory
RepositoryDto createDto(Repository repository) {
Links.Builder linksBuilder = linkingTo().self(resourceLinks.repository().self(repository.getNamespace(), repository.getName()));
if (RepositoryPermissions.delete(repository).isPermitted()) {
linksBuilder.single(link("delete", resourceLinks.repository().delete(target.getNamespace(), target.getName())));
linksBuilder.single(link("delete", resourceLinks.repository().delete(repository.getNamespace(), repository.getName())));
}
if (RepositoryPermissions.modify(repository).isPermitted()) {
linksBuilder.single(link("update", resourceLinks.repository().update(target.getNamespace(), target.getName())));
linksBuilder.single(link("update", resourceLinks.repository().update(repository.getNamespace(), repository.getName())));
}
if (RepositoryPermissions.permissionRead(repository).isPermitted()) {
linksBuilder.single(link("permissions", resourceLinks.repositoryPermission().all(target.getNamespace(), target.getName())));
linksBuilder.single(link("permissions", resourceLinks.repositoryPermission().all(repository.getNamespace(), repository.getName())));
}
try (RepositoryService repositoryService = serviceFactory.create(repository)) {
if (RepositoryPermissions.pull(repository).isPermitted()) {
@@ -53,26 +54,27 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper<Reposit
linksBuilder.array(protocolLinks);
}
if (repositoryService.isSupported(Command.TAGS)) {
linksBuilder.single(link("tags", resourceLinks.tag().all(target.getNamespace(), target.getName())));
linksBuilder.single(link("tags", resourceLinks.tag().all(repository.getNamespace(), repository.getName())));
}
if (repositoryService.isSupported(Command.BRANCHES)) {
linksBuilder.single(link("branches", resourceLinks.branchCollection().self(target.getNamespace(), target.getName())));
linksBuilder.single(link("branches", resourceLinks.branchCollection().self(repository.getNamespace(), repository.getName())));
}
if (repositoryService.isSupported(Feature.INCOMING_REVISION)) {
linksBuilder.single(link("incomingChangesets", resourceLinks.incoming().changesets(target.getNamespace(), target.getName())));
linksBuilder.single(link("incomingDiff", resourceLinks.incoming().diff(target.getNamespace(), target.getName())));
linksBuilder.single(link("incomingChangesets", resourceLinks.incoming().changesets(repository.getNamespace(), repository.getName())));
linksBuilder.single(link("incomingDiff", resourceLinks.incoming().diff(repository.getNamespace(), repository.getName())));
}
if (repositoryService.isSupported(Command.MERGE)) {
linksBuilder.single(link("merge", resourceLinks.merge().merge(target.getNamespace(), target.getName())));
linksBuilder.single(link("mergeDryRun", resourceLinks.merge().dryRun(target.getNamespace(), target.getName())));
linksBuilder.single(link("merge", resourceLinks.merge().merge(repository.getNamespace(), repository.getName())));
linksBuilder.single(link("mergeDryRun", resourceLinks.merge().dryRun(repository.getNamespace(), repository.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("changesets", resourceLinks.changeset().all(repository.getNamespace(), repository.getName())));
linksBuilder.single(link("sources", resourceLinks.source().selfWithoutRevision(repository.getNamespace(), repository.getName())));
appendLinks(new EdisonLinkAppender(linksBuilder), repository);
Embedded.Builder embeddedBuilder = embeddedBuilder();
applyEnrichers(new EdisonHalAppender(linksBuilder, embeddedBuilder), repository);
target.add(linksBuilder.build());
return new RepositoryDto(linksBuilder.build(), embeddedBuilder.build());
}
private Link createProtocolLink(ScmProtocol protocol) {

View File

@@ -1,5 +1,6 @@
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.Embedded;
import de.otto.edison.hal.HalRepresentation;
import de.otto.edison.hal.Links;
import lombok.Getter;
@@ -15,10 +16,8 @@ public class TagDto extends HalRepresentation {
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);
TagDto(Links links, Embedded embedded) {
super(links, embedded);
}
}

View File

@@ -1,21 +1,22 @@
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.Embedded;
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 org.mapstruct.ObjectFactory;
import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.Tag;
import javax.inject.Inject;
import static de.otto.edison.hal.Embedded.embeddedBuilder;
import static de.otto.edison.hal.Link.link;
import static de.otto.edison.hal.Links.linkingTo;
@Mapper
public abstract class TagToTagDtoMapper extends LinkAppenderMapper {
public abstract class TagToTagDtoMapper extends HalAppenderMapper {
@Inject
private ResourceLinks resourceLinks;
@@ -23,15 +24,16 @@ public abstract class TagToTagDtoMapper extends LinkAppenderMapper {
@Mapping(target = "attributes", ignore = true) // We do not map HAL attributes
public abstract TagDto map(Tag tag, @Context NamespaceAndName namespaceAndName);
@AfterMapping
void appendLinks(Tag tag, @MappingTarget TagDto target, @Context NamespaceAndName namespaceAndName) {
@ObjectFactory
TagDto createDto(@Context NamespaceAndName namespaceAndName, Tag tag) {
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("changeset", resourceLinks.changeset().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getRevision())));
.self(resourceLinks.tag().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), tag.getName()))
.single(link("sources", resourceLinks.source().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), tag.getRevision())))
.single(link("changeset", resourceLinks.changeset().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), tag.getRevision())));
appendLinks(new EdisonLinkAppender(linksBuilder), tag, namespaceAndName);
Embedded.Builder embeddedBuilder = embeddedBuilder();
applyEnrichers(new EdisonHalAppender(linksBuilder, embeddedBuilder), tag, namespaceAndName);
target.add(linksBuilder.build());
return new TagDto(linksBuilder.build(), embeddedBuilder.build());
}
}

View File

@@ -1,6 +1,7 @@
package sonia.scm.api.v2.resources;
import com.fasterxml.jackson.annotation.JsonInclude;
import de.otto.edison.hal.Embedded;
import de.otto.edison.hal.HalRepresentation;
import de.otto.edison.hal.Links;
import lombok.Getter;
@@ -33,9 +34,7 @@ public class UserDto extends HalRepresentation {
private String type;
private Map<String, String> properties;
@Override
@SuppressWarnings("squid:S1185") // We want to have this method available in this package
protected HalRepresentation add(Links links) {
return super.add(links);
UserDto(Links links, Embedded embedded) {
super(links, embedded);
}
}

View File

@@ -1,10 +1,10 @@
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.Embedded;
import de.otto.edison.hal.Links;
import org.mapstruct.AfterMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.ObjectFactory;
import sonia.scm.security.PermissionPermissions;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
@@ -12,6 +12,7 @@ import sonia.scm.user.UserPermissions;
import javax.inject.Inject;
import static de.otto.edison.hal.Embedded.embeddedBuilder;
import static de.otto.edison.hal.Link.link;
import static de.otto.edison.hal.Links.linkingTo;
@@ -31,25 +32,26 @@ public abstract class UserToUserDtoMapper extends BaseMapper<User, UserDto> {
@Inject
private ResourceLinks resourceLinks;
@AfterMapping
protected void appendLinks(User user, @MappingTarget UserDto target) {
Links.Builder linksBuilder = linkingTo().self(resourceLinks.user().self(target.getName()));
@ObjectFactory
UserDto createDto(User user) {
Links.Builder linksBuilder = linkingTo().self(resourceLinks.user().self(user.getName()));
if (UserPermissions.delete(user).isPermitted()) {
linksBuilder.single(link("delete", resourceLinks.user().delete(target.getName())));
linksBuilder.single(link("delete", resourceLinks.user().delete(user.getName())));
}
if (UserPermissions.modify(user).isPermitted()) {
linksBuilder.single(link("update", resourceLinks.user().update(target.getName())));
linksBuilder.single(link("update", resourceLinks.user().update(user.getName())));
if (userManager.isTypeDefault(user)) {
linksBuilder.single(link("password", resourceLinks.user().passwordChange(target.getName())));
linksBuilder.single(link("password", resourceLinks.user().passwordChange(user.getName())));
}
}
if (PermissionPermissions.read().isPermitted()) {
linksBuilder.single(link("permissions", resourceLinks.userPermissions().permissions(target.getName())));
linksBuilder.single(link("permissions", resourceLinks.userPermissions().permissions(user.getName())));
}
appendLinks(new EdisonLinkAppender(linksBuilder), user);
Embedded.Builder embeddedBuilder = embeddedBuilder();
applyEnrichers(new EdisonHalAppender(linksBuilder, embeddedBuilder), user);
target.add(linksBuilder.build());
return new UserDto(linksBuilder.build(), embeddedBuilder.build());
}
}

View File

@@ -24,12 +24,12 @@ class BranchToBranchDtoMapperTest {
@Test
void shouldAppendLinks() {
LinkEnricherRegistry registry = new LinkEnricherRegistry();
HalEnricherRegistry registry = new HalEnricherRegistry();
registry.register(Branch.class, (ctx, appender) -> {
NamespaceAndName namespaceAndName = ctx.oneRequireByType(NamespaceAndName.class);
Branch branch = ctx.oneRequireByType(Branch.class);
appender.appendOne("ka", "http://" + namespaceAndName.logString() + "/" + branch.getName());
appender.appendLink("ka", "http://" + namespaceAndName.logString() + "/" + branch.getName());
});
mapper.setRegistry(registry);

View File

@@ -0,0 +1,61 @@
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.Embedded;
import de.otto.edison.hal.HalRepresentation;
import de.otto.edison.hal.Link;
import de.otto.edison.hal.Links;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.List;
import static de.otto.edison.hal.Embedded.embeddedBuilder;
import static de.otto.edison.hal.Links.linkingTo;
import static org.assertj.core.api.Assertions.assertThat;
class EdisonHalAppenderTest {
private Links.Builder linksBuilder;
private Embedded.Builder embeddedBuilder;
private EdisonHalAppender appender;
@BeforeEach
void prepare() {
linksBuilder = linkingTo();
embeddedBuilder = embeddedBuilder();
appender = new EdisonHalAppender(linksBuilder, embeddedBuilder);
}
@Test
void shouldAppendOneLink() {
appender.appendLink("self", "https://scm.hitchhiker.com");
Links links = linksBuilder.build();
assertThat(links.getLinkBy("self").get().getHref()).isEqualTo("https://scm.hitchhiker.com");
}
@Test
void shouldAppendMultipleLinks() {
appender.linkArrayBuilder("items")
.append("one", "http://one")
.append("two", "http://two")
.build();
List<Link> items = linksBuilder.build().getLinksBy("items");
assertThat(items).hasSize(2);
}
@Test
void shouldAppendEmbedded() {
HalRepresentation one = new HalRepresentation();
appender.appendEmbedded("one", one);
HalRepresentation two = new HalRepresentation();
appender.appendEmbedded("two", new HalRepresentation());
Embedded embedded = embeddedBuilder.build();
assertThat(embedded.getItemsBy("one")).containsOnly(one);
assertThat(embedded.getItemsBy("two")).containsOnly(two);
}
}

View File

@@ -1,43 +0,0 @@
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.Link;
import de.otto.edison.hal.Links;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.List;
import static de.otto.edison.hal.Links.linkingTo;
import static org.assertj.core.api.Assertions.assertThat;
class EdisonLinkAppenderTest {
private Links.Builder builder;
private EdisonLinkAppender appender;
@BeforeEach
void prepare() {
builder = linkingTo();
appender = new EdisonLinkAppender(builder);
}
@Test
void shouldAppendOneLink() {
appender.appendOne("self", "https://scm.hitchhiker.com");
Links links = builder.build();
assertThat(links.getLinkBy("self").get().getHref()).isEqualTo("https://scm.hitchhiker.com");
}
@Test
void shouldAppendMultipleLinks() {
appender.arrayBuilder("items")
.append("one", "http://one")
.append("two", "http://two")
.build();
List<Link> items = builder.build().getLinksBy("items");
assertThat(items).hasSize(2);
}
}

View File

@@ -73,13 +73,13 @@ public class FileObjectToFileObjectDtoMapperTest {
@Test
public void shouldAppendLinks() {
LinkEnricherRegistry registry = new LinkEnricherRegistry();
HalEnricherRegistry registry = new HalEnricherRegistry();
registry.register(FileObject.class, (ctx, appender) -> {
NamespaceAndName repository = ctx.oneRequireByType(NamespaceAndName.class);
FileObject fo = ctx.oneRequireByType(FileObject.class);
String rev = ctx.oneRequireByType(String.class);
appender.appendOne("hog", "http://" + repository.logString() + "/" + fo.getName() + "/" + rev);
appender.appendLink("hog", "http://" + repository.logString() + "/" + fo.getName() + "/" + rev);
});
mapper.setRegistry(registry);

View File

@@ -11,7 +11,6 @@ import org.mockito.InjectMocks;
import sonia.scm.group.Group;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.stream.IntStream;
import static java.util.stream.Collectors.toList;
@@ -91,10 +90,10 @@ public class GroupToGroupDtoMapperTest {
@Test
public void shouldAppendLinks() {
LinkEnricherRegistry registry = new LinkEnricherRegistry();
HalEnricherRegistry registry = new HalEnricherRegistry();
registry.register(Group.class, (ctx, appender) -> {
Group group = ctx.oneRequireByType(Group.class);
appender.appendOne("some", "http://" + group.getName());
appender.appendLink("some", "http://" + group.getName());
});
mapper.setRegistry(registry);

View File

@@ -0,0 +1,64 @@
package sonia.scm.api.v2.resources;
import com.google.common.collect.ImmutableSet;
import org.junit.jupiter.api.Test;
import java.util.Set;
import static org.assertj.core.api.Java6Assertions.assertThat;
class HalEnricherAutoRegistrationTest {
@Test
void shouldRegisterAllAvailableLinkEnrichers() {
HalEnricher one = new One();
HalEnricher two = new Two();
HalEnricher three = new Three();
HalEnricher four = new Four();
Set<HalEnricher> enrichers = ImmutableSet.of(one, two, three, four);
HalEnricherRegistry registry = new HalEnricherRegistry();
LinkEnricherAutoRegistration autoRegistration = new LinkEnricherAutoRegistration(registry, enrichers);
autoRegistration.contextInitialized(null);
assertThat(registry.allByType(String.class)).containsOnly(one, two);
assertThat(registry.allByType(Integer.class)).containsOnly(three);
}
@Enrich(String.class)
public static class One implements HalEnricher {
@Override
public void enrich(HalEnricherContext context, HalAppender appender) {
}
}
@Enrich(String.class)
public static class Two implements HalEnricher {
@Override
public void enrich(HalEnricherContext context, HalAppender appender) {
}
}
@Enrich(Integer.class)
public static class Three implements HalEnricher {
@Override
public void enrich(HalEnricherContext context, HalAppender appender) {
}
}
public static class Four implements HalEnricher {
@Override
public void enrich(HalEnricherContext context, HalAppender appender) {
}
}
}

View File

@@ -1,64 +0,0 @@
package sonia.scm.api.v2.resources;
import com.google.common.collect.ImmutableSet;
import org.junit.jupiter.api.Test;
import java.util.Set;
import static org.assertj.core.api.Java6Assertions.assertThat;
class LinkEnricherAutoRegistrationTest {
@Test
void shouldRegisterAllAvailableLinkEnrichers() {
LinkEnricher one = new One();
LinkEnricher two = new Two();
LinkEnricher three = new Three();
LinkEnricher four = new Four();
Set<LinkEnricher> enrichers = ImmutableSet.of(one, two, three, four);
LinkEnricherRegistry registry = new LinkEnricherRegistry();
LinkEnricherAutoRegistration autoRegistration = new LinkEnricherAutoRegistration(registry, enrichers);
autoRegistration.contextInitialized(null);
assertThat(registry.allByType(String.class)).containsOnly(one, two);
assertThat(registry.allByType(Integer.class)).containsOnly(three);
}
@Enrich(String.class)
public static class One implements LinkEnricher {
@Override
public void enrich(LinkEnricherContext context, LinkAppender appender) {
}
}
@Enrich(String.class)
public static class Two implements LinkEnricher {
@Override
public void enrich(LinkEnricherContext context, LinkAppender appender) {
}
}
@Enrich(Integer.class)
public static class Three implements LinkEnricher {
@Override
public void enrich(LinkEnricherContext context, LinkAppender appender) {
}
}
public static class Four implements LinkEnricher {
@Override
public void enrich(LinkEnricherContext context, LinkAppender appender) {
}
}
}

View File

@@ -15,7 +15,6 @@ import org.mockito.quality.Strictness;
import sonia.scm.group.GroupNames;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import sonia.scm.user.UserPermissions;
import sonia.scm.user.UserTestData;
import java.net.URI;
@@ -170,12 +169,12 @@ class MeDtoFactoryTest {
void shouldAppendLinks() {
prepareSubject(UserTestData.createTrillian());
LinkEnricherRegistry registry = new LinkEnricherRegistry();
HalEnricherRegistry registry = new HalEnricherRegistry();
meDtoFactory.setRegistry(registry);
registry.register(Me.class, (ctx, appender) -> {
User user = ctx.oneRequireByType(User.class);
appender.appendOne("profile", "http://hitchhiker.com/users/" + user.getName());
appender.appendLink("profile", "http://hitchhiker.com/users/" + user.getName());
});
MeDto dto = meDtoFactory.create();

View File

@@ -211,10 +211,10 @@ public class RepositoryToRepositoryDtoMapperTest {
@Test
public void shouldAppendLinks() {
LinkEnricherRegistry registry = new LinkEnricherRegistry();
HalEnricherRegistry registry = new HalEnricherRegistry();
registry.register(Repository.class, (ctx, appender) -> {
Repository repository = ctx.oneRequireByType(Repository.class);
appender.appendOne("id", "http://" + repository.getId());
appender.appendLink("id", "http://" + repository.getId());
});
mapper.setRegistry(registry);

View File

@@ -22,11 +22,11 @@ class TagToTagDtoMapperTest {
@Test
void shouldAppendLinks() {
LinkEnricherRegistry registry = new LinkEnricherRegistry();
HalEnricherRegistry registry = new HalEnricherRegistry();
registry.register(Tag.class, (ctx, appender) -> {
NamespaceAndName repository = ctx.oneRequireByType(NamespaceAndName.class);
Tag tag = ctx.oneRequireByType(Tag.class);
appender.appendOne("yo", "http://" + repository.logString() + "/" + tag.getName());
appender.appendLink("yo", "http://" + repository.logString() + "/" + tag.getName());
});
mapper.setRegistry(registry);

View File

@@ -155,8 +155,8 @@ public class UserToUserDtoMapperTest {
public void shouldAppendLink() {
User trillian = UserTestData.createTrillian();
LinkEnricherRegistry registry = new LinkEnricherRegistry();
registry.register(User.class, (ctx, appender) -> appender.appendOne("sample", "http://" + ctx.oneByType(User.class).get().getName()));
HalEnricherRegistry registry = new HalEnricherRegistry();
registry.register(User.class, (ctx, appender) -> appender.appendLink("sample", "http://" + ctx.oneByType(User.class).get().getName()));
mapper.setRegistry(registry);
UserDto userDto = mapper.map(trillian);

View File

@@ -136,10 +136,13 @@ public class GitLfsITCase {
}
private void createUser(User user) {
UserDto dto = new UserToUserDtoMapperImpl(){
@Override
protected void appendLinks(User user, UserDto target) {}
}.map(user);
UserDto dto = new UserDto();
dto.setName(user.getName());
dto.setMail(user.getMail());
dto.setDisplayName(user.getDisplayName());
dto.setType(user.getType());
dto.setActive(user.isActive());
dto.setAdmin(user.isAdmin());
dto.setPassword(user.getPassword());
createResource(adminClient, "users")
.accept("*/*")