Skip to content

Commit 06432b6

Browse files
committed
fix customer-service error handling and align client/OpenAPI contract structure
- unify broken request exception handling under ApiRequestExceptionHandler - restore RFC 9457 problem mapping for validation, bad request, type mismatch, not found, method not allowed, and internal error cases - expand controller integration tests for success and error scenarios - remove incorrect GlobalErrorResponsesCustomizer-based error schema approach - switch client problem decoding to Spring ProblemDetail - harden client fallback handling for non-json, unparsable, empty, and status-unavailable upstream responses - fix fallback problem type URNs to use customer-service-client namespace - rename generic client packages to customer-service-specific package structure - align generated package naming for multi-client usage - add Javadocs and clarify contract-aware OpenAPI wrapper registration - refresh docs for api-contract Maven Central usage and updated module flow
1 parent 4143436 commit 06432b6

43 files changed

Lines changed: 1646 additions & 1646 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
# Compiled source #
2-
###################
3-
*.com
1+
############################
2+
# Compiled / binary files
3+
############################
44
*.class
55
*.dll
66
*.exe
77
*.o
88
*.so
9+
*.com
910

10-
# Packages #
11-
############
12-
# it's better to unpack these files and commit the raw source
13-
# git has its own built in compression methods
11+
############################
12+
# Archives / packaged files
13+
############################
1414
*.7z
1515
*.dmg
1616
*.gz
@@ -20,38 +20,37 @@
2020
*.tar
2121
*.zip
2222

23-
# Logs and databases #
24-
######################
23+
############################
24+
# Logs / databases
25+
############################
2526
*.log
2627
*.sql
2728
*.sqlite
2829

29-
# OS generated files #
30-
######################
30+
############################
31+
# OS generated files
32+
############################
3133
.DS_Store
3234
.DS_Store?
3335
._*
3436
.Spotlight-V100
3537
.Trashes
36-
ehthumbs.db
3738
Thumbs.db
39+
ehthumbs.db
3840

39-
.classpath
40-
.project
41-
.settings/
41+
############################
42+
# Build outputs
43+
############################
4244
target/
43-
/build/
44-
/bin/
45-
46-
47-
# IDE specific files and folders
48-
.idea/
49-
.project
50-
.classpath
51-
.settings/
45+
build/
46+
bin/
47+
generated-sources/
48+
generated-classes/
5249

53-
# Maven specific files and folders
54-
**/target/
50+
############################
51+
# Maven
52+
############################
53+
.mvn/
5554
pom.xml.tag
5655
pom.xml.releaseBackup
5756
pom.xml.versionsBackup
@@ -60,11 +59,16 @@ pom.xml.bak
6059
release.properties
6160
dependency-reduced-pom.xml
6261
buildNumber.properties
63-
.mvn/
6462

63+
############################
64+
# IDEs
65+
############################
66+
.idea/
67+
.project
68+
.classpath
69+
.settings/
6570

66-
# Generated source folders (common choices)
67-
target/
68-
generated-sources/
69-
generated-classes/
70-
/HELP.md
71+
############################
72+
# Misc
73+
############################
74+
HELP.md

PULL_REQUEST_TEMPLATE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ Please confirm the following:
101101

102102
**Type:** `feature` / `bugfix` / `docs` / `refactor` / `chore` / `test` / `ci`
103103
**Related Issue / Discussion:** (optional) `#number`
104-
**Target Release:** (optional) e.g. `v0.7.7`
104+
**Target Release:** (optional) e.g. `v0.7.8`
105105

106106
---
107107

README.md

Lines changed: 63 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -54,54 +54,50 @@ The result is a **deterministic, type‑safe API boundary** with **Page‑aware
5454

5555
## 📦 Modules
5656

57-
* **[api-contract](api-contract/README.md)**
58-
Shared, framework-agnostic API contract defining the canonical `{ data, meta }` response model,
59-
pagination primitives, and RFC 9457 error extensions.
60-
This module is the **single source of truth** shared by both server and client.
57+
* **[api-contract](api-contract/README.md)**
58+
Shared, framework-agnostic API contract defining the canonical `{ data, meta }` response model,
59+
pagination primitives, and RFC 9457 error extensions.
60+
This module is published to **Maven Central** and serves as the **single source of truth** shared by both server and client.
6161

62-
* **[customer-service](customer-service/README.md)**
63-
Spring Boot API producer exposing a deterministic **OpenAPI 3.1** specification enriched with
62+
* **[customer-service](customer-service/README.md)**
63+
Spring Boot API producer exposing a deterministic **OpenAPI 3.1** specification enriched with
6464
generics semantics (`ServiceResponse<T>`, `ServiceResponse<Page<T>>`).
6565

66-
* **[customer-service-client](customer-service-client/README.md)**
67-
Generated Java client that **reuses the canonical contract** and preserves generics
66+
* **[customer-service-client](customer-service-client/README.md)**
67+
Generated Java client that **reuses the canonical contract** and preserves generics
6868
without duplicating envelopes or paging models.
6969

7070
---
7171

7272
## ⚡ Quick Start
7373

74-
This repository uses an **aggregator (root) build** to guarantee that the shared **`api-contract`** module is always available to both the server and the client.
75-
For first-time users, **start from the repo root**.
74+
The shared `api-contract` module is published to Maven Central as `0.7.7`,
75+
so individual modules can now be built and run independently without requiring
76+
a root-level bootstrap step just to make the contract available locally.
7677

7778
---
7879

79-
### ✅ Option A — Recommended (Deterministic, First-Time Setup)
80+
### ✅ Option A — Recommended for Normal Use
8081

81-
This is the **canonical way** to get everything running after cloning the repository.
82-
It installs `api-contract` locally and builds all modules in the correct order.
82+
Build and run modules directly in their own scope:
8383

8484
```bash
85-
# 1) Build everything once from the repo root
86-
mvn -q -ntp clean install
87-
88-
# 2) Run the backend service
89-
cd customer-service && mvn -q -ntp spring-boot:run
85+
cd customer-service
86+
mvn -q -ntp clean package
87+
java -jar target/customer-service-*.jar
9088
```
9189

92-
At this point:
90+
This works because:
9391

94-
* `api-contract` is installed into your local Maven repository
95-
* `customer-service` is running
96-
* `customer-service-client` has been generated and compiled
97-
98-
No additional setup is required.
92+
* `api-contract:0.7.7` is resolved directly from Maven Central
93+
* no prior local installation step is required
94+
* the service can now be treated like a normal standalone module
9995

10096
---
10197

10298
### 🔄 Option B — Regenerate the Client from the Live OpenAPI Spec
10399

104-
Use this flow **only when you change the server contract** and want to regenerate
100+
Use this flow when you change the server contract and want to regenerate
105101
client wrappers from the live OpenAPI definition.
106102

107103
```bash
@@ -117,7 +113,7 @@ curl -s http://localhost:8084/customer-service/v3/api-docs.yaml \
117113
mvn -q -ntp clean install
118114
```
119115

120-
This regenerates **thin wrappers** extending the canonical contract:
116+
This regenerates thin wrappers extending the canonical contract:
121117

122118
```java
123119
ServiceResponse<T>
@@ -130,27 +126,28 @@ ServiceResponse<Page<T>>
130126

131127
Generated client sources are written to:
132128

133-
```
129+
```text
134130
customer-service-client/target/generated-sources/openapi/src/gen/java
135131
```
136132

137-
They are **automatically added to compilation** via `build-helper-maven-plugin`.
133+
They are automatically added to compilation via `build-helper-maven-plugin`.
138134

139135
---
140136

141137
### 📝 Notes
142138

143-
* You do **not** need to manually build or install `api-contract`.
144-
The root build handles this by design.
145-
* If you skip the root build and run the client directly, the build may fail
146-
because `api-contract` is not yet available.
147-
* For CI and local parity, all commands use `-ntp` (no transfer progress).
139+
* You do **not** need to manually install `api-contract` locally.
140+
* The shared contract is consumed as **`io.github.bsayli:api-contract:0.7.7`** from Maven Central.
141+
* Root-level builds are still useful for full repository verification, but they are no longer required just to make the contract resolvable.
142+
* For CI and local parity, commands use `-ntp` (no transfer progress).
148143

149144
---
150145

151146
> **Rule of thumb:**
152-
> - If you just cloned the repo → **build from root**
153-
> - If you changed the API contract → **regenerate the client**
147+
>
148+
> * If you want to run or build a module normally → **run it directly**
149+
> * If you changed the published API contract surface → **regenerate the client**
150+
> * If you want full repository verification → **build from root**
154151
155152
---
156153

@@ -187,8 +184,9 @@ This scales poorly and makes contract evolution painful.
187184

188185
---
189186

190-
> **Background:** The rationale behind the canonical `ServiceResponse<T>` contract is explained here (updated Jan 2026):
187+
> **Background:** The rationale behind the canonical `ServiceResponse<T>` contract is explained here (updated Jan 2026):
191188
> [We Made OpenAPI Generator Think in Generics](https://medium.com/@baris.sayli/type-safe-generic-api-responses-with-spring-boot-3-4-openapi-generator-and-custom-templates-ccd93405fb04)
189+
192190
---
193191

194192
## 💡 The Core Idea
@@ -217,28 +215,29 @@ ServiceResponse<T>
217215

218216
Provided by the shared module:
219217

220-
```
221-
io.github.bsayli:api-contract
218+
```text
219+
io.github.bsayli:api-contract:0.7.7
222220
```
223221

224222
### Supported Shapes
225223

226224
| Shape | Supported | Notes |
227225
| -------------------------- | --------- | --------------------------------------------------- |
228-
| `ServiceResponse<T>` || Canonical success envelope (guaranteed) |
229-
| `ServiceResponse<Page<T>>` || **Guaranteed nested generic** |
226+
| `ServiceResponse<T>` | | Canonical success envelope (guaranteed) |
227+
| `ServiceResponse<Page<T>>` | | **Guaranteed nested generic** |
230228
| `ServiceResponse<List<T>>` | ⚠️ | Uses OpenAPI Generator default behavior (unchanged) |
231-
| Arbitrary nested generics || Outside the supported contract |
229+
| Arbitrary nested generics | | Outside the supported contract |
232230

233231
This implementation **does not alter or restrict** OpenAPI Generator’s default handling
234232
of standard collection types such as `List<T>`.
235233

236-
It **defines explicit guarantees only** for:
237-
- `ServiceResponse<T>`
238-
- `ServiceResponse<Page<T>>`
234+
It defines explicit guarantees only for:
235+
236+
* `ServiceResponse<T>`
237+
* `ServiceResponse<Page<T>>`
239238

240239
All other shapes follow the generator’s default behavior and are intentionally kept
241-
**outside the canonical contract** to preserve deterministic schemas and
240+
outside the canonical contract to preserve deterministic schemas and
242241
generator-safe evolution.
243242

244243
---
@@ -292,8 +291,9 @@ generator-safe evolution.
292291

293292
> **Key principle**
294293
>
295-
> The OpenAPI specification is the *single source of truth*.
296-
> Generated code is disposable; contracts are not.
294+
> The shared contract library defines the runtime source of truth.
295+
> The OpenAPI specification provides a semantic contract projection used for client generation.
296+
> Generated sources are treated as build artifacts aligned with the published contract.
297297
298298
---
299299

@@ -352,17 +352,21 @@ No duplicated envelope. No lost generics.
352352

353353
## 🧠 Design Guarantees
354354

355-
This repository guarantees:
355+
This repository focuses on a **contract-driven approach** for building
356+
type-safe API boundaries across server and client.
357+
358+
It provides:
356359

357-
* **One shared response contract** across server and client
358-
* **No duplicated envelopes** in generated clients
359-
* **Page-only nested generics** (`ServiceResponse<Page<T>>`)
360-
* **Deterministic schema names** for the guaranteed shapes
361-
* **RFC 9457-first error handling** (Problem Details)
362-
* **Generator-safe evolution** through minimal, explicit template overlays
360+
* A **single shared response contract** reused on both producer and consumer sides
361+
* **No envelope duplication** in generated client models
362+
* Explicit support for **nested pagination generics** (`ServiceResponse<Page<T>>`)
363+
* **Deterministic and stable schema naming** for supported response shapes
364+
* **RFC 9457-compliant error handling** based on Problem Details
365+
* **Generator-safe evolution** via minimal and explicit template overlays
363366

364-
This is not a demo.
365-
It is a **reference implementation**.
367+
The implementation demonstrates a practical setup where
368+
OpenAPI acts as a **semantic contract bridge**, while shared models
369+
remain the primary source of runtime truth.
366370

367371
---
368372

@@ -390,7 +394,9 @@ Step-by-step integration guides live under [`docs/adoption`](docs/adoption):
390394

391395
## 🤝 Contributing & Feedback
392396

393-
This repository is a **reference implementation**, not a closed framework.
397+
This repository presents a contract-driven reference setup
398+
for building type-safe API boundaries with shared response models
399+
and generics-aware client generation.
394400

395401
If you:
396402

customer-service-client/README.md

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,11 @@ It exists solely to adapt the generated OpenAPI client into a clean, Spring-frie
4949

5050
## 🔧 TL;DR — Generate in 1 Minute
5151

52-
> ℹ️ **First‑time setup (important)**
53-
> This repository is a **multi‑module build** and includes a shared **`api-contract`** module.
54-
> If you just cloned the repo, install all modules once from the **repository root**:
52+
> ℹ️ **Dependency note**
53+
> The shared `api-contract` library is published to Maven Central (`io.github.bsayli:api-contract:0.7.7`).
54+
> Client generation and compilation therefore do not require a root-level bootstrap build.
55+
> You can work with the modules independently, as in a typical multi-repository setup.
5556
>
56-
> ```bash
57-
> mvn -q clean install
58-
> ```
59-
>
60-
> After this initial step, you can work with `customer-service` and
61-
> `customer-service-client` independently.
6257
6358
---
6459

customer-service-client/pom.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>io.github.bsayli</groupId>
88
<artifactId>customer-service-client</artifactId>
9-
<version>0.7.7</version>
9+
<version>0.7.8</version>
1010
<name>customer-service-client</name>
1111
<description>Generated client (RestClient) using generics-aware OpenAPI templates</description>
1212
<packaging>jar</packaging>
@@ -261,9 +261,9 @@
261261
<output>${project.build.directory}/generated-sources/openapi</output>
262262

263263
<!-- Packages -->
264-
<apiPackage>io.github.bsayli.openapi.client.generated.api</apiPackage>
265-
<modelPackage>io.github.bsayli.openapi.client.generated.dto</modelPackage>
266-
<invokerPackage>io.github.bsayli.openapi.client.generated.invoker</invokerPackage>
264+
<apiPackage>io.github.bsayli.customerservice.client.generated.api</apiPackage>
265+
<modelPackage>io.github.bsayli.customerservice.client.generated.dto</modelPackage>
266+
<invokerPackage>io.github.bsayli.customerservice.client.generated.invoker</invokerPackage>
267267

268268
<!-- Templates -->
269269
<templateDirectory>${openapi.templates.effective}/Java</templateDirectory>

0 commit comments

Comments
 (0)