Skip to content

Commit dc5f022

Browse files
authored
Merge pull request #42480 from frne/smallrye-health/fix-openapi-schema
Fix SmallRye Health OpenAPI definitions
2 parents b624a88 + 6394b66 commit dc5f022

File tree

3 files changed

+136
-178
lines changed

3 files changed

+136
-178
lines changed
Lines changed: 115 additions & 176 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package io.quarkus.smallrye.health.deployment;
22

3-
import java.util.ArrayList;
43
import java.util.Collections;
5-
import java.util.HashMap;
64
import java.util.List;
75
import java.util.Map;
86

@@ -12,9 +10,7 @@
1210
import org.eclipse.microprofile.openapi.models.PathItem;
1311
import org.eclipse.microprofile.openapi.models.Paths;
1412
import org.eclipse.microprofile.openapi.models.media.Content;
15-
import org.eclipse.microprofile.openapi.models.media.MediaType;
1613
import org.eclipse.microprofile.openapi.models.media.Schema;
17-
import org.eclipse.microprofile.openapi.models.responses.APIResponse;
1814
import org.eclipse.microprofile.openapi.models.responses.APIResponses;
1915

2016
import io.smallrye.openapi.api.models.ComponentsImpl;
@@ -31,9 +27,42 @@
3127
* Create OpenAPI entries (if configured)
3228
*/
3329
public class HealthOpenAPIFilter implements OASFilter {
30+
3431
private static final List<String> MICROPROFILE_HEALTH_TAG = Collections.singletonList("MicroProfile Health");
35-
private static final String SCHEMA_HEALTH_RESPONSE = "HealthCheckResponse";
36-
private static final String SCHEMA_HEALTH_STATUS = "HealthCheckStatus";
32+
private static final String HEALTH_RESPONSE_SCHEMA_NAME = "HealthResponse";
33+
private static final String HEALTH_CHECK_SCHEMA_NAME = "HealthCheck";
34+
35+
private static final Schema healthResponseSchemaDefinition = new SchemaImpl(HEALTH_RESPONSE_SCHEMA_NAME)
36+
.type(Schema.SchemaType.OBJECT)
37+
.properties(Map.ofEntries(
38+
39+
Map.entry("status",
40+
new SchemaImpl()
41+
.type(Schema.SchemaType.STRING)
42+
.enumeration(List.of("UP", "DOWN"))),
43+
44+
Map.entry("checks",
45+
new SchemaImpl()
46+
.type(Schema.SchemaType.ARRAY)
47+
.items(new SchemaImpl().ref("#/components/schemas/" + HEALTH_CHECK_SCHEMA_NAME)))));
48+
49+
private static final Schema healthCheckSchemaDefinition = new SchemaImpl(HEALTH_CHECK_SCHEMA_NAME)
50+
.type(Schema.SchemaType.OBJECT)
51+
.properties(Map.ofEntries(
52+
53+
Map.entry("name",
54+
new SchemaImpl()
55+
.type(Schema.SchemaType.STRING)),
56+
57+
Map.entry("status",
58+
new SchemaImpl()
59+
.type(Schema.SchemaType.STRING)
60+
.enumeration(List.of("UP", "DOWN"))),
61+
62+
Map.entry("data",
63+
new SchemaImpl()
64+
.type(Schema.SchemaType.OBJECT)
65+
.nullable(Boolean.TRUE))));
3766

3867
private final String rootPath;
3968
private final String livenessPath;
@@ -52,192 +81,102 @@ public void filterOpenAPI(OpenAPI openAPI) {
5281
if (openAPI.getComponents() == null) {
5382
openAPI.setComponents(new ComponentsImpl());
5483
}
55-
openAPI.getComponents().addSchema(SCHEMA_HEALTH_RESPONSE, createHealthCheckResponse());
56-
openAPI.getComponents().addSchema(SCHEMA_HEALTH_STATUS, createHealthCheckStatus());
84+
openAPI.getComponents().addSchema(HEALTH_RESPONSE_SCHEMA_NAME, healthResponseSchemaDefinition);
85+
openAPI.getComponents().addSchema(HEALTH_CHECK_SCHEMA_NAME, healthCheckSchemaDefinition);
5786

5887
if (openAPI.getPaths() == null) {
5988
openAPI.setPaths(new PathsImpl());
6089
}
61-
Paths paths = openAPI.getPaths();
90+
91+
final Paths paths = openAPI.getPaths();
6292

6393
// Health
64-
paths.addPathItem(rootPath, createHealthPathItem());
94+
paths.addPathItem(
95+
rootPath,
96+
createHealthEndpoint(
97+
"MicroProfile Health Endpoint",
98+
"MicroProfile Health provides a way for your application to distribute " +
99+
"information about its healthiness state to state whether or not it is able to " +
100+
"function properly",
101+
"Check the health of the application",
102+
"microprofile_health_root",
103+
"An aggregated view of the Liveness, Readiness and Startup of this application"));
65104

66105
// Liveness
67-
paths.addPathItem(livenessPath, createLivenessPathItem());
106+
paths.addPathItem(
107+
livenessPath,
108+
createHealthEndpoint(
109+
"MicroProfile Health - Liveness Endpoint",
110+
"Liveness checks are utilized to tell whether the application should be " +
111+
"restarted",
112+
"Check the liveness of the application",
113+
"microprofile_health_liveness",
114+
"The Liveness check of this application"));
68115

69116
// Readiness
70-
paths.addPathItem(readinessPath, createReadinessPathItem());
117+
paths.addPathItem(
118+
readinessPath,
119+
createHealthEndpoint(
120+
"MicroProfile Health - Readiness Endpoint",
121+
"Readiness checks are used to tell whether the application is able to " +
122+
"process requests",
123+
"Check the readiness of the application",
124+
"microprofile_health_readiness",
125+
"The Readiness check of this application"));
71126

72127
// Startup
73-
paths.addPathItem(startupPath, createStartupPathItem());
74-
}
75-
76-
private PathItem createHealthPathItem() {
77-
PathItem pathItem = new PathItemImpl();
78-
pathItem.setDescription("MicroProfile Health Endpoint");
79-
pathItem.setSummary(
80-
"MicroProfile Health provides a way for your application to distribute information about its healthiness state to state whether or not it is able to function properly");
81-
pathItem.setGET(createHealthOperation());
82-
return pathItem;
83-
}
84-
85-
private PathItem createLivenessPathItem() {
86-
PathItem pathItem = new PathItemImpl();
87-
pathItem.setDescription("MicroProfile Health - Liveness Endpoint");
88-
pathItem.setSummary(
89-
"Liveness checks are utilized to tell whether the application should be restarted");
90-
pathItem.setGET(createLivenessOperation());
91-
return pathItem;
92-
}
93-
94-
private PathItem createReadinessPathItem() {
95-
PathItem pathItem = new PathItemImpl();
96-
pathItem.setDescription("MicroProfile Health - Readiness Endpoint");
97-
pathItem.setSummary(
98-
"Readiness checks are used to tell whether the application is able to process requests");
99-
pathItem.setGET(createReadinessOperation());
100-
return pathItem;
101-
}
102-
103-
private PathItem createStartupPathItem() {
104-
PathItem pathItem = new PathItemImpl();
105-
pathItem.setDescription("MicroProfile Health - Startup Endpoint");
106-
pathItem.setSummary(
107-
"Startup checks are an used to tell when the application has started");
108-
pathItem.setGET(createStartupOperation());
109-
return pathItem;
110-
}
111-
112-
private Operation createHealthOperation() {
113-
Operation operation = new OperationImpl();
114-
operation.setDescription("Check the health of the application");
115-
operation.setOperationId("microprofile_health_root");
116-
operation.setTags(MICROPROFILE_HEALTH_TAG);
117-
operation.setSummary("An aggregated view of the Liveness, Readiness and Startup of this application");
118-
operation.setResponses(createAPIResponses());
119-
return operation;
120-
}
121-
122-
private Operation createLivenessOperation() {
123-
Operation operation = new OperationImpl();
124-
operation.setDescription("Check the liveness of the application");
125-
operation.setOperationId("microprofile_health_liveness");
126-
operation.setTags(MICROPROFILE_HEALTH_TAG);
127-
operation.setSummary("The Liveness check of this application");
128-
operation.setResponses(createAPIResponses());
129-
return operation;
130-
}
131-
132-
private Operation createReadinessOperation() {
133-
Operation operation = new OperationImpl();
134-
operation.setDescription("Check the readiness of the application");
135-
operation.setOperationId("microprofile_health_readiness");
136-
operation.setTags(MICROPROFILE_HEALTH_TAG);
137-
operation.setSummary("The Readiness check of this application");
138-
operation.setResponses(createAPIResponses());
139-
return operation;
140-
}
141-
142-
private Operation createStartupOperation() {
143-
Operation operation = new OperationImpl();
144-
operation.setDescription("Check the startup of the application");
145-
operation.setOperationId("microprofile_health_startup");
146-
operation.setTags(MICROPROFILE_HEALTH_TAG);
147-
operation.setSummary("The Startup check of this application");
148-
operation.setResponses(createAPIResponses());
149-
return operation;
150-
}
151-
152-
private APIResponses createAPIResponses() {
153-
APIResponses responses = new APIResponsesImpl();
154-
responses.addAPIResponse("200", createAPIResponse("OK"));
155-
responses.addAPIResponse("503", createAPIResponse("Service Unavailable"));
156-
responses.addAPIResponse("500", createAPIResponse("Internal Server Error"));
157-
return responses;
158-
}
159-
160-
private APIResponse createAPIResponse(String description) {
161-
APIResponse response = new APIResponseImpl();
162-
response.setDescription(description);
163-
response.setContent(createContent());
164-
return response;
165-
}
166-
167-
private Content createContent() {
168-
Content content = new ContentImpl();
169-
content.addMediaType("application/json", createMediaType());
170-
return content;
171-
}
172-
173-
private MediaType createMediaType() {
174-
MediaType mediaType = new MediaTypeImpl();
175-
mediaType.setSchema(new SchemaImpl().ref("#/components/schemas/" + SCHEMA_HEALTH_RESPONSE));
176-
return mediaType;
128+
paths.addPathItem(
129+
startupPath,
130+
createHealthEndpoint(
131+
"MicroProfile Health - Startup Endpoint",
132+
"Startup checks are an used to tell when the application has started",
133+
"Check the startup of the application",
134+
"microprofile_health_startup",
135+
"The Startup check of this application"));
177136
}
178137

179138
/**
180-
* HealthCheckResponse:
181-
* type: object
182-
* properties:
183-
* data:
184-
* type: object
185-
* nullable: true
186-
* name:
187-
* type: string
188-
* status:
189-
* $ref: '#/components/schemas/HealthCheckStatus'
139+
* Creates a {@link PathItem} containing the endpoint definition and GET {@link Operation} for health endpoints.
190140
*
191-
* @return Schema representing HealthCheckResponse
141+
* @param endpointDescription The description for the endpoint definition
142+
* @param endpointSummary The summary for the endpoint definition
143+
* @param operationDescription The description for the operation definition
144+
* @param operationId The operation-id for the operation definition
145+
* @param operationSummary The summary for the operation definition
192146
*/
193-
private Schema createHealthCheckResponse() {
194-
Schema schema = new SchemaImpl(SCHEMA_HEALTH_RESPONSE);
195-
schema.setType(Schema.SchemaType.OBJECT);
196-
schema.setProperties(createProperties());
197-
return schema;
198-
}
199-
200-
private Map<String, Schema> createProperties() {
201-
Map<String, Schema> map = new HashMap<>();
202-
map.put("data", createData());
203-
map.put("name", createName());
204-
map.put("status", new SchemaImpl().ref("#/components/schemas/" + SCHEMA_HEALTH_STATUS));
205-
return map;
206-
}
207-
208-
private Schema createData() {
209-
Schema schema = new SchemaImpl("data");
210-
schema.setType(Schema.SchemaType.OBJECT);
211-
schema.setNullable(Boolean.TRUE);
212-
return schema;
213-
}
214-
215-
private Schema createName() {
216-
Schema schema = new SchemaImpl("name");
217-
schema.setType(Schema.SchemaType.STRING);
218-
return schema;
219-
}
220-
221-
/**
222-
* HealthCheckStatus:
223-
* enum:
224-
* - DOWN
225-
* - UP
226-
* type: string
227-
*
228-
* @return Schema representing Status
229-
*/
230-
private Schema createHealthCheckStatus() {
231-
Schema schema = new SchemaImpl(SCHEMA_HEALTH_STATUS);
232-
schema.setEnumeration(createStateEnumValues());
233-
schema.setType(Schema.SchemaType.STRING);
234-
return schema;
235-
}
236-
237-
private List<Object> createStateEnumValues() {
238-
List<Object> values = new ArrayList<>();
239-
values.add("DOWN");
240-
values.add("UP");
241-
return values;
147+
private PathItem createHealthEndpoint(
148+
String endpointDescription,
149+
String endpointSummary,
150+
String operationDescription,
151+
String operationId,
152+
String operationSummary) {
153+
final Content content = new ContentImpl()
154+
.addMediaType(
155+
"application/json",
156+
new MediaTypeImpl()
157+
.schema(new SchemaImpl().ref("#/components/schemas/" + HEALTH_RESPONSE_SCHEMA_NAME)));
158+
159+
final APIResponses responses = new APIResponsesImpl()
160+
.addAPIResponse(
161+
"200",
162+
new APIResponseImpl().description("OK").content(content))
163+
.addAPIResponse(
164+
"503",
165+
new APIResponseImpl().description("Service Unavailable").content(content))
166+
.addAPIResponse(
167+
"500",
168+
new APIResponseImpl().description("Internal Server Error").content(content));
169+
170+
final Operation getOperation = new OperationImpl()
171+
.operationId(operationId)
172+
.description(operationDescription)
173+
.tags(MICROPROFILE_HEALTH_TAG)
174+
.summary(operationSummary)
175+
.responses(responses);
176+
177+
return new PathItemImpl()
178+
.description(endpointDescription)
179+
.summary(endpointSummary)
180+
.GET(getOperation);
242181
}
243182
}

extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/DeprecatedHealthOpenAPITest.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,21 @@ void testOpenApiPathAccessResource() {
2929
.when().get(OPEN_API_PATH)
3030
.then()
3131
.header("Content-Type", "application/json;charset=UTF-8")
32+
3233
.body("paths", Matchers.hasKey("/q/health/ready"))
3334
.body("paths", Matchers.hasKey("/q/health/live"))
3435
.body("paths", Matchers.hasKey("/q/health/started"))
3536
.body("paths", Matchers.hasKey("/q/health"))
36-
.body("components.schemas.HealthCheckResponse.type", Matchers.equalTo("object"));
37+
38+
.body("components.schemas.HealthResponse.type", Matchers.equalTo("object"))
39+
.body("components.schemas.HealthResponse.properties.status.type", Matchers.equalTo("string"))
40+
.body("components.schemas.HealthResponse.properties.checks.type", Matchers.equalTo("array"))
41+
42+
.body("components.schemas.HealthCheck.type", Matchers.equalTo("object"))
43+
.body("components.schemas.HealthCheck.properties.status.type", Matchers.equalTo("string"))
44+
.body("components.schemas.HealthCheck.properties.name.type", Matchers.equalTo("string"))
45+
.body("components.schemas.HealthCheck.properties.data.type", Matchers.equalTo("object"))
46+
.body("components.schemas.HealthCheck.properties.data.nullable", Matchers.is(true));
3747

3848
}
3949

extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/HealthOpenAPITest.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,16 @@ void testOpenApiPathAccessResource() {
3232
.body("paths", Matchers.hasKey("/q/health/live"))
3333
.body("paths", Matchers.hasKey("/q/health/started"))
3434
.body("paths", Matchers.hasKey("/q/health"))
35-
.body("components.schemas.HealthCheckResponse.type", Matchers.equalTo("object"));
35+
36+
.body("components.schemas.HealthResponse.type", Matchers.equalTo("object"))
37+
.body("components.schemas.HealthResponse.properties.status.type", Matchers.equalTo("string"))
38+
.body("components.schemas.HealthResponse.properties.checks.type", Matchers.equalTo("array"))
39+
40+
.body("components.schemas.HealthCheck.type", Matchers.equalTo("object"))
41+
.body("components.schemas.HealthCheck.properties.status.type", Matchers.equalTo("string"))
42+
.body("components.schemas.HealthCheck.properties.name.type", Matchers.equalTo("string"))
43+
.body("components.schemas.HealthCheck.properties.data.type", Matchers.equalTo("object"))
44+
.body("components.schemas.HealthCheck.properties.data.nullable", Matchers.is(true));
3645

3746
}
3847

0 commit comments

Comments
 (0)