use of org.wildfly.swarm.microprofile.openapi.runtime.util.JandexUtil.JaxRsParameterInfo in project wildfly-swarm by wildfly-swarm.
the class OpenApiAnnotationScanner method processJaxRsMethod.
/**
* Process a single JAX-RS method to produce an OpenAPI Operation.
* @param openApi
* @param resource
* @param method
* @param methodAnno
* @param methodType
* @param resourceTags
*/
private void processJaxRsMethod(OpenAPIImpl openApi, ClassInfo resource, MethodInfo method, AnnotationInstance methodAnno, HttpMethod methodType, Set<String> resourceTags) {
LOG.debugf("Processing jax-rs method: {0}", method.toString());
// Figure out the path for the operation. This is a combination of the App, Resource, and Method @Path annotations
String path;
if (method.hasAnnotation(OpenApiConstants.DOTNAME_PATH)) {
AnnotationInstance pathAnno = method.annotation(OpenApiConstants.DOTNAME_PATH);
String methodPath = pathAnno.value().asString();
path = makePath(this.currentAppPath, this.currentResourcePath, methodPath);
} else {
path = makePath(this.currentAppPath, this.currentResourcePath);
}
// Get or create a PathItem to hold the operation
PathItem pathItem = ModelUtil.paths(openApi).get(path);
if (pathItem == null) {
pathItem = new PathItemImpl();
ModelUtil.paths(openApi).addPathItem(path, pathItem);
}
// Figure out the current @Produces and @Consumes (if any)
currentConsumes = null;
currentProduces = null;
AnnotationInstance consumesAnno = method.annotation(OpenApiConstants.DOTNAME_CONSUMES);
if (consumesAnno == null) {
consumesAnno = JandexUtil.getClassAnnotation(method.declaringClass(), OpenApiConstants.DOTNAME_CONSUMES);
}
AnnotationInstance producesAnno = method.annotation(OpenApiConstants.DOTNAME_PRODUCES);
if (producesAnno == null) {
producesAnno = JandexUtil.getClassAnnotation(method.declaringClass(), OpenApiConstants.DOTNAME_PRODUCES);
}
if (consumesAnno != null) {
AnnotationValue annotationValue = consumesAnno.value();
if (annotationValue != null) {
currentConsumes = annotationValue.asStringArray();
} else {
currentConsumes = OpenApiConstants.DEFAULT_CONSUMES;
}
}
if (producesAnno != null) {
AnnotationValue annotationValue = producesAnno.value();
if (annotationValue != null) {
currentProduces = annotationValue.asStringArray();
} else {
currentProduces = OpenApiConstants.DEFAULT_PRODUCES;
}
}
Operation operation = new OperationImpl();
// ///////////////////////////////////////
if (method.hasAnnotation(OpenApiConstants.DOTNAME_OPERATION)) {
AnnotationInstance operationAnno = method.annotation(OpenApiConstants.DOTNAME_OPERATION);
// If the operation is marked as hidden, just bail here because we don't want it as part of the model.
if (operationAnno.value(OpenApiConstants.PROP_HIDDEN) != null && operationAnno.value(OpenApiConstants.PROP_HIDDEN).asBoolean()) {
return;
}
// Otherwise, set various bits of meta-data from the values in the @Operation annotation
operation.setSummary(JandexUtil.stringValue(operationAnno, OpenApiConstants.PROP_SUMMARY));
operation.setDescription(JandexUtil.stringValue(operationAnno, OpenApiConstants.PROP_DESCRIPTION));
operation.setOperationId(JandexUtil.stringValue(operationAnno, OpenApiConstants.PROP_OPERATION_ID));
operation.setDeprecated(JandexUtil.booleanValue(operationAnno, OpenApiConstants.PROP_DEPRECATED));
}
// Process tags - @Tag and @Tags annotations combines with the resource tags we've already found (passed in)
// ///////////////////////////////////////
boolean hasOpTags = false;
Set<String> tags = new HashSet<>();
if (method.hasAnnotation(OpenApiConstants.DOTNAME_TAG)) {
hasOpTags = true;
AnnotationInstance tagAnno = method.annotation(OpenApiConstants.DOTNAME_TAG);
if (JandexUtil.isRef(tagAnno)) {
String tagRef = JandexUtil.stringValue(tagAnno, OpenApiConstants.PROP_REF);
tags.add(tagRef);
} else if (JandexUtil.isEmpty(tagAnno)) {
// Nothing to do here. The @Tag() was empty.
} else {
Tag tag = readTag(tagAnno);
if (tag.getName() != null) {
openApi.addTag(tag);
tags.add(tag.getName());
}
}
}
if (method.hasAnnotation(OpenApiConstants.DOTNAME_TAGS)) {
hasOpTags = true;
AnnotationInstance tagsAnno = method.annotation(OpenApiConstants.DOTNAME_TAGS);
AnnotationValue tagsArrayVal = tagsAnno.value();
if (tagsArrayVal != null) {
AnnotationInstance[] tagsArray = tagsArrayVal.asNestedArray();
for (AnnotationInstance tagAnno : tagsArray) {
if (JandexUtil.isRef(tagAnno)) {
String tagRef = JandexUtil.stringValue(tagAnno, OpenApiConstants.PROP_REF);
tags.add(tagRef);
} else {
Tag tag = readTag(tagAnno);
if (tag.getName() != null) {
openApi.addTag(tag);
tags.add(tag.getName());
}
}
}
}
List<String> listValue = JandexUtil.stringListValue(tagsAnno, OpenApiConstants.PROP_REFS);
if (listValue != null) {
tags.addAll(listValue);
}
}
if (!hasOpTags) {
tags.addAll(resourceTags);
}
if (!tags.isEmpty()) {
operation.setTags(new ArrayList<>(tags));
}
// Process @Parameter annotations
// ///////////////////////////////////////
List<AnnotationInstance> parameterAnnotations = JandexUtil.getRepeatableAnnotation(method, OpenApiConstants.DOTNAME_PARAMETER, OpenApiConstants.DOTNAME_PARAMETERS);
for (AnnotationInstance annotation : parameterAnnotations) {
Parameter parameter = readParameter(annotation);
if (parameter == null) {
// Param was hidden
continue;
}
AnnotationTarget target = annotation.target();
// If the target is METHOD_PARAMETER, then the @Parameter is on one of the method's arguments (THIS ONE WE CARE ABOUT)
if (target != null && target.kind() == Kind.METHOD_PARAMETER) {
In in = parameterIn(target.asMethodParameter());
parameter.setIn(in);
// if the Parameter model we read does *NOT* have a Schema at this point, then create one from the method argument's type
if (!ModelUtil.parameterHasSchema(parameter)) {
Type paramType = JandexUtil.getMethodParameterType(method, target.asMethodParameter().position());
Schema schema = typeToSchema(paramType);
ModelUtil.setParameterSchema(parameter, schema);
}
} else {
// TODO if the @Parameter is on the method, we could perhaps find the one it refers to by name
// and use its type to create a Schema (if missing)
}
operation.addParameter(parameter);
}
// Now process any jax-rs parameters that were NOT annotated with @Parameter (do not yet exist in the model)
List<Type> parameters = method.parameters();
for (int idx = 0; idx < parameters.size(); idx++) {
JaxRsParameterInfo paramInfo = JandexUtil.getMethodParameterJaxRsInfo(method, idx);
if (paramInfo != null && !ModelUtil.operationHasParameter(operation, paramInfo.name)) {
Type paramType = parameters.get(idx);
Parameter parameter = new ParameterImpl();
parameter.setName(paramInfo.name);
parameter.setIn(paramInfo.in);
parameter.setRequired(true);
Schema schema = typeToSchema(paramType);
parameter.setSchema(schema);
operation.addParameter(parameter);
}
}
// TODO @Parameter can be located on a field - what does that mean?
// TODO need to handle the case where we have @BeanParam annotations
// Process any @RequestBody annotation
// ///////////////////////////////////////
// note: the @RequestBody annotation can be found on a method argument *or* on the method
List<AnnotationInstance> requestBodyAnnotations = JandexUtil.getRepeatableAnnotation(method, OpenApiConstants.DOTNAME_REQUEST_BODY, null);
for (AnnotationInstance annotation : requestBodyAnnotations) {
RequestBody requestBody = readRequestBody(annotation);
// TODO if the method argument type is Request, don't generate a Schema!
if (!ModelUtil.requestBodyHasSchema(requestBody)) {
Type requestBodyType = null;
if (annotation.target().kind() == AnnotationTarget.Kind.METHOD_PARAMETER) {
requestBodyType = JandexUtil.getMethodParameterType(method, annotation.target().asMethodParameter().position());
} else if (annotation.target().kind() == AnnotationTarget.Kind.METHOD) {
requestBodyType = JandexUtil.getRequestBodyParameterClassType(method);
}
if (requestBodyType != null) {
Schema schema = typeToSchema(requestBodyType);
ModelUtil.setRequestBodySchema(requestBody, schema, currentConsumes);
}
}
operation.setRequestBody(requestBody);
}
// method declares that it @Consumes data
if (operation.getRequestBody() == null && currentConsumes != null) {
Type requestBodyType = JandexUtil.getRequestBodyParameterClassType(method);
if (requestBodyType != null) {
Schema schema = typeToSchema(requestBodyType);
if (schema != null) {
RequestBody requestBody = new RequestBodyImpl();
ModelUtil.setRequestBodySchema(requestBody, schema, currentConsumes);
operation.setRequestBody(requestBody);
}
}
}
// Process @APIResponse annotations
// ///////////////////////////////////////
List<AnnotationInstance> apiResponseAnnotations = JandexUtil.getRepeatableAnnotation(method, OpenApiConstants.DOTNAME_API_RESPONSE, OpenApiConstants.DOTNAME_API_RESPONSES);
for (AnnotationInstance annotation : apiResponseAnnotations) {
String responseCode = JandexUtil.stringValue(annotation, OpenApiConstants.PROP_RESPONSE_CODE);
if (responseCode == null) {
responseCode = APIResponses.DEFAULT;
}
APIResponse response = readResponse(annotation);
APIResponses responses = ModelUtil.responses(operation);
responses.addApiResponse(responseCode, response);
}
// If there are no responses from annotations, try to create a response from the method return value.
if (operation.getResponses() == null || operation.getResponses().isEmpty()) {
createResponseFromJaxRsMethod(method, operation);
}
// Process @SecurityRequirement annotations
// /////////////////////////////////////////
List<AnnotationInstance> securityRequirementAnnotations = JandexUtil.getRepeatableAnnotation(method, OpenApiConstants.DOTNAME_SECURITY_REQUIREMENT, OpenApiConstants.DOTNAME_SECURITY_REQUIREMENTS);
securityRequirementAnnotations.addAll(JandexUtil.getRepeatableAnnotation(resource, OpenApiConstants.DOTNAME_SECURITY_REQUIREMENT, OpenApiConstants.DOTNAME_SECURITY_REQUIREMENTS));
for (AnnotationInstance annotation : securityRequirementAnnotations) {
SecurityRequirement requirement = readSecurityRequirement(annotation);
if (requirement != null) {
operation.addSecurityRequirement(requirement);
}
}
// Process @Callback annotations
// ///////////////////////////////////////
List<AnnotationInstance> callbackAnnotations = JandexUtil.getRepeatableAnnotation(method, OpenApiConstants.DOTNAME_CALLBACK, OpenApiConstants.DOTNAME_CALLBACKS);
Map<String, Callback> callbacks = new LinkedHashMap<>();
for (AnnotationInstance annotation : callbackAnnotations) {
String name = JandexUtil.stringValue(annotation, OpenApiConstants.PROP_NAME);
if (name == null && JandexUtil.isRef(annotation)) {
name = JandexUtil.nameFromRef(annotation);
}
if (name != null) {
callbacks.put(name, readCallback(annotation));
}
if (!callbacks.isEmpty()) {
operation.setCallbacks(callbacks);
}
}
// Process @Server annotations
// /////////////////////////////////
List<AnnotationInstance> serverAnnotations = JandexUtil.getRepeatableAnnotation(method, OpenApiConstants.DOTNAME_SERVER, OpenApiConstants.DOTNAME_SERVERS);
if (serverAnnotations.isEmpty()) {
serverAnnotations.addAll(JandexUtil.getRepeatableAnnotation(method.declaringClass(), OpenApiConstants.DOTNAME_SERVER, OpenApiConstants.DOTNAME_SERVERS));
}
for (AnnotationInstance annotation : serverAnnotations) {
Server server = readServer(annotation);
operation.addServer(server);
}
// /////////////////////////////////////////
switch(methodType) {
case DELETE:
pathItem.setDELETE(operation);
break;
case GET:
pathItem.setGET(operation);
break;
case HEAD:
pathItem.setHEAD(operation);
break;
case OPTIONS:
pathItem.setOPTIONS(operation);
break;
case PATCH:
pathItem.setPATCH(operation);
break;
case POST:
pathItem.setPOST(operation);
break;
case PUT:
pathItem.setPUT(operation);
break;
case TRACE:
pathItem.setTRACE(operation);
break;
default:
break;
}
}
Aggregations