Java Inheritance Annotation - java

I didn't understand how works the inheritance annotation on interface. For my case, I use openapi-generator to generate a server interface. After that, I create my own implementation of the interface and it works. I only need to add the #RestController on my implementation class.
The question is :
The #Path annotation on my generate interface don't have #Inherited annotation (like the #Post method), so if I correctly understood I shouldn't have my #Path (and #Post) inherited on my implementation. So I'm wondering why my application work and the path is correct ? Shouldn't I have a 404 ?
The generated interface :
#Path("/esignCallback")
#Api(description = "the esignCallback API")
#javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2022-05-30T18:27:51.487+02:00[Europe/Berlin]")public interface EsignCallbackApi {
#POST
#Consumes({ "application/json" })
#ApiOperation(value = "callback for esign service", notes = "", tags={ "EsignCallback" })
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Successfully received the callback", response = Void.class),
#ApiResponse(code = 404, message = "Request not found", response = Void.class) })
Response esignCallback(#Valid #NotNull CallbackRequestChange callbackRequestChange);
}
My implementation :
#RestController
public class EsignCallback implements EsignCallbackApi {
#Override
public Response esignCallback(
#Valid #NotNull CallbackRequestChange callbackRequestChange) {
// implementation
}
}
thanks in advance for your help.

Related

Swagger V3 Annotation Enum

I am trying to get swagger annotations to generate for the code below. Below is my API endpoint, I removed the response etc to keep this very basic. It takes in a body request of UMQueryRequest.
public void query( #RequestBody(description = "Unified Model Query Request", required = true,
content = #Content(
schema = #Schema(implementation = UMQueryRequest.class))) UMQueryRequest request) {
//Implementation Here:
}
UMQueryRequest has an enum property ModelPurpose inside of it that swagger will not generate.
#Schema(description = "UMQueryRequest")
public class UMQueryRequest {
#JsonProperty
private ModelPurpose modelPurpose;
}
ModelPurpose.class
#Schema(enumAsRef = true, implementation = ModelPurpose.class)
public enum ModelPurpose {
Default {
//Implementation Here
}, Alternative {
//Implementation Here
}
}
Any Ideas on how to make swagger generate this code? I have tried a lot of stack overflow but nothing seems to work. Thanks.

How to hide endpoints from OpenAPI documentation with Springdoc

Springdoc automatically generates a API documentation for all handler methods. Even if there are no OpenAPI annotations.
How can I hide endpoints from the API documentation?
The #io.swagger.v3.oas.annotations.Hidden annotation can be used at the method or class level of a controller to hide one or all endpoints.
(See: https://springdoc.org/faq.html#how-can-i-hide-an-operation-or-a-controller-from-documentation)
Example:
#Hidden // Hide all endpoints
#RestController
#RequestMapping(path = "/test")
public class TestController {
private String test = "Test";
#Operation(summary = "Get test string", description = "Returns a test string", tags = { "test" })
#ApiResponses(value = { #ApiResponse(responseCode = "200", description = "Success" ) })
#GetMapping(value = "", produces = MediaType.TEXT_PLAIN_VALUE)
public #ResponseBody String getTest() {
return test;
}
#Hidden // Hide this endpoint
#PutMapping(value = "", consumes = MediaType.TEXT_PLAIN_VALUE)
#ResponseStatus(HttpStatus.OK)
public void setTest(#RequestBody String test) {
this.test = test;
}
}
Edit:
Its also possible to generate the API documentation only for controllers of specific packages.
Add following to your application.properties file:
springdoc.packagesToScan=package1, package2
(See: https://springdoc.org/faq.html#how-can-i-explicitly-set-which-packages-to-scan)
If you are working with Swagger Api and you want to hide specific endpoint then use #ApiOperation(value = "Get Building",hidden=true) on that endpoint...hidden attribute should be true.
#RestController
#Api(tags="Building")
#RequestMapping(value="/v2/buildings")
public class BuildingsController {
#ApiOperation(value = "Get Building",hidden=true)
#GetMapping(value = "/{reference}")
public Account getBuildings(#PathVariable String reference) {
....
}
Its also possible to generate the API doc only for specific Path.
Add following to your application.yml file:
springdoc:
paths-to-match: /api/**, /v1

JAXB - #XMLTransient fields disappear when returned to UI

My application uses several POJOs in the backend to marshall data from the backend to the UI. The data comes from the DB as a string, it gets mapped using Jackson into our POJOs, and then we return the object in the API call using #Produces(MediaType.APPLICATION_JSON). When migrating the app to JBoss 7 EAP, we noticed that any field marked with #XmlTransient was not getting marshalled into JSON when it was returned to the UI. The POJO object had all the fields populated, but on the UI end they would not show up in the JSON string at all. Example:
//class POJO
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class FetchDataVO {
#XmlTransient
private String Id;
private String name;
#XmlTransient
private String domain;
}
And our API response would look like:
#GET
#Path("/{id}")
#Produces(MediaType.APPLICATION_JSON)
#ApiOperation(value = "getUserById", nickname = "getUserById")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success", response = FetchDataVO.class),
#ApiResponse(code = 401, message = "Unauthorized"),
#ApiResponse(code = 403, message = "Forbidden"),
#ApiResponse(code = 404, message = "Not Found"),
#ApiResponse(code = 500, message = "Failure")})
public #ResponseBody
#Valid fetchDataVO getUserById(
#PathParam("id") String id){
FetchDataVO fetchVO = callDataBase.getUserById(id);
//All the data will be present here, everything is correct so far
log.info("fetchVO contents - " + fetchVO.printDetails());
return fetchVO;
}
Our backend code would print out the POJO with all the fields correct. However, when we call it in our UI, we see the response as:
{"name":null}
The other fields don't even show up. Like I mentioned, this only happened after migrating to version 3.0+ of jackson due to the JBoss upgrade.
Jackson is capable of recognizing JAXB annotations to configure the serialization/deserialization.
Unfortunately, at some point Wildfly/JBoss JAX-RS implementation, RestEasy, enabled this feature by default. So, if your bean is annotated with #XmlRootElement, Jackson will honour #XmlTransient annotations and hence ignore the field.
As a workaround to disable it, you can use a JAX-RS ContextResolver to configure the Jackson ObjectMapper without this feature.
To get a plain ObjectMapper just add something like this on your REST module:
#Provider
public class JacksonObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
private final ObjectMapper mapper;
public JacksonObjectMapperContextResolver() {
mapper = new ObjectMapper();
// additional configuration here if needed
}
#Override
public ObjectMapper getContext(Class<?> type) {
return mapper;
}
}

Using different jackson ObjectMappers for seperate RequestMapping

I have a single #RequestMapping that consumes a custom MIME type. The request uses an ObjectMapper bean defined in the #Configuration to enabled the JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER.
This feature allows typically invalid json (treating backslashes as a non-special character) to be consumed, which is a requirement of this particular #RequestMapping to allow google encoded polylines to be parsed directly. However this means that this ObjectMapper is now being used for ALL of my #RequestMapping when it is really only a requirement for one.
Is there a way to differentiate the ObjectMapper being used for each #Controller or #RequestMapping?
Object Mapper Bean
#Bean
public ObjectMapper objectMapper() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.featuresToEnable(
JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER);
return builder.build();
}
Request Mapping Interface Method
#ApiOperation(value = "Returns the toll cost breakdown for a journey", notes = "", response = TotalCost.class, tags={ "pricing", })
#ApiResponses(value = {
#ApiResponse(code = 200, message = "successful operation", response = TotalCost.class) })
#RequestMapping(value = "/pricing/journeyCost",
produces = { "application/json" },
consumes = { "application/vnd.toll-pricing+json" },
method = RequestMethod.POST)
ResponseEntity<TotalCost> getTollBreakdownFromEncodedPoly(#ApiParam(value = "Request object representing the journey" ,required=true ) #RequestBody Journey body);
I found the answer in another stackoverflow question linked to me by another user - https://stackoverflow.com/a/45157169/2073800
I just had to add the following #Bean to my #Configuration
#Bean
public HttpMessageConverters customConverters() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.featuresToEnable(
JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER);
final AbstractJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(builder.build());
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.valueOf("application/vnd.toll-pricing+json")));
return new HttpMessageConverters(converter);
}
If you've a custom MIME type, then you can register a custom HttpMessageConverter that uses a special ObjectMapper for your MIME type, and returns false from canRead/canWrite for regular MIME types. You register your custom HttpMessageConverter like so:
#EnableWebMvc
#Configuration
#ComponentScan
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(
List<HttpMessageConverter<?>> converters) {
messageConverters.add(myCustomMessageConverter());
super.configureMessageConverters(converters);
}
}
Think of it as related to content negotiation, and not related to URL mapping; URL mapping (#RequestMapping) is meant for finding a handler, not for choosing what marshaller/unmarshaller to use.

Best practices incoming object message inheritance using JAX-RS

I was wondering if there is a good solution to my problem, or is there good practices, on handling different messages from an object hierarchy.
So in short: I have an object hierarchy, lets say:
interface IMessage
abstract Message implements IMessage
class SimpleMessage extends Message
class ReportMessage extends SimpleMessage
class CostReportMessage extends ReportMessage
class IncomeReportMessage extends ReportMessage
... (like 3 other types, similar to the CostReportMessage)
So what I would like is to have one incoming JAX-RS endpoint method, since most of the handler code is the same for the classes, but we need some conditional parts in the code.
E.g:
#POST
#Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
#Produces({ MediaType.APPLICATION_XML + "; charset=UTF-8", MediaType.APPLICATION_JSON + "; charset=UTF-8"})
public Response createReport( final ReportMessage, #Context HttpHeaders headers ) {
...
...
}
So we have some methods like the one above, but I would like to make it one, like receive an incoming IMessage, and later handle it as the object's class needs it.
Do you have any advice on this problem? Or do you know any best practices, on how to solve this kind of problem?
You can use the parent class as the consumed object then copy all the properties using BeanUtils.copyProperties() to the new dedicated message object based on certain conditions, eg: you have messageTypeID variable in SimpleMessage to tell what kind of message it should be.
#POST
#Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
#Produces({ MediaType.APPLICATION_XML + "; charset=UTF-8", MediaType.APPLICATION_JSON + "; charset=UTF-8"})
public Response createReport( SimpleMessage message, #Context HttpHeaders headers ) {
SimpleMessage convertedMessage;
if(//condition//) {
convertedMessage = new ReportMessage();
BeanUtils.copyProperties(convertedMessage, message);
} else { ... }
...
}
Although I don't find the following solution "nice", or "elegant", but this seems to work:
step 1. create a function
private IMessage parseRequest(String request) throws IOException, JAXBException{
StringReader reader = new StringReader(request);
// unmarshaller here is a simple JAXB Unmarshaller for the message package
IMessage message = (SimpleMessage) unmarshaller.unmarshal(reader);
return message;
}
step 2. modify jaxb entry point
#POST
#Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
#Produces({ MediaType.APPLICATION_XML + "; charset=UTF-8", MediaType.APPLICATION_JSON + "; charset=UTF-8"})
public Response createReport( final String request, #Context HttpHeaders headers ) {
IMessage message = parseRequest(request);
...
...
return response;
}
So I think, this is because of the following (and this is only my theory, but I don't know where to look for the truth):
JAX-RS simply checks for the type, and creates an Unmarshaller correspondent to the type given in the method signature.
JAX-RS will fail on giving IMessage in method param (since the interface doesn't have a constructor)
Since the Unmarshaller created by JAX-RS will only create a context, that is aware of SimpleMessage, it won't be able to unmarshal any child class, e.g. CostReportMessage
The good solution would be providing a custom MessageBodyReader (#Provider annotation), but I couldn't get it to work
This may be because of how the jersey finds a content provider for a given type (goes from the most generic to the most specific, and because it finds a more generic content provider, than mine, it won't use it)

Categories