OpenAPI Codegen "oneOf": Java classes not generated correctly - spring-boot

TLDR:
OpenAPI offers 'oneOf' property. The resulting java classes created do not seem to allow one of the possible instances.
Details:
I am creating the spring/java server side rest api code using OpenAPI maven plugin.
The request class is not such that the passed object is not parsed correctly. The following error is printed in the console.
JSON parse error: Could not resolve subtype of [simple type, class com.model.Issuer]: missing type id property 'type' (for POJO property 'issuer'); nested exception is com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve subtype of [simple type, class com.Issuer]: missing type id property 'type' (for POJO property 'issuer')\n at [Source: (PushbackInputStream); line: 3, column: 15] (through reference chain: com.IssueCredentialRequest[\"credential\"]->com.Credential[\"issuer\"])
The reason seem to be that the generated class does not have subtypes list in the annotation:
package com.sphereon.vdp.vc.service.model;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
})
public interface OneOfIssuer {
}
There is another class which is generated correctly. The reason that this one is generated correctly is probably that this one deals with non-primitive types.
package com.sphereon.vdp.vc.service.model;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(value = VerifyPresentationRequest.class, name = "VerifyPresentationRequest"),
#JsonSubTypes.Type(value = ProoflessVerifyPresentationRequest.class, name = "ProoflessVerifyPresentationRequest")
})
public interface OneOfpresentationsVerifyBody {
}
Can someone point to how to fix the code generation for primitive types?
<plugin>
<groupId>io.swagger.codegen.v3</groupId>
<artifactId>swagger-codegen-maven-plugin</artifactId>
<version>3.0.33</version>
<dependencies>
<dependency>
<groupId>com.github.jknack</groupId>
<artifactId>handlebars</artifactId>
<version>4.3.0</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>vc-rest-api-issuer-source-generation</id>
<goals>
<goal>generate</goal>
</goals>
<phase>generate-sources</phase>
<configuration>
<inputSpec>${pom.basedir}/specifications/issuer.yml</inputSpec>
<language>spring</language>
<apiPackage>com.company.vdp.vc.service.api</apiPackage>
<modelPackage>com.company.vdp.vc.service.model</modelPackage>
<artifactVersion>${project.version}</artifactVersion>
<generateModels>true</generateModels>
<generateApis>true</generateApis>
<generateModelDocumentation>true</generateModelDocumentation>
<generateSupportingFiles>true</generateSupportingFiles>
<verbose>${openapi-codegen-verbose}</verbose>
<output>${project.basedir}/target/generated-sources/java/api</output>
<ignoreFileOverride>${project.basedir}/target/generated-sources/java/api/.swagger-codegen-ignore</ignoreFileOverride>
<configOptions>
<delegatePattern>true</delegatePattern>
<dateLibrary>java8</dateLibrary>
<useTags>true</useTags>
</configOptions>
</configuration>
</execution>
<execution>
<id>vc-rest-api-verifier-source-generation</id>
<goals>
<goal>generate</goal>
</goals>
<phase>generate-sources</phase>
<configuration>
<inputSpec>${pom.basedir}/specifications/verifier.yml</inputSpec>
<language>spring</language>
<apiPackage>com.company.vdp.vc.service.api</apiPackage>
<modelPackage>com.company.vdp.vc.service.model</modelPackage>
<artifactVersion>${project.version}</artifactVersion>
<generateModels>true</generateModels>
<generateApis>true</generateApis>
<generateModelDocumentation>true</generateModelDocumentation>
<generateSupportingFiles>true</generateSupportingFiles>
<verbose>${openapi-codegen-verbose}</verbose>
<output>${project.basedir}/target/generated-sources/java/api</output>
<ignoreFileOverride>${project.basedir}/target/generated-sources/java/api/.swagger-codegen-ignore</ignoreFileOverride>
<configOptions>
<delegatePattern>true</delegatePattern>
<dateLibrary>java8</dateLibrary>
<useTags>true</useTags>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
Following is the spec file that I am using without editing.
https://github.com/w3c-ccg/vc-api/blob/main/components/Issuer.yml
https://github.com/w3c-ccg/vc-api/blob/main/verifier.yml

Related

With the Maven Swagger code generation plugin, how can I generated DTOs from schemas with proper required annotations (e.g. #NotNull)?

I'm using the Maven swagger codegen plugin (v 3.0.17). I was curious how, if possible, would I configure the plugin so that when my DTOs are generated from my defined schemas, the required attributes are marked with "#NotNull" or "#NotEmpty" annotations. I have this defined in my inputSpec .yml
...
components:
...
schemas:
...
MyObjectDTO:
type: object
properties:
id:
type: integer
format: int32
readOnly: true
groupId:
type: integer
format: int64
required: true
...
required:
- groupId
description: my object
The generated DTO looks like
#Schema(description = "my object")
#Validated
#javax.annotation.Generated(value = "com.myco.codegen.SpringCodegen", date = "2022-07-22T15:18:11.693263100-05:00[America/Chicago]")
public class MyObjectDTO {
,,,
#JsonProperty("groupId")
private Integer groupId = null;
When this DTO is passed to a Spring REST controller which is #Validated, if the "groupId" field isn't populated, I would like validation to fail, and ultimately a 400 (bad request) error to be returned.
Maven plugin configuration is
<configuration>
<language>com.myco.myproject.mypackage.swagger.codegen.OAS3SpringCodegen
</language>
<apiPackage>com.myco.myproject.mypackage.api
</apiPackage>
<modelPackage>com.myco.mypackage.api.model
</modelPackage>
<languageSpecificPrimitives>true</languageSpecificPrimitives>
<generateApis>true</generateApis>
<generateApiTests>false</generateApiTests>
<generateModelTests>false</generateModelTests>
<generateApiDocumentation>true</generateApiDocumentation>
<generateModels>true</generateModels>
<generateSupportingFiles>false</generateSupportingFiles>
<importMappings>
<importMapping>LocalDateTime=OffsetDateTime</importMapping>
</importMappings>
<configOptions>
<throwsException>true</throwsException>
<interfaceOnly>true</interfaceOnly>
<java8>false</java8>
<dateLibrary>java8</dateLibrary>
<sourceFolder>.</sourceFolder>
<useTags>true</useTags>
</configOptions>
</configuration>
Try adding the org.springframework.boot:spring-boot-starter-validation
(or just javax.validation:validation-api / jakarta.validation:jakarta.validation-api) dependency to your project and enable useBeanValidation in the plugin configuration:
<configuration>
...
<configOptions>
...
<useBeanValidation>true</useBeanValidation>
</configOptions>
</configuration>
Also, try changing the language parameter to the standard <language>java</language> or <language>spring</language>.

spring-boot-maven-plugin #ConditionalOnProperty

I am trying to use #ConditionalOnProperty while starting the application with the spring-boot-maven-plugin.
If I start the project with eclipse I just have to add -Dexample=true to the vm arguments.
#ConditionalOnProperty( name = "example", havingValue = "true", matchIfMissing = false )
I tried to do the same with the spring-boot-maven-plugin:
<jvmArguments>-Dexample=true</jvmArguments>
<jvmArguments>-Dspring-boot.run.arguments="--example=true"</jvmArguments>
<arguments>
<argument>-Dexample=true</argument>
<argument>-Dspring-boot.run.arguments="--example=true"</argument>
</arguments>
but none of these works.
If I add a profile
<jvmArguments>-Dspring.profiles.active=exampleProfile</jvmArguments>
which contains the argument example:true it works.
EDIT:
The exact argument is
<arguments>
<argument>openapi.offline=true</argument>
</arguments>
but the property is still not found
- #ConditionalOnProperty (openapi.offline=true) did not find property 'offline'
My conditional:
#ConditionalOnProperty( prefix = "openapi", name = "offline", havingValue = "true", matchIfMissing = false )
Solution:
I had multiple
<jvmArguments> -example1 </jvmArguments>
<jvmArguments> -example2 </jvmArguments>
but they override each other. So I had to put them all in one jvmarguments field
<jvmArguments> -example1 -example2 </jvmArguments>
you don't need the -D
Try this:
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>....</version>
<configuration>
<arguments>
<argument>--openapi.offline=true</argument>
</arguments>
</configuration>

Setting descriptions for mojos and parameters in maven plugin

I am writing a maven plugin and I would like to add some documentation for the available goals and parameters.
When I run mvn help:describe -Dplugin=myplugin -Ddetail it prints out available goals and parameters. However it lists (no description) everywhere. From searching the internet I could not figure out where such a description is to be set.
For reference, my plugin is written in scala and looks roughly like this.
import org.apache.maven.plugins.annotations.{ Component, Parameter }
class MyMojo extends AbstractMojo {
#Parameter(defaultValue = "false", readonly = false)
private var skipFormatting: Boolean = _
}
So my question would be: where can set a description, such that it will show up with mvn help:describe -Dplugin=myplugin?
I strongly recommend to use the following:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<id>default-descriptor</id>
<phase>process-classes</phase>
</execution>
<execution>
<id>generate-helpmojo</id>
<goals>
<goal>helpmojo</goal>
</goals>
</execution>
</executions>
</plugin>
which will generate the help part during the build process. And yes you have to add some javadoc like this:
#Mojo(name = "failure", defaultPhase = LifecyclePhase.NONE,
requiresDependencyResolution = ResolutionScope.NONE, threadSafe = true)
public class FailureMojo extends AbstractMojo {
I don't understand why you haven't have any annotations on your Mojo?
The documentation like this: https://maven.apache.org/plugins/maven-install-plugin/plugin-info.html will be generated from the javadoc on parameters etc. https://github.com/apache/maven-install-plugin/blob/master/src/main/java/org/apache/maven/plugins/install/InstallMojo.java#L69
Based on examples when running the help:describe on familiar plugins like the maven-jar-plugin, tt appears it's based on the Javadoc of the Mojo class.

Updating Apache Felix SCR to OSGi Declarative Services R6 - #Property

I'm updating from Apache Felix SCR Annotations to OSGi DS R6 ones and the one is causing me more problem is the #Property inside the class.
Before I had:
#Component (immediate = true)
#Service (A.class)
public class AImpl implements A
{
#Property (intValue = 604800)
public static final String A = "a";
...
}
Now I have:
#Component (service = A.class, immediate = true)
#Designate (ocd = Configuration.class)
public class AImpl implements A
{
...
}
and
#ObjectClassDefinition (name = "Bla")
public #interface Configuration
{
#AttributeDefinition (name = "A", type = AttributeType.INTEGER)
int A() default 604800;
}
The most bizarre thing on all of this is:
Before, I could see my AImpl class as a component.
Now, I couldn't see my AImpl class as a component and everyone who uses it cannot start because of unsatisfied references.
How come changing just configurations like this can cause this behaviour ? Maybe I'm missing something ?
The stranger part on all of this is my xml is inside the .jar and seems ok.
The scr:info is getting me nullpointer exception and I cannot see my component, meaning the scr:list will no help in anything.
XML BELLOW:
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.3.0" name="AImpl" immediate="true" activate="init" deactivate="stop">
<implementation class="AImpl"/>
<service>
<provide interface="A"/>
</service>
<reference name="Bla1" interface="Bla1Service" bind="bindBla1Service" unbind="unbindBla1Service"/>
<property name="PROP.EVENT.INTERVAL" type="Long" value="900000"/>
</scr:component>
Ps.: The classes are with strange names and so on because it's from a private company.
STACKTRACE:
2017-12-11T16:40:27.689+0100 [Framework Event Dispatcher] ERROR o.o.p.l.l.internal.FrameworkHandler:144 frameworkEvent FrameworkEvent ERROR - org.apache.felix.scr
org.osgi.framework.BundleException: The activator org.apache.felix.scr.impl.Activator for bundle org.apache.felix.scr is invalid
at org.eclipse.osgi.framework.internal.core.AbstractBundle.loadBundleActivator(AbstractBundle.java:172)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.start(BundleContextImpl.java:679)
at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:381)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.updateWorker(AbstractBundle.java:645)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.update(AbstractBundle.java:592)
at org.apache.felix.webconsole.internal.core.UpdateHelper.doRun(UpdateHelper.java:60)
at org.apache.felix.webconsole.internal.core.BaseUpdateInstallHelper.doRun(BaseUpdateInstallHelper.java:93)
at org.apache.felix.webconsole.internal.core.UpdateHelper.doRun(UpdateHelper.java:70)
at org.apache.felix.webconsole.internal.core.BaseUpdateInstallHelper.run(BaseUpdateInstallHelper.java:123)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.ClassCastException: org.apache.felix.scr.impl.Activator cannot be cast to org.osgi.framework.BundleActivator
at org.eclipse.osgi.framework.internal.core.AbstractBundle.loadBundleActivator(AbstractBundle.java:167)
... 9 common frames omitted
Part of POM.XML who install on karaf my bundles:
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-kar-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<inherited>false</inherited>
<configuration>
<includeScope>runtime</includeScope>
<prependGroupId>true</prependGroupId>
<excludeTransitive>true</excludeTransitive>
<artifactItems>
<artifactItem>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.framework</artifactId>
<version>${org.osgi.framework.version}</version>
</artifactItem>
<artifactItem>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<version>${org.osgi.core.version}</version>
</artifactItem>
<artifactItem>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr</artifactId>
<version>${org.apache.felix.scr.version}</version>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
This part looks like the error: service=AImpl.class. Your component should be published as a service using its interface A, not the implementation class.
This normally happens implicitly because the component directly implements interface A, but you have overridden that.
The solution should be to simply delete the service=AImpl.class attribute from the #Component annotation.
Your AImpl class still being a Component. However, now it's a "Configuration" Component, hence it has a #Designate annotation linking to a #ObjectClassDefinition Property class.
Go to the Configuration tab and you should see your Component and its properties.

Generate DDL script at MAVEN build with Hibernate4 / JPA 2.1

It seems like the hibernate3-maven-plugin used to generate DDL create/drop scripts is not compatible any more with Hibernate 4.3 and newer versions (using JPA 2.1).
I use this plugin configuration :
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>hibernate3-maven-plugin</artifactId>
<version>3.0</version>
<executions>
<execution>
<id>generate-sql-schema</id>
<phase>process-sources</phase>
<goals>
<goal>hbm2ddl</goal>
</goals>
<configuration>
<hibernatetool>
<jpaconfiguration persistenceunit="${persistenceUnitName}" />
<hbm2ddl update="true" create="true" export="false"
outputfilename="src/main/sql/schema.sql" format="true"
console="true" />
</hibernatetool>
</configuration>
</execution>
</executions>
</plugin>
But I get the following error :
[ERROR] Failed to execute goal org.codehaus.mojo:hibernate3-maven-plugin:3.0:hbm2ddl (generate-sql-schema) on project my-project: There was an error creating the AntRun task.
An Ant BuildException has occured: java.lang.NoClassDefFoundError: org/hibernate/util/ReflectHelper: org.hibernate.util.ReflectHelper -> [Help 1]
This class as migrated to a new package : org.hibernate.internal.util.ReflectHelper
However i found no clear way to keep generating DDL create scripts at MAVEN build.
There is no hibernate4-maven-plugin, or any other official way to do it.
So what ? Isn't it a main feature that should be supported ? How to do it ?
As Hibernate 4.3+ now implements JPA 2.1 the appropriate way to generate DDL scripts is to use following set of JPA 2.1 properties :
<property name="javax.persistence.schema-generation.scripts.action" value="create"/>
<property name="javax.persistence.schema-generation.create-source" value="metadata"/>
<property name="javax.persistence.schema-generation.scripts.create-target" value="target/jpa/sql/create-schema.sql"/>
A nice summary of others properties and context of schema generation in JPA 2.1 can be found here :
https://blogs.oracle.com/arungupta/entry/jpa_2_1_schema_generation
And official JPA 2.1 specifications here :
https://jcp.org/aboutJava/communityprocess/final/jsr338/index.html
As this will be generated at runtime, you may want to execute this DDL generation at build.
Here is the JPA 2.1 approach to generate this script programmatically :
import java.io.IOException;
import java.util.Properties;
import javax.persistence.Persistence;
import org.hibernate.jpa.AvailableSettings;
public class JpaSchemaExport {
public static void main(String[] args) throws IOException {
execute(args[0], args[1]);
System.exit(0);
}
public static void execute(String persistenceUnitName, String destination) {
System.out.println("Generating DDL create script to : " + destination);
final Properties persistenceProperties = new Properties();
// XXX force persistence properties : remove database target
persistenceProperties.setProperty(org.hibernate.cfg.AvailableSettings.HBM2DDL_AUTO, "");
persistenceProperties.setProperty(AvailableSettings.SCHEMA_GEN_DATABASE_ACTION, "none");
// XXX force persistence properties : define create script target from metadata to destination
// persistenceProperties.setProperty(AvailableSettings.SCHEMA_GEN_CREATE_SCHEMAS, "true");
persistenceProperties.setProperty(AvailableSettings.SCHEMA_GEN_SCRIPTS_ACTION, "create");
persistenceProperties.setProperty(AvailableSettings.SCHEMA_GEN_CREATE_SOURCE, "metadata");
persistenceProperties.setProperty(AvailableSettings.SCHEMA_GEN_SCRIPTS_CREATE_TARGET, destination);
Persistence.generateSchema(persistenceUnitName, persistenceProperties);
}
}
As you can see it's very simple !
Now you can use this in an AntTask, or MAVEN build like this (for MAVEN) :
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<id>generate-ddl-create</id>
<phase>process-classes</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<!-- ANT Task definition -->
<java classname="com.orange.tools.jpa.JpaSchemaExport"
fork="true" failonerror="true">
<arg value="${persistenceUnitName}" />
<arg value="target/jpa/sql/schema-create.sql" />
<!-- reference to the passed-in classpath reference -->
<classpath refid="maven.compile.classpath" />
</java>
</target>
</configuration>
</execution>
</executions>
</plugin>
Note that the official hibernate-maven-plugin also may, or may not, do the trick in some way :
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-maven-plugin</artifactId>
<version>4.3.1.Final</version>
</dependency>
Enjoy ! :)

Resources