use of org.eclipse.microprofile.openapi.models.responses.APIResponses 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;
}
}
use of org.eclipse.microprofile.openapi.models.responses.APIResponses in project wildfly-swarm by wildfly-swarm.
the class OpenApiAnnotationScanner method readCallbackOperationResponses.
/**
* Reads an array of APIResponse annotations into an {@link APIResponses} model.
* @param value
*/
private APIResponses readCallbackOperationResponses(AnnotationValue value) {
if (value == null) {
return null;
}
LOG.debug("Processing a list of @APIResponse annotations into an APIResponses model.");
APIResponses responses = new APIResponsesImpl();
AnnotationInstance[] nestedArray = value.asNestedArray();
for (AnnotationInstance nested : nestedArray) {
String responseCode = JandexUtil.stringValue(nested, OpenApiConstants.PROP_RESPONSE_CODE);
if (responseCode != null) {
responses.put(responseCode, readResponse(nested));
}
}
return responses;
}
use of org.eclipse.microprofile.openapi.models.responses.APIResponses in project wildfly-swarm by wildfly-swarm.
the class OpenApiAnnotationScanner method createResponseFromJaxRsMethod.
/**
* Called when a jax-rs method's APIResponse annotations have all been processed but
* no response was actually created for the operation. This method will create a response
* from the method information and add it to the given operation. It will try to do this
* by examining the method's return value and the type of operation (GET, PUT, POST, DELETE).
*
* If there is a return value of some kind (a non-void return type) then the response code
* is assumed to be 200.
*
* If there not a return value (void return type) then either a 201 or 204 is returned,
* depending on the type of request.
*
* TODO generate responses for each checked exception?
* @param method
* @param operation
*/
private void createResponseFromJaxRsMethod(MethodInfo method, Operation operation) {
Type returnType = method.returnType();
Schema schema;
APIResponses responses;
APIResponse response;
ContentImpl content;
if (returnType.kind() == Type.Kind.VOID) {
String code = "204";
if (method.hasAnnotation(OpenApiConstants.DOTNAME_POST)) {
code = "201";
}
responses = ModelUtil.responses(operation);
response = new APIResponseImpl();
responses.addApiResponse(code, response);
} else {
schema = typeToSchema(returnType);
responses = ModelUtil.responses(operation);
response = new APIResponseImpl();
content = new ContentImpl();
String[] produces = this.currentProduces;
if (produces == null || produces.length == 0) {
produces = OpenApiConstants.DEFAULT_PRODUCES;
}
for (String producesType : produces) {
MediaType mt = new MediaTypeImpl();
mt.setSchema(schema);
content.addMediaType(producesType, mt);
}
response.setContent(content);
responses.addApiResponse("200", response);
}
}
use of org.eclipse.microprofile.openapi.models.responses.APIResponses in project wildfly-swarm by wildfly-swarm.
the class MergeUtil method mergeMaps.
/**
* Merges two Maps. Any values missing from Map1 but present in Map2 will be added. If a value
* is present in both maps, it will be overridden or merged.
* @param values1
* @param values2
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private static Map mergeMaps(Map values1, Map values2) {
if (values1 == null && values2 == null) {
return null;
}
if (values1 != null && values2 == null) {
return values1;
}
if (values1 == null && values2 != null) {
return values2;
}
for (Object key : values2.keySet()) {
if (values1.containsKey(key)) {
Object pval1 = values1.get(key);
Object pval2 = values2.get(key);
if (pval1 instanceof Map) {
values1.put(key, mergeMaps((Map) pval1, (Map) pval2));
} else if (pval1 instanceof List) {
values1.put(key, mergeLists((List) pval1, (List) pval2));
} else if (pval1 instanceof Constructible) {
values1.put(key, mergeObjects(pval1, pval2));
} else {
values1.put(key, pval2);
}
} else {
Object pval2 = values2.get(key);
values1.put(key, pval2);
}
}
if (values1 instanceof Constructible) {
if (values1 instanceof Reference) {
Reference ref1 = (Reference) values1;
Reference ref2 = (Reference) values2;
if (ref2.getRef() != null) {
ref1.setRef(ref2.getRef());
}
}
if (values1 instanceof Extensible) {
Extensible extensible1 = (Extensible) values1;
Extensible extensible2 = (Extensible) values2;
extensible1.setExtensions(mergeMaps(extensible1.getExtensions(), extensible2.getExtensions()));
}
if (values1 instanceof APIResponses) {
APIResponses responses1 = (APIResponses) values1;
APIResponses responses2 = (APIResponses) values2;
responses1.defaultValue(mergeObjects(responses1.getDefault(), responses2.getDefault()));
}
}
return values1;
}
Aggregations