I am using openapi 3.0.3
spec with openapi-generator-maven-plugin
for Java code generation to build interfaces that are implemented in a spring-boot project.
/user/search:
get:
parameters:
- in: query
name: sortBy
description: Field to sort by
required: true
schema:
$ref: "#/components/schemas/UserSearchSortBy"
# omitting some code to shorted the copy-pasted spec
schemas:
UserSearchSortBy:
type: string
enum: [first_name, last_name, email, phone_number]
The above results in an interface that I can implement. The enum for UserSearchSortBy
is created fine.
@RequestMapping(
method = RequestMethod.GET,
value = "/user/search",
produces = { "application/json" }
)
default ResponseEntity<UsersResponsePageableModel> _searchUsers(
@NotNull @Min(1) @Parameter(name = "currentPage", description = "Page number", required = true) @Valid @RequestParam(value = "currentPage", required = true) Integer currentPage,
@NotNull @Min(1) @Max(100) @Parameter(name = "pageSize", description = "Number of records to show per page", required = true) @Valid @RequestParam(value = "pageSize", required = true) Integer pageSize,
@NotNull @Parameter(name = "sortOrder", description = "Sort order", required = true) @Valid @RequestParam(value = "sortOrder", required = true) SortOrderEnumModel sortOrder,
@NotNull @Parameter(name = "sortBy", description = "Field to sort by", required = true) @Valid @RequestParam(value = "sortBy", required = true) UserSearchSortByModel sortBy,
@NotNull @Size(max = 128) @Parameter(name = "searchQuery", description = "Search field", required = true) @Valid @RequestParam(value = "searchQuery", required = true) String searchQuery
) {
return searchUsers(currentPage, pageSize, sortOrder, sortBy, searchQuery);
}
I expect the values that are being submitted to an API to be validated against the UserSearchSortBy
enum. The issue is that there is no validation present. It looks like the generator is not generating a piece that is responsible for validating values against the enum. Any help is appreciated.
The following post helped me to create a converter factory.
@Component
public class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {
private static class StringToEnumConverter<T extends Enum> implements Converter<String, T> {
private Class<T> enumType;
public StringToEnumConverter(Class<T> enumType) {
this.enumType = enumType;
}
public T convert(String source) {
return (T) Enum.valueOf(this.enumType, source.trim().toUpperCase());
}
}
@Override
public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToEnumConverter(targetType);
}
}
And the following exception handler helps me craft a nice-looking error response.
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public ResponseEntity<?> handleMismatchException(MethodArgumentTypeMismatchException e) {
String message = e.getMessage();
Class<?> parameterType = e.getParameter().getParameterType();
if (parameterType.isEnum()) {
Object[] enumConstants = parameterType.getEnumConstants();
if (enumConstants != null && enumConstants.length > 0) {
if (e.getName() != null && !e.getName().isEmpty() && e.getValue() != null) {
message = String.format("Invalid value '%s' for field '%s'.", e.getValue(), e.getName()) + " Valid values are " + Arrays.asList(enumConstants);
}
}
}
Map<String, String> errors = new HashMap<>();
errors.put("message", message);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).header("Content-Type", MediaType.APPLICATION_JSON_VALUE).body(errors);
}