Skip to content

Commit 084bd53

Browse files
committed
chore: update
1 parent 9f45b18 commit 084bd53

10 files changed

Lines changed: 76 additions & 33 deletions

File tree

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,15 @@ Die aktivierbare Engineering-Aufgabe läuft mit:
7777
./mvnw -Pguided-gaps test
7878
```
7979

80-
Dieser Test schlägt am Anfang erwartbar fehl. Er wird erst grün, wenn die Migration datenbankseitige Regeln enthält.
80+
Dieser Test schlägt am Anfang erwartbar fehl. Er wird erst grün, wenn eine neue Migration datenbankseitige Regeln enthält.
81+
82+
Die Lösung wird nicht in `V1__starter_ticket_schema.sql` eingetragen. `V1` bleibt der dokumentierte Startzustand. Studierende schreiben stattdessen eine neue Migration, zum Beispiel:
83+
84+
```text
85+
src/main/resources/db/migration/V2__enforce_ticket_rules.sql
86+
```
87+
88+
Diese `V2` ergänzt Pflichtfelder und einen Status-Constraint für die Ticket-Tabelle.
8189

8290
## Teststrategie
8391

docs/guided-gaps.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,13 @@ Analysiere `V1__starter_ticket_schema.sql`.
1313

1414
## Aufgabe 2: Datenbanklogik ergaenzen
1515

16-
Verbessere die Migration so, dass PostgreSQL mindestens Folgendes garantiert:
16+
Erstelle eine neue Migration:
17+
18+
```text
19+
src/main/resources/db/migration/V2__enforce_ticket_rules.sql
20+
```
21+
22+
Aendere `V1__starter_ticket_schema.sql` nicht. `V1` bleibt der bewusst schwache Ausgangszustand. Die neue `V2` soll PostgreSQL mindestens Folgendes garantieren:
1723

1824
- ein Ticket hat immer einen Titel
1925
- ein Ticket hat immer einen Status
@@ -33,4 +39,3 @@ Analysiere den Datenfluss fuer `GET /api/tickets?status=open`.
3339
- Welche Methode erzeugt die Datenbankabfrage?
3440
- Welche Rolle hat der Service?
3541
- Warum ist die Repository Method fuer diesen Fall noch lesbar?
36-

pom.xml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
<properties>
2121
<java.version>24</java.version>
22+
<mapstruct.version>1.6.3</mapstruct.version>
23+
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
2224
<testcontainers.version>1.21.4</testcontainers.version>
2325
</properties>
2426

@@ -39,6 +41,11 @@
3941
<groupId>org.springframework.boot</groupId>
4042
<artifactId>spring-boot-starter-flyway</artifactId>
4143
</dependency>
44+
<dependency>
45+
<groupId>org.mapstruct</groupId>
46+
<artifactId>mapstruct</artifactId>
47+
<version>${mapstruct.version}</version>
48+
</dependency>
4249
<dependency>
4350
<groupId>org.springdoc</groupId>
4451
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
@@ -106,6 +113,16 @@
106113
<groupId>org.projectlombok</groupId>
107114
<artifactId>lombok</artifactId>
108115
</path>
116+
<path>
117+
<groupId>org.projectlombok</groupId>
118+
<artifactId>lombok-mapstruct-binding</artifactId>
119+
<version>${lombok-mapstruct-binding.version}</version>
120+
</path>
121+
<path>
122+
<groupId>org.mapstruct</groupId>
123+
<artifactId>mapstruct-processor</artifactId>
124+
<version>${mapstruct.version}</version>
125+
</path>
109126
</annotationProcessorPaths>
110127
</configuration>
111128
</plugin>

src/main/java/ch/hftm/db2/ticketsystem/OpenApiConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
info = @Info(
1010
title = "DB-2 Ticket System API",
1111
version = "0.1.0",
12-
description = "Spring-Boot-Starterprojekt für DB-2 mit bewusst offenen Guided Gaps in der Datenbanklogik."
12+
description = "Spring-Boot-Projekt für DB-2"
1313
)
1414
)
1515
class OpenApiConfig {

src/main/java/ch/hftm/db2/ticketsystem/ticket/CreateTicketRequest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99
@Data
1010
@NoArgsConstructor
1111
@AllArgsConstructor
12-
@Schema(description = "Eingabe für ein neues Starter-Ticket")
12+
@Schema(description = "Eingabe für ein neues Ticket")
1313
class CreateTicketRequest {
1414

1515
@NotBlank
1616
@Schema(description = "Kurzer Tickettitel", example = "Datenbankverbindung prüfen")
1717
private String title;
1818

1919
@NotBlank
20-
@Schema(description = "Ticketstatus im Starterprojekt", example = "open")
20+
@Schema(description = "Ticketstatus", example = "open")
2121
private String status;
2222
}

src/main/java/ch/hftm/db2/ticketsystem/ticket/TicketController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ List<TicketResponse> findTickets(
3131

3232
@PostMapping
3333
@ResponseStatus(HttpStatus.CREATED)
34-
@Operation(summary = "Ticket erstellen", description = "Erstellt ein minimales Ticket im Starter-Schema app_starter.")
34+
@Operation(summary = "Ticket erstellen", description = "Erstellt ein Ticket.")
3535
TicketResponse createTicket(@Valid @RequestBody CreateTicketRequest request) {
3636
return ticketService.createTicket(request);
3737
}
Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,14 @@
11
package ch.hftm.db2.ticketsystem.ticket;
22

3-
import org.springframework.stereotype.Component;
3+
import org.mapstruct.Mapper;
4+
import org.mapstruct.Mapping;
45

5-
@Component
6-
class TicketMapper {
6+
@Mapper(componentModel = "spring")
7+
interface TicketMapper {
78

8-
TicketResponse toResponse(TicketEntity entity) {
9-
return new TicketResponse(
10-
entity.getId(),
11-
entity.getTitle(),
12-
entity.getStatus(),
13-
entity.getCreatedAt()
14-
);
15-
}
9+
TicketResponse toResponse(TicketEntity entity);
1610

17-
TicketEntity toEntity(CreateTicketRequest request) {
18-
return new TicketEntity(request.getTitle(), request.getStatus());
19-
}
11+
@Mapping(target = "id", ignore = true)
12+
@Mapping(target = "createdAt", ignore = true)
13+
TicketEntity toEntity(CreateTicketRequest request);
2014
}

src/test/java/ch/hftm/db2/ticketsystem/ticket/DatabaseEngineeringGapTest.java

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,41 @@
55
import java.io.IOException;
66
import java.nio.file.Files;
77
import java.nio.file.Path;
8+
import java.util.Locale;
89
import org.junit.jupiter.api.Tag;
910
import org.junit.jupiter.api.Test;
1011

1112
@Tag("guided-gap")
1213
class DatabaseEngineeringGapTest {
1314

1415
@Test
15-
void migrationContainsDatabaseRulesForTickets() throws IOException {
16-
String migration = Files.readString(Path.of("src/main/resources/db/migration/V1__starter_ticket_schema.sql"));
16+
void ticketRulesAreAddedWithNewMigration() throws IOException {
17+
Path v1Path = Path.of("src/main/resources/db/migration/V1__starter_ticket_schema.sql");
18+
Path v2Path = Path.of("src/main/resources/db/migration/V2__enforce_ticket_rules.sql");
1719

18-
assertThat(migration)
19-
.as("Tickets brauchen datenbankseitige Pflichtfelder.")
20-
.contains("title TEXT NOT NULL")
21-
.contains("status TEXT NOT NULL");
20+
String v1 = Files.readString(v1Path);
21+
assertThat(v1)
22+
.as("V1 bleibt der bewusst schwache Ausgangszustand. Schreibe eine neue V2-Migration.")
23+
.doesNotContain("title TEXT NOT NULL")
24+
.doesNotContain("status TEXT NOT NULL")
25+
.doesNotContain("CHECK (status IN");
2226

23-
assertThat(migration)
27+
assertThat(v2Path)
28+
.as("Lege eine neue Migration src/main/resources/db/migration/V2__enforce_ticket_rules.sql an.")
29+
.exists();
30+
31+
String v2 = Files.readString(v2Path).replaceAll("\\s+", " ").toUpperCase(Locale.ROOT);
32+
33+
assertThat(v2)
34+
.as("Tickets brauchen datenbankseitige Pflichtfelder in einer neuen Migration.")
35+
.contains("ALTER TABLE APP_STARTER.TICKETS ALTER COLUMN TITLE SET NOT NULL")
36+
.contains("ALTER TABLE APP_STARTER.TICKETS ALTER COLUMN STATUS SET NOT NULL");
37+
38+
assertThat(v2)
2439
.as("Gueltige Ticket-Status sollen in PostgreSQL abgesichert werden.")
25-
.contains("CHECK (status IN");
40+
.contains("CHECK (STATUS IN")
41+
.contains("'OPEN'")
42+
.contains("'WAITING'")
43+
.contains("'CLOSED'");
2644
}
2745
}
28-

src/test/java/ch/hftm/db2/ticketsystem/ticket/TicketMapperTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
import static org.assertj.core.api.Assertions.assertThat;
44

55
import org.junit.jupiter.api.Test;
6+
import org.mapstruct.factory.Mappers;
67

78
class TicketMapperTest {
89

9-
private final TicketMapper mapper = new TicketMapper();
10+
private final TicketMapper mapper = Mappers.getMapper(TicketMapper.class);
1011

1112
@Test
1213
void mapsCreateRequestToEntity() {

src/test/java/ch/hftm/db2/ticketsystem/ticket/TicketServiceTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88

99
import java.util.List;
1010
import org.junit.jupiter.api.Test;
11+
import org.mapstruct.factory.Mappers;
1112

1213
class TicketServiceTest {
1314

1415
private final TicketRepository ticketRepository = mock(TicketRepository.class);
15-
private final TicketService ticketService = new TicketService(ticketRepository, new TicketMapper());
16+
private final TicketMapper ticketMapper = Mappers.getMapper(TicketMapper.class);
17+
private final TicketService ticketService = new TicketService(ticketRepository, ticketMapper);
1618

1719
@Test
1820
void usesStatusRepositoryMethodWhenStatusFilterIsPresent() {
@@ -36,4 +38,3 @@ void savesMappedTicketWhenCreatingTicket() {
3638
assertThat(ticket.status()).isEqualTo("open");
3739
}
3840
}
39-

0 commit comments

Comments
 (0)