Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.autoinvestor.application.UpdateUserUseCase;

public record UpdateUserCommand(
String userId,
int riskLevel
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package io.autoinvestor.application.UpdateUserUseCase;

import io.autoinvestor.application.UserDTO;
import io.autoinvestor.application.UserNotFound;
import io.autoinvestor.application.UsersReadModel;
import io.autoinvestor.domain.EventStore;
import io.autoinvestor.domain.events.Event;
import io.autoinvestor.domain.events.EventPublisher;
import io.autoinvestor.domain.model.User;
import io.autoinvestor.domain.model.UserId;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
@RequiredArgsConstructor
public class UpdateUserCommandHandler {

private final EventStore eventStore;
private final EventPublisher eventPublisher;
private final UsersReadModel readModel;


public void handle (UpdateUserCommand command) {

String userIdToUpdate = String.valueOf(readModel.getById(command.userId())
.map(UserDTO::riskLevel)
.orElseThrow(() -> UserNotFound.with(command.userId())));
User user = this.eventStore.get(UserId.from(userIdToUpdate));
user.update(userIdToUpdate, command.riskLevel());

List<Event<?>> events = user.getUncommittedEvents();

this.eventStore.save(user);

UserDTO dto = new UserDTO(
user.getState().userId().value(),
user.getState().userEmail().value(),
user.getState().firstName().value(),
user.getState().lastName().value(),
command.riskLevel()
);
this.readModel.update(dto);

this.eventPublisher.publish(events);

user.markEventsAsCommitted();

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.autoinvestor.application.UpdateUserUseCase;

public class UserDoesntExist extends RuntimeException {
private UserDoesntExist(String message) {
super(message);
}

public static UserDoesntExist with(String userId) {
String message = "User with id " + userId + "doesn't exist";
return new UserDoesntExist(message);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ public interface UsersReadModel {
void save(UserDTO user);
Optional<UserDTO> get(String email);
Optional<UserDTO> getById(String userId);
void update(UserDTO dto);
}
4 changes: 4 additions & 0 deletions src/main/java/io/autoinvestor/domain/events/Event.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ public abstract class Event<P extends EventPayload> {
private final Date occurredAt;
private final int version;

protected Event(Id aggregateId, String type, P payload) {
this(aggregateId, type, payload, 1);
}

protected Event(Id aggregateId, String type, P payload, int version) {
this.id = EventId.generate();
this.aggregateId = aggregateId;
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/io/autoinvestor/domain/model/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,22 @@ public static User create(String firstName, String lastName, String email, int r
return user;
}

public void update(String userId, int riskLevel) {
this.apply(UserWasUpdatedEvent.with(
UserId.from(userId),
RiskLevel.from(riskLevel)
));
}

@Override
protected void when(Event<?> event) {
switch (event.getType()) {
case UserWasRegisteredEvent.TYPE :
whenUserCreated((UserWasRegisteredEvent) event);
break;
case UserWasUpdatedEvent.TYPE:
whenUserUpdated((UserWasUpdatedEvent) event);
break;
default:
throw new IllegalArgumentException("Unknown event type");
}
Expand All @@ -61,4 +71,8 @@ private void whenUserCreated(UserWasRegisteredEvent event) {
assert this.state != null;
this.state = this.state.withUserCreated(event);
}

private void whenUserUpdated(UserWasUpdatedEvent event) {
this.state = this.state.withUserDeleted(event);
}
}
12 changes: 12 additions & 0 deletions src/main/java/io/autoinvestor/domain/model/UserState.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,16 @@ public UserState withUserCreated(UserWasRegisteredEvent event) {
event.getOccurredAt(),
new Date());
}

public UserState withUserDeleted(UserWasUpdatedEvent event) {
UserWasUpdatedEventPayload payload = event.getPayload();
return new UserState(
UserId.from(event.getAggregateId().value()),
this.firstName,
this.lastName,
this.userEmail,
RiskLevel.from(payload.riskLevel()),
event.getOccurredAt(),
new Date());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package io.autoinvestor.domain.model;

import io.autoinvestor.domain.Id;
import io.autoinvestor.domain.events.Event;
import io.autoinvestor.domain.events.EventId;

import java.util.Date;

public class UserWasUpdatedEvent extends Event<UserWasUpdatedEventPayload>{
public static final String TYPE = "SUBSCRIPTION_UPDATED";

private UserWasUpdatedEvent(Id aggregateId, UserWasUpdatedEventPayload payload) {
super(aggregateId, TYPE, payload);
}

protected UserWasUpdatedEvent(
EventId id,
Id aggregateId,
UserWasUpdatedEventPayload payload,
Date occurredAt,
int version) {
super(id, aggregateId, TYPE, payload, occurredAt, version);
}

public static UserWasUpdatedEvent with (UserId userId,
RiskLevel riskLevel) {
UserWasUpdatedEventPayload payload = new UserWasUpdatedEventPayload(
riskLevel.value());
return new UserWasUpdatedEvent(userId, payload);
}

public static UserWasUpdatedEvent hydrate (EventId id,
Id aggregateId,
UserWasUpdatedEventPayload payload,
Date occurredAt,
int version){
return new UserWasUpdatedEvent(id, aggregateId, payload, occurredAt, version);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.autoinvestor.domain.model;

import io.autoinvestor.domain.events.EventPayload;

import java.util.Map;

public record UserWasUpdatedEventPayload(
int riskLevel
) implements EventPayload {
@Override
public Map<String, Object> asMap() {
return Map.of(
"riskLevel", riskLevel
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,9 @@ public Optional<UserDTO> getById(String userId) {
.filter(user -> user.userId().equals(userId))
.findFirst();
}

@Override
public void update(UserDTO dto) {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Repository;

import java.util.Optional;
Expand Down Expand Up @@ -46,4 +47,12 @@ public Optional<UserDTO> getById(String userId) {
return Optional.ofNullable(doc)
.map(mapper::toDTO);
}

@Override
public void update(UserDTO dto) {
Query query = new Query(Criteria.where("userId").is(dto.userId()));
Update update = new Update()
.set("riskLevel", dto.riskLevel());
this.template.updateFirst(query, update, UserDocument.class, COLLECTION);
}
}
4 changes: 3 additions & 1 deletion src/main/java/io/autoinvestor/ui/GlobalExceptionHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice(assignableTypes = {RegisterUserController.class, GetUserController.class})
@RestControllerAdvice(assignableTypes = {RegisterUserController.class, GetUserController.class, UpdateUserController.class})
public class GlobalExceptionHandler {

@ExceptionHandler(DuplicatedException.class)
Expand Down Expand Up @@ -61,4 +61,6 @@ public ResponseEntity<ErrorResponse> handleRiskLevelNotValid(RiskLevelNotValid e
public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFound ex) {
return ErrorResponse.builder().status(HttpStatus.valueOf(404)).message(ex.getMessage()).build();
}


}
27 changes: 27 additions & 0 deletions src/main/java/io/autoinvestor/ui/UpdateUserController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.autoinvestor.ui;

import io.autoinvestor.application.UpdateUserUseCase.UpdateUserCommand;
import io.autoinvestor.application.UpdateUserUseCase.UpdateUserCommandHandler;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
@RequiredArgsConstructor
public class UpdateUserController {

private final UpdateUserCommandHandler updateUserCommandHandler;

public ResponseEntity<Void> handle (
@RequestHeader(value = "X-User-Id", required = false) String userId,
@RequestBody UpdateUserDTO dto
){
updateUserCommandHandler.handle(new UpdateUserCommand(userId, dto.riskLevel()));
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
}
5 changes: 5 additions & 0 deletions src/main/java/io/autoinvestor/ui/UpdateUserDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.autoinvestor.ui;

public record UpdateUserDTO(
int riskLevel
) {}