openapi codegen - generating List<byte[]> instead of byte[] - java

I have an application with the following structure (simplified for brevity):
project structure
myApp
src/main/java/
com.something
controllers
UserApi
UserController
model
UpsertRequest
pom.xml
myApp-generated-client
target
pom.xml
UserApi
#PutMapping(USER_UPSERT_PATH)
UpsertResponse userUpsert(
#RequestBody UpsertRequest upsertRequest,
#RequestHeader(name = COMPRESSED_DATA, required = false) byte[] compressedData);
pom.xml (myApp):
<plugin>
<groupId>com.github.kongchen</groupId>
<artifactId>swagger-maven-plugin</artifactId>
<version>3.1.8</version>
<configuration>
<skipSwaggerGeneration>false</skipSwaggerGeneration>
<apiSources>
<apiSource>
<schemes>http,https</schemes>
<swaggerDirectory>
${project.basedir}/../myApp-generated-client/
</swaggerDirectory>
<locations>
<location>com.something.controllers</location>
</locations>
<springmvc>true</springmvc>
<outputFormats>json</outputFormats>
<attachSwaggerArtifact>true</attachSwaggerArtifact>
</apiSource>
</apiSources>
</configuration>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
pom.xml (myApp-generated-client)
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>4.1.2</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>
${project.basedir}/swagger.json
</inputSpec>
<supportingFilesToGenerate>
ApiClient.java,JSON.java,OAuth.java,Authentication.java,ApiKeyAuth.java,HttpBasicAuth.java,ApiException.java,ApiResponse.java,Configuration.java,Pair.java,RFC3339DateFormat.java,StringUtil.java,CustomInstantDeserializer.java,Client.java
</supportingFilesToGenerate>
<skipValidateSpec>true</skipValidateSpec>
<apiPackage>com.something.api</apiPackage>
<generatorName>java</generatorName>
<templateDirectory>${project.basedir}/templates</templateDirectory>
<generateApiTests>false</generateApiTests>
<generateModelTests>false</generateModelTests>
<generateModels>true</generateModels>
<configOptions>
<library>jersey2</library>
</configOptions>
<typeMappings>Resource=File</typeMappings>
<importMappings>
<importMapping>UpsertRequest=com.something.model.upsertRequest</importMapping>
</importMappings>
<modelPackage>com.something.api</modelPackage>
<modelsToGenerate/>
</configuration>
</execution>
</executions>
</plugin>
The problem is after compiling, my generated client keeps using List<byte[]> instead of byte[] for the upsert endpoint:
public ApiResponse<UpsertResponse> userUpsertWithHttpInfo(List<byte[]> compressedData, UpsertRequest body) throws ApiException
I read here that
Arrays become lists when you use the Swagger Codegen tool to generate Java clients.
For example, a field of type String[] becomes List.
Is there any way of overcoming this limitation (i.e., some converter class or some annotation)? For that matter, is there any good explanations/documentation about how to use converters to resolve types?
Update:
After seeing this link, I added the following code and updated my generated-client pom, but am getting the same results:
package com.something.converter;
public class ByteArrayFixerModelConverter extends AbstractModelConverter {
public ByteArrayFixerModelConverter() {
super(Json.mapper());
}
#Override
public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterator<ModelConverter> chain) {
if (isByteArray(type)) {
//bypass the chain! It would convert the ByteArrayProperty to an Array of ByteArrayProperty (bug in ModelModifier I think)
return new ByteArraySchema();
}
return chain.hasNext()
? chain.next().resolve(type, context, chain)
: null;
}
private boolean isByteArray(AnnotatedType annotatedType) {
Type type = annotatedType.getType();
boolean ret = type instanceof Class && type == byte[].class;
if (!ret && type instanceof ArrayType) {
ArrayType at = (ArrayType) type;
JavaType contentType = at.getContentType();
if (contentType instanceof SimpleType) {
SimpleType st = (SimpleType) contentType;
ret = st.getRawClass() == byte.class;
}
}
return ret;
}
}
pom.xml (myApp-generated-client, updated)
<plugin>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-maven-plugin</artifactId>
<version>2.1.1</version>
<configuration>
<modelConverterClasses>com.something.converter.ByteArrayFixerModelConverter</modelConverterClasses>
</configuration>
</plugin>
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
... //same as before
</plugin>

Related

Invalid signature file digest for Manifest main attributes after maven shade packaging

following this example and this guide I created a program that works in Eclipse development environment, however if I try to package it via maven (mvn clean package) and run it standalone I get this error:
Error: A JNI error has occurred, please check your installation and
try again Exception in thread "main" java.lang.SecurityException:
Invalid signature file digest for Manifest main attributes
I tried several ways (including using other maven plugin, like maven-assembly-plugin with jar-with-dependencies descriptorRef), without success getting different error.
this is my main (and only) class:
package it.factory.pub;
import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.google.api.core.ApiFuture;
import com.google.api.core.ApiFutureCallback;
import com.google.api.core.ApiFutures;
import com.google.api.gax.rpc.ApiException;
import com.google.cloud.pubsub.v1.Publisher;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.protobuf.ByteString;
import com.google.pubsub.v1.PubsubMessage;
import com.google.pubsub.v1.TopicName;
public class PublishWithErrorHandler {
//
private static final Logger log = LogManager.getLogger(PublishWithErrorHandler.class);
//
public static void main(String... args) throws Exception {
String projectId = args[0];
String topicId = args[1];
String[] messages = Arrays.copyOfRange(args, 2, args.length);
log.info("projectId: " + projectId);
log.info("projectId: " + topicId);
log.info("messages: " + Arrays.toString(messages));
publishWithErrorHandlerExample(projectId, topicId, messages);
}
private static void publishWithErrorHandlerExample(String projectId, String topicId, String[] messages) throws IOException, InterruptedException {
TopicName topicName = TopicName.of(projectId, topicId);
Publisher publisher = null;
try {
// Create a publisher instance with default settings bound to the topic
publisher = Publisher.newBuilder(topicName).build();
// List<String> messages = Arrays.asList("first message", "second message");
for (final String message : messages) {
log.info("Publishing message: '" + message + "'");
ByteString data = ByteString.copyFromUtf8(message);
PubsubMessage pubsubMessage = PubsubMessage.newBuilder().setData(data).build();
// Once published, returns a server-assigned message id (unique within the topic)
ApiFuture<String> future = publisher.publish(pubsubMessage);
// Add an asynchronous callback to handle success / failure
ApiFutures.addCallback(future, new ApiFutureCallback<String>(){
#Override
public void onFailure(Throwable throwable) {
if (throwable instanceof ApiException) {
ApiException apiException = ((ApiException) throwable);
// details on the API exception
log.info(apiException.getStatusCode().getCode());
log.info(apiException.isRetryable());
}
log.error("Error publishing message : " + message);
}
#Override
public void onSuccess(String messageId) {
// Once published, returns server-assigned message ids (unique within the topic)
log.info("Published message ID: " + messageId);
}
}, MoreExecutors.directExecutor());
}
} finally {
if (publisher != null) {
// When finished with the publisher, shutdown to free up resources.
publisher.shutdown();
publisher.awaitTermination(1, TimeUnit.MINUTES);
}
}
}
}
and this is my pom.xml file:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>it.factory</groupId>
<artifactId>poc-pubsub</artifactId>
<version>0.0.2-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<customer>a2a</customer>
<log4j2.version>2.12.1</log4j2.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/MANIFEST.MF</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformer" />
<transformer implementation="org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformer" />
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>it.factory.pub.PublishWithErrorHandler</Main-Class>
</manifestEntries>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<!-- pub-sub google -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>libraries-bom</artifactId>
<version>19.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-pubsub</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
I am guessing you already figured this out, but there is a typo in pom.xml, it should be mainClass instead of Main-Class.
Aside from that, what resolved the issue for me was to exclude additional types:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>it.factory.pub.PublishWithErrorHandler</mainClass>
</transformer>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
Since there were some additional SF and RSA files which were triggering the error

How to merge two pom.xml by dom4j

I have two pom.xml come from two similar project. I have to merge these two pom.xml into one and replace the original pom files with it.
The first pom.xml:
...
<dependencies>
<dependency>xxx</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>zzz</groupId>
<artifactId>zzz</artifactId>
<executions>
<execution>...</execution>
</executions>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
...
The other one:
...
<dependencies>
<dependency>yyy</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>zzz</groupId>
<artifactId>zzz</artifactId>
<configuration>
<annotationProcessorPaths>...</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
...
Desired output:
...
<dependencies>
<dependency>xxx</dependency>
<dependency>yyy</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>zzz</groupId>
<artifactId>zzz</artifactId>
<executions>
<execution>...</execution>
</executions>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<annotationProcessorPaths>...</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
...
I've tried to code with Dom4j, it only work with "dependencies". I dont know how to merge "plugins". This is my code:
Document docOfTarget = readXml(outPutFile);
Document docOfSource = readXml(request.getSrcTemplateFile());
Element dependenciesOfTarget = docOfTarget.getRootElement().element("dependencies");
Element dependenciesOfSource = docOfSource.getRootElement().element("dependencies");
List<Node> targetContents = dependenciesOfTarget.content();
List<Node> addContents = new LinkedList<>();
for (Node addContent : dependenciesOfSource.content()) {
if (includeNode(targetContents, addContent)) {
continue;
}
addContents.add(addContent);
}
targetContents.addAll(addContents);
protected final boolean includeNode(List<Node> contents, Node comparatorNode) {
NodeComparator nodeComparator = new NodeComparator();
for (Node content : contents) {
if (nodeComparator.compare(content, comparatorNode) == 0) {
return true;
}
}
return false;
}
How should I do next?
Thanks for your help!

Why jcabi-aspects annotations doesn't work

I have this code:
public static void main(String[] args)
{
testAnnotation();
}
#RetryOnFailure(attempts = 2)
public static void testAnnotation() {
System.out.println("enter here");
int x = 1/0;
}
But it runs the function just one time. This is the output:
enter here
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Main.testAnnotation(Main.java:16)
at Main.main(Main.java:10)
This my pom:
<dependency>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-aspects</artifactId>
<version>0.22.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.2</version>
<scope>runtime</scope>
</dependency>
Plus this plugin:
<plugin>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-maven-plugin</artifactId>
<version>0.14.1</version>
<executions>
<execution>
<goals>
<goal>ajc</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
</dependencies>
</plugin>
My program doesn't recognize any annotation not just the retry. How can I make it recognize the annotations? thank you
Having a similar issue. Added the annotation to a method on a project and tried to call it on a junit test, but it doesn't retry when occurs a mocked exception. It should try to execute twice.
My method
#RetryOnFailure(attempts = 2, //
delay = 5, //
unit = TimeUnit.SECONDS, //
types = {DataRequestException.class, HttpHostConnectException.class})
protected String post(final String postContent, final String url, final CloseableHttpClient httpClient,
final Map<String, String> headers) throws DataRequestException {
final HttpPost httpPost = new HttpPost(url);
headers.forEach(httpPost::addHeader);
httpPost.setEntity(new StringEntity(postContent, StandardCharsets.UTF_8));
try (final CloseableHttpResponse httpResponse = httpClient.execute(httpPost)) {
return handleResponse(httpResponse);
} catch (final DataRequestException e) {
e.setRequest(postContent);
throw e;
} catch (final Exception e) {
throw new DataRequestException(e, postContent);
}
}
My test
CloseableHttpClient httpClient;
#Before
public void setup() {
httpClient = Mockito.mock(CloseableHttpClient.class);
}
#Test
public void test() throws ClientProtocolException, IOException {
assertThrows(DataRequestException.class, () -> {
Mockito.when(httpClient.execute(Mockito.any(HttpPost.class))).thenThrow(DataRequestException.class);
new RestRequest().post("", "http://teste.com", httpClient, new HashMap<>());
});
Mockito.verify(httpClient, Mockito.times(2)).execute(Mockito.any(HttpPost.class));
}
pom.xml
<dependencies>
[...]
<dependency>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-aspects</artifactId>
<version>0.22.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-maven-plugin</artifactId>
<version>0.14.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
<executions>
<execution>
<goals>
<goal>ajc</goal>
</goals>
<configuration>
<aspectsDirectories>
<directory>src/main/java</directory>
</aspectsDirectories>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-aspects</artifactId>
<version>0.22.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.1</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.7</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<encoding>UTF-8</encoding>
<showWeaveInfo>true</showWeaveInfo>
<source>1.8</source>
<target>1.8</target>
<verbose>true</verbose>
<aspectLibraries>
<aspectLibrary>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
<executions>
<execution>
<id>weave-classes</id>
<phase>process-classes</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.3</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</dependencyManagement>

Spring RestDocs generating pages for frontend

My spring RestApi Application successfully generates snippets using RestDoc but I'm not able use the snippets to automatically generate pages to run on frontend. http://localhost/docs returns 404 and no html is generated in static/docs/
so far my pom.xml looks like this
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope>
</dependency>
<plugins>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>1.5.3</version>
<executions>
<execution>
<id>generate-docs</id>
<phase>prepare-package</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html</backend>
<doctype>book</doctype>
<outputDirectory>
${project.build.outputDirectory}/static/docs
</outputDirectory>
<attributes>
<snippets>${project.build.directory}/generated-snippets</snippets>
</attributes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
</configuration>
</plugin>
</plugins>
My Junit Api Test looks like this
#AutoConfigureMockMvc
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = AppLoader.class)
public class ApiDocumentationJUnit5IntegrationTest {
#Rule
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("target/generated-snippets");
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
private ObjectMapper objectMapper = new ObjectMapper();
private RestDocumentationResultHandler documentationHandler;
#Before
public void setUp() {
this.documentationHandler = document("{method-name}",
preprocessRequest(removeHeaders("Authorization")),
preprocessResponse(prettyPrint()));
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation))
.build();
}
#Test
public void responseCodeTest() throws Exception {
this.mockMvc.perform(
get("/cms/status")
.accept(MediaType.APPLICATION_JSON)
)
.andExpect(status().isOk())
.andDo(
document("status")
);
}
#Test
public void retrieveDocumentTest() throws Exception {
this.mockMvc.perform(get("/api/active", 1L))
.andExpect(status().isOk()).andDo(
document("active")
);
}
}

How do you filter integers in source files

Anyone know how to filter text outside of quotes in a java file via the resource plugin?
I have a resource filter set with a delimiter of # but I only see the version.designator replaced. This makes me think there's a trick to filtering text outside of quotes in a java file.
Thanks for the help
Peter
Pom
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>update Version file from pom</id>
<phase>generate-resources</phase>
<goals><goal>copy-resources</goal></goals>
<configuration>
<overwrite>true</overwrite>
<delimiters>
<delimiter>#</delimiter>
</delimiters>
<outputDirectory>${project.build.sourceDirectory}/</outputDirectory>
<resources>
<resource>
<directory>${project.basedir}/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
Source
public final class Version {
public static final int MAJOR_VERSION = #version.major#;
public static final String DESIGNATOR_VERSION = "#version.designator#";
Result
public final class Version {
public static final int MAJOR_VERSION = #version.major#;
public static final String DESIGNATOR_VERSION = "BETA";

Categories

Resources