openapi-tools / swagger-maven-plugin

Maven plugin to activate the Swagger Core library to generate OpenAPI documentation.
MIT License
72 stars 46 forks source link

The generation-result is not deterministic #47

Closed ahoehma closed 4 years ago

ahoehma commented 4 years ago

Describe the bug I have a controller-class. I'm able to generate json and yaml. But every time I run the plugin the result looks different to the prev. one. This is not so helpfull because I put the result into my git-repo.

<plugin>
        <groupId>io.swagger.core.v3</groupId>
        <artifactId>swagger-maven-plugin</artifactId>
        <version>${swagger-maven-plugin.version}</version>
        <configuration>
          <outputPath>${basedir}/src/openapi/</outputPath>
          <outputFileName>cc-${api.version}</outputFileName>
          <outputFormat>JSONANDYAML</outputFormat>
          <prettyPrint>TRUE</prettyPrint>
          <attachSwaggerArtifact>true</attachSwaggerArtifact>
          <resourceClasses>
            <resourceClasse>com.siemens.spice.cc.rest.service.ConfigurationController</resourceClasse>
          </resourceClasses>
        </configuration>
        <executions>
          <execution>
            <phase>compile</phase>
            <goals>
              <goal>resolve</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

To Reproduce Simply run the generation multiple time without any change.

Expected behavior The generated json/yaml must create always the same output.

I tried to "fix" this behavior in io.openapitools.swagger.GenerateMojo.execute()

OpenAPI swagger = reader.read(reflectiveScanner.classes());

// XXX .... try to bring all the props in a "stable" order ...
swagger.paths(sortPaths(swagger.getPaths()));
swagger.components(sortComponents(swagger.getComponents()));

...


private Components sortComponents(Components components) {
        if (components==null)
            return null;
        Components r = new Components();
        r.setSchemas(sort(components.getSchemas()));
        return r;
    }

    private io.swagger.v3.oas.models.Paths sortPaths(io.swagger.v3.oas.models.Paths paths) {
        if (paths==null)
            return null;
        io.swagger.v3.oas.models.Paths r = new io.swagger.v3.oas.models.Paths();
        r.putAll(sort(paths));
        return r;
    }

    private <K extends Comparable<K>, V> Map<K, V> sort(Map<K, V> map) {
        return map
                .entrySet()
                .stream()
                .sorted(Map.Entry.comparingByKey())
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    }
``´

But I'm not sure if this is the right direction to go :)
langecode commented 4 years ago

I am not sure whether the plugin is actually behaving differently than the Swagger framework itself. I mean we could add a sorting, as you suggest, but I am thinking whether this would more correctly be solved in Swagger itself as we are merely wrapping it.

wanno-drijfhout commented 4 years ago

This problem seems to be reoccurring quite often in the Swagger framework itself. There are many open issues about this, even from 2017:

https://github.com/swagger-api/swagger-core/issues/2775 https://github.com/swagger-api/swagger-core/issues/2828 https://github.com/swagger-api/swagger-core/pull/3266 https://github.com/swagger-api/swagger-core/issues/2400

What is the best way to solve this matter both properly and swiftly you reckon, @langecode ? How can I help?

langecode commented 4 years ago

Well, if there is that many open issued with Swagger Code it seems not very high priority and I do acknowledge it can be a problem if you want to do a simple diff between outputs (we actually did do a diff tool for OpenAPI specifications that does it semantically - however I do not think we ported that to OpenAPI 3 yet: https://github.com/openapi-tools/open-api-diff).

If it has value to you I think it would be ok to include a sorting option to the plugin as suggested by @ahoehma - I am not working much with Java these days so it is a little hard to find the time to implement it - but feel free to do a PR and I shall find the time to look through that.

hiddewie commented 4 years ago

@wanno-drijfhout @langecode @ahoehma We generated a pull request for this issue. This causes blocking problems when adopting this plugin.

wanno-drijfhout commented 4 years ago

@langecode I fear updating to the new version doesn't lead to a deterministic or sorted output. @hiddewie found a bug in the refactoring and created PR #50 .

ahoehma commented 4 years ago

Feedback ... now I'm using 2.1.4 and I still have some random jumps of paths in the output.json/yaml.

<plugin>
        <groupId>io.openapitools.swagger</groupId>
        <artifactId>swagger-maven-plugin</artifactId>
        <version>${swagger-maven-plugin.version}</version>
        <configuration>
          <outputDirectory>${basedir}/src/openapi/</outputDirectory>
          <outputFormats>JSON,YAML</outputFormats>
          <prettyPrint>true</prettyPrint>
          <attachSwaggerArtifact>true</attachSwaggerArtifact>
          <swaggerConfig>
            <info>
              <title>SPICE Configuration Service API</title>
              <license>
                <name>The Siemens Inner Source License - 1.1</name>
                <url>https://code.siemens.com/ebconf/spice-configuration-cluster/-/blob/develop/LICENSE</url>
              </license>
              <description></description>
              <version>${api.version}</version>
            </info>
          </swaggerConfig>
        </configuration>
        <executions>
          <execution>
            <id>cc-configuration</id>
            <phase>process-classes</phase>
            <goals>
              <goal>generate</goal>
            </goals>
            <configuration>
              <resourcePackages>
                <resourcePackage>com.siemens.spice.cc.rest.service.configuration</resourcePackage>
              </resourcePackages>
              <outputFilename>cc-${api.version}-configuration</outputFilename>
            </configuration>
          </execution>
          <execution>
            <id>cc-settings</id>
            <phase>process-classes</phase>
            <goals>
              <goal>generate</goal>
            </goals>
            <configuration>
              <resourcePackages>
                <resourcePackage>com.siemens.spice.cc.rest.service.settings</resourcePackage>
              </resourcePackages>
              <outputFilename>cc-${api.version}-settings</outputFilename>
            </configuration>
          </execution>
          <execution>
            <id>cc-products</id>
            <phase>process-classes</phase>
            <goals>
              <goal>generate</goal>
            </goals>
            <configuration>
              <resourcePackages>
                <resourcePackage>com.siemens.spice.cc.rest.service.products</resourcePackage>
              </resourcePackages>
              <outputFilename>cc-${api.version}-products</outputFilename>
            </configuration>
          </execution>
          <execution>
            <id>cc-brain</id>
            <phase>process-classes</phase>
            <goals>
              <goal>generate</goal>
            </goals>
            <configuration>
              <resourcePackages>
                <resourcePackage>com.siemens.spice.cc.rest.service.brain</resourcePackage>
              </resourcePackages>
              <outputFilename>cc-${api.version}-brain</outputFilename>
            </configuration>
          </execution>
          <execution>
            <id>cc-history</id>
            <phase>process-classes</phase>
            <goals>
              <goal>generate</goal>
            </goals>
            <configuration>
              <resourcePackages>
                <resourcePackage>com.siemens.spice.cc.rest.service.history</resourcePackage>
              </resourcePackages>
              <outputFilename>cc-${api.version}-history</outputFilename>
            </configuration>
          </execution>
        </executions>
        <dependencies>
          <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>runtime</scope>
          </dependency>
        </dependencies>
      </plugin>
hiddewie commented 4 years ago

@ahoehma There seem to be more things that need to be sorted, e.g. https://github.com/openapi-tools/swagger-maven-plugin/pull/50#issuecomment-671770396. The current solution is a workaround, and should be solved in swagger-core). PRs and issues have also been referenced opened there.