Skip to content

Commit 10c65f4

Browse files
committed
feat: integrate openapi-generator-maven-plugin
1 parent a43da2f commit 10c65f4

File tree

11 files changed

+186
-79
lines changed

11 files changed

+186
-79
lines changed

acceptance-test/src/test/java/packagename/cucumber/ExampleStepDef.java

+25-13
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
11
package packagename.cucumber;
22

3+
import static org.assertj.core.api.Assertions.assertThat;
4+
import static org.assertj.core.api.Assertions.within;
5+
36
import io.cucumber.datatable.DataTable;
47
import io.cucumber.java8.En;
58
import io.cucumber.java8.HookNoArgsBody;
9+
import java.time.LocalDateTime;
10+
import java.time.temporal.ChronoUnit;
11+
import java.util.List;
12+
import java.util.Map;
13+
import java.util.stream.Collectors;
614
import org.springframework.boot.test.web.client.TestRestTemplate;
715
import org.springframework.boot.test.web.server.LocalServerPort;
816
import org.springframework.http.HttpStatus;
917
import org.springframework.http.ResponseEntity;
1018
import packagename.domain.model.Example;
11-
import packagename.domain.model.ExampleInfo;
1219
import packagename.repository.dao.ExampleDao;
1320
import packagename.repository.entity.ExampleEntity;
14-
import packagename.rest.exception.ExampleExceptionResponse;
15-
16-
import java.util.List;
17-
import java.util.Map;
18-
import java.util.stream.Collectors;
19-
20-
import static org.assertj.core.api.Assertions.assertThat;
21+
import packagename.rest.generated.model.ExampleInfo;
22+
import packagename.rest.generated.model.ProblemDetail;
2123

2224
public class ExampleStepDef implements En {
2325

@@ -70,17 +72,27 @@ public ExampleStepDef(TestRestTemplate restTemplate, ExampleDao exampleDao) {
7072
"user requests for examples by id {string} that does not exists",
7173
(String code) -> {
7274
String url = LOCALHOST + port + API_URI + "/" + code;
73-
responseEntity = restTemplate.getForEntity(url, ExampleExceptionResponse.class);
75+
responseEntity = restTemplate.getForEntity(url, ProblemDetail.class);
7476
});
7577

7678
Then(
7779
"the user gets an exception {string}",
7880
(String exception) -> {
7981
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
80-
Object body = responseEntity.getBody();
81-
assertThat(body).isNotNull();
82-
assertThat(body).isInstanceOf(ExampleExceptionResponse.class);
83-
assertThat(((ExampleExceptionResponse) body).getMessage()).isEqualTo(exception);
82+
var actualResponse = (ProblemDetail) responseEntity.getBody();
83+
var expectedProblemDetail = ProblemDetail.builder()
84+
.type("https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404")
85+
.status(HttpStatus.NOT_FOUND.value())
86+
.detail("Example with code 10000 does not exist")
87+
.instance("/api/v1/examples/10000")
88+
.title("Example not found")
89+
.build();
90+
assertThat(actualResponse).isNotNull();
91+
assertThat(actualResponse).usingRecursiveComparison()
92+
.ignoringFields("timestamp")
93+
.isEqualTo(expectedProblemDetail);
94+
assertThat(actualResponse.getTimestamp()).isCloseTo(
95+
LocalDateTime.now(), within(100L, ChronoUnit.SECONDS));
8496
});
8597

8698
Then(

domain-api/src/main/java/packagename/domain/model/ExampleInfo.java

-16
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
package packagename.domain.port;
22

3+
import java.util.List;
34
import lombok.NonNull;
45
import packagename.domain.model.Example;
5-
import packagename.domain.model.ExampleInfo;
66

77
public interface RequestExample {
88

9-
ExampleInfo getExamples();
9+
List<Example> getExamples();
1010

1111
Example getExampleByCode(@NonNull Long code);
1212
}

domain/src/main/java/packagename/domain/ExampleDomain.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package packagename.domain;
22

3+
import lombok.NonNull;
34
import packagename.domain.exception.ExampleNotFoundException;
45
import packagename.domain.model.Example;
5-
import packagename.domain.model.ExampleInfo;
66
import packagename.domain.port.ObtainExample;
77
import packagename.domain.port.RequestExample;
88

9+
import java.util.List;
10+
911
public class ExampleDomain implements RequestExample {
1012

1113
private final ObtainExample obtainExample;
@@ -19,12 +21,12 @@ public ExampleDomain(ObtainExample obtainExample) {
1921
}
2022

2123
@Override
22-
public ExampleInfo getExamples() {
23-
return ExampleInfo.builder().examples(obtainExample.getAllExamples()).build();
24+
public List<Example> getExamples() {
25+
return obtainExample.getAllExamples();
2426
}
2527

2628
@Override
27-
public Example getExampleByCode(Long code) {
29+
public Example getExampleByCode(@NonNull Long code) {
2830
var example = obtainExample.getExampleByCode(code);
2931
return example.orElseThrow(() -> new ExampleNotFoundException(code));
3032
}

domain/src/test/java/packagename/AcceptanceTest.java

+6-8
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,9 @@ public void getExamplesFromHardCoded() {
2828
ObtainExample - right side port
2929
*/
3030
var requestExample = new ExampleDomain(); // the example is hard coded
31-
var exampleInfo = requestExample.getExamples();
32-
assertThat(exampleInfo).isNotNull();
33-
assertThat(exampleInfo.getExamples())
34-
.isNotEmpty()
31+
var examples = requestExample.getExamples();
32+
assertThat(examples)
33+
.hasSize(1)
3534
.extracting("description")
3635
.contains(
3736
"If you could read a leaf or tree\r\nyoud have no need of books.\r\n-- Alistair Cockburn (1987)");
@@ -50,10 +49,9 @@ public void getExamplesFromMockedStub(@Mock ObtainExample obtainExample) {
5049
Mockito.lenient().when(obtainExample.getAllExamples()).thenReturn(List.of(example));
5150
// hexagon
5251
var requestExample = new ExampleDomain(obtainExample);
53-
var exampleInfo = requestExample.getExamples();
54-
assertThat(exampleInfo).isNotNull();
55-
assertThat(exampleInfo.getExamples())
56-
.isNotEmpty()
52+
var examples = requestExample.getExamples();
53+
assertThat(examples)
54+
.hasSize(1)
5755
.extracting("description")
5856
.contains(
5957
"I want to sleep\r\nSwat the flies\r\nSoftly, please.\r\n\r\n-- Masaoka Shiki (1867-1902)");

rest-adapter/pom.xml

+36
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,42 @@
3434
</dependencies>
3535
<build>
3636
<plugins>
37+
<plugin>
38+
<groupId>org.openapitools</groupId>
39+
<artifactId>openapi-generator-maven-plugin</artifactId>
40+
<version>6.3.0</version>
41+
<executions>
42+
<execution>
43+
<id>generate-open-api-source</id>
44+
<goals>
45+
<goal>generate</goal>
46+
</goals>
47+
<configuration>
48+
<inputSpec>${project.basedir}/src/main/resources/open-api.yaml</inputSpec>
49+
<generatorName>spring</generatorName>
50+
<library>spring-boot</library>
51+
<schemaMappings>
52+
<schemaMapping>Example=packagename.domain.model.Example</schemaMapping>
53+
</schemaMappings>
54+
<typeMappings>
55+
<typeMapping>OffsetDateTime=java.time.LocalDateTime</typeMapping>
56+
</typeMappings>
57+
<apiPackage>packagename.rest.generated.api</apiPackage>
58+
<modelPackage>packagename.rest.generated.model</modelPackage>
59+
<configOptions>
60+
<performBeanValidation>false</performBeanValidation>
61+
<useSpringBoot3>true</useSpringBoot3>
62+
<interfaceOnly>true</interfaceOnly>
63+
<dateLibrary>java8</dateLibrary>
64+
<useTags>true</useTags>
65+
<openApiNullable>false</openApiNullable>
66+
<additionalModelTypeAnnotations>@lombok.AllArgsConstructor @lombok.Builder @lombok.Data @lombok.NoArgsConstructor
67+
</additionalModelTypeAnnotations>
68+
</configOptions>
69+
</configuration>
70+
</execution>
71+
</executions>
72+
</plugin>
3773
<plugin>
3874
<groupId>com.societegenerale.commons</groupId>
3975
<artifactId>arch-unit-maven-plugin</artifactId>

rest-adapter/src/main/java/packagename/rest/ExampleResource.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
import org.springframework.web.bind.annotation.RequestMapping;
77
import org.springframework.web.bind.annotation.RestController;
88
import packagename.domain.model.Example;
9-
import packagename.domain.model.ExampleInfo;
109
import packagename.domain.port.RequestExample;
10+
import packagename.rest.generated.api.ExampleApi;
11+
import packagename.rest.generated.model.ExampleInfo;
1112

1213
@RestController
1314
@RequestMapping("/api/v1/examples")
14-
public class ExampleResource {
15+
public class ExampleResource implements ExampleApi {
1516

1617
private final RequestExample requestExample;
1718

@@ -21,7 +22,7 @@ public ExampleResource(RequestExample requestExample) {
2122

2223
@GetMapping
2324
public ResponseEntity<ExampleInfo> getExamples() {
24-
return ResponseEntity.ok(requestExample.getExamples());
25+
return ResponseEntity.ok(ExampleInfo.builder().examples(requestExample.getExamples()).build());
2526
}
2627

2728
@GetMapping("/{code}")
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,30 @@
11
package packagename.rest.exception;
22

3+
import java.time.LocalDateTime;
34
import org.springframework.http.HttpStatus;
45
import org.springframework.http.ResponseEntity;
56
import org.springframework.web.bind.annotation.ExceptionHandler;
67
import org.springframework.web.bind.annotation.RestControllerAdvice;
78
import org.springframework.web.context.request.ServletWebRequest;
89
import org.springframework.web.context.request.WebRequest;
910
import packagename.domain.exception.ExampleNotFoundException;
11+
import packagename.rest.generated.model.ProblemDetail;
1012

1113
@RestControllerAdvice(basePackages = {"packagename"})
1214
public class ExampleExceptionHandler {
1315

1416
@ExceptionHandler(value = ExampleNotFoundException.class)
15-
public final ResponseEntity<ExampleExceptionResponse> handleExampleNotFoundException(
17+
public final ResponseEntity<ProblemDetail> handleExampleNotFoundException(
1618
final Exception exception, final WebRequest request) {
19+
var problem = ProblemDetail.builder()
20+
.type("https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404")
21+
.status(HttpStatus.NOT_FOUND.value())
22+
.title("Example not found")
23+
.detail(exception.getMessage())
24+
.instance(((ServletWebRequest) request).getRequest().getRequestURI())
25+
.timestamp(LocalDateTime.now())
26+
.build();
1727
return ResponseEntity.status(HttpStatus.NOT_FOUND)
18-
.body(
19-
ExampleExceptionResponse.builder()
20-
.message(exception.getMessage())
21-
.path(((ServletWebRequest) request).getRequest().getRequestURI())
22-
.build());
28+
.body(problem);
2329
}
2430
}

rest-adapter/src/main/java/packagename/rest/exception/ExampleExceptionResponse.java

-17
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
---
2+
openapi: 3.0.1
3+
info:
4+
title: Example API Documentation
5+
version: v1
6+
tags:
7+
- name: Example
8+
description: Resource to manage example
9+
paths:
10+
"/api/v1/examples":
11+
get:
12+
tags:
13+
- Example
14+
summary: Get all examples
15+
operationId: getExamples
16+
responses:
17+
'200':
18+
description: OK
19+
content:
20+
"*/*":
21+
schema:
22+
"$ref": "#/components/schemas/ExampleInfo"
23+
"/api/v1/examples/{code}":
24+
get:
25+
tags:
26+
- Example
27+
summary: Get example by code
28+
operationId: getExampleByCode
29+
parameters:
30+
- name: code
31+
in: path
32+
required: true
33+
schema:
34+
type: integer
35+
format: int64
36+
responses:
37+
'200':
38+
description: OK
39+
content:
40+
"*/*":
41+
schema:
42+
"$ref": "#/components/schemas/Example"
43+
components:
44+
schemas:
45+
Example:
46+
type: object
47+
properties:
48+
code:
49+
type: integer
50+
format: int64
51+
description:
52+
type: string
53+
ExampleInfo:
54+
type: object
55+
properties:
56+
examples:
57+
type: array
58+
items:
59+
"$ref": "#/components/schemas/Example"
60+
ProblemDetail:
61+
type: object
62+
properties:
63+
type:
64+
type: string
65+
title:
66+
type: string
67+
status:
68+
type: integer
69+
format: int32
70+
detail:
71+
type: string
72+
instance:
73+
type: string
74+
timestamp:
75+
type: string
76+
format: date-time

0 commit comments

Comments
 (0)