first working state.
Customer can be registered and be written into the db
This commit is contained in:
@@ -46,5 +46,46 @@
|
||||
<version>${org.lombok.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct</artifactId>
|
||||
<version>${org.mapstruct.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>14</source> <!-- depending on your project -->
|
||||
<target>14</target> <!-- depending on your project -->
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct-processor</artifactId>
|
||||
<version>${org.mapstruct.version}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${org.lombok.version}</version>
|
||||
</path>
|
||||
<!-- other annotation processors -->
|
||||
</annotationProcessorPaths>
|
||||
<compilerArgs>
|
||||
<compilerArg>
|
||||
-Amapstruct.defaultInjectionStrategy=constructor
|
||||
</compilerArg>
|
||||
<compilerArg>
|
||||
-Amapstruct.defaultComponentModel=spring
|
||||
</compilerArg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -3,12 +3,14 @@ package de.strasser.peter.hexagonal.persistence;
|
||||
import de.strasser.peter.hexagonal.application.customer.domain.Customer;
|
||||
import de.strasser.peter.hexagonal.application.customer.port.out.LoadCustomerAdapter;
|
||||
import de.strasser.peter.hexagonal.application.customer.port.out.SaveCustomerAdapter;
|
||||
import de.strasser.peter.hexagonal.persistence.mapper.CustomerMapper;
|
||||
import de.strasser.peter.hexagonal.persistence.model.CustomerEntity;
|
||||
import de.strasser.peter.hexagonal.persistence.repository.CustomerRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.time.LocalDate;
|
||||
import java.util.HashMap;
|
||||
import java.util.Optional;
|
||||
@@ -18,14 +20,16 @@ import java.util.Optional;
|
||||
@RequiredArgsConstructor
|
||||
public class CustomerDao implements SaveCustomerAdapter, LoadCustomerAdapter {
|
||||
private final CustomerRepository customerRepository;
|
||||
private final CustomerMapper customerMapper;
|
||||
|
||||
@Override
|
||||
public void upsert(Customer customer) {
|
||||
log.info("saving customer");
|
||||
customerRepository.save(customerMapper.toDbEntity(customer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Customer findById(Integer id) {
|
||||
public Customer findById(BigInteger id) {
|
||||
Optional<CustomerEntity> byId = customerRepository.findById(id);
|
||||
return Customer.createCustomer(id,
|
||||
"max",
|
||||
|
||||
@@ -3,13 +3,15 @@ package de.strasser.peter.hexagonal.persistence.mapper;
|
||||
import de.strasser.peter.hexagonal.application.customer.domain.Address;
|
||||
import de.strasser.peter.hexagonal.application.customer.domain.Customer;
|
||||
import de.strasser.peter.hexagonal.persistence.model.CustomerEntity;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Mapper
|
||||
@Component
|
||||
public class CustomerMapper {
|
||||
Customer toDomain(CustomerEntity customerEntity) {
|
||||
public Customer toDomain(CustomerEntity customerEntity) {
|
||||
final HashMap<Address.AddressType, Address> addresses = createAddressMap(customerEntity);
|
||||
return Customer.createCustomer(
|
||||
customerEntity.getId(),
|
||||
@@ -21,7 +23,35 @@ public class CustomerMapper {
|
||||
}
|
||||
|
||||
|
||||
CustomerEntity toDbEntity(Customer customer) {
|
||||
public CustomerEntity toDbEntity(Customer customer) {
|
||||
return new CustomerEntity(
|
||||
customer.getId(),
|
||||
customer.getName(),
|
||||
customer.getHashedPassword(),
|
||||
customer.getBirthday(),
|
||||
customer.isActive(),
|
||||
getAddressAttribute(customer, Address.AddressType.DEFAULT, Address::getStreet),
|
||||
getAddressAttribute(customer, Address.AddressType.DEFAULT, Address::getHouseNumber),
|
||||
getAddressAttribute(customer, Address.AddressType.DEFAULT, Address::getZipCode),
|
||||
getAddressAttribute(customer, Address.AddressType.DEFAULT, Address::getCountry),
|
||||
getAddressAttribute(customer, Address.AddressType.SHIPPING, Address::getStreet),
|
||||
getAddressAttribute(customer, Address.AddressType.SHIPPING, Address::getHouseNumber),
|
||||
getAddressAttribute(customer, Address.AddressType.SHIPPING, Address::getZipCode),
|
||||
getAddressAttribute(customer, Address.AddressType.SHIPPING, Address::getCountry),
|
||||
getAddressAttribute(customer, Address.AddressType.BILLING, Address::getStreet),
|
||||
getAddressAttribute(customer, Address.AddressType.BILLING, Address::getHouseNumber),
|
||||
getAddressAttribute(customer, Address.AddressType.BILLING, Address::getZipCode),
|
||||
getAddressAttribute(customer, Address.AddressType.BILLING, Address::getCountry));
|
||||
}
|
||||
|
||||
private <T> T getAddressAttribute(Customer customer, Address.AddressType type, Converter<Address, T> getter) {
|
||||
final Map<Address.AddressType, Address> addresses = customer.getAddresses();
|
||||
if (addresses != null) {
|
||||
final Address address = addresses.get(type);
|
||||
if (address != null) {
|
||||
return getter.convert(address);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.data.annotation.Id;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Data
|
||||
@@ -12,25 +13,25 @@ import java.time.LocalDate;
|
||||
@NoArgsConstructor
|
||||
public class CustomerEntity {
|
||||
@Id
|
||||
private Integer id;
|
||||
private BigInteger id;
|
||||
private String name;
|
||||
private String hashedPassword;
|
||||
private LocalDate birthday;
|
||||
private boolean active;
|
||||
|
||||
private String street;
|
||||
private int houseNumber;
|
||||
private int zipCode;
|
||||
private Integer houseNumber;
|
||||
private Integer zipCode;
|
||||
private String country;
|
||||
|
||||
private String shippingStreet;
|
||||
private int shippingHouseNumber;
|
||||
private int shippingZipCode;
|
||||
private Integer shippingHouseNumber;
|
||||
private Integer shippingZipCode;
|
||||
private String shippingCountry;
|
||||
|
||||
private String billingStreet;
|
||||
private int billingHouseNumber;
|
||||
private int billingZipCode;
|
||||
private Integer billingHouseNumber;
|
||||
private Integer billingZipCode;
|
||||
private String billingCountry;
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import de.strasser.peter.hexagonal.persistence.model.CustomerEntity;
|
||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
@Repository
|
||||
public interface CustomerRepository extends MongoRepository<CustomerEntity, Integer> {
|
||||
public interface CustomerRepository extends MongoRepository<CustomerEntity, BigInteger> {
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@@ -20,7 +21,7 @@ public class AddAddressController {
|
||||
|
||||
|
||||
@PostMapping("/v1/address")
|
||||
public void addAddress(@RequestParam Integer customerId, @RequestBody AddAddressRequest addAddressRequest) {
|
||||
public void addAddress(@RequestParam BigInteger customerId, @RequestBody AddAddressRequest addAddressRequest) {
|
||||
final List<AddAddressCommand> addAddressCmds = List.of(addAddressMapper.toCmd(addAddressRequest));
|
||||
addAddressUseCase.addAddresses(customerId, addAddressCmds);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package de.strasser.peter.hexagonal.webadapter.errors;
|
||||
|
||||
import de.strasser.peter.hexagonal.application.customer.exception.BusinessException;
|
||||
import lombok.Value;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@ControllerAdvice
|
||||
public class ErrorHandling {
|
||||
|
||||
@ExceptionHandler(ConstraintViolationException.class)
|
||||
public ResponseEntity<ErrorResponse> constrainViolationException(HttpServletRequest req, ConstraintViolationException exc) {
|
||||
return ErrorResponse.createErrorResp(req, HttpStatus.BAD_REQUEST, exc);
|
||||
}
|
||||
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
public ResponseEntity<ErrorResponse> constrainViolationException(HttpServletRequest req, BusinessException exc) {
|
||||
return ErrorResponse.createErrorResp(req, HttpStatus.BAD_REQUEST, exc);
|
||||
}
|
||||
|
||||
@Value
|
||||
public static class ErrorResponse {
|
||||
String timestamp;
|
||||
Integer status;
|
||||
String error;
|
||||
String message;
|
||||
String path;
|
||||
|
||||
public static ResponseEntity<ErrorResponse> createErrorResp(HttpServletRequest req, HttpStatus code, Exception e) {
|
||||
final ErrorResponse errResponse = new ErrorResponse(
|
||||
LocalDateTime.now().toString(),
|
||||
code.value(),
|
||||
e.getClass().getName(),
|
||||
e.getMessage(),
|
||||
req.getContextPath());
|
||||
return ResponseEntity.status(code).body(errResponse);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,10 @@ package de.strasser.peter.hexagonal.webadapter.mapper;
|
||||
import de.strasser.peter.hexagonal.application.customer.port.in.commands.RegisterCustomerCommand;
|
||||
import de.strasser.peter.hexagonal.webadapter.model.RegisterCustomerRequest;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
@Mapper
|
||||
public interface RegisterCustomerRequestMapper {
|
||||
@Mapping(source = "password", target = "clearPassword")
|
||||
RegisterCustomerCommand toCmd(RegisterCustomerRequest registerCustomerRequest);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package de.strasser.peter.hexagonal.webadapter.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor(force = true)
|
||||
public class AddAddressRequest {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
package de.strasser.peter.hexagonal.webadapter.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Value;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Value
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor(force = true)
|
||||
public class RegisterCustomerRequest {
|
||||
String name;
|
||||
LocalDate birthDay;
|
||||
String clearPassword;
|
||||
String password;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<artifactId>application</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>application</name>
|
||||
<description>Business logik for demo project</description>
|
||||
<description>Business logic for demo project</description>
|
||||
<properties>
|
||||
<java.version>14</java.version>
|
||||
<org.mapstruct.version>1.3.1.Final</org.mapstruct.version>
|
||||
|
||||
@@ -6,8 +6,8 @@ import lombok.Value;
|
||||
@Value
|
||||
public class Address {
|
||||
String street;
|
||||
int houseNumber;
|
||||
int zipCode;
|
||||
Integer houseNumber;
|
||||
Integer zipCode;
|
||||
String country;
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import de.strasser.peter.hexagonal.application.customer.exception.DefaultAdressR
|
||||
import de.strasser.peter.hexagonal.application.customer.exception.UserIsTooYoungExc;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.time.LocalDate;
|
||||
import java.time.Period;
|
||||
import java.util.HashMap;
|
||||
@@ -11,7 +12,7 @@ import java.util.Map;
|
||||
|
||||
@Getter
|
||||
public class Customer {
|
||||
private final Integer id;
|
||||
private final BigInteger id;
|
||||
private String name;
|
||||
private String hashedPassword;
|
||||
private LocalDate birthday;
|
||||
@@ -19,7 +20,7 @@ public class Customer {
|
||||
private Map<Address.AddressType, Address> addresses;
|
||||
private boolean active;
|
||||
|
||||
private Customer(Integer id, String name, String hashedPassword, LocalDate birthDate, Map<Address.AddressType, Address> addresses, boolean active) {
|
||||
private Customer(BigInteger id, String name, String hashedPassword, LocalDate birthDate, Map<Address.AddressType, Address> addresses, boolean active) {
|
||||
this.id = id;
|
||||
this.active = active;
|
||||
this.age = Period.between(birthDate, LocalDate.now()).getYears();
|
||||
@@ -46,7 +47,7 @@ public class Customer {
|
||||
}
|
||||
|
||||
public static Customer createCustomer(
|
||||
Integer id,
|
||||
BigInteger id,
|
||||
String name,
|
||||
String hashedPassword,
|
||||
LocalDate birthDate,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package de.strasser.peter.hexagonal.application.customer.exception;
|
||||
|
||||
public class AddressTypeDoesNotExistsExc extends IllegalArgumentException {
|
||||
public class AddressTypeDoesNotExistsExc extends BusinessException {
|
||||
public AddressTypeDoesNotExistsExc(String type) {
|
||||
super(String.format("Adress of type '%s' does not exist!", type));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package de.strasser.peter.hexagonal.application.customer.exception;
|
||||
|
||||
public abstract class BusinessException extends IllegalStateException {
|
||||
public BusinessException(String error) {
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package de.strasser.peter.hexagonal.application.customer.exception;
|
||||
|
||||
public class DefaultAdressRequiredToActivateExc extends IllegalStateException {
|
||||
public class DefaultAdressRequiredToActivateExc extends BusinessException {
|
||||
|
||||
public DefaultAdressRequiredToActivateExc() {
|
||||
super("Customer needs to have at least a default adress to be able to be activated!");
|
||||
|
||||
@@ -2,7 +2,7 @@ package de.strasser.peter.hexagonal.application.customer.exception;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
|
||||
public class UserIsTooYoungExc extends IllegalArgumentException {
|
||||
public class UserIsTooYoungExc extends BusinessException {
|
||||
public UserIsTooYoungExc(int age) {
|
||||
super(MessageFormat.format("Customer is too young. Expected: > 18 yrs, Actual {0}", age));
|
||||
}
|
||||
|
||||
@@ -5,8 +5,9 @@ import de.strasser.peter.hexagonal.application.customer.port.in.commands.AddAddr
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
public interface AddAddressUseCase {
|
||||
void addAddresses(@Min(0) Integer customerId, @Valid @NotEmpty List<AddAddressCommand> addresses);
|
||||
void addAddresses(@Min(0) BigInteger customerId, @Valid @NotEmpty List<AddAddressCommand> addresses);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package de.strasser.peter.hexagonal.application.customer.port.in.commands;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Value;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
|
||||
@Value
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor(force = true)
|
||||
public class AddAddressCommand {
|
||||
|
||||
@@ -3,14 +3,14 @@ package de.strasser.peter.hexagonal.application.customer.port.in.commands;
|
||||
|
||||
import de.strasser.peter.hexagonal.common.validators.SecurePassword;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Value;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.Past;
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Value
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor(force = true)
|
||||
public class RegisterCustomerCommand {
|
||||
|
||||
@@ -2,6 +2,8 @@ package de.strasser.peter.hexagonal.application.customer.port.out;
|
||||
|
||||
import de.strasser.peter.hexagonal.application.customer.domain.Customer;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
public interface LoadCustomerAdapter {
|
||||
Customer findById(Integer id);
|
||||
Customer findById(BigInteger id);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package de.strasser.peter.hexagonal.application.customer.port.out.commands;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor(force = true)
|
||||
public class ValidateAddressCommand {
|
||||
String street;
|
||||
int houseNumber;
|
||||
int zipCode;
|
||||
Integer houseNumber;
|
||||
Integer zipCode;
|
||||
String country;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.springframework.stereotype.Service;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import java.math.BigInteger;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -27,7 +28,7 @@ class AddressService implements AddAddressUseCase {
|
||||
private final AddAddressMapper addAddressMapper;
|
||||
|
||||
@Override
|
||||
public void addAddresses(@Min(0) Integer customerId, @Valid @NotEmpty List<AddAddressCommand> addAddressCmds) {
|
||||
public void addAddresses(@Min(0) BigInteger customerId, @Valid @NotEmpty List<AddAddressCommand> addAddressCmds) {
|
||||
final Customer customer = loadCustomerAdapter.findById(customerId);
|
||||
|
||||
final Map<Address.AddressType, Address> addresses = new HashMap<>();
|
||||
|
||||
@@ -21,10 +21,6 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
|
||||
@@ -21,7 +21,7 @@ public @interface SecurePassword {
|
||||
"# a digit must occur at least once\n" +
|
||||
"# a lower case letter must occur at least once\n" +
|
||||
"# an upper case letter must occur at least once\n" +
|
||||
"# a special character must occur at least once\n" +
|
||||
"# a special character must occur at least once ( one of !@#$%^&*(),.?\":{}|<>) \n" +
|
||||
"# no whitespace allowed in the entire string\n" +
|
||||
"# anything, at least eight places though";
|
||||
|
||||
@@ -33,7 +33,7 @@ public @interface SecurePassword {
|
||||
|
||||
@Override
|
||||
public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
|
||||
return s.matches("^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\\S+$).{8,}$");
|
||||
return s != null && s.matches("^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*(),.?\":{}|<>])(?=\\S+$).{8,}$");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS=false
|
||||
logging.level.org.springframework.data.mongodb.core.MongoTemplate=DEBUG
|
||||
|
||||
Reference in New Issue
Block a user