I am developing jersey endpoints and I have to do the integration test for this endpoints, my doubt is how can I make the unit test for this endpoint without adding more dependencies to the pom or additional libraries, because it is a restriction. There is a way to achieve that? I am using junit version 5. This is an example of my endpoint. I was using jerseyTest but I had to revert my changes, because of the restriction:
#GET
#Path("/")
#ApiResponses(value = {
#ApiResponse(code = SC_OK, response = HorasPaginatedResult.class,
message = "Successfully retrievedHoras from Labor Service"),
#ApiResponse(code = SC_BAD_REQUEST, response = ErrorMessage.class, message = "The request was malformed or missing a required field"),
#ApiResponse(code = SC_INTERNAL_SERVER_ERROR, response = ErrorMessage.class, message = "There was an issue processing the request and the server has failed out of the action"),
})
#ApiOperation(value = "Used to query the time service for timeclocks.",
response = HorasPaginatedResult.class,
tags = "Horas",
nickname = "get-Horas.all")
public PaginatedResult<Horas> getHorasAll(
#Nullable #ApiParam("the token to the next page of results") #QueryParam("token") String token,
#Nullable #ApiParam(value = "the field to sort by - this may through a 500 error if the index does not exsist", example = "timestamp") #QueryParam("sortBy") String sortBy,
#Nullable #ApiParam("boolean to determine which direction the sort should be in") #QueryParam("sortAscending") Boolean sortAscending,
#Nullable #ApiParam("the maximum number of 'records' to return per page") #QueryParam("recordLimit") Integer recordLimit,
#Nullable #ApiParam(value = "object", example = "%7B%22field%22%") #QueryParam("expression") List<Query.Expression> expressions) {
Query query;
if (token != null) {
query = new Query(token);
} else {
query = new Query(sortBy, recordLimit, sortAscending);
if (expressions != null) {
expressions.forEach(query.getExpressions()::add);
}
}
logger.info("creating query of {}", query);
return HorasService.getHoras(query);
}
private static class HorasPaginatedResult extends PaginatedResult<BanquetLaborHoras> {
}
Any ideas?
Related
I'm in a journey to upgrade swagger-jaxrs2-jakarta 1.6.8 to 2.2.7, I got almost everything working except some object parameters that should be exploded as inputs in the Swagger-ui and them still be interpreted as JSON input... and not as 2 distinct inputs
Java: 17 Springboot: 3.0.0
Jersey: 3.1.0
swagger-jaxrs2-jakarta: 2.2.7
Resource Interface
#Tag(name = "MyResource", description = "MyResource enpoint")
#Path("/")
#RequestMapping
public interface MyResourceAPI {
#GET
#Path("/get/{name}/something")
#Produces(MediaType.APPLICATION_JSON)
#Operation(summary = "MyResource")
#GetMapping(value = "/get/{name}/something")
#ApiResponses(value = {
#ApiResponse(responseCode = "404", description = "Not found"),
#ApiResponse(responseCode = "400", description = "Bad request"),
#ApiResponse(responseCode = "200", description = "Sucesso", content = #Content(schema = #Schema(implementation = MyResourcehResponse.class)))
})
Response search(#Context HttpServletRequest servletRequest, #BeanParam MyCustomRequest myRequest);
}
Resource Impl
#Component
public class MyResourceAPIImpl extends implements MyResourceAPI {
#Override
public Response search(HttpServletRequest servletRequest, MyCustomRequest myRequest) {
#hidden logic
return Response.ok().entity(myResponse).build();
}
}
Request's classes
public class MyCustomRequest extends Request {
}
public class Request {
#BeanParam
#Builder.Default
private Pagination pagination = new Pagination();
}
public class Pagination {
#QueryParam("limit")
#DefaultValue("200")
private Integer limit = 200;
#QueryParam("offset")
#DefaultValue("0")
private Integer offset = 0;
}
Using the version: 1.6.8, swagger-ui shows
After upgrade:
If I remove #QueryParam from Pagination items, they disappear, and if I remove #BeanParam from Pagination declaration, it works as a single JSON input.
I'm without any clue to fix this... anyone already got this issue or something similar and can help me?
I have interfaces generated by Swagger Codegen. It looks like this:
#PostMapping(value = "/ipc/conf", produces = {"application/json", "application/problem+json"}, consumes = {
"application/json"})
default ResponseEntity<CustomResponseEntity> ipcConfPost(
#ApiParam(value = "ID", required = true) #RequestHeader(value = "X-Request-ID", required = true) String xRequestID,
#ApiParam(value = "Value for identifying a single transaction across multiple services up to the backend.", required = true) #RequestHeader(value = "X-Correlation-ID", required = true) String xCorrelationID,
#ApiParam(value = "The payload to transmit", required = true) #Valid #RequestBody IPcData ipcConfData,
#ApiParam(value = "The business context is a general classification for a larger number of requests.") #RequestHeader(value = "X-Business-Context", required = false) String xBusinessContext) {
getRequest().ifPresent(request -> {
for (MediaType mediaType : MediaType.parseMediaTypes(request.getHeader("Accept"))) {
if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
String exampleString = "{ \"id\" : \"id\", \"error\" : \"error\" }";
ApiUtil.setExampleResponse(request, "application/json", exampleString);
break;
}
}
});
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
In the implementation I want to have a full list of request headers (I need some of them in the response) or to be able to get a value of a header that is not listed in the API. The thing is I cannot change the signature of the endpoint since it will cause a major headache in further releases.
So is there any way to achieve this?
You have the request object in your code already so you can get the headers from it. i.e. request.getHeaderNames() then loop through them.
After that, you can add them to the response with
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("key", "value");
ResponseEntity.ok().headers(responseHeaders).body("some body");
I seem to be butting heads with a limiter somewhere. One of my Spring-Boot REST endpoint (POST) parameters (surveyResults) is looking for a string of JSON:
private static final String SURVEY_RESULTS_ENDPOINT = "/survey/results";
#PostMapping(
value = SURVEY_RESULTS_ENDPOINT,
produces = { "application/hal+json", "application/json" }
)
#ApiOperation(value = "Save one survey results")
public Resource<SurveyResult> createSurveyResults(
#ApiParam(value = "who/what process created this record", required = true) #Valid
#RequestParam(value = "recordCreatedBy", required = true) String createdBy,
#ApiParam(value = "was an issue identified", required = true)
#RequestParam(value = "hadFailure", required = true) Boolean hadFailure,
#ApiParam(value = "JSON representation of the results", required = true)
#RequestParam(value = "surveyResults", required = true) String surveyResult
) ...
If I post to this with about 1500 characters, it works. Somewhere just over that and it will fail with a HTTP 400 error bad request. The whole payload is less than 2K with the other parameters.
I just moved from Wildfly to a new server setup. My company is adopting continuous deployment to cloud servers so i don't have much control nor visibility to this new load balanced server. The server is "server": "openresty/1.13.6.2" - any idea what limit I am running into?
Please use #RequestBody instead of #RequestParam.
#RequestBody annotation maps the HTTP request's body to an object. #RequestParam maps the request parameter in the request, which is in the URL and not in the body.
Most browsers have a limitation to the number of characters supported in a request parameter, and you just hit that limit.
What I would suggest is to create a POJO that looks like this
public class Body {
private String createdBy;
private Boolean hadFailure;
private String surveyResult;
// getters and setters
}
Now your controller will be simpler
#PostMapping(
value = SURVEY_RESULTS_ENDPOINT,
produces = { "application/hal+json", "application/json" }
)
public Resource<SurveyResult> createSurveyResults(#RequestBody Body body) {
}
Wherever you are posting, you will have to now post a JSON (Content-Type = application/json) that looks like the following
{ "createdBy" : "foo", "hadFailure" : false, "surveyResult" : "the foo bar"}
I have a spring boot application. I have chosen to implement my controllers as an interface defining the endpoint and its respective implementation (i.e EndpointX, EndpointXController w/ EndpointXController being the implementation). I have all of my annotations for swagger in the interface files to prevent cluttering of the implementation class; however, I see duplicate endpoints on swagger UI as shown below:
This is my docket setup:
#Bean
public Docket customImplementation() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.withMethodAnnotation(RequestMapping.class))
.paths(PathSelectors.ant("/consent/*"))
.build()
.directModelSubstitute(java.time.LocalDate.class, java.sql.Date.class)
.directModelSubstitute(java.time.OffsetDateTime.class, java.util.Date.class)
.apiInfo(apiInfo());
}
How can I tell swagger/swagger-ui to only show one endpoint for the rest service? I.e consent-api-controller wouldn't be shown or picked up by swagger.
Edit: Posted Controller and interface code
#Controller
public class ConsentApiController implements ConsentApi {
#Autowired
private IConsentApiService consentApiService;
private final ObjectMapper objectMapper;
private final HttpServletRequest request;
#Autowired
public ConsentApiController(ObjectMapper objectMapper, HttpServletRequest request) {
this.objectMapper = objectMapper;
this.request = request;
}
#Override
public Optional<ObjectMapper> getObjectMapper() {
return Optional.ofNullable(objectMapper);
}
#Override
public Optional<HttpServletRequest> getRequest() {
return Optional.ofNullable(request);
}
public ResponseEntity getConsent(#ApiParam(value = "Identifier for the consent object to be retrieved", required = true) #Valid #RequestBody ConsentReadRequestParent consentReadRequestParent) {
return consentApiService.getConsent(consentReadRequestParent);
}
public ResponseEntity postConsent(#ApiParam(value = "Populated consent object") #Valid #RequestBody ConsentParentRequest consentObj) {
// Pass request to service where it will be split into DTOs and passed to DAOs and get response
return consentApiService.postConsent(consentObj);
}
public ResponseEntity searchConsent(#Valid #RequestBody SearchParentRequest spr){
return consentApiService.searchConsent(spr);
}
}
#Api(value = "consent")
public interface ConsentApi {
default Optional<ObjectMapper> getObjectMapper() {
return Optional.empty();
}
default Optional<HttpServletRequest> getRequest() {
return Optional.empty();
}
default Optional<String> getAcceptHeader() {
return getRequest().map(r -> r.getHeader("Accept"));
}
#ApiOperation(value = "The Read Consent API is a resource that conforms to a RESTful syntax to retrieve the details of a single consent record.", nickname = "getConsent", notes = "Cannot read without parameters. Minimum of 2 characters in each field. Maximum of 50 characters in each field. Should be able to handle special characters.", response = Consent.class, tags = {"consent",})
#ApiResponses(value = {
#ApiResponse(code = 200, message = "OK", response = Consent.class),
#ApiResponse(code = 400, message = "Bad Request"),
#ApiResponse(code = 405, message = "Method Not Allowed"),
#ApiResponse(code = 500, message = "Internal Server Error"),
#ApiResponse(code = 604, message = "Could Not Retrieve Data for Consent"),
#ApiResponse(code = 714, message = "Consent Not Found Matching Input Values")})
#RequestMapping(value = "/consent/read",
method = RequestMethod.POST,
consumes = {MediaType.APPLICATION_JSON_VALUE},
produces = {MediaType.APPLICATION_JSON_VALUE})
ResponseEntity<?> getConsent(#ApiParam(value = "Identifier for the consent object to be retrieved.", required = true) #Valid #RequestBody ConsentReadRequestParent consentReadRequestParent);
#ApiOperation(value = "The Create Consent API is a resource that conforms to a RESTful syntax to persist a single consent record.", nickname = "postConsent", notes = "<business and backend logic/requirements>", response = ConsentResponseParent.class, tags = {"consent",})
#ApiResponses(value = {
#ApiResponse(code = 200, message = "OK", response = ConsentResponseParent.class),
#ApiResponse(code = 400, message = "Bad Request"),
#ApiResponse(code = 405, message = "Method Not Allowed"),
#ApiResponse(code = 500, message = "Internal Server Error")})
#RequestMapping(value = "/consent/create",
method = RequestMethod.POST,
consumes = {MediaType.APPLICATION_JSON_VALUE},
produces = {MediaType.APPLICATION_JSON_VALUE})
ResponseEntity<?> postConsent(#ApiParam(value = "filled-out consent object") #Valid #RequestBody ConsentParentRequest consentObj);
#ApiOperation(value = "The Search Consent API is a resource that conforms to a RESTful syntax to query consent records.", response = SearchParentResponse.class, tags = {"consent",})
#ApiResponses(value = {
#ApiResponse(code = 200, message = "OK", response = SearchParentResponse.class),
#ApiResponse(code = 400, message = "Bad Request"),
#ApiResponse(code = 405, message = "Method Not Allowed"),
#ApiResponse(code = 500, message = "Internal Server Error")})
#RequestMapping(value = "/consent/search",
method = RequestMethod.POST,
consumes = {MediaType.APPLICATION_JSON_VALUE},
produces = {MediaType.APPLICATION_JSON_VALUE})
ResponseEntity<?> searchConsent(#Valid #RequestBody SearchParentRequest spr);
}
Remove attribute "tags" from #ApiOperation annotation. Adding tags will make the api appear under the context menu as well as under its own separate menu.
Another approach is jus the #Api for the controller class:
#Controller
#Api(tags = "consent")
public class ConsentApiController implements ConsentApi {
Or into the interface:
#Api(value = "consent", tags = "consent")
public interface ConsentApi {
You could explicitly exclude the controllers that should not be shown by moving the interfaces to a different package and using
.apis(RequestHandlerSelectors.basePackage("my.impl.package"))
or by writing your own, custom set of predicates and passing them into .apis().
However this would me more like a workaround. Are you certain there are no #RequestMapping annotations used in the implementing classes? I could not replicate this behaviour locally.
I Have followed the below
blog entry:
http://kingsfleet.blogspot.co.uk/2014/02/transparent-patch-support-in-jax-rs-20.html
https://github.com/jersey/jersey/tree/2.6/examples/http-patch
To create end point to support HTTP "PATCH" method in Jersey 2.6
Dependency Versions:
-Jersey: 2.6
-swagger-jersey2-jaxrs_2.10: 1.3.12
Question?
Why Patch end point is not getting listed as part of the swagger ui documentation?
Analysis:
If I am annotating with this annotation, then documentation for that end point getting generated, but no interaction .
#com.wordnik.swagger.jaxrs.PATCH
Configurations
JerssyApplicationInitializer
packages(true, "com.test.account.endpoint", "com.wordnik.swagger.jaxrs.json");
//Swagger Configuration
register(new ApiListingResourceJSON(), 10);
register(JerseyApiDeclarationProvider.class);
register(JerseyResourceListingProvider.class);
//Genson Converter
register(GensonJsonConverter.class, 1);
register(createMoxyJsonResolver());
I am not sure, if I am missing something, any help or guide will be helpful.
Patch method doscumets:
public static final String PATCH_MEDIA_TYPE = "application/json-patch+json";
#PATCH
//#com.wordnik.swagger.jaxrs.PATCH
#PreAuthorize(userAuthenticationRequire=true)
#Consumes(PATCH_MEDIA_TYPE)
#Path("{id: .\\d+}")
#ApiOperation(value = "Update Client Details in UIM System."
, response = State.class
, notes="Requesting User, should be the owner of the Client."
, consumes = PATCH_MEDIA_TYPE)
#ApiResponses({
#ApiResponse(code = _401, message = "If the access token is invalid.", response = String.class),
#ApiResponse(code = _498, message = "If the access token is expired.", response = String.class),
#ApiResponse(code = _420, message = "If Provided Input is not valid according to requirment specification."),
#ApiResponse(code = _404, message = "If no client/app Found."),
#ApiResponse(code = _200, message = "If Client Account has been Updated successfully. ", response=String.class)
})
public State updateClientDetails(#ApiParam(value="Client Id to be Updated.", required=true) #PathParam(CLIENT_ID) String clientId
, #ApiParam(value = "Updated field and Value.", required = true) final State newState){
//LOG.info("[ENTRY]- Received requst for updating Client {} from System.", clientId);
System.out.println("----->" + someBean.test());
//LOG.info("[EXIT]- Client Id {} Updation has been completed.", clientId);
Test t = new Test();
t.name = "Hello Test";
System.out.println(t.name);
return newState;
}
Take a look at your index.html. That controls which HTTP operations are interactive--by changing it to this:
window.swaggerUi = new SwaggerUi({
url: url,
dom_id: "swagger-ui-container",
supportedSubmitMethods: ['get', 'post', 'put', 'delete', 'patch'],
You will have interaction on the PATCH method: