in geronimo-openapi-impl/src/main/java/org/apache/geronimo/microprofile/openapi/impl/processor/AnnotationProcessor.java [246:449]
private Operation buildOperation(final OpenAPI api, final AnnotatedMethodElement m, final AnnotatedElement declaring,
final String httpVerb, final String path) {
final Optional<org.eclipse.microprofile.openapi.annotations.Operation> opOpt = ofNullable(m.getAnnotation(org.eclipse.microprofile.openapi.annotations.Operation.class));
if (opOpt.map(org.eclipse.microprofile.openapi.annotations.Operation::hidden).orElse(false)) {
return null;
}
final Optional<List<String>> produces = findProduces(m);
final OperationImpl operation = new OperationImpl();
if (opOpt.isPresent()) {
final org.eclipse.microprofile.openapi.annotations.Operation op = opOpt.get();
if (!op.operationId().isEmpty()) {
operation.operationId(op.operationId());
} else {
operation.operationId(createOperationId(m, httpVerb, path));
}
if (!op.summary().isEmpty()) {
operation.summary(op.summary());
}
operation.deprecated(op.deprecated());
if (!op.description().isEmpty()) {
operation.description(op.description());
}
} else {
operation.operationId(createOperationId(m, httpVerb, path));
}
final Optional<String> servers = ofNullable(config.read(OASConfig.SERVERS_OPERATION_PREFIX + operation.getOperationId(), null));
if (servers.isPresent()) {
servers.ifPresent(s -> operation.servers(mapConfiguredServers(s)));
} else {
ofNullable(ofNullable(findServers(m)).orElseGet(() -> findServers(declaring)))
.ifPresent(it -> operation.servers(Stream.of(it).map(this::mapServer).collect(toList())));
}
of(m.getAnnotationsByType(Callback.class)).filter(it -> it.length > 0).ifPresent(cbs -> {
final Map<String, org.eclipse.microprofile.openapi.models.callbacks.Callback> callbacks = Stream.of(cbs)
.collect(toMap(it -> of(it.name()).filter(n -> !n.isEmpty()).orElseGet(() -> it.ref()),
it -> mapCallback(api, it)));
operation.callbacks(callbacks);
});
ofNullable(m.getAnnotation(SecurityScheme.class))
.ifPresent(s -> {
org.eclipse.microprofile.openapi.models.Components components = api.getComponents();
if (components == null) {
components = new ComponentsImpl();
api.setComponents(components);
}
components.addSecurityScheme(s.ref(), mapSecurityScheme(s));
});
ofNullable(m.getAnnotationsByType(Extension.class))
.map(this::mapExtensions)
.ifPresent(exts -> exts.forEach(operation::addExtension));
operation.security(of(Stream.concat(
Stream.of(m.getAnnotationsByType(SecurityRequirement.class)),
Stream.of(m.getDeclaringClass().getAnnotationsByType(SecurityRequirement.class)))
.map(this::mapSecurity).collect(toList()))
.filter(s -> !s.isEmpty())
.orElse(null));
ofNullable(findTags(m, declaring))
.map(tags -> Stream.of(tags)
.map(it -> of(it.name())
.filter(v -> !v.isEmpty())
.map(tag -> {
api.addTag(mapTag(it));
return tag;
}).filter(v -> !v.isEmpty())
.orElseGet(() -> {
final String ref = it.ref();
return Stream.of(declaring.getAnnotationsByType(Tag.class))
.filter(t -> t.name().equals(ref))
.findFirst()
.map(Tag::name)
.filter(v -> !v.isEmpty())
.orElseGet(() -> api.getTags().stream()
.filter(t -> t.getName().equals(ref))
.findFirst()
.map(org.eclipse.microprofile.openapi.models.tags.Tag::getName)
.orElse(ref));
}))
.distinct()
.filter(v -> !v.isEmpty())
.collect(toList()))
.ifPresent(operation::tags);
of(m.getAnnotationsByType(APIResponse.class)).filter(s -> s.length > 0).ifPresent(items -> {
final APIResponses responses = new APIResponsesImpl();
responses.putAll(Stream.of(items).collect(toMap(it -> of(it.responseCode()).filter(c -> !c.isEmpty()).orElse("200"),
it -> mapResponse(() -> getOrCreateComponents(api), it, produces.orElse(null)), (a, b) -> b)));
responses.values().stream()
.filter(it -> it.getContent() == null || it.getContent().isEmpty() ||
it.getContent().values().stream().anyMatch(c -> c.getSchema() == null))
.forEach(v -> {
Type returnType = m.getReturnType();
if (returnType == void.class || returnType == Response.class) {
if (v.getContent() == null || v.getContent().isEmpty()) {
final ContentImpl content = new ContentImpl();
produces
.orElseGet(() -> singletonList("*/*"))
.forEach(mt -> content.addMediaType(mt, new MediaTypeImpl()));
v.content(content);
}
return;
}
if (ParameterizedType.class.isInstance(returnType)) {
final ParameterizedType pt = ParameterizedType.class.cast(returnType);
if (pt.getActualTypeArguments().length > 0) {
if (pt.getRawType() == CompletionStage.class) {
returnType = pt.getActualTypeArguments()[0];
}
}
}
final org.eclipse.microprofile.openapi.models.media.Schema schema =
schemaProcessor.mapSchemaFromClass(
() -> getOrCreateComponents(api), returnType);
if (v.getContent() == null || v.getContent().isEmpty()) {
final ContentImpl content = new ContentImpl();
final MediaTypeImpl mediaType = new MediaTypeImpl();
mediaType.setSchema(schema);
content.addMediaType("", mediaType);
v.content(content);
} else {
v.getContent().values().stream()
.filter(it -> it.getSchema() == null)
.forEach(it -> it.schema(schema));
}
});
responses.values().stream().filter(r -> r.getContent() != null)
.forEach(resp -> ofNullable(resp.getContent().remove(""))
.ifPresent(updated -> produces.ifPresent(mt -> mt.forEach(type -> resp.getContent().addMediaType(type, updated)))));
operation.responses(responses);
});
operation.parameters(Stream.of(m.getParameters())
.filter(it -> it.isAnnotationPresent(Parameter.class) || hasJaxRsParams(it))
.flatMap(it -> buildParameter(it, api)
.orElseGet(() -> {
final ParameterImpl parameter = new ParameterImpl();
parameter.schema(schemaProcessor.mapSchemaFromClass(
() -> getOrCreateComponents(api), it.getType()));
return Stream.of(parameter);
}))
.filter(Objects::nonNull).collect(toList()));
Stream.of(m.getParameters())
.filter(it -> it.isAnnotationPresent(Parameters.class))
.map(it -> it.getAnnotation(Parameters.class).value())
.forEach(params -> operation.parameters(mapParameters(api, params)));
Stream.of(m.getParameters())
.filter(p -> p.isAnnotationPresent(RequestBody.class) ||
(!p.isAnnotationPresent(Suspended.class) && !p.isAnnotationPresent(Context.class) &&
!p.isAnnotationPresent(Parameter.class) && !hasJaxRsParams(p)))
.findFirst()
.ifPresent(p -> operation.requestBody(mapRequestBody(
produces.filter(it -> !it.isEmpty()).map(it -> it.iterator().next()).orElse(null), p,
() -> getOrCreateComponents(api), ofNullable(p.getAnnotation(RequestBody.class))
.orElseGet(() -> m.getAnnotation(RequestBody.class)))));
// ensure parameters have all schemas
operation.getParameters().stream()
.filter(it -> it.getSchema() == null)
.forEach(it -> Stream.of(m.getParameters())
.filter(mp -> findAnnotatedParameterByName(it, mp))
.findFirst()
.ifPresent(mp -> it.setSchema(schemaProcessor.mapSchemaFromClass(() -> getOrCreateComponents(api), mp.getType()))));
// ensure parameter contents have all schemas
operation.getParameters().stream()
.filter(it -> it.getContent() != null && it.getContent().values().stream().anyMatch(c -> c.getSchema() == null || c.getSchema().getType() == null))
.forEach(it -> Stream.of(m.getParameters())
.filter(mp -> findAnnotatedParameterByName(it, mp))
.findFirst()
.ifPresent(mp -> it.getContent().values().stream().filter(c -> c.getSchema() == null || c.getSchema().getType() == null).forEach(mt -> {
final org.eclipse.microprofile.openapi.models.media.Schema schema = schemaProcessor.mapSchemaFromClass(() -> getOrCreateComponents(api), mp.getType());
if (mt.getSchema() == null) {
mt.setSchema(schema);
} else {
mt.getSchema().type(schema.getType());
}
})));
if (operation.getResponses() == null) {
final APIResponsesImpl responses = new APIResponsesImpl();
operation.responses(responses);
final boolean normalResponse = Stream.of(m.getParameters()).noneMatch(it -> it.isAnnotationPresent(Suspended.class));
final ContentImpl content = new ContentImpl();
if (normalResponse) {
final MediaType impl = new MediaTypeImpl();
impl.setSchema(schemaProcessor.mapSchemaFromClass(() -> getOrCreateComponents(api), m.getReturnType()));
produces.orElseGet(() -> singletonList("*/*")).forEach(key -> content.addMediaType(key, impl));
}
final org.eclipse.microprofile.openapi.models.responses.APIResponse defaultResponse =
new APIResponseImpl().description("default response").content(content);
responses.addApiResponse(
m.getReturnType() == void.class || m.getReturnType() == Void.class && normalResponse ?
"204" : "200", defaultResponse);
responses.defaultValue(defaultResponse);
}
return operation;
}