sdaschner / jaxrs-analyzer

Creates REST documentation for JAX-RS projects
Apache License 2.0
319 stars 101 forks source link

Cannot generate swagger.json when JAX-RS annotated endpoints comes from superclass #140

Open afrunt opened 6 years ago

afrunt commented 6 years ago

Cannot generate swagger.json when JAX-RS annotated endpoints comes from superclass. This issue related to maven plugin (0.16). In case I extend generic abstract class with JAX-RS endpoints and do add any endpoint to actual resource, then I'm receiving empty endpoint in swagger.json.


{
    "swagger": "2.0",
    "info": {
        "version": "0.1",
        "title": "web"
    },
    "host": "",
    "basePath": "/web/api",
    "schemes": [
        "http"
    ],
    "paths": {
        "/news": {
            "get": {
                "consumes": [
                ],
                "produces": [
                ],
                "parameters": [
                ],
                "responses": {
                    "204": {
                        "description": "No Content",
                        "headers": {
                        }
                    }
                }
            }
        },
        "/news/some": {
            "post": {
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "parameters": [
                ],
                "responses": {
                    "204": {
                        "description": "No Content",
                        "headers": {
                        }
                    }
                }
            }
        }
    },
    "definitions": {
    }
}

When I adding the endpoint to the actual resource implementation, its metadata exposed in swagger.json (like news/some at the above code snippet), but not the metadata of the endpoints from the parent abstract classes. I will also try to debug the jaxrs-analyzer by myself. Thanks!

TristanPerry commented 4 years ago

This issue is caused by the ASM library (used to analyze a class) not scanning super classes by design - hence ProjectAnalyzer.analyseClass() only returns that class' method results.

A general fix approach is to recursively visit any superclasses that exist (if a class has no extends ..., its superclass will be java/lang/Object):

    private void analyzeClass(final String className, ClassResult classResult) {
        try {
            final ClassReader classReader = new ContextClassReader(className);
            final ClassVisitor visitor = new JAXRSClassVisitor(classResult);

            classReader.accept(visitor, ClassReader.EXPAND_FRAMES);

            // recursively visit any super classes
            if (!classReader.getSuperName().equals("java/lang/Object")) {
                analyzeClass(classReader.getSuperName(), classResult);
            }
        } catch (IOException e) {
            LogProvider.error("The class " + className + " could not be loaded!");
            LogProvider.debug(e);
        }
    }

With the above approach, I was then seeing issues with the JavaDocs missing but the following fixed it for me - i.e. replace JavaDocAnalyzer.combineResults() with the below, and remove JavaDocAnalyzer.findMethodResult() completely:

    private void combineResults(final Set<ClassResult> classResults) {
        classResults.stream()
            .flatMap(classResult -> classResult.getMethods().stream()) // flatten our set of sets (multiple methods within multiple class results) into one set
            .forEach(methodResult -> {
                // For our single methodResult, see if we have a matching entry in the methodComments map (if we do, set the 'methodDoc' property)
                this.methodComments.entrySet().stream()
                    .filter(entry -> equalsSimpleTypeNames(entry.getKey(), methodResult))
                    .map(Entry::getValue)
                    .findAny()
                    .ifPresent(methodResult::setMethodDoc);
            });
    }

I'll look to submit a PR for this shortly.