Skip to content
This repository has been archived by the owner on Jul 26, 2024. It is now read-only.

Split JAX-RS resource and service (separation of concerns) #8

Merged
merged 3 commits into from
Jun 18, 2024
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
17 changes: 17 additions & 0 deletions customer-api-provider/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-bom</artifactId>
<version>3.26.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

Expand Down Expand Up @@ -56,6 +63,16 @@
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-mockito</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-validator</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import jakarta.json.bind.annotation.JsonbProperty;
import jakarta.json.bind.annotation.JsonbTransient;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
Expand All @@ -22,10 +23,12 @@ public class Customer {
@Setter(onMethod_ = @JsonbTransient)
private UUID uuid;
@Size(min = 3, max = 100)
@NotNull
private String name;
@JsonbProperty("birth_date") // TODO -> use snake_case globally?
private LocalDate birthdate;
@Pattern(regexp = "active|locked|disabled")
@NotNull
private String state;

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,30 @@
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriBuilder;
import lombok.RequiredArgsConstructor;

import java.time.LocalDate;
import java.time.Month;
import java.util.*;
import java.util.Collection;
import java.util.UUID;

@Path("/customers")
@RequiredArgsConstructor
public class CustomersResource {

private final Map<UUID, Customer> customers = new HashMap<>();

{
var customer = new Customer(
UUID.randomUUID(),
"Tom",
LocalDate.of(2000, Month.DECEMBER, 6),
"active"
);
customers.put(customer.getUuid(), customer);
}
private final CustomersService service;

@GET
@Produces(MediaType.APPLICATION_JSON)
public Collection<Customer> getCustomers() {
return customers.values();
return service
.getAll()
.toList();
}

@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response createCustomer(@Valid Customer customer) {
customer.setUuid(UUID.randomUUID());
customers.put(customer.getUuid(), customer);
service.createCustomer(customer);
final var location = UriBuilder.fromResource(CustomersResource.class)
.path(CustomersResource.class, "findCustomerById")
.build(customer.getUuid().toString());
Expand All @@ -58,14 +50,14 @@ public Response createCustomer(@Valid Customer customer) {
@GET
@Path("/{uuid}")
public Customer findCustomerById(@PathParam("uuid") UUID uuid) {
return Optional.ofNullable(customers.get(uuid))
.orElseThrow(NotFoundException::new);
return service.getByUuid(uuid)
.orElseThrow(NotFoundException::new);
}

@DELETE
@Path("/{uuid}")
public Response deleteCustomerById(@PathParam("uuid") UUID uuid) {
if (customers.remove(uuid) == null) {
if (!service.delete(uuid)) {
throw new NotFoundException();
}
return Response
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package de.schulung.sample.quarkus;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;

import java.time.LocalDate;
import java.time.Month;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Stream;

@ApplicationScoped
public class CustomersService {

private final Map<UUID, Customer> customers = new HashMap<>();

{ // TODO replace this?
var customer = new Customer(
UUID.randomUUID(),
"Tom",
LocalDate.of(2000, Month.DECEMBER, 6),
"active"
);
customers.put(customer.getUuid(), customer);
}

public Stream<Customer> getAll() {
return this.customers
.values()
.stream();
}

public Stream<Customer> getByState(
@NotNull
@Pattern(regexp = "active|locked|disabled")
String state
) {
return this.getAll()
.filter(c -> c.getState().equals(state));
}

public void createCustomer(@Valid Customer customer) {
customer.setUuid(UUID.randomUUID());
customers.put(customer.getUuid(), customer);
}

public Optional<Customer> getByUuid(@NotNull UUID uuid) {
return Optional.ofNullable(customers.get(uuid));
}

public boolean exists(@NotNull UUID uuid) {
return customers.containsKey(uuid);
}

public boolean delete(@NotNull UUID uuid) {
return customers.remove(uuid) != null;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package de.schulung.sample.quarkus;

import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import io.restassured.http.ContentType;
import org.junit.jupiter.api.Test;

import java.util.Optional;
import java.util.UUID;

import static io.restassured.RestAssured.given;
import static org.mockito.Mockito.*;

@QuarkusTest
public class CustomerApiMockedServiceTests {

@InjectMock
CustomersService service;

@Test
void shouldReturn404WhenCustomerNotExists() {

var uuid = UUID.randomUUID();
when(service.getByUuid(uuid))
.thenReturn(Optional.empty());

given()
.when()
.accept(ContentType.JSON)
.get("/customers/{uuid}", uuid)
.then()
.statusCode(404);

verify(service).getByUuid(uuid);

}

// TODO: wenn invalider Kunde angelegt werden soll, dann 400 + kein Service-Aufruf

@Test
void shouldNotInvokeServiceWhenInvalidCustomerIsCreated() {
given()
.when()
.contentType(ContentType.JSON)
.body("""
{
"name": "T",
"birth_date": "2000-10-04",
"state": "active"
}
""")
.accept(ContentType.JSON)
.post("/customers")
.then()
.statusCode(400);

verifyNoInteractions(service);

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package de.schulung.sample.quarkus;

import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThatThrownBy;

@QuarkusTest
public class CustomersServiceIntegrationTests {

@Inject
CustomersService service;

// TODO Testfall: Customer anlegen mit zu kurzem Namen -> Validierungsfehler

@Test
void shouldNotCreateInvalidCustomer() {
var customer = new Customer();
assertThatThrownBy(() -> service.createCustomer(customer))
.isNotNull();
}

@Test
void shouldNotCreateNullCustomer() {
assertThatThrownBy(() -> service.createCustomer(null))
.isNotNull();
}

@Test
void shouldNotInvokeSearchWithNullUuid() {
assertThatThrownBy(() -> service.getByUuid(null))
.isNotNull();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package de.schulung.sample.quarkus;

import org.junit.jupiter.api.Test;

import java.time.LocalDate;
import java.time.Month;

import static org.assertj.core.api.Assertions.assertThat;

class CustomersServiceTests {

private final CustomersService service = new CustomersService();

@Test
void shouldAddUuidToNewCustomer() {
var customer = new Customer();
customer.setName("Tom");
customer.setBirthdate(LocalDate.of(2000, Month.FEBRUARY, 2));
customer.setState("active");

service.createCustomer(customer);

assertThat(customer.getUuid())
.isNotNull();
}

@Test
void shouldFindNewCustomer() {
var customer = new Customer();
customer.setName("Tom");
customer.setBirthdate(LocalDate.of(2000, Month.FEBRUARY, 2));
customer.setState("active");
service.createCustomer(customer);
var uuid = customer.getUuid();

var result = service.getByUuid(uuid);

assertThat(result).isNotEmpty();
}

}
Loading