Javadoc comments and hypermedia tweaks

This commit is contained in:
Kenny Bastani
2016-12-07 02:46:43 -08:00
parent 8d6597c712
commit d0e8b2eba8
21 changed files with 369 additions and 157 deletions

View File

@@ -0,0 +1,36 @@
package demo;
import demo.account.Account;
import demo.account.AccountController;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.ResourceProcessor;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
@Configuration
public class AccountResourceConfig {
/**
* Enriches the {@link Account} resource with hypermedia links.
*
* @return a hypermedia processor for the {@link Account} resource
*/
@Bean
public ResourceProcessor<Resource<Account>> accountProcessor() {
return new ResourceProcessor<Resource<Account>>() {
@Override
public Resource<Account> process(Resource<Account> resource) {
resource.add(
linkTo(AccountController.class)
.slash("accounts")
.slash(resource.getContent().getAccountId())
.slash("commands")
.withRel("commands"));
return resource;
}
};
}
}

View File

@@ -1,10 +1,10 @@
package demo.event;
package demo;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableBinding(ProducerChannels.class)
public class StreamConfig {
@EnableBinding(Source.class)
public class AccountStreamConfig {
}

View File

@@ -1,5 +1,6 @@
package demo.account;
import com.fasterxml.jackson.annotation.JsonIgnore;
import demo.domain.BaseEntity;
import demo.event.AccountEvent;
@@ -12,6 +13,8 @@ import java.util.Set;
* a user's account. The status of an account is event sourced using
* events logged to the {@link AccountEvent} collection attached to
* this resource.
*
* @author kbastani
*/
@Entity
public class Account extends BaseEntity {
@@ -39,11 +42,12 @@ public class Account extends BaseEntity {
this.status = status;
}
public Long getId() {
@JsonIgnore
public Long getAccountId() {
return id;
}
public void setId(Long id) {
public void setAccountId(Long id) {
this.id = id;
}

View File

@@ -1,5 +1,12 @@
package demo.account;
/**
* The {@link AccountCommand} represents an action that can be performed to an
* {@link Account} aggregate. Commands initiate an action that can mutate the state of
* an account entity as it transitions between {@link AccountEventStatus} values.
*
* @author kbastani
*/
public enum AccountCommand {
CONFIRM_ACCOUNT,
ACTIVATE_ACCOUNT,

View File

@@ -2,5 +2,11 @@ package demo.account;
import org.springframework.hateoas.ResourceSupport;
/**
* A hypermedia resource that describes the collection of commands that
* can be applied to a {@link Account} aggregate.
*
* @author kbastani
*/
public class AccountCommandsResource extends ResourceSupport {
}

View File

@@ -38,6 +38,13 @@ public class AccountController {
.orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
@GetMapping(path = "/accounts/{id}/events")
public ResponseEntity getAccountEvents(@PathVariable Long id) {
return Optional.ofNullable(accountService.getAccountEventResources(id))
.map(e -> new ResponseEntity<>(e, HttpStatus.OK))
.orElseThrow(() -> new IllegalArgumentException("Could not get account events"));
}
@PostMapping(path = "/accounts/{id}/events")
public ResponseEntity createAccount(@PathVariable Long id, @RequestBody AccountEvent event) {
return Optional.ofNullable(accountService.appendEventResource(id, event))

View File

@@ -4,6 +4,8 @@ package demo.account;
* The {@link AccountEventStatus} describes the state of an {@link Account}.
* The aggregate state of a {@link Account} is sourced from attached domain
* events in the form of {@link demo.event.AccountEvent}.
*
* @author kbastani
*/
public enum AccountEventStatus {
ACCOUNT_CREATED,

View File

@@ -1,5 +1,11 @@
package demo.account;
/**
* The {@link AccountEventType} represents a collection of possible events that describe
* state transitions of {@link AccountEventStatus} on the {@link Account} aggregate.
*
* @author kbastani
*/
public enum AccountEventType {
ACCOUNT_CREATED,
ACCOUNT_CONFIRMED,

View File

@@ -0,0 +1,56 @@
package demo.account;
import com.fasterxml.jackson.annotation.JsonIgnore;
import demo.event.AccountEvent;
import demo.event.EventController;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Resources;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
public class AccountEvents extends Resources<AccountEvent> {
private Long accountId;
/**
* Creates an empty {@link Resources} instance.
*/
public AccountEvents(Long accountId, Iterable<AccountEvent> content) {
this(content);
this.accountId = accountId;
// Add hypermedia links to resources parent
add(linkTo(AccountController.class)
.slash("accounts")
.slash(accountId)
.slash("events")
.withSelfRel(),
linkTo(AccountController.class)
.slash("accounts")
.slash(accountId)
.withRel("account"));
// Add hypermedia links to each item of the collection
content.forEach(event -> event.add(
linkTo(EventController.class)
.slash("events")
.slash(event.getEventId())
.withSelfRel()
));
}
/**
* Creates a {@link Resources} instance with the given content and {@link Link}s (optional).
*
* @param content must not be {@literal null}.
* @param links the links to be added to the {@link Resources}.
*/
public AccountEvents(Iterable<AccountEvent> content, Link... links) {
super(content, links);
}
@JsonIgnore
public Long getAccountId() {
return accountId;
}
}

View File

@@ -6,6 +6,5 @@ import org.springframework.data.repository.query.Param;
public interface AccountRepository extends JpaRepository<Account, Long> {
Account findAccountByUserId(@Param("userId") Long userId);
Account findAccountByAccountNumber(@Param("accountNumber") String accountNumber);
}

View File

@@ -21,6 +21,8 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
* Events can be appended to an {@link Account}, which contains a append-only log of
* actions that can be used to support remediation for distributed transactions that encountered
* a partial failure.
*
* @author kbastani
*/
@Service
@Transactional
@@ -101,17 +103,15 @@ public class AccountService {
if (event != null) {
eventResource = new Resource<>(event,
entityLinks.linkFor(AccountEvent.class, event.getId())
.slash(event.getId())
linkTo(AccountController.class)
.slash("accounts")
.slash(accountId)
.slash("events")
.withSelfRel(),
linkTo(AccountController.class)
.slash("accounts")
.slash(accountId)
.withRel("account"),
entityLinks.linkFor(AccountEvent.class, event.getId())
.slash(event.getId())
.slash("logs")
.withRel("logs")
.withRel("account")
);
}
@@ -128,55 +128,61 @@ public class AccountService {
public Resource<Account> applyCommand(Long id, AccountCommand accountCommand) {
Resource<Account> account = getAccountResource(id);
if (account != null) {
Assert.notNull(account, "The account for the supplied id could not be found");
AccountEventStatus status = account.getContent().getStatus();
AccountEventStatus status = account.getContent().getStatus();
switch (accountCommand) {
case CONFIRM_ACCOUNT:
Assert.isTrue(status == ACCOUNT_CREATED, "The account has already been confirmed");
switch (accountCommand) {
case CONFIRM_ACCOUNT:
Assert.isTrue(status == ACCOUNT_CREATED, "The account has already been confirmed");
// Confirm the account
Account updateAccount = account.getContent();
updateAccount.setStatus(ACCOUNT_CONFIRMED);
account = updateAccountResource(id, updateAccount);
appendEvent(id, new AccountEvent(AccountEventType.ACCOUNT_CONFIRMED));
break;
case ACTIVATE_ACCOUNT:
Assert.isTrue(status != ACCOUNT_ACTIVE, "The account is already active");
Assert.isTrue(Arrays.asList(ACCOUNT_CONFIRMED, ACCOUNT_SUSPENDED, ACCOUNT_ARCHIVED)
.contains(status), "The account cannot be activated");
// Confirm the account
Account updateAccount = account.getContent();
updateAccount.setStatus(ACCOUNT_CONFIRMED);
account = updateAccountResource(id, updateAccount);
appendEvent(id, new AccountEvent(AccountEventType.ACCOUNT_CONFIRMED));
break;
case ACTIVATE_ACCOUNT:
Assert.isTrue(status != ACCOUNT_ACTIVE, "The account is already active");
Assert.isTrue(Arrays.asList(ACCOUNT_CONFIRMED, ACCOUNT_SUSPENDED, ACCOUNT_ARCHIVED)
.contains(status), "The account cannot be activated");
// Activate the account
account.getContent().setStatus(ACCOUNT_ACTIVE);
account = updateAccountResource(id, account.getContent());
appendEvent(id, new AccountEvent(AccountEventType.ACCOUNT_ACTIVATED));
break;
case SUSPEND_ACCOUNT:
Assert.isTrue(status == ACCOUNT_ACTIVE, "An inactive account cannot be suspended");
// Activate the account
account.getContent().setStatus(ACCOUNT_ACTIVE);
account = updateAccountResource(id, account.getContent());
appendEvent(id, new AccountEvent(AccountEventType.ACCOUNT_ACTIVATED));
break;
case SUSPEND_ACCOUNT:
Assert.isTrue(status == ACCOUNT_ACTIVE, "An inactive account cannot be suspended");
// Suspend the account
account.getContent().setStatus(ACCOUNT_SUSPENDED);
account = updateAccountResource(id, account.getContent());
appendEvent(id, new AccountEvent(AccountEventType.ACCOUNT_SUSPENDED));
break;
case ARCHIVE_ACCOUNT:
Assert.isTrue(status == ACCOUNT_ACTIVE, "An inactive account cannot be archived");
// Suspend the account
account.getContent().setStatus(ACCOUNT_SUSPENDED);
account = updateAccountResource(id, account.getContent());
appendEvent(id, new AccountEvent(AccountEventType.ACCOUNT_SUSPENDED));
break;
case ARCHIVE_ACCOUNT:
Assert.isTrue(status == ACCOUNT_ACTIVE, "An inactive account cannot be archived");
// Archive the account
account.getContent().setStatus(ACCOUNT_ARCHIVED);
account = updateAccountResource(id, account.getContent());
appendEvent(id, new AccountEvent(AccountEventType.ACCOUNT_ARCHIVED));
break;
default:
Assert.notNull(accountCommand,
"The provided command cannot be applied to this account in its current state");
}
// Archive the account
account.getContent().setStatus(ACCOUNT_ARCHIVED);
account = updateAccountResource(id, account.getContent());
appendEvent(id, new AccountEvent(AccountEventType.ACCOUNT_ARCHIVED));
break;
default:
Assert.notNull(accountCommand,
"The provided command cannot be applied to this account in its current state");
}
return account;
}
/**
* Get the {@link AccountCommand} hypermedia resource that lists the available commands that can be applied
* to an {@link Account} entity.
*
* @param id is the {@link Account} identifier to provide command links for
* @return an {@link AccountCommandsResource} with a collection of embedded command links
*/
public AccountCommandsResource getCommandsResource(Long id) {
// Get the account resource for the identifier
Resource<Account> accountResource = getAccountResource(id);
@@ -205,6 +211,22 @@ public class AccountService {
return commandResource;
}
/**
* Get {@link AccountEvents} for the supplied {@link Account} identifier.
*
* @param id is the unique identifier of the {@link Account}
* @return a list of {@link AccountEvent} wrapped in a hypermedia {@link AccountEvents} resource
*/
public AccountEvents getAccountEventResources(Long id) {
return eventService.getEvents(id);
}
/**
* Generate a {@link LinkBuilder} for generating the {@link AccountCommandsResource}.
*
* @param id is the unique identifier for a {@link Account}
* @return a {@link LinkBuilder} for the {@link AccountCommandsResource}
*/
private LinkBuilder getCommandLinkBuilder(Long id) {
return linkTo(AccountController.class)
.slash("accounts")
@@ -212,6 +234,12 @@ public class AccountService {
.slash("commands");
}
/**
* Get a hypermedia enriched {@link Account} entity.
*
* @param account is the {@link Account} to enrich with hypermedia links
* @return is a hypermedia enriched resource for the supplied {@link Account} entity
*/
private Resource<Account> getAccountResource(Account account) {
Resource<Account> accountResource;
@@ -219,23 +247,36 @@ public class AccountService {
accountResource = new Resource<>(account,
linkTo(AccountController.class)
.slash("accounts")
.slash(account.getId())
.slash(account.getAccountId())
.withSelfRel(),
entityLinks.linkFor(Account.class, account.getId())
.slash(account.getId())
linkTo(AccountController.class)
.slash("accounts")
.slash(account.getAccountId())
.slash("events")
.withRel("events"),
getCommandLinkBuilder(account.getId())
getCommandLinkBuilder(account.getAccountId())
.withRel("commands")
);
return accountResource;
}
/**
* Get an {@link Account} entity for the supplied identifier.
*
* @param id is the unique identifier of a {@link Account} entity
* @return an {@link Account} entity
*/
private Account getAccount(Long id) {
return accountRepository.findOne(id);
}
/**
* Create a new {@link Account} entity.
*
* @param account is the {@link Account} to create
* @return the newly created {@link Account}
*/
private Account createAccount(Account account) {
// Assert for uniqueness constraint
Assert.isNull(accountRepository.findAccountByUserId(account.getUserId()),
@@ -247,32 +288,40 @@ public class AccountService {
account = accountRepository.save(account);
// Trigger the account creation event
appendEventResource(account.getId(),
appendEventResource(account.getAccountId(),
new AccountEvent(AccountEventType.ACCOUNT_CREATED));
return account;
}
/**
* Update an {@link Account} entity with the supplied identifier.
*
* @param id is the unique identifier of the {@link Account} entity
* @param account is the {@link Account} containing updated fields
* @return the updated {@link Account} entity
*/
private Account updateAccount(Long id, Account account) {
Assert.notNull(id);
Assert.notNull(account);
Assert.isTrue(Objects.equals(id, account.getId()));
Assert.isTrue(Objects.equals(id, account.getAccountId()));
return accountRepository.save(account);
}
/**
* Append a new {@link AccountEvent} to the {@link Account} reference for the supplied identifier.
*
* @param accountId is the unique identifier for the {@link Account}
* @param event is the {@link AccountEvent} to append to the {@link Account} entity
* @return the newly appended {@link AccountEvent}
*/
private AccountEvent appendEvent(Long accountId, AccountEvent event) {
Account account = accountRepository.findOne(accountId);
if (account != null) {
event.setAccount(account);
event = eventService.createEvent(event).getContent();
if (event != null) {
account.getEvents().add(event);
accountRepository.save(account);
}
} else {
event = null;
}
Account account = getAccount(accountId);
Assert.notNull(account, "The account with the supplied id does not exist");
event.setAccount(account);
event = eventService.createEvent(event).getContent();
account.getEvents().add(event);
accountRepository.save(account);
return event;
}
}

View File

@@ -3,13 +3,14 @@ package demo.domain;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import org.springframework.hateoas.ResourceSupport;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseEntity {
public class BaseEntity extends ResourceSupport {
@CreatedDate
private Long createdAt;

View File

@@ -19,6 +19,8 @@ import java.util.Set;
* This event resource also provides a transaction log that can be used to append
* actions to the event. The collection of {@link Log} items can be used to remediate
* partial failures.
*
* @author kbastani
*/
@Entity
@RestResource(path = "events", rel = "events")
@@ -46,11 +48,12 @@ public class AccountEvent extends BaseEntity {
this.type = type;
}
public Long getId() {
@JsonIgnore
public Long getEventId() {
return id;
}
public void setId(Long id) {
public void setEventId(Long id) {
this.id = id;
}

View File

@@ -1,8 +1,12 @@
package demo.event;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource(path = "events", collectionResourceRel = "events", itemResourceRel = "event")
public interface EventRepository extends JpaRepository<AccountEvent, Long> {
Page<AccountEvent> findAccountEventsByAccountId(@Param("accountId") Long accountId, Pageable pageable);
}

View File

@@ -3,8 +3,12 @@ package demo.event;
import demo.account.Account;
import demo.account.AccountController;
import demo.account.AccountEventType;
import demo.account.AccountEvents;
import demo.log.Log;
import demo.log.LogRepository;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.rest.webmvc.support.RepositoryEntityLinks;
import org.springframework.hateoas.Resource;
import org.springframework.integration.support.MessageBuilder;
@@ -22,6 +26,8 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
* entities of the Account Service. Account domain events are generated with a {@link AccountEventType},
* and action logs are appended to the {@link AccountEvent}. The logs resource provides an append-only transaction
* log that can be used to source the state of the {@link Account}
*
* @author kbastani
*/
@Service
@Transactional
@@ -30,57 +36,58 @@ public class EventService {
private final EventRepository eventRepository;
private final LogRepository logRepository;
private final RepositoryEntityLinks entityLinks;
private final ProducerChannels producer;
private final Source accountStreamSource;
public EventService(EventRepository eventRepository, LogRepository logRepository,
RepositoryEntityLinks entityLinks, ProducerChannels producerChannels) {
RepositoryEntityLinks entityLinks, Source accountStreamSource) {
this.eventRepository = eventRepository;
this.logRepository = logRepository;
this.entityLinks = entityLinks;
this.producer = producerChannels;
this.accountStreamSource = accountStreamSource;
}
/**
* Create a new {@link AccountEvent} and publish it to the account stream.
*
* @param event is the {@link AccountEvent} to publish to the account stream
* @return a hypermedia {@link AccountEvent} resource
*/
public Resource<AccountEvent> createEvent(AccountEvent event) {
Resource<AccountEvent> eventResource = null;
// Save new event
event = addEvent(event);
Assert.notNull(event, "The event could not be appended to the account");
if (event != null) {
// Create account event resource
eventResource = new Resource<>(event, Arrays.asList(
entityLinks.linkFor(AccountEvent.class, event.getId())
.slash(event.getId())
.withRel("self"),
entityLinks.linkFor(AccountEvent.class)
.slash(event.getId())
.slash("logs")
.withRel("logs"),
linkTo(AccountController.class)
.slash("accounts")
.slash(event.getAccount().getId())
.withRel("account"))
);
// Create account event resource
eventResource = getAccountEventResource(event);
// Produce account event
producer.output()
.send(MessageBuilder
.withPayload(eventResource)
.build());
}
// Append the account event to the stream
accountStreamSource.output()
.send(MessageBuilder
.withPayload(eventResource)
.build());
return eventResource;
}
private AccountEvent addEvent(AccountEvent event) {
event = eventRepository.save(event);
return event;
}
/**
* Get an {@link AccountEvent} with the supplied identifier.
*
* @param id is the unique identifier for the {@link AccountEvent}
* @return an {@link AccountEvent}
*/
public AccountEvent getEvent(Long id) {
return eventRepository.findOne(id);
}
/**
* Update an {@link AccountEvent} with the supplied identifier.
*
* @param id is the unique identifier for the {@link AccountEvent}
* @param event is the {@link AccountEvent} to update
* @return the updated {@link AccountEvent}
*/
public AccountEvent updateEvent(Long id, AccountEvent event) {
Assert.notNull(id);
Assert.isTrue(event.getId() == null || Objects.equals(id, event.getId()));
@@ -88,6 +95,13 @@ public class EventService {
return eventRepository.save(event);
}
/**
* Append a {@link Log} to an {@link AccountEvent} entity.
*
* @param eventId is the unique identifier for an {@link AccountEvent}
* @param log is the {@link Log} descirbing an action performed on an {@link AccountEvent}
* @return a hypermedia resource for the appended {@link Log}
*/
public Resource<Log> appendEventLog(Long eventId, Log log) {
Assert.notNull(eventId);
Assert.notNull(log);
@@ -95,20 +109,75 @@ public class EventService {
Resource<Log> logResource = null;
AccountEvent event = getEvent(eventId);
if (event != null) {
log = logRepository.save(log);
event.getLogs().add(log);
Assert.notNull(event, "The event with the supplied id could not be found");
logResource = new Resource<>(log, Arrays.asList(
entityLinks.linkFor(Log.class)
.slash(log.getId())
.withSelfRel(),
entityLinks.linkFor(AccountEvent.class)
.slash(event.getId())
.withRel("event")
));
}
log = logRepository.save(log);
event.getLogs().add(log);
logResource = getLogResource(log, event);
return logResource;
}
/**
* Gets a hypermedia resource for a {@link Log} entity.
*
* @param log is the {@link Log} descirbing an action performed on an {@link AccountEvent}
* @param event is the {@link AccountEvent} to associate with the {@link Log}
* @return a hypermedia resource for the appended {@link Log}
*/
private Resource<Log> getLogResource(Log log, AccountEvent event) {
return new Resource<>(log, Arrays.asList(
entityLinks.linkFor(Log.class)
.slash(log.getLogId())
.withSelfRel(),
entityLinks.linkFor(AccountEvent.class)
.slash(event.getId())
.withRel("event")
));
}
/**
* Gets a hypermedia resource for a {@link AccountEvent} entity.
*
* @param event is the {@link AccountEvent} to enrich with hypermedia
* @return a hypermedia resource for the supplied {@link AccountEvent} entity
*/
private Resource<AccountEvent> getAccountEventResource(AccountEvent event) {
return new Resource<>(event, Arrays.asList(
linkTo(AccountController.class)
.slash("events")
.slash(event.getEventId())
.withSelfRel(),
entityLinks.linkFor(AccountEvent.class)
.slash(event.getId())
.slash("logs")
.withRel("logs"),
linkTo(AccountController.class)
.slash("accounts")
.slash(event.getAccount().getAccountId())
.withRel("account"))
);
}
/**
* Add a {@link AccountEvent} to an {@link Account} entity.
*
* @param event is the {@link AccountEvent} to append to an {@link Account} entity
* @return the newly appended {@link AccountEvent} entity
*/
private AccountEvent addEvent(AccountEvent event) {
event = eventRepository.save(event);
return event;
}
/**
* Get {@link AccountEvents} for the supplied {@link Account} identifier.
*
* @param id is the unique identifier of the {@link Account}
* @return a list of {@link AccountEvent} wrapped in a hypermedia {@link AccountEvents} resource
*/
public AccountEvents getEvents(Long id) {
Page<AccountEvent> events = eventRepository.findAccountEventsByAccountId(id, new PageRequest(0, Integer.MAX_VALUE));
return new AccountEvents(id, events);
}
}

View File

@@ -1,10 +0,0 @@
package demo.event;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.messaging.MessageChannel;
public interface ProducerChannels {
@Output
MessageChannel output();
}

View File

@@ -1,5 +1,6 @@
package demo.log;
import com.fasterxml.jackson.annotation.JsonIgnore;
import demo.account.AccountEventType;
import demo.domain.BaseEntity;
@@ -25,11 +26,12 @@ public class Log extends BaseEntity {
this.action = action;
}
public Long getId() {
@JsonIgnore
public Long getLogId() {
return id;
}
public void setId(Long id) {
public void setLogId(Long id) {
this.id = id;
}

View File

@@ -35,10 +35,6 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
@@ -49,10 +45,6 @@
<version>1.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>

View File

@@ -1,21 +1,10 @@
package demo.domain;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import org.springframework.hateoas.ResourceSupport;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseEntity extends ResourceSupport {
@CreatedDate
private Long createdAt;
@LastModifiedDate
private Long lastModified;
public BaseEntity() {
@@ -42,6 +31,6 @@ public class BaseEntity extends ResourceSupport {
return "BaseEntity{" +
"createdAt=" + createdAt +
", lastModified=" + lastModified +
'}';
"} " + super.toString();
}
}

View File

@@ -8,6 +8,7 @@ import org.apache.log4j.Logger;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.cloud.stream.messaging.Sink;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.client.Traverson;
import org.springframework.messaging.MessageHeaders;

View File

@@ -1,11 +0,0 @@
package demo.event;
import org.springframework.cloud.stream.annotation.Input;
import org.springframework.messaging.SubscribableChannel;
public interface Sink {
String INPUT = "input";
@Input(INPUT)
SubscribableChannel input();
}