How to get Java interface #Path value from other class - java

I have the following kind of interface. Is there any way to get #Path("/bucket-definitions") value "/bucket-definitions" from other class?
#Path("/bucket-definitions")
#Api(
value = "Bucket definition",
authorizations = {#Authorization("token")}
)
public interface BucketDefinitionResource {
#GET
#Path("/{operator-id}")
#Produces({"application/json"})
#ApiOperation(
value = "Get all bucket definitions.",
notes = "Returns all bucket definitions.",
response = BucketDefinitionList.class
)
BucketDefinitionList get(#ApiParam(value = "Bucket definitions of the operator to be fetched.",required = true) #PathParam("operator-id") String var1, #ApiParam(value = "Page number",required = false) #DefaultValue("1") #QueryParam("page") Integer var2, #ApiParam("Items per page") #DefaultValue("20") #QueryParam("per_page") Integer var3);
}

I discovered the following solution after trying in several ways. I was only interested to get the value of #Path("/bucket-definitions") that is "bucket-definitions". It is not from any website. So it is completely my way of getting the value of #Path annotation. Other experts can suggest me a better way. Hopefully, this solution will be helpful for others.
Annotation annotation = BucketDefinitionResource.class.getAnnotations()[0];
if (annotation.toString().contains("Path")) {
String SERVICE_NAME = annotation.toString().substring(annotation.toString().indexOf("/"), annotation.toString().indexOf(")"));
}

Related

openapi-generator spring boot: combining json and text response

I am using the openapi-generator to create a spring-boot server. I am currently still in the experimentation phase.
The goal is that the server is able to provide multiple response media-types, depending on the media type that is accepted by the requester. According to different sources, this should be possible. See here for example of how the yaml file would then look. Other similar examples can be found here on stack overflow.
Concrete example. Let's say we have a post request where if a name is posted the name is returned (just a silly example). In case the requester sends the name John Doe and does not accept application/json, the response, in plain text, should look like this:
John Doe
In case the requester accepts application/json the response should look like this:
{"name": "John Doe"}
For explaining my question/problem I created an example spring boot server. At one point it has the path /user for which the response is:
responses:
'200':
description: The username.
content:
application/json:
schema:
properties:
name:
type: string
example: John Doe
text/plain:
schema:
type: string
example: John Doe
On the other hand I created the path /getuser (name is not really fortunate but it is for this example) which returns the following response:
'200':
description: The username.
content:
text/plain:
schema:
type: string
example: John Doe
application/json:
schema:
properties:
name:
type: string
example: John Doe
My problem is the following: for the first example, where I put the application/json first in the yaml file, the API looks like this:
default ResponseEntity<UserPost200Response> userPost(
#Parameter(name = "name", description = "The name of the user.") #Valid #RequestParam(value = "name", required = false) String name,
#Parameter(name = "UserPostRequest", description = "") #Valid #RequestBody(required = false) UserPostRequest userPostRequest
) {
getRequest().ifPresent(request -> {
for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
String exampleString = "{ \"name\" : \"John Doe\" }";
ApiUtil.setExampleResponse(request, "application/json", exampleString);
break;
}
}
});
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
If, however, I would like to return a ResponseEntity<String> this gives an error since UserPost200Response is not used.
For the path /getuser, where the String response is first defined in the yaml file, my API looks like this:
default ResponseEntity<String> getuserPost(
#Parameter(name = "name", description = "The name of the user.") #Valid #RequestParam(value = "name", required = false) String name,
#Parameter(name = "UserPostRequest", description = "") #Valid #RequestBody(required = false) UserPostRequest userPostRequest
) {
getRequest().ifPresent(request -> {
for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
String exampleString = "\"John Doe\"";
ApiUtil.setExampleResponse(request, "application/json", exampleString);
break;
}
}
});
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
This API makes it possible to return a ResponseEntity<String> but not a ResponseEntity<UserPost200Response> which defines the above mentioned json-model.
One workaround that I found, would be to use the path where the string-response is declared first in the yaml file (see /getuser) in the example above and that returns a ResponseEntity<String> and override and do something like this:
default ResponseEntity<String> getuserPost(
#Parameter(name = "name", description = "The name of the user.") #Valid #RequestParam(value = "name", required = false) String name,
#Parameter(name = "UserPostRequest", description = "") #Valid #RequestBody(required = false) UserPostRequest userPostRequest
) {
getRequest().ifPresent(request -> {
for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
String exampleString = "{ \"name\" : \"John Doe\" }";
ApiUtil.setExampleResponse(request, "application/json", exampleString);
break;
}
if (mediaType.isCompatibleWith(MediaType.valueOf("text/plain"))) {
String exampleString = "John Doe";
ApiUtil.setExampleResponse(request, "text/plain", exampleString);
break;
}
}
});
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
In this case, I don't use the Model created by the openapi-generator and treat the JSON basically as a string.
Another option is to go for a wildcard like ResponseEntity<?> but from what I understand, except proven the contrary, this seems bad practice. I haven't figured out how to declare this in the .yml file that is used by the openapi-generator.
Neither options seem to respect the contract.
I wonder if (1) I am doing something wrong here and (2) how it could be better implemented. The goal is of course to not rewrite the API's and only implement the logic in the Controllers. Any ResponseEntity in the API's should thus not be changed.

SpringRest api auto add {? (curcly braces) after PathVariable

I create Spring RestFul API, I used swagger to test my APIs.
My case is described as below:
URL 1 (not work)
#GetMapping(value = "/api/my/{id}")
public ResponseEntity<MyDTO> getInfo(#PathVariable("id") Long courseId,
#RequestParam(defaultValue = "0", value = "pageNo") Integer pageNo,
#RequestParam(defaultValue = "25", value = "pageSize") Integer pageSize,
#RequestParam(defaultValue = "id", value = "sortBy") String sortBy) {
}
Swagger URL will generated as:
http://localhost:8080/api/my/1{?pageNo%2CpageSize%2CsortBy%7D=&pageNo=0&pageSize=25&sortBy=id
Error:
"detail": "Failed to convert value of type 'java.lang.String' to required type 'java.lang.Long'; nested exception is java.lang.NumberFormatException: For input string:
Because: the value of id is 1{
I want to build the URL as:
http://localhost:8080/api/my/1?pageNo=0&pageSize=25&sortBy=id
URL 2 (worked- it means other components are corrected)
It worked if I change all request to PathVariable like
#GetMapping(value = "/api/my/{id}")
public ResponseEntity<MyDTO> getInfo(#PathVariable("id") Long courseId,
#PathVariable("pageNo") int pageNo, #PathVariable("pageSize") int pageSize) {
}
Swagger URL will generated as:
http://localhost:8080/api/my/1/0/1
Anyone what's wrong in this case with URL1 above ?
Thanks a lot!!!

How to annotate array of objects response in Swagger

I have to debug a REST API Java project that has been developed using Swagger.I'm new to it, so I'm a little bit confused on how to do certain things. For example, here's one method:
#GET
#Path("/location/name")
#Produces({MediaType.APPLICATION_JSON})
#Operation(
summary = "Get location information",
tags = {"Information"},
responses = {
#ApiResponse(responseCode = "200", content = #Content(schema = #Schema(implementation = LocationResponse.class)), description = "Get location information"),
#ApiResponse(responseCode = "500", description = "Error: Internal Server Error")
}
)
public Response searchLocationByName(
#Parameter(description = "Location name", required = true) #DefaultValue("Barcelona") #QueryParam("name") String locationName
) { /* METHOD CODE */ }
The #ApiResponse for the code 200 is not of type LocationResponse but of type ArrayList<LocationResponse>, as it can return more than one location. What would be the correct syntax for this change? I've been reading the documentation at https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Annotations#operation-annotations but I couldn't find an appropriate example...
Thanks!
Use #ArraySchema instead of plain #Schema to define input or output data for array types.
For the PageDto<T> we can simply create ResponseDto which extends PageDto<T> and use it in swagger as : #ApiResponse(responseCode = "200", content = #Content(array = #ArraySchema(schema = #Schema(implementation = ResponseDto.class))), description = "Get location information"),. Thank You

Gson converter factory retrofit2. Different types in serialization/deserialization

I'm working with wordpress post metas and rest api, i've exposed to rest a meta field called "picture_collection" wich store data as an array of integers where every number represents the ID of an attachment.
I've then modified the response when interrogating the api to give me a list of links instead of the attachment ids, like this:
function get_pic_coll ($object, $field_name, $request) {
include_once dirname(__FILE__) . '/attach_coll.php';
$pic_coll = get_post_meta ($object['id'], $field_name, true);
$json_coll = array();
if($pic_coll != null || !empty($pic_coll)){
foreach ($pic_coll as $pic){
$media_id = $pic;
$link_med = wp_get_attachment_image_src($media_id, 'medium');
$link_full = wp_get_attachment_image_src($media_id, 'full');
$medium_size = $link_med[0];
$full_size = $link_full[0];
$obj = new attach_coll($media_id, $medium_size, $full_size);
$element = $obj->return_coll_object();
$json_coll[] = $element;
}
return $json_coll;
}
}
while the attach_coll object is:
class attach_coll{
public function __construct($media_id, $medium_url, $orig_url){
$this->attach_id = $media_id;
$this->medium_size_pic = $medium_url;
$this->full_size_pic = $orig_url;
}
private $attach_id;
private $medium_size_pic;
private $full_size_pic;
public function get_media_id(){
return $this->attach_id;
}
public function get_medium_pic(){
return $this->medium_size_pic;
}
public function get_orig_pic(){
return $this->full_size_pic;
}
public function return_coll_object(){
$ret_coll = array(
"ID" => $this->get_media_id(),
"medium" => $this->get_medium_pic(),
"full" => $this->get_orig_pic()
);
return $ret_coll;
}
}
Java side the things goes like this:
1)the user make a picture and upload her, he receive in exchange the ID of the attachment that is stored inside an Integers ArrayList.
2)when he has done the program update the post_meta passing to the api the entire list.
3)the program receive the response as a json containing the whole post with my custom field, it looks like this:
{...
"id":"someValue",
"title":"myTitle",
"pic_collection":[ {'ID':'picID','mediumSizePic':'someUrl', 'FullSizePic':'SomeOtherUrl},{...}],
The php code works well as i see from the ResponseBody the json i was expecting, the problem is that i'm getting an error 'gson expected a Integer and found an Object' that is logical because the pojo is defined like:
#SerializedName("pic_collection")
private List<Integer> idList = new ArrayList<Integer>();
public void setList(List<Integer> list){
this.idList=list;
}
I tried to change my list to:
List<PicCollection> picList = new ArrayList<PicCollection>();
public class PicCollection{
#SerializedName("ID")
private int picId;
#SerializedName("medium_size")
private String medSizeUrl;
#SerializedName("full_size")
private String fullSizeUrl;
Getters and Setters
}
But that just complicated everything up and didn't resolved the problem as i'm still having a the same gson error 'ID expecting an int but found an object' and no links returned at all.
A glimpse on the code to set the id's:
iterator=idList.iterator;
while(iterator.hasNext()){
FotoCollection fc = new FotoCollection();
fc.ID = iterator.next
What can i do to resove the problem? I need a custom converter?
I just created 2 objects:
One for the request and one for the response.

Foreach java 8 from list of String to String

I have a member function that will retrieve all membershipId of a member(one member might have multiples of membershipId).After retrieve all membershipId using List,it will call the url like this.
This is my service:
RestRequest request = RestRequest.newBuilder()
.url("/membership/" + membershipId + "/outlet")
.get();
This is my controller:
#RequestMapping(value = "/favouriteStores", method = RequestMethod.GET)
public Object FavouriteStores(ModelMap modelMap,HttpSession session)throws Exception {
String memberId = "5677a7075e3f1b998fc7483b";
List<Membership> membershipList= memberService.getMembershipByMemberId(memberId);
List<String> membershipIds = membershipList.stream().map(m->m.getId()).collect(Collectors.toList());
String membershipId = membershipIds.toString();
Set<Outlet> outletSet = membershipService.getOutletByMembershipId(membershipId);
My problem is it will transform the whole membershipId in one url like this
"membership/[12345, 54321]/outlet"
It should be two url like "membership/[12345]/outlet" and "membership/[54321]/outlet"
I know we can use foreach to do that in controller,but i don't know how.Thanks for any helps.
Try map method of Stream instead :
You can achieve this using map method of Stream.
Set<Outlet> outletSet = membershipIds.stream()
.map(membershipService::getOutletByMembershipId)
.collect(Collectors.toSet());
Even you can combine your previous stream operations and omit creation of intermediate list objects :
String memberId = "5677a7075e3f1b998fc7483b";
Set<Outlet> outletSet = memberService.getMembershipByMemberId(memberId)
.stream()
.map(Membership::getId)
.map(membershipService::getOutletByMembershipId)
.collect(Collectors.toSet())

Categories

Resources