openapi-generator icon indicating copy to clipboard operation
openapi-generator copied to clipboard

[REQ] Support multiple YAML files for openapi-generator-maven-plugin

Open khaiqnguyen93 opened this issue 5 years ago • 14 comments

I have a project that needs to use multiple YAML files, but the inputSpec doesn't support this. I try a workaround solution by adding multiple executions of the maven plugin into the pom, this issue can be temporarily solved. But in case I need to generate for many more files (YAML1, YAML2, YAML3,...), and in case my execution contains a big block of code like below:

<execution>
	<goals>
		<goal>generate</goal>
	</goals>
	<configuration>
		<inputSpec>${basedir}/api-spec/${swagger.version}/example1.yaml</inputSpec>
		<output>${basedir}</output>
		<templateDirectory>${basedir}/template/${swagger.version}</templateDirectory>
		<additionalProperties>apiVersion=${swagger.version},useApiAnnotation=false</additionalProperties>
		<generateSupportingFiles>false</generateSupportingFiles>
		<generateModelTests>false</generateModelTests>
		<generateApiTests>false</generateApiTests>
		<generateModelDocumentation>false</generateModelDocumentation>
		<generateApiDocumentation>false</generateApiDocumentation>
		<generatorName>jaxrs-spec</generatorName>
		<configOptions>
			<sourceFolder>src/main/java</sourceFolder>
			<implFolder>src/main/java</implFolder>
			<apiPackage>${swagger.api.package}</apiPackage>
			<interfaceOnly>true</interfaceOnly>
			<java8>true</java8>
			<modelPackage>${swagger.model.package}</modelPackage>
			<useSwaggerAnnotations>false</useSwaggerAnnotations>
			<generatePom>false</generatePom>
			<useBeanValidation>false</useBeanValidation>
		</configOptions>
		<typeMappings>
			<typeMapping>Date=String</typeMapping>
			<typeMapping>LocalDate=String</typeMapping>
		</typeMappings>
	</configuration>
</execution>
<execution>
           ..
</execution>
<execution>
           ...
</execution>

The above workaround is not good, can you support us for a feature that supports multiple YAML files?

khaiqnguyen93 avatar May 20 '20 11:05 khaiqnguyen93

Do we have a fix available for this

sreenath1506 avatar Nov 02 '20 12:11 sreenath1506

Actually, it is possible. I use a setup where I define the plugin in a pluginManagement section and a project with a plugin definition that generates two sets of models, one even inheriting from the other. The managed plugin looks like this:

<plugin>
	<groupId>org.openapitools</groupId>
	<artifactId>openapi-generator-maven-plugin</artifactId>
	<version>${version-openapi-generator}</version>
	<configuration>
		<generatorName>jaxrs-spec</generatorName>
		<configOptions>
			<sortParamsByRequiredFlag>false</sortParamsByRequiredFlag>
			<java8>true</java8>
			<interfaceOnly>true</interfaceOnly>
			<sourceFolder>src/main/java</sourceFolder>
			<useSwaggerAnnotations>false</useSwaggerAnnotations>
			<useBeanValidation>false</useBeanValidation>
			<dateLibrary>java8</dateLibrary>
			<generatePom>false</generatePom>
			<disableHtmlEscaping>true</disableHtmlEscaping>
			<returnResponse>true</returnResponse>
			<openApiSpecFileLocation>${project.build.directory}/generated-sources/openapi/src/main/resources/META-INF</openApiSpecFileLocation>
			</configOptions>
		<configHelp>false</configHelp>
		<skipValidateSpec>false</skipValidateSpec>
		<generateApiTests>false</generateApiTests>
		<generateModels>true</generateModels>
		<generateModelTests>false</generateModelTests>
		<generateModelDocumentation>false</generateModelDocumentation>
		<generateSupportingFiles>false</generateSupportingFiles>
		<withXml>false</withXml>
	</configuration>
</plugin>

Then the plugin in the project pom looks like:

<plugin>
  <groupId>org.openapitools</groupId>
  <artifactId>openapi-generator-maven-plugin</artifactId>
  <executions>
	  <execution>
		  <id>base-model</id>
		  <goals>
		        <goal>generate</goal>
		  </goals>
		  <configuration>
                        <inputSpec>${project.basedir}/grapevine-api.yaml</inputSpec>
                        <apiPackage>com.vidinexus.grapevine.api</apiPackage>
                        <modelPackage>com.vidinexus.grapevine.api.model</modelPackage>
		  </configuration>
	  </execution>
	  <execution>
		  <id>inquiry</id>
		  <goals>
		        <goal>generate</goal>
		  </goals>
		  <configuration>
			<inputSpec>${project.basedir}/inquiry-api.yaml</inputSpec>
			<apiPackage>com.vidinexus.grapevine.api</apiPackage>
			<modelPackage>com.vidinexus.grapevine.api.model.inquiry</modelPackage>
			<languageSpecificPrimitives>Item,GeoLocation</languageSpecificPrimitives>
			<importMappings>Item=com.vidinexus.grapevine.api.model.Item, GeoLocation=com.vidinexus.grapevine.api.model.GeoLocation</importMappings>
			<typeMappings>Item=com.vidinexus.grapevine.api.model.Item</typeMappings>
  <generateApis>false</generateApis>
		  </configuration>
	  </execution>
  </executions>
</plugin>

Result: two sets of files are generated, one after the other. I myself was quite surprised it worked. I got a hint from here: https://maven.apache.org/guides/mini/guide-default-execution-ids.html.

reitsma avatar Apr 07 '21 18:04 reitsma

Will you excuse a late question? Do we know that the managed plugin is necessary? With versions 6.3.0 and 6.4.0I tried a naïve way with just two executions in the same plugin in my pom.xml, but it seems the plugin is overwriting generated Java files when generating for the second input file in spite of me specifying different package names. This leads to requests being routed to the wrong API and hence failing. Should I expect a managed plugin to fix this? Generating from Swagger2.0 JSON files using the java generator.

ole-v-v avatar Mar 08 '23 14:03 ole-v-v

@reitsma

I have to generate an API from those yaml I've received, all going into the same model, api generation folders:

catalogresponses.yaml
config.yaml
coverages.yaml
featuretypes.yaml
manifests.yaml

owsservices.yaml
settings.yaml
workspaces.yaml
catalog.yaml
coveragestores.yaml

datastores.yaml
layers.yaml
namespaces.yaml
styles.yaml

1) I don't understand really your inheritance system: who shall inherit from one another in my case?

2) Whatever, this would lead to add in my pom at least 70 lines (100 lines?) to perform my generation, as these 14 files would require 13 copy paste of the same block, with only one line changing inside each block.

Don't you think this would be difficult and clumsy, in comparison to setting:

<inputspec>a,b,c,d</inputspec>

or

<inputspec>a b c d</inputspec>

or

<inputspect>*.yaml</inputspec> ?

mlebihan avatar Sep 01 '23 03:09 mlebihan

@ole-v-v : I don't think the managed plugin is really necessary, it should be a convenience in case the plugin is used multiple POMs. My knowledge of maven is not extensive enough to be 100% sure. It is complicated matter if Maven configuration is your full-time job. And openApiGenerator is also not known for its ease of use.

reitsma avatar Sep 01 '23 12:09 reitsma

@mlebihan : The inheritance between the generated models appeared indeed an issue. In the above example I could not get the inheritance correctly modelled. I ended up by using a single yaml file as within a single file the modelling of the inheritance was feasible.

Answering your question 2: You are quite right, unless you also want different model packages for each section. In my case I had a base API and a few derived APIs. I did want to mix them in a single package and, as a consequence, I needed to specify each package.

But as I said: I abandoned the idea of multiple executions in the end, because I couldn't get the relations between the schemas correctly modelled.

reitsma avatar Sep 01 '23 12:09 reitsma

Hi I solved it like this: maven with ant plugin, which performs a for of a list and calls mvn generate-sources with variables, the yaml list takes up one line, and perhaps it is also possible to read it from an external file

It's kind of ugly, but if you keep the models and apiInterfaces in a separate project that you then import into your main project it's not too annoying, if you have 20+ yaml file my solution could be useful

(tested on windows, not tested on linux)

ant-build.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="maven-antrun-" default="foreach-task">
    <target name="foreach-task">
        <!-- Definisci una lista di elementi su cui iterare -->
        <property
            name="yaml-service-list"
            value="yaml1,yaml2,yaml3"
        />

        <!-- Utilizza il costrutto foreach per iterare sugli elementi -->
        <foreach list="${yaml-service-list}" param="yaml-service" target="run-mvn-generate-sources"/>
    </target>

    <!-- Questo è il target che verrà chiamato per ciascun elemento -->
    <target name="run-mvn-generate-sources">
        <property name="mvn.pom" value="-f ./pom.xml"/>
        <property name="mvn.opts" value="-PgenerateSourceYaml -Dyaml-service=${yaml-service} -DavoidRecursiveLoop=true"/>
        <condition property="is-unix">
            <os family="unix"/>
        </condition>
        <if>
            <isset property="is-unix"/>
            <then>
                <property name="run.executable" value="sh"/>
                <property name="mvn.command" value="-c 'mvn ${mvn.pom} ${mvn.opts} generate-sources'"/>
            </then>
            <else>
                <property name="run.executable" value="cmd"/>
                <property name="mvn.command" value="/c mvn ${mvn.pom} ${mvn.opts} generate-sources"/>
            </else>
        </if>
        <echo level="info">${run.executable}> ${mvn.command}</echo>
        <exec dir="." executable="${run.executable}" failonerror="true">
            <arg line="${mvn.command}"/>
        </exec>
    </target>
</project>

Maven

to execute only with right profile

  <profiles>
    <profile>
      <id>generateSourceYaml</id>
      <activation>
        <activeByDefault>false</activeByDefault>
      </activation>
      <build>
        <plugins>
          <plugin>
            <groupId>org.openapitools</groupId>
            <artifactId>openapi-generator-maven-plugin</artifactId>
            <version>${openapi-generator.version}</version>
            <executions>
              <execution>
                <id>GenerateSpringAPIs</id>
                <goals>
                  <goal>generate</goal>
                </goals>
                <phase>generate-sources</phase>
                <configuration>
                  <generatorName>spring</generatorName>
                  <inputSpec>${project.basedir}/src/main/resources/yaml/${yaml-service}.yaml</inputSpec>
                  <output>${project.build.directory}/generated-sources/</output>
                  <modelPackage>com.mycompany.openapi.model.${yaml-service}</modelPackage>
                  <apiPackage>com.mycompany.openapi.service</apiPackage>
                  <configOptions>
                    <!--global config-->
                    <library>spring-boot</library>
                    <!--model config-->
                    <hideGenerationTimestamp>true</hideGenerationTimestamp>
                    <serializableModel>true</serializableModel>
                    <openApiNullable>false</openApiNullable>
                    <dateLibrary>java8</dateLibrary>
                    <!--api config-->
                    <useTags>true</useTags>
                    <delegatePattern>false</delegatePattern>
                    <implicitHeaders>true</implicitHeaders>
                    <useResponseEntity>true</useResponseEntity>
                    <useFeignClientUrl>false</useFeignClientUrl>
                    <unhandledException>true</unhandledException>
                    <interfaceOnly>true</interfaceOnly>
                    <skipDefaultInterface>true</skipDefaultInterface>
                  </configOptions>
                </configuration>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
    </profile>

  </profiles>

to execute with any profile (or starting profile)

<plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <executions>
          <execution>
            <id>generate-sources-all-yaml</id>
            <goals>
              <goal>run</goal>
            </goals>
            <phase>generate-sources</phase>
            <configuration>
              <target>
                <taskdef resource="net/sf/antcontrib/antcontrib.properties" classpathref="maven.plugin.classpath"/>
                <if>
                  <isset property="avoidRecursiveLoop"/>
                  <then>
                    <echo level="info">avoid recursive loop</echo>
                  </then>
                  <else>
                    <echo level="info">run ant-build.xml</echo>
                    <ant antfile="ant-build.xml"/>
                  </else>
                </if>
              </target>
            </configuration>
          </execution>
        </executions>
        <dependencies>
          <dependency>
            <groupId>org.apache.ant</groupId>
            <artifactId>ant</artifactId>
            <version>1.10.14</version>
          </dependency>
          <dependency>
            <groupId>org.apache.ant</groupId>
            <artifactId>ant-jsch</artifactId>
            <version>1.10.14</version>
          </dependency>
          <dependency>
            <groupId>ant-contrib</groupId>
            <artifactId>ant-contrib</artifactId>
            <version>1.0b3</version>
          </dependency>
        </dependencies>
      </plugin>

If there was a smarter solution I would love it

Pierre491 avatar Sep 14 '23 01:09 Pierre491

Hi, I was able to generate sources for two different specs. I added a second execution goal and specified different output directories for the specs using the option from the config:

<executions>
  <execution>
    <id>api-1</id>
    <inputSpec>${project.basedir}/api-1.yaml</inputSpec>
    ...
    <output>${project.build.directory}/generated-sources/openapi-1</output>
  </execution>
  <execution>
    <id>api-2</id>
    <inputSpec>${project.basedir}/api-2.yaml</inputSpec>
    ...
    <output>${project.build.directory}/generated-sources/openapi-2</output>
  </execution>
<executions>

SebastianSuchowiak avatar Dec 04 '23 13:12 SebastianSuchowiak

For me, it only processes the first <execution>, the first one is ignored. I use different values. Version is 7.2.0 Update: My fault was to specify <cleanupOutput> on both but as I had the same root folder, this deleted the first generated client.

rupert-jung-mw avatar Feb 21 '24 18:02 rupert-jung-mw

I think it is already supported with inputSpecRootDirectory (works properly with 7.4.0 version)

So the config can look like that:

         <plugin>
            <groupId>org.openapitools</groupId>
            <artifactId>openapi-generator-maven-plugin</artifactId>
            <version>${openapi-generator-maven-plugin.version}</version>
            <executions>
                <execution>
                    <id>Generate API from multiple YAML files</id>
                    <goals>
                        <goal>generate</goal>
                    </goals>
                    <configuration>
                        <inputSpecRootDirectory>${project.basedir}/src/main/resources/folder-with-api-yamls</inputSpecRootDirectory>
                        <generatorName>spring</generatorName>
                        <generateApiTests>false</generateApiTests>
                        <generateModelTests>false</generateModelTests>
                        <configOptions>
                            <sourceFolder>src/main/java</sourceFolder>
                            <basePackage>com.mycompany.generated</basePackage>
                            <configPackage>com.mycompany.generated</configPackage>
                            <modelPackage>com.mycompany.generated</modelPackage>
                            <apiPackage>com.mycompany.generated</apiPackage>
                            <useSpringBoot3>true</useSpringBoot3>
                            <interfaceOnly>true</interfaceOnly>
                            <skipOverwrite>true</skipOverwrite>
                            <dateLibrary>java8</dateLibrary>
                            <java8>false</java8>
                            <useTags>true</useTags>
                            <defaultInterfaces>false</defaultInterfaces>
                            <implicitHeaders>false</implicitHeaders>
                        </configOptions>
                    </configuration>
                </execution>
            </executions>
        </plugin>

valb3r avatar Mar 26 '24 13:03 valb3r

inputSpecRootDirectory

Do you know how to use inputSpecRootDirectory with openapi-generator-cli

minesunny avatar Apr 15 '24 00:04 minesunny

@minesunny openapi-generator generate -g spring -o out --input-spec-root-directory <MY-DIRECTORY-PATH> worked well for me (openapi-generator version 7.4.0)

npm based generator works with that as well:

"openapi-gen-merged": "openapi-generator-cli generate -g typescript-angular -o src/app/api2 --input-spec-root-directory <MY-DIRECTORY-PATH>",

valb3r avatar Apr 15 '24 07:04 valb3r

@minesunny openapi-generator generate -g spring -o out --input-spec-root-directory <MY-DIRECTORY-PATH> worked well for me (openapi-generator version 7.4.0)

npm based generator works with that as well:

"openapi-gen-merged": "openapi-generator-cli generate -g typescript-angular -o src/app/api2 --input-spec-root-directory <MY-DIRECTORY-PATH>",

thank you,it does work

minesunny avatar Apr 16 '24 01:04 minesunny

I think it is already supported with inputSpecRootDirectory (works properly with 7.4.0 version)

This indeed works but all the generated classes are heaped onto a single package. Is there any way to have separate packages for separate source files?

davidkubecka-ext43694 avatar Jun 27 '24 09:06 davidkubecka-ext43694

This doesn't work for v2.0 specs https://github.com/OpenAPITools/openapi-generator/issues/20352

mridang avatar Mar 12 '25 11:03 mridang