how to generate URI parameter for dynamic feign basePath using swagger codegen? - swagger

I have a pretty simple question :)
According to feign documents, they are supporting in changing the basePath of a feign client object dynamically by passing URI parameter trough the api function like so:
GOOD Example:
interface MyClient {
#RequestLine("GET /internal-service")
String internalService(URI baseUrl);
}
The thing is I'm using swagger, who generates my API from a yaml file and adding #Param annotation to all function parameters, which is not good for me.
BAD Example:
interface MyClient {
#RequestLine("GET {baseUrl}/internal-service")
String internalService(#Param("baseUrl") String host);
}
Is there a way to make swagger generator to generate an API with a URI param, without the #Param annotation?
Desired Outcome Example:
interface MyClient {
#RequestLine("POST /internal-service")
String internalService(URI baseUrl, #Param("someParam") String someParam);
}

Initial solution:
After some investigation I came to realize that there is no support in swagger-codegen 2.2.3 for generating URI parameter as part of the client API function.
But there is an option in swagger-codegen-maven-plugin configuration of
"templateDirectory - directory with mustache templates", for giving a path to a folder including mustache templates, which will take the templates in that folder and override the existing with the same name.
example:
<plugin>
<groupId>io.swagger</groupId>
<artifactId>swagger-codegen-maven-plugin</artifactId>
<executions>
<execution>
<id>my-project-api-client-kit</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.build.directory}/my-project-api.yaml</inputSpec>
<language>java</language>
<configOptions>
<dateLibrary>java8</dateLibrary>
<sourceFolder>src/main/java</sourceFolder>
</configOptions>
<modelPackage>my.project.ck.resources.models</modelPackage>
<apiPackage>my.project.ck.resources.interfaces</apiPackage>
<library>feign</library>
<templateDirectory>/myTemplateFolder/</templateDirectory>
</configuration>
</execution>
</executions>
</plugin>
And inside the custom templates folder, put your own api.mustache file with the additional "URI basePath" parameter:
...
{{#returnType}}{{{returnType}}} {{/returnType}}{{^returnType}}void {{/returnType}}{{nickname}}(URI basePath, {{#allParams}}{{^isBodyParam}}{{^legacyDates}}#Param("{{paramName}}") {{/legacyDates}}{{#legacyDates}}#Param(value="{{paramName}}", expander=ParamExpander.class) {{/legacyDates}}{{/isBodyParam}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});
...
Note:
Swagger provides a lot of templates for a variety of uses, make sure to take and modify the correct api.mustache template file according to which you are using.
in the above example I modified (and override) the java.libraries.feign/api.mustache file because this is the file my plugin is configured too (as in the example).

Related

Duplicate headers in restAssured requests in parallel run

I support implemented tests framework and can't change version of tools, so need to resolve it with current version.
The test framework use RestAssered, Cucumber, Serenity.
When tests run in parallel sometimes they are failed due to several Authorization headers in request. In single run, everything is ok.
As I know Serenity just reuse features of the framework. So I think, maybe I configured in wrong way RestAssured.
My flow is:
In Before method I set up
#cucumber.api.java.Before
public void beforeScenario(Scenario scenario) {
RestAssured.baseURI = "https://test.net";
RestAssured.requestSpecification = new
Specification().requestSpecificationWithAdminToken(token);
RestAssured.responseSpecification = new Specification().responseSpecification();
RestAssured.config = RestAssuredConfig.config().decoderConfig(decoderConfig().noContentDecoders());
}
public class Specification {
public RequestSpecification requestSpecificationWithAdminToken(String token) {
return new RequestSpecBuilder().
addHeader("Authorization", "Bearer " + token).
setAccept(ContentType.JSON).
setContentType(ContentType.JSON).
log(LogDetail.ALL).
build();
}
public ResponseSpecification responseSpecification() {
return new ResponseSpecBuilder().
log(LogDetail.ALL).
build();
}
}
Tests like this
SomeBinding bindings = SerenityRest.
given().queryParams(params).
when().get("/api/some/GetEntities").
then().statusCode(HttpStatus.SC_OK).extract().response().as(SomeBinding.class);
For parallel run I use maven-failsafe-plugin with configuration
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<argLine>-Xmx1024m</argLine>
<includes>
<include>${test.include}</include>
</includes>
<excludes>
<exclude>${test.exclude}</exclude>
</excludes>
<parallel>classes</parallel>
<threadCount>${parallel.count}</threadCount>
<perCoreThreadCount>false</perCoreThreadCount>
<rerunFailingTestsCount>1</rerunFailingTestsCount>
</configuration>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
When I run tests in parallel I faced with issue that sometimes requests contain several Authorithation headers.
Authorization=Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImJjYzM3YjliOTM4ODQ3ODJiOWRhYjN
Authorization=Bearer OTM4ODQ3ODJiOWRhYjNmYjI5NDgyYzdjIiwianRpIjoiMjg3M2VjNDA0MmFjNGNkNzk0ODE5NTE0N2I5YzczMjUiL
Accept=application/json, application/javascript, text/javascript, text/json
Content-Type=application/json; charset=UTF-8
Could somebody please give advice how to resolve it?

Generate OpenAPI descriptions from JavaDoc

I have an application which provides an API with JAX-RS (Java API for RESTful Web Services / JSR-311).
For documentation purposes I provide an URL according to the OpenAPI-Specification, which is generated by Eclipse MicroProfile OpenAPI.
Everything is working fine, except the descriptions of the methods and parameters, which I need to add twice - in annotations and in JavaDoc:
/**
* Finds all resources with the given prefix.
*
* #param prefix
* the prefix of the resource
* #return the resources that start with the prefix
*/
#GET
#Path("/find/{prefix}")
#Produces(MediaType.APPLICATION_JSON)
#Operation(description = "Finds all resources with the given prefix")
public List<Resource> find(
#Parameter(description = "The prefix of the resource")
#PathParam("prefix") final String prefix) {
...
}
I know that no runtime library can read the JavaDoc (because it is not part of the class files), which is the main reason for the annotations. But I wonder if there is some other option for one of the OpenAPI generation tools (Swagger, Eclipse MicroProfile OpenAPI, ...), which prevents me from manually syncing the documentation?
In another project for example I'm using a doclet which serializes the JavaDoc and stores it in the class path, to present an Beans API documentation to the user at runtime. But even if I make use of this doclet here, I see no option to provide that JavaDoc descriptions to the OpenAPI libraries during runtime.
I know that I could drop the JavaDoc, if the users of my API use only "foreign languages", as they wouldn't see the JavaDoc anyway. But what happens if the other side of the API is a JAX-RS client? In that case the JavaDoc would be a huge support.
I got it running with Eclipse Microprofile OpenAPI.
I had to define my own OASFilter:
public class JavadocOASDescriptionFilter implements OASFilter {
#Override
public void filterOpenAPI(final OpenAPI openAPI) {
openAPI.getComponents().getSchemas().forEach(this::initializeSchema);
openAPI.getPaths().forEach(this::initializePathItem);
}
private void initializeSchema(final String name, final Schema schema) {
final SerializedJavadoc javadoc = findJavadocForSchema(name);
if (StringUtils.isEmpty(schema.getDescription())) {
schema.setDescription(javadoc.getTypeComment());
}
if (schema.getProperties() != null) {
schema.getProperties().forEach((property, propertySchema) -> {
if (StringUtils.isEmpty(propertySchema.getDescription())) {
propertySchema.setDescription(javadoc.getAttributeComments().get(property));
}
});
}
}
...
}
Then I had to declare that filter in META-INF/microprofile-config.properties:
mp.openapi.filter=mypackage.JavadocOASDescriptionReader
See here for the discussion on this topic: https://github.com/eclipse/microprofile-open-api/issues/485

Using openapi.yaml in springdoc

I can customize OpenAPI from code.
Can I do same over openapi.yaml like swagger-petstore
I create simple springboot project with one #RestController.
I create openapi.yaml and copy it to /src/main/resources/.
But I see default values on open swagger-ui page.
This is available from the FAQ page in the spring-doc documentation.
See What is a proper way to set up Swagger UI to use provided spec.yml? and How can use custom json/yml file instead of generated one ? of the same page.
Example from the FAQ page
Turn off auto-generation in the project property file springdoc.api-docs.enabled=false
Put your yaml file in src/main/resources/static such as src/main/resources/static/myApiFile.yaml
Set the swagger-ui url for the file springdoc.swagger-ui.url=/myApiFile.yaml
Enable the minimal beans configuration
import org.springdoc.core.SpringDocConfigProperties;
import org.springdoc.core.SpringDocConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class SpringDocsConfiguration {
#Bean
SpringDocConfiguration springDocConfiguration() {
return new SpringDocConfiguration();
}
#Bean
public SpringDocConfigProperties springDocConfigProperties() {
return new SpringDocConfigProperties();
}
}
The code below is all what we needed to do to use openapi.yaml specification file instead of the default one that is generated from code.
Explanation:
org.springdoc.webflux.api.OpenApiResource is Controller that handles /v3/api-docs and /v3/api-docs.yaml endpoints. Swagger UI is using that endpoint to show swagger ui page - /swagger-ui.html. You can see the configuration when you hit /v3/api-docs/swagger-config endpoint.
org.springdoc.webflux.api.OpenApiResource bean is registered only if missing. You can see it in SpringDocWebFluxConfiguration. The method that creates the bean is annotated with #ConditionalOnMissingBean. So you just need to extend it and adjust OpenApi specification retrieval (see below).
org.springdoc.webflux.api.OpenApiResource is using getOpenApi() method to retrieve OpenAPI specification (by default the specification is generated based on the class annotation from code). So you just need to override getOpenApi() method and provide the specification from yaml file itself (getYamlMapper() method is also provided for you in the parent classes, so it's really that easy how it is in the file below)
You can see OpenApiResource is in webflux package because we use org.springdoc:springdoc-openapi-webflux-ui, Spring WebFlux. It is done similarly in Spring MVC.
Hope this helps :) When you hit /swagger-ui.html you should see the docs directly from .yaml spec. When you hit /v3/api-docs you should see the specs itself in JSON. When you hit /v3/api-docs.yaml you should see the specs itself in YAML. No Spring Configuration code is needed. Just the controller as you see below :)
Just to be clear. Our OpenAPI spec is in src/main/resources/openapi/api.yaml
package com.your.package;
...imports omitted for readability...
import org.springdoc.webflux.api.OpenApiResource;
#RestController
public class OpenApiController extends OpenApiResource {
#Value("classpath:openapi/api.yaml")
private Resource openAPIResource;
private OpenAPI openAPI;
public OpenApiController(ObjectFactory<OpenAPIBuilder> openAPIBuilderObjectFactory, AbstractRequestBuilder requestBuilder, GenericResponseBuilder responseBuilder, OperationBuilder operationParser, RequestMappingInfoHandlerMapping requestMappingHandlerMapping, Optional<List<OperationCustomizer>> operationCustomizers, Optional<List<OpenApiCustomiser>> openApiCustomisers, SpringDocConfigProperties springDocConfigProperties, Optional<ActuatorProvider> actuatorProvider) {
super(openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, requestMappingHandlerMapping, operationCustomizers, openApiCustomisers, springDocConfigProperties, actuatorProvider);
}
#SneakyThrows
#PostConstruct
public void initOpenAPI() {
openAPI = getYamlMapper().readValue(openAPIResource.getInputStream(), OpenAPI.class);
}
#Override
protected synchronized OpenAPI getOpenApi() {
return openAPI;
}
}
If you need configuration file, you can have a look at the FAQ, documentation:
https://springdoc.org/faq.html#can-i-use-spring-property-with-swagger-annotations
And here is the link for code samples:
https://raw.githubusercontent.com/springdoc/springdoc-openapi/master/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app15/SpringDocApp15Test.java

can't set up swagger with jax-rs

I can't understand, why swagger doesn't work with my spring boot jax-rs app.
I add this dependencies to pom.xml:
<!-- Swagger -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-service-description-swagger</artifactId>
<version>3.3.4</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>swagger-ui</artifactId>
<version>3.1.5</version>
</dependency>
also I set up -> cxf.jaxrs.component-scan=true in application properties,my rest requests:
#Path("/")
#Api("/")
#Service
public interface IService {
#GET
#Path("/health")
#ApiOperation("/health")
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Status getStatus();
#GET
#Path("/info")
#ApiOperation("/info")
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Info getInfo();
}
work as I expected, but I can't get documentation, when I call:
http://localhost:8000/swagger-ui.html
get -> Whitelabel Error Page.
Maybe someone can help me?
just in case here is my properties file:
#cxf url mapping
cxf.path=/api
cxf.jaxrs.component-scan=true
cxf.jaxrs.classes-scan-packages=com,org.apache.cxf.jaxrs.swagger.Swagger2Feature,org.codehaus.jackson.jaxrs,org.apache.cxf.jaxrs.swagger.ui.SwaggerUiResourceLocator
#port
server.port=8000
server.servlet.context-path=/
Can you please share the complete project?
You'll find working samples in https://github.com/apache/cxf/tree/cxf-3.3.4/distribution/src/main/release/samples/jax_rs, if you start a new project I suggest to go with OpenAPI v3 instead of Swagger.

Using oauth2 Service Account within a Java-Webapplication

I'm develop a simple HTML5-Client for DFA-Reporting. For this I use a Google OAuth Service account, which works pretty fine on a client solution. But know I've been in trouble when I want to read the .p12-File within my Webapplication-Backend. Following problem appears:
java.io.IOException: DerInputStream.getLength(): lengthTag=111, too big.
at sun.security.util.DerInputStream.getLength(DerInputStream.java:561)
The application runs on an embedded Tomcat (Spring Boot) and I'm using the following approach the read to file:
File p12File = new java.io.File(this.getClass().getResource("/test-privatekey.p12").toURI());
this.httpTransport = GoogleNetHttpTransport.newTrustedTransport();
credential = new GoogleCredential.Builder().setTransport(httpTransport)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountScopes(SCOPES)
.setServiceAccountPrivateKeyFromP12File(p12File)
.build();
The Client-Solutions read the p12-File like this:
File p12File = new java.io.File("test-privatekey.p12");
Did anybody have a solution, or an solution-idea for this problem?
Many thanks and greetings from Berlin
Exclude p12 files in your POM.
<build>
...
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<nonFilteredFileExtensions>
<nonFilteredFileExtension>p12</nonFilteredFileExtension>
</nonFilteredFileExtensions>
</configuration>
</plugin>
</plugins>
</build>

Resources