简体   繁体   中英

Swagger annotation alias does not generate proper OpenAPI

I've created the following alias:

@Retention(RetentionPolicy.RUNTIME)
@Parameter(in = ParameterIn.PATH,
           name = "FieldId",
           required = true,
           extensions = { @Extension(properties = @ExtensionProperty(name = "custom-type",
                                                                     value = "FieldId")) })
@AnnotationCollector
public @interface MyAnnotator {
}

When generating the OpenAPI definition, if I use the @Parameter directly in my resource, it works, but if I use @MyAnnotator , it is ignored. So for example:

@POST
@Consumes(MediaType.APPLICATION_JSON)
@Path("/{FieldId}")
void create(@Parameter(in = ParameterIn.PATH,
                        name = "FieldId",
                        required = true,
                        extensions = { @Extension(properties = @ExtensionProperty(name = "custom-type",
                                                                                 value = "FieldId")) },
                        schema = @Schema(type = "string")) final FieldId fieldId)

would generate

post:
  parameters:
  - name: FieldId
    in: path
    required: true
    schema:
      type: string
    x-custom-type: FieldId

but doing

@POST
@Consumes(MediaType.APPLICATION_JSON)
@Path("/{FieldId}")
void create(@MyAnnotator FieldId fieldId)

does not. How come?

See the Update for a working solution

Unfortunately the swagger-core doesn't support this feature. @Parameter annotation must be placed on parameter directly.

At first JaxRS Reader implementation tries to find and resolve @Parameter annotation like this.

io.swagger.v3.oas.annotations.Parameter paramAnnotation = AnnotationsUtils.getAnnotation(io.swagger.v3.oas.annotations.Parameter.class, paramAnnotations[i]);
Type paramType = ParameterProcessor.getParameterType(paramAnnotation, true);
if (paramType == null) {
    paramType = type;
} else {
    if (!(paramType instanceof Class)) {
        paramType = type;
    }
}
ResolvedParameter resolvedParameter = getParameters(paramType, Arrays.asList(paramAnnotations[i]), operation, classConsumes, methodConsumes, jsonViewAnnotation);

AnnotationUtils doesn't go deeper

public static <T> T getAnnotation(Class<T> cls, Annotation... annotations) {
    if (annotations == null) {
        return null;
    }
    for (Annotation annotation : annotations) {
        if (cls.isAssignableFrom(annotation.getClass())) {
            return (T)annotation;
        }
    }
    return null;
}

Finally getParameters(...) method is stricter. It checks is the annotation's type exactly same as the desired one.

    for (Annotation annotation : annotations) {
        if (annotation instanceof QueryParam) {
            QueryParam param = (QueryParam) annotation;
            // ...
        } else if (annotation instanceof PathParam) {
            PathParam param = (PathParam) annotation;
            // ...
        } else if (annotation instanceof MatrixParam) {
            MatrixParam param = (MatrixParam) annotation;
            // ...
        }
        // ... and so on
    }

But there are some light at the end of the tunnel. You can create a custom ParameterExtension service implementing OpenAPIExtension and you can handle those custom annotations.

Update: Working solution

As I mentioned before creating a custom ParameterExtension service is allowed. I made a minimal parameter resolver extension which extends the DefaultParameterExtension .

Base concept

All Parameter template needs to be created as a custom annotation like this:

@Retention(RetentionPolicy.RUNTIME)
@Parameter(in = ParameterIn.PATH,
        name = "FieldId",
        required = true,
        extensions = {@Extension(properties = @ExtensionProperty(name = "custom-type",
                value = "FieldId"))})
public @interface MyAnnotator {
}

The extended ParameterExtension reads @ParameterAlias annotation which has a mandatory attribute.

@Path("/v1")
@Tags(@Tag(name = "test", description = ""))
public class FooResource {

    @PUT
    @Consumes(MediaType.APPLICATION_JSON)
    @Path("/{FieldId}")
    @Operation(operationId = "modifyFoo", summary = "Modifies a Foo entity")
    public void modify(@ParameterAlias(MyAnnotator.class) final FieldId fieldId) {

    }
}

Finally the extended extension ParameterAliasExtension handles the @ParameterAliasExtension

public class ParameterAliasExtension extends DefaultParameterExtension {

    @Override
    public ResolvedParameter extractParameters(List<Annotation> annotations,
                                               Type type,
                                               Set<Type> typesToSkip,
                                               Components components,
                                               javax.ws.rs.Consumes classConsumes,
                                               javax.ws.rs.Consumes methodConsumes,
                                               boolean includeRequestBody,
                                               JsonView jsonViewAnnotation,
                                               Iterator<OpenAPIExtension> chain) {
        List<Annotation> extendedAnnotations = null;
        if (null != annotations) {
            extendedAnnotations = new ArrayList<>(annotations);
            ParameterAlias alias = AnnotationsUtils.getAnnotation(ParameterAlias.class, annotations.toArray(new Annotation[0]));
            if (null != alias) {
                Parameter aliasParameter = AnnotationsUtils.getAnnotation(Parameter.class, alias.value().getDeclaredAnnotations());
                if (null != aliasParameter) {
                    extendedAnnotations.add(aliasParameter);
                }
            }
        }
        return super.extractParameters(extendedAnnotations == null ? annotations : extendedAnnotations, 
                type, 
                typesToSkip, 
                components, 
                classConsumes, 
                methodConsumes, 
                includeRequestBody, 
                jsonViewAnnotation, 
                chain);
    }
}

This example is avaliable on my GitHub repo: https://github.com/zforgo/stackoverflow/tree/master/openapi-alias

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM