From e9c23a156fc04eab76d43681d88e673c58b4ab5c Mon Sep 17 00:00:00 2001 From: Tobias Wietelmann Date: Wed, 5 Aug 2026 09:23:13 +0200 Subject: [PATCH 1/2] Add pagination and tighten API boundaries --- README.md | 91 ++++++++++++++----- docs/index.md | 47 +++++++--- .../processapi/processcheck/ProcessCheck.java | 16 +++- .../processcheck/ProcessCheckController.java | 18 +++- .../processcheck/ProcessCheckRepository.java | 6 +- .../processcheck/ProcessCheckRequest.java | 5 +- .../processcheck/ProcessCheckService.java | 45 ++++++--- src/main/resources/application.properties | 4 +- .../ProcessCheckApiIntegrationTests.java | 75 +++++++++++++-- 9 files changed, 236 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index 9eba0b8..90bfde2 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,11 @@ It demonstrates: - REST endpoints for a small process-related resource - layered backend structure with controller, service and repository -- request validation -- status-based filtering +- request validation and matching database constraints +- status-based filtering and pagination - explicit HTTP success and error behavior - basic persistence with Spring Data JPA +- restrained structured logging for write operations - an H2 in-memory database for local development and tests - automated API integration tests with MockMvc - a reproducible Maven Wrapper workflow @@ -52,10 +53,12 @@ It complements my main Data/BI portfolio projects around SQL, Python, Power BI, | Language | Java 21 | Main implementation language | | Framework | Spring Boot 4.1 | REST API application framework | | API layer | Spring Web MVC | HTTP endpoints and JSON responses | +| Pagination | Spring Data `Pageable` and `PagedModel` | Bounded list responses with stable page metadata | | Persistence | Spring Data JPA | Repository abstraction and entity persistence | | Database | H2 | In-memory local development and test database | | Validation | Jakarta Validation | Validation for incoming request data | | Error format | Spring `ProblemDetail` | Consistent `application/problem+json` responses | +| Logging | SLF4J | Structured create, update and delete messages | | Tests | JUnit 5, Spring Boot Test, MockMvc | API integration and persistence verification | | Build tool | Maven Wrapper | Reproducible builds on Windows, macOS and Linux | | CI | GitHub Actions | Automated Java 21 Maven verification | @@ -86,13 +89,21 @@ The API uses request and response records instead of exposing the JPA entity dir | Method | Endpoint | Success | Purpose | |---|---|---:|---| -| `GET` | `/api/process-checks` | `200 OK` | Return all process-check records | -| `GET` | `/api/process-checks?status=OK` | `200 OK` | Filter records by `OK`, `WARNING` or `CRITICAL` | +| `GET` | `/api/process-checks?page=0&size=20` | `200 OK` | Return one page of process-check records | +| `GET` | `/api/process-checks?status=OK&page=0&size=20` | `200 OK` | Filter and page records by status | | `GET` | `/api/process-checks/{id}` | `200 OK` | Return one process-check record by ID | | `POST` | `/api/process-checks` | `201 Created` | Create a record and return its URI in `Location` | | `PUT` | `/api/process-checks/{id}` | `200 OK` | Replace the editable values of an existing record | | `DELETE` | `/api/process-checks/{id}` | `204 No Content` | Delete an existing record | +List endpoints accept the standard Spring Data parameters: + +- `page`: zero-based page number, default `0` +- `size`: requested page size, default `20`, capped at `100` +- `sort`: field and direction, for example `sort=processName,asc` + +The default list order is `lastCheckedAt,desc`. + Typical client errors: | Situation | Result | @@ -103,27 +114,39 @@ Typical client errors: --- -## Example Process-Check Record +## Example Paged Response ```json { - "id": 1, - "processName": "Daily sales import", - "owner": "Data Operations", - "status": "OK", - "lastCheckedAt": "2026-07-10T00:25:00", - "slaMinutes": 60 + "content": [ + { + "id": 1, + "processName": "Daily sales import", + "owner": "Data Operations", + "status": "OK", + "lastCheckedAt": "2026-07-10T00:25:00", + "slaMinutes": 60 + } + ], + "page": { + "size": 20, + "totalElements": 1, + "totalPages": 1, + "number": 0 + } } ``` Request validation requires: -- a non-blank `processName` -- a non-blank `owner` +- a non-blank `processName` with at most 120 characters +- a non-blank `owner` with at most 120 characters - a valid status: `OK`, `WARNING` or `CRITICAL` - a non-null ISO local date-time value - `slaMinutes` of at least `1` +The entity mirrors the non-null and maximum-length constraints so the API and database schema enforce the same basic rules. + --- ## Error Response Example @@ -164,10 +187,18 @@ Then open: http://localhost:8080/api/process-checks ``` -At first startup, the API returns an empty JSON array because the H2 database is empty: +At first startup, the H2 database is empty, so the list endpoint returns an empty page: ```json -[] +{ + "content": [], + "page": { + "size": 20, + "totalElements": 0, + "totalPages": 0, + "number": 0 + } +} ``` --- @@ -189,13 +220,14 @@ At first startup, the API returns an empty JSON array because the H2 database is The automated suite verifies: - application context startup +- default and requested pagination - unfiltered and status-filtered list requests - empty filter results - invalid status handling - lookup by ID - `404` Problem Detail responses - successful creation with `201 Created` and `Location` -- request validation failures +- blank, invalid and oversized request values - update behavior and persisted values - successful deletion with `204 No Content` - update and delete behavior for unknown IDs @@ -223,11 +255,12 @@ The workflow has read-only repository permissions and cancels superseded runs fo The full CRUD flow can also be exercised manually with curl, an API client or an IDE HTTP client: ```text -POST /api/process-checks create a process-check record -GET /api/process-checks list all process-check records -GET /api/process-checks/1 read one process-check record -PUT /api/process-checks/1 update one process-check record -DELETE /api/process-checks/1 delete one process-check record +POST /api/process-checks +GET /api/process-checks?page=0&size=20 +GET /api/process-checks?status=OK&page=0&size=20 +GET /api/process-checks/1 +PUT /api/process-checks/1 +DELETE /api/process-checks/1 ``` Example test data: @@ -242,6 +275,14 @@ slaMinutes: 60 --- +## Logging + +Create, update and delete operations write one structured application log entry containing the record ID and, where useful, its status. + +Read requests and complete request bodies are not logged. This keeps the example useful for troubleshooting without producing noisy logs or copying input data unnecessarily. + +--- + ## H2 Database Note This project uses an **H2 in-memory database** for local development and API testing. @@ -314,12 +355,14 @@ This repository demonstrates a small but realistic backend foundation: - Spring Boot application structure - REST endpoint and HTTP-status design - JSON request and response handling -- CRUD operations and status filtering +- paged CRUD queries and status filtering - layered backend organization -- request validation +- request validation aligned with persistence constraints - standard Problem Detail error responses - explicit transaction boundaries +- JPA dirty checking for managed updates - persistence abstraction with Spring Data JPA +- restrained structured logging - local development and testing with H2 - automated integration testing - reproducible Maven builds @@ -332,7 +375,7 @@ This repository demonstrates a small but realistic backend foundation: This is a learning project. -It does not include production database configuration, Docker deployment, authentication and authorization, a frontend UI, cloud deployment, monitoring infrastructure, pagination or enterprise-scale operational error handling. +It does not include production database configuration, Docker deployment, authentication and authorization, a frontend UI, cloud deployment, metrics, tracing or enterprise-scale operational error handling. These omissions are intentional. The current scope is limited to a clean, understandable and tested Spring Boot REST API baseline. diff --git a/docs/index.md b/docs/index.md index 337ddbb..0caef18 100644 --- a/docs/index.md +++ b/docs/index.md @@ -5,7 +5,7 @@ description: Small Java 21 / Spring Boot REST API portfolio project # Spring Boot Process API Basics -**Small Java 21 / Spring Boot REST API project exposing validated process-check data through a layered backend structure, automated tests and H2 persistence.** +**Small Java 21 / Spring Boot REST API project exposing validated and paginated process-check data through a layered backend structure, automated tests and H2 persistence.** [View repository](https://github.com/DataTideHH/spring-boot-process-api-basics) · [Read the full README](https://github.com/DataTideHH/spring-boot-process-api-basics/blob/main/README.md) · [View CI](https://github.com/DataTideHH/spring-boot-process-api-basics/actions/workflows/ci.yml) · [DataTideHH portfolio](https://datatidehh.de/) @@ -17,7 +17,7 @@ This project is a deliberately compact backend learning project. It demonstrates how process-related records can be represented, validated, persisted and exposed through a small REST API using Spring Boot. -The goal is not to present a production service or an enterprise backend system. The goal is to document a clean first step from Java basics toward a small layered REST API with explicit HTTP behavior, persistence and automated verification. +The goal is not to present a production service or an enterprise backend system. The goal is to document a clean first step from Java basics toward a small layered REST API with explicit HTTP behavior, bounded list queries, persistence and automated verification. --- @@ -37,11 +37,14 @@ It follows the [IPv4 Subnet Calculator Multilang](https://datatidehh.github.io/i - controller, service and repository separation - Spring Data JPA repository usage - request and response records -- Jakarta Validation +- Jakarta Validation aligned with entity constraints - H2 in-memory persistence - CRUD endpoints for process-check data -- optional status filtering +- status filtering and pagination +- stable page metadata through Spring Data `PagedModel` - standard `ProblemDetail` error responses +- explicit transaction boundaries and JPA dirty checking +- restrained structured logging for write operations - integration tests with Spring Boot Test and MockMvc - reproducible Maven Wrapper builds - GitHub Actions verification on Java 21 @@ -52,30 +55,44 @@ It follows the [IPv4 Subnet Calculator Multilang](https://datatidehh.github.io/i | Method | Endpoint | Result | Purpose | |---|---|---:|---| -| `GET` | `/api/process-checks` | `200` | List all records | -| `GET` | `/api/process-checks?status=OK` | `200` | Filter by `OK`, `WARNING` or `CRITICAL` | +| `GET` | `/api/process-checks?page=0&size=20` | `200` | List one page of records | +| `GET` | `/api/process-checks?status=OK&page=0&size=20` | `200` | Filter and page by status | | `GET` | `/api/process-checks/{id}` | `200` | Read one record | | `POST` | `/api/process-checks` | `201` | Create a record and return `Location` | | `PUT` | `/api/process-checks/{id}` | `200` | Update a record | | `DELETE` | `/api/process-checks/{id}` | `204` | Delete a record | +The list endpoint defaults to 20 records, sorts by `lastCheckedAt` descending and caps requested page sizes at 100. + Invalid input returns `400 Bad Request`. Unknown record IDs return `404 Not Found` as `application/problem+json`. --- -## Example process-check record +## Example paged response ```json { - "id": 1, - "processName": "Daily sales import", - "owner": "Data Operations", - "status": "OK", - "lastCheckedAt": "2026-07-10T00:25:00", - "slaMinutes": 60 + "content": [ + { + "id": 1, + "processName": "Daily sales import", + "owner": "Data Operations", + "status": "OK", + "lastCheckedAt": "2026-07-10T00:25:00", + "slaMinutes": 60 + } + ], + "page": { + "size": 20, + "totalElements": 1, + "totalPages": 1, + "number": 0 + } } ``` +`processName` and `owner` are required and limited to 120 characters. The JPA entity mirrors these length and nullability rules. + --- ## Local usage @@ -104,7 +121,7 @@ http://localhost:8080/api/process-checks ## Verification -The integration suite covers list and filter behavior, lookup by ID, creation, validation failures, updates, deletion, persistence effects and `404` Problem Detail responses. +The integration suite covers pagination, sorting, status filtering, lookup by ID, creation, blank and oversized input, updates, deletion, persistence effects and `404` Problem Detail responses. The GitHub Actions workflow runs `clean verify` with Eclipse Temurin Java 21 for pull requests and pushes to `main`. @@ -136,4 +153,4 @@ The project uses an H2 in-memory database. Data is reset when the application st The sample data is synthetic and does not contain personal, customer or production data. -This is a learning project with a deliberately limited scope. It does not claim production deployment, authentication, cloud operation or enterprise-scale infrastructure. +This is a learning project with a deliberately limited scope. It does not claim production deployment, authentication, cloud operation, monitoring infrastructure or enterprise-scale operation. diff --git a/src/main/java/de/datatidehh/processapi/processcheck/ProcessCheck.java b/src/main/java/de/datatidehh/processapi/processcheck/ProcessCheck.java index 8a0fc86..e9020d2 100644 --- a/src/main/java/de/datatidehh/processapi/processcheck/ProcessCheck.java +++ b/src/main/java/de/datatidehh/processapi/processcheck/ProcessCheck.java @@ -1,6 +1,13 @@ package de.datatidehh.processapi.processcheck; -import jakarta.persistence.*; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + import java.time.LocalDateTime; @Entity @@ -10,13 +17,20 @@ public class ProcessCheck { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Column(nullable = false, length = 120) private String processName; + + @Column(nullable = false, length = 120) private String owner; @Enumerated(EnumType.STRING) + @Column(nullable = false, length = 20) private ProcessStatus status; + @Column(nullable = false) private LocalDateTime lastCheckedAt; + + @Column(nullable = false) private Integer slaMinutes; protected ProcessCheck() { diff --git a/src/main/java/de/datatidehh/processapi/processcheck/ProcessCheckController.java b/src/main/java/de/datatidehh/processapi/processcheck/ProcessCheckController.java index 315abb0..6ee3bc6 100644 --- a/src/main/java/de/datatidehh/processapi/processcheck/ProcessCheckController.java +++ b/src/main/java/de/datatidehh/processapi/processcheck/ProcessCheckController.java @@ -1,6 +1,10 @@ package de.datatidehh.processapi.processcheck; import jakarta.validation.Valid; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PagedModel; +import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -14,7 +18,6 @@ import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import java.net.URI; -import java.util.List; @RestController @RequestMapping("/api/process-checks") @@ -27,10 +30,15 @@ public ProcessCheckController(ProcessCheckService service) { } @GetMapping - public List findAll( - @RequestParam(required = false) ProcessStatus status + public PagedModel findAll( + @RequestParam(required = false) ProcessStatus status, + @PageableDefault( + size = 20, + sort = "lastCheckedAt", + direction = Sort.Direction.DESC + ) Pageable pageable ) { - return service.findAll(status); + return new PagedModel<>(service.findAll(status, pageable)); } @GetMapping("/{id}") @@ -70,4 +78,4 @@ private URI buildLocation(Long id) { .buildAndExpand(id) .toUri(); } -} \ No newline at end of file +} diff --git a/src/main/java/de/datatidehh/processapi/processcheck/ProcessCheckRepository.java b/src/main/java/de/datatidehh/processapi/processcheck/ProcessCheckRepository.java index 9f947cd..a504696 100644 --- a/src/main/java/de/datatidehh/processapi/processcheck/ProcessCheckRepository.java +++ b/src/main/java/de/datatidehh/processapi/processcheck/ProcessCheckRepository.java @@ -1,10 +1,10 @@ package de.datatidehh.processapi.processcheck; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; -import java.util.List; - public interface ProcessCheckRepository extends JpaRepository { - List findAllByStatus(ProcessStatus status); + Page findAllByStatus(ProcessStatus status, Pageable pageable); } diff --git a/src/main/java/de/datatidehh/processapi/processcheck/ProcessCheckRequest.java b/src/main/java/de/datatidehh/processapi/processcheck/ProcessCheckRequest.java index 8822853..64eb498 100644 --- a/src/main/java/de/datatidehh/processapi/processcheck/ProcessCheckRequest.java +++ b/src/main/java/de/datatidehh/processapi/processcheck/ProcessCheckRequest.java @@ -3,12 +3,13 @@ import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; import java.time.LocalDateTime; public record ProcessCheckRequest( - @NotBlank String processName, - @NotBlank String owner, + @NotBlank @Size(max = 120) String processName, + @NotBlank @Size(max = 120) String owner, @NotNull ProcessStatus status, @NotNull LocalDateTime lastCheckedAt, @NotNull @Min(1) Integer slaMinutes diff --git a/src/main/java/de/datatidehh/processapi/processcheck/ProcessCheckService.java b/src/main/java/de/datatidehh/processapi/processcheck/ProcessCheckService.java index 4d9f44d..d78a727 100644 --- a/src/main/java/de/datatidehh/processapi/processcheck/ProcessCheckService.java +++ b/src/main/java/de/datatidehh/processapi/processcheck/ProcessCheckService.java @@ -1,28 +1,33 @@ package de.datatidehh.processapi.processcheck; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.List; - @Service @Transactional(readOnly = true) public class ProcessCheckService { + private static final Logger log = LoggerFactory.getLogger(ProcessCheckService.class); + private final ProcessCheckRepository repository; public ProcessCheckService(ProcessCheckRepository repository) { this.repository = repository; } - public List findAll(ProcessStatus status) { - List processChecks = status == null - ? repository.findAll() - : repository.findAllByStatus(status); + public Page findAll( + ProcessStatus status, + Pageable pageable + ) { + Page processChecks = status == null + ? repository.findAll(pageable) + : repository.findAllByStatus(status, pageable); - return processChecks.stream() - .map(ProcessCheckResponse::fromEntity) - .toList(); + return processChecks.map(ProcessCheckResponse::fromEntity); } public ProcessCheckResponse findById(Long id) { @@ -39,7 +44,15 @@ public ProcessCheckResponse create(ProcessCheckRequest request) { request.slaMinutes() ); - return ProcessCheckResponse.fromEntity(repository.save(processCheck)); + ProcessCheck savedProcessCheck = repository.save(processCheck); + + log.info( + "Created process check id={} status={}", + savedProcessCheck.getId(), + savedProcessCheck.getStatus() + ); + + return ProcessCheckResponse.fromEntity(savedProcessCheck); } @Transactional @@ -54,17 +67,25 @@ public ProcessCheckResponse update(Long id, ProcessCheckRequest request) { request.slaMinutes() ); - return ProcessCheckResponse.fromEntity(repository.save(processCheck)); + log.info( + "Updated process check id={} status={}", + processCheck.getId(), + processCheck.getStatus() + ); + + return ProcessCheckResponse.fromEntity(processCheck); } @Transactional public void delete(Long id) { ProcessCheck processCheck = findEntityById(id); repository.delete(processCheck); + + log.info("Deleted process check id={}", id); } private ProcessCheck findEntityById(Long id) { return repository.findById(id) .orElseThrow(() -> new ProcessCheckNotFoundException(id)); } -} \ No newline at end of file +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 9cc2a2b..301d6b0 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -11,7 +11,9 @@ spring.jpa.hibernate.ddl-auto=create-drop spring.jpa.show-sql=false spring.jpa.open-in-view=false +spring.data.web.pageable.max-page-size=100 + spring.h2.console.enabled=true spring.h2.console.path=/h2-console -spring.mvc.problemdetails.enabled=true \ No newline at end of file +spring.mvc.problemdetails.enabled=true diff --git a/src/test/java/de/datatidehh/processapi/processcheck/ProcessCheckApiIntegrationTests.java b/src/test/java/de/datatidehh/processapi/processcheck/ProcessCheckApiIntegrationTests.java index 2780bcd..dcf5785 100644 --- a/src/test/java/de/datatidehh/processapi/processcheck/ProcessCheckApiIntegrationTests.java +++ b/src/test/java/de/datatidehh/processapi/processcheck/ProcessCheckApiIntegrationTests.java @@ -30,6 +30,7 @@ class ProcessCheckApiIntegrationTests { private static final long MISSING_ID = 999_999L; + private static final int MAX_TEXT_LENGTH = 120; @Autowired private MockMvc mockMvc; @@ -69,11 +70,33 @@ void tearDown() { void findAllWithoutStatusReturnsRecordsOfAllStatuses() throws Exception { mockMvc.perform(get("/api/process-checks")) .andExpect(status().isOk()) - .andExpect(jsonPath("$", hasSize(2))) + .andExpect(jsonPath("$.content", hasSize(2))) .andExpect(jsonPath( - "$[*].status", + "$.content[*].status", containsInAnyOrder("OK", "WARNING") - )); + )) + .andExpect(jsonPath("$.page.number", is(0))) + .andExpect(jsonPath("$.page.size", is(20))) + .andExpect(jsonPath("$.page.totalElements", is(2))) + .andExpect(jsonPath("$.page.totalPages", is(1))); + } + + @Test + void findAllUsesRequestedPageSizeAndSortOrder() throws Exception { + mockMvc.perform(get("/api/process-checks") + .param("page", "0") + .param("size", "1") + .param("sort", "processName,asc")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.content", hasSize(1))) + .andExpect(jsonPath( + "$.content[0].processName", + is("Daily sales import") + )) + .andExpect(jsonPath("$.page.number", is(0))) + .andExpect(jsonPath("$.page.size", is(1))) + .andExpect(jsonPath("$.page.totalElements", is(2))) + .andExpect(jsonPath("$.page.totalPages", is(2))); } @Test @@ -81,24 +104,26 @@ void findAllWithOkStatusReturnsOnlyOkRecords() throws Exception { mockMvc.perform(get("/api/process-checks") .param("status", "OK")) .andExpect(status().isOk()) - .andExpect(jsonPath("$", hasSize(1))) + .andExpect(jsonPath("$.content", hasSize(1))) .andExpect(jsonPath( - "$[*].status", + "$.content[*].status", everyItem(is("OK")) )) .andExpect(jsonPath( - "$[0].processName", + "$.content[0].processName", is("Daily sales import") )); } @Test - void findAllWithCriticalStatusReturnsEmptyArrayWhenNoRecordsMatch() + void findAllWithCriticalStatusReturnsEmptyPageWhenNoRecordsMatch() throws Exception { mockMvc.perform(get("/api/process-checks") .param("status", "CRITICAL")) .andExpect(status().isOk()) - .andExpect(jsonPath("$", hasSize(0))); + .andExpect(jsonPath("$.content", hasSize(0))) + .andExpect(jsonPath("$.page.totalElements", is(0))) + .andExpect(jsonPath("$.page.totalPages", is(0))); } @Test @@ -200,6 +225,40 @@ void createWithInvalidRequestReturnsBadRequest() throws Exception { .hasSize(2); } + @Test + void createWithOversizedProcessNameReturnsBadRequest() throws Exception { + mockMvc.perform(post("/api/process-checks") + .contentType(MediaType.APPLICATION_JSON) + .content(requestJson( + "x".repeat(MAX_TEXT_LENGTH + 1), + "Data Quality", + "OK", + "2026-07-11T08:30:00", + 30 + ))) + .andExpect(status().isBadRequest()); + + org.assertj.core.api.Assertions.assertThat(repository.findAll()) + .hasSize(2); + } + + @Test + void createWithOversizedOwnerReturnsBadRequest() throws Exception { + mockMvc.perform(post("/api/process-checks") + .contentType(MediaType.APPLICATION_JSON) + .content(requestJson( + "Customer data quality check", + "x".repeat(MAX_TEXT_LENGTH + 1), + "OK", + "2026-07-11T08:30:00", + 30 + ))) + .andExpect(status().isBadRequest()); + + org.assertj.core.api.Assertions.assertThat(repository.findAll()) + .hasSize(2); + } + @Test void updateChangesAndPersistsExistingRecord() throws Exception { mockMvc.perform(put( From a00e476c124b6711301b3c583f7a7923316af2d1 Mon Sep 17 00:00:00 2001 From: DataTideHH <219566149+DataTideHH@users.noreply.github.com> Date: Wed, 5 Aug 2026 09:41:58 +0200 Subject: [PATCH 2/2] Clarify logging terminology --- README.md | 8 ++++---- docs/index.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 90bfde2..533ba91 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ It demonstrates: - status-based filtering and pagination - explicit HTTP success and error behavior - basic persistence with Spring Data JPA -- restrained structured logging for write operations +- restrained parameterized logging for write operations - an H2 in-memory database for local development and tests - automated API integration tests with MockMvc - a reproducible Maven Wrapper workflow @@ -277,7 +277,7 @@ slaMinutes: 60 ## Logging -Create, update and delete operations write one structured application log entry containing the record ID and, where useful, its status. +Create, update and delete operations write one parameterized application log entry containing the record ID and, where useful, its status. Read requests and complete request bodies are not logged. This keeps the example useful for troubleshooting without producing noisy logs or copying input data unnecessarily. @@ -355,14 +355,14 @@ This repository demonstrates a small but realistic backend foundation: - Spring Boot application structure - REST endpoint and HTTP-status design - JSON request and response handling -- paged CRUD queries and status filtering +- paginated list queries and status filtering - layered backend organization - request validation aligned with persistence constraints - standard Problem Detail error responses - explicit transaction boundaries - JPA dirty checking for managed updates - persistence abstraction with Spring Data JPA -- restrained structured logging +- restrained parameterized logging - local development and testing with H2 - automated integration testing - reproducible Maven builds diff --git a/docs/index.md b/docs/index.md index 0caef18..5a32798 100644 --- a/docs/index.md +++ b/docs/index.md @@ -44,7 +44,7 @@ It follows the [IPv4 Subnet Calculator Multilang](https://datatidehh.github.io/i - stable page metadata through Spring Data `PagedModel` - standard `ProblemDetail` error responses - explicit transaction boundaries and JPA dirty checking -- restrained structured logging for write operations +- restrained parameterized logging for write operations - integration tests with Spring Boot Test and MockMvc - reproducible Maven Wrapper builds - GitHub Actions verification on Java 21