OpenAPITools / openapi-generator

OpenAPI Generator allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically given an OpenAPI Spec (v2, v3)
https://openapi-generator.tech
Apache License 2.0
21.49k stars 6.5k forks source link

[BUG][JAVA] Generate from spec within JAR-dependency - File found then File _not found_ #2738

Open thokaab opened 5 years ago

thokaab commented 5 years ago
Description

When migrated from swagger-codegen-maven-plugin to openapi-generator-maven-plugin we are no longer able to generate Java code from an OpenAPI specification located in a Maven JAR-dependency. The specification file is first found, then at a later stage it's no longer found.

Please note that the codegen is successful with the exact same configuration if I put the api.yml in the local project and point <inputSpec> to the local file, e.g.

<inputSpec>${project.basedir}/src/main/resources/api.yml</inputSpec>

Hence:

Detailed Description

When building verbose I can see that the generator first prints my YML-file (api.yml) as JSON (OK):

{
  "openapi" : "3.0.2",
  "info" : {
    "title" : "Example API",
    "description" : "Example API",
    "contact" : {
      "name" : "John Doe",
      "email" : "john.doe@example.com"
    },
    "version" : "1.0"
  },
...
}

Then writing model files (schemas), e.g.

[INFO] writing file C:\code\example-impl\functions\target\generated-sources\swagger\com\example\api\model\SomeModel.java

Then Model Info is printed (OK):

############ Model info ############
{...}

Then Supporting file info is printed (OK):

############ Supporting file info ############`
{...}

Then writing supporting files:

[INFO] writing file C:\code\example-impl\functions\target\generated-sources\swagger/com/example/api\ApiClient.java
[INFO] writing file C:\code\example-impl\functions\target\generated-sources\swagger/com/example/api\ApiException.java
...

So far everything looks ok and the YML-file has obviously been found by the plugin. But directly after the supporting files have been generated I get a FileNotFoundException:

[ERROR] C:\code\example-impl\api.yml [0:0]: unexpected error in Open-API generation
java.io.FileNotFoundException: api.yml (The system cannot find the file specified)
    at java.io.FileInputStream.open0(Native Method)
    at java.io.FileInputStream.open(FileInputStream.java:195)
    at java.io.FileInputStream.<init>(FileInputStream.java:138)
    at com.google.common.io.Files$FileByteSource.openStream(Files.java:129)
    at com.google.common.io.Files$FileByteSource.openStream(Files.java:119)
    at com.google.common.io.ByteSource.copyTo(ByteSource.java:245)
    at com.google.common.io.ByteSource.hash(ByteSource.java:329)
    at org.openapitools.codegen.plugin.CodeGenMojo.execute(CodeGenMojo.java:637)
    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:134)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:207)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
    at org.apache.maven.lifecycle.internal.MojoExecutor.executeForkedExecutions(MojoExecutor.java:352)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:197)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:116)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:80)
    at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128)
    at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:307)
    at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:193)
    at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:106)
    at org.apache.maven.cli.MavenCli.execute(MavenCli.java:863)
    at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:288)
    at org.apache.maven.cli.MavenCli.main(MavenCli.java:199)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
    at org.codehaus.classworlds.Launcher.main(Launcher.java:47)
[ERROR] 
java.io.FileNotFoundException: api.yml (The system cannot find the file specified)
    at java.io.FileInputStream.open0(Native Method)
    at java.io.FileInputStream.open(FileInputStream.java:195)
    at java.io.FileInputStream.<init>(FileInputStream.java:138)
    at com.google.common.io.Files$FileByteSource.openStream(Files.java:129)
    at com.google.common.io.Files$FileByteSource.openStream(Files.java:119)
    at com.google.common.io.ByteSource.copyTo(ByteSource.java:245)
    at com.google.common.io.ByteSource.hash(ByteSource.java:329)
    at org.openapitools.codegen.plugin.CodeGenMojo.execute(CodeGenMojo.java:637)
    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:134)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:207)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
    at org.apache.maven.lifecycle.internal.MojoExecutor.executeForkedExecutions(MojoExecutor.java:352)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:197)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:116)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:80)
    at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128)
    at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:307)
    at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:193)
    at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:106)
    at org.apache.maven.cli.MavenCli.execute(MavenCli.java:863)
    at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:288)
    at org.apache.maven.cli.MavenCli.main(MavenCli.java:199)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
    at org.codehaus.classworlds.Launcher.main(Launcher.java:47)

The maven plugin is located at C:\code\example-impl\functions\pom.xml I run mvn clean install from C:\code\example-impl\ which has a parent pom.xml to the functions\pom.xml (functions is a module). One thing I noticed is that the error message above looks for the file at C:\code\example-impl\api.yml, i.e. the folder of the POM I'm running. If I run mvn clean install on the module POM C:\code\example-impl\functions\pom.xml then it's the same error, but it now fails with:

[ERROR] C:\code\example-impl\functions\api.yml [0:0]: unexpected error in Open-API generation
java.io.FileNotFoundException: api.yml (The system cannot find the file specified)
    at java.io.FileInputStream.open0(Native Method)

The file is inside the JAR-dependency, not on any of the project paths.

My plugin definition:

In C:\code\example-impl\functions\pom.xml:

      <plugin>
        <groupId>org.openapitools</groupId>
        <artifactId>openapi-generator-maven-plugin</artifactId>
        <version>3.3.4</version>
        <dependencies>
          <dependency>
            <groupId>com.example</groupId>
            <artifactId>example-api</artifactId>
            <version>${example-api.version}</version>
          </dependency>
        </dependencies>
        <executions>
          <execution>
            <goals>
              <goal>generate</goal>
            </goals>
            <configuration>
              <validateSpec>true</validateSpec>
              <inputSpec>api.yml</inputSpec>
              <generatorName>java</generatorName>
              <modelNamePrefix>ExApi</modelNamePrefix>
              <verbose>true</verbose>
              <configOptions>
                <sourceFolder>swagger</sourceFolder>
                <dateLibrary>java8</dateLibrary>
              </configOptions>
              <typeMappings>
                <typeMapping>OffsetDateTime=Instant</typeMapping>
              </typeMappings>
              <importMappings>
                <importMapping>java.time.OffsetDateTime=java.time.Instant</importMapping>
              </importMappings>
              <library>jersey2</library>
              <output>${project.build.directory}/generated-sources</output>
              <modelPackage>${default.package}.api.model</modelPackage>
              <generateModels>true</generateModels>
              <generateApis>false</generateApis>
              <generateApiTests>false</generateApiTests>
              <generateApiDocumentation>false</generateApiDocumentation>
              <generateModelTests>false</generateModelTests>
              <generateModelDocumentation>false</generateModelDocumentation>
            </configuration>
          </execution>
        </executions>
      </plugin>
openapi-generator version

3.3.4

OpenAPI declaration file content or url

N/A

Steps to reproduce

See description.

Related issues/PRs

None found.

Suggest a fix
geojins commented 3 years ago

Here is a workaround until this is fixed: Using maven-dependency-plugin, unpack the jar , copy open api yaml to a local directory, point generator to the local directory. The generator will be happy now.


...
<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>3.1.1</version>
                <executions>
                    <execution>
                        <id>unpack</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>unpack</goal>
                        </goals>
                        <configuration>
                            <artifactItems>
                                <artifactItem>
                                    <groupId>your-group-id</groupId>
                                    <artifactId>your-artifact-id</artifactId>
                                    <version>your-version</version>
                                    <type>jar</type>
                                    <overWrite>false</overWrite>
                                    <includes>**/*.yaml</includes>
                                </artifactItem>
                            </artifactItems>
                            <outputDirectory>${project.build.directory}/classes/</outputDirectory>
                            <overWriteReleases>true</overWriteReleases>
                            <overWriteSnapshots>false</overWriteSnapshots>
                            <overWriteIfNewer>true</overWriteIfNewer>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

<plugin>
  <groupId>org.openapitools</groupId>
  <artifactId>openapi-generator-maven-plugin</artifactId>
  <version>5.0.1</version>
  <executions>
      <execution>
          <goals>
              <goal>generate</goal>
          </goals>
          <configuration>
              <inputSpec>
                  ${project.build.directory}/classes/openapi.yaml
              </inputSpec>
              <generatorName>spring</generatorName>
              <apiPackage>com.example.gen</apiPackage>
              <modelPackage>com.example.gen.model</modelPackage>
              <configOptions>
                  <interfaceOnly>true</interfaceOnly>
              </configOptions>
          </configuration>
      </execution>
  </executions>
</plugin>
...
xuelishen commented 11 months ago

Is this issue fixed? Can I generate directly from the file in a jar file?

Saljack commented 10 months ago

It works for me. We have artifacts only as dependencies of the openapi-generator-maven-plugin . So it is fixed it worked also in version 3 of this plugin. There is no reason to use maven-dependency-plugin.

ILyaCyclone commented 9 months ago

@Saljack Could you please provide an example of specifying inputSpec from maven dependency?

Saljack commented 9 months ago

@ILyaCyclone Here you are:

 <plugin>
        <groupId>org.openapitools</groupId>
        <artifactId>openapi-generator-maven-plugin</artifactId>

        <dependencies>
          <dependency>
            <groupId>com.example</groupId>
            <artifactId>your-artificat-with-openapi</artifactId>
            <version>your-version</version>
          </dependency>

          <!-- You can add multiple dependencies -->

        </dependencies>

        <executions>
          <execution>
            <id>openapi</id>
            <goals>
              <goal>generate</goal>
            </goals>
            <configuration>
              <!-- The path of OpenAPI in your file. The origin path in project is /src/main/resources/... -->
              <inputSpec>openapi/openapi.yaml</inputSpec>
              <!-- Another configuration -->
            </configuration>
          </execution>

          <!-- You can add multiple executions-->

        <executions>
 </plugin>
coffee4dev commented 5 months ago

Is it possible to do it with gradle? Doesn't work for me currently, the plugin complains that it cannot find the inputSpec file, searching for it in project root.

Edit: succeeded with the suggested workaround (unpacked the JAR with the OpenAPI specification), just wondered if, being still relatively new to gradle, I'm missing something and the workaround can be avoided.