Annotation Retrieval given the URI - java

I want to retrieve the annotations of a service ( in particular #RolesAllowed ) given the URI corresponding to the service.
Here an example:
The service:
#GET
#Path("/example")
#RolesAllowed({ "BASIC_USER", "ADMIN" })
public Response foo() {
//Service implementation
}
I want to retrieve { "BASIC_USER", "ADMIN" } given the String "/example".
I use RestAssured for testing so, if possible, I prefer a solution with the latter.
Thank you.

I am not familiar with RestAssured, but I wrote the following Junit test and it works. Perhaps you can adapt it to work with RestAssured.
First the Service:
public class Service {
#GET
#Path("/example")
#RolesAllowed({ "BASIC_USER", "ADMIN" })
public Response foo() {
return new Response();
}
}
And this is the corresponding Junit test:
#Test
public void testFooRoles() throws Exception {
Method method = Service.class.getMethod("foo");
Annotation path = method.getDeclaredAnnotation(javax.ws.rs.Path.class);
assertTrue(((Path) path).value().equals("/example"));
RolesAllowed annotation = method.getDeclaredAnnotation(RolesAllowed.class);
List<String> roles = Arrays.asList(annotation.value());
assertEquals(2, roles.size());
assertTrue(roles.contains("BASIC_USER"));
assertTrue(roles.contains("ADMIN"));
}

Related

#Timed and #Metered doesn't work for methods for Dropwizard with metrics-jersey2 and generated API interfaces

I have a Dropwizard project which has resources based on generated Java interfaces, Resource looks like:
#Path("/foo")
public class FooResource implements FooApi {
#Override // This method overrides the one from FooApi interface
public Response fooGet() {
...
}
}
FooApi is the interface which was generated from OpenAPI contract:
#Path("/foo")
#Api(description = "Will list foos.")
#javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2077-10-23T19:04:20.922+03:00[Europe/Kiev]")
public interface FooApi {
#GET
#Produces({ "application/json" })
#ApiOperation(value = "Will list foos.", notes = "Will list foos.", tags={ "Foo" })
#ApiResponses(value = {
#ApiResponse(code = 200, message = "OK", response = Foo.class, responseContainer = "List") })
Response fooGet();
}
So now I'm trying to integrate this resource with jersey metrics by annotate methods with #Timed or #Metered, like
#Timed
#Override // This method overrides the one from FooApi interface
public Response fooGet() {
...
}
But in this case NO metrics will be produced.
I've also tried to annotate class instead:
#Timed
#Path("/foo")
public class FooResource implements FooApi {
And this time I see that metrics were produced but those are too generic for me.
I suspect that problem is related to InstrumentedResourceMethodApplicationListener and the way it extracts annotated methods, but maybe there is more easy solution rather then override this listener?

How to populate database before running tests with Micronaut

I'm looking for a way to execute some SQL scripts before my test class is executed. With Spring I can easily annotate my test class (or test method) with the #Sql annotation. I haven't found any particular way to do the same with Micronaut.
The only way I found was to manually populate the data programmatically in the test method itself, but, in my experience, there are times when you have to perform multiple inserts to test a single case.
I've came up with the following code to test a REST controller:
Code
#Validated
#Controller("/automaker")
public class AutomakerController {
private AutomakerService automakerService;
public AutomakerController(AutomakerService automakerService) {
this.automakerService = automakerService;
}
#Get("/{id}")
public Automaker getById(Integer id) {
return automakerService.getById(id).orElse(null);
}
#Get("/")
public List<Automaker> getAll() {
return automakerService.getAll();
}
#Post("/")
public HttpResponse<Automaker> save(#Body #Valid AutomakerSaveRequest request) {
var automaker = automakerService.create(request);
return HttpResponse
.created(automaker)
.headers(headers -> headers.location(location(automaker.getId())));
}
#Put("/{id}")
#Transactional
public HttpResponse<Automaker> update(Integer id, #Body #Valid AutomakerSaveRequest request) {
var automaker = automakerService.getById(id).orElse(null);
return Objects.nonNull(automaker)
? HttpResponse
.ok(automakerService.update(automaker, request))
.headers(headers -> headers.location(location(id)))
: HttpResponse
.notFound();
}
}
Test
#Client("/automaker")
public interface AutomakerTestClient {
#Get("/{id}")
Automaker getById(Integer id);
#Post("/")
HttpResponse<Automaker> create(#Body AutomakerSaveRequest request);
#Put("/{id}")
HttpResponse<Automaker> update(Integer id, #Body AutomakerSaveRequest request);
}
#MicronautTest
public class AutomakerControllerTest {
#Inject
#Client("/automaker")
AutomakerTestClient client;
#Test
public void testCreateAutomakerWhenBodyIsValid() {
var request = new AutomakerSaveRequest("Honda", "Japan");
var response = client.create(request);
assertThat(response.code()).isEqualTo(HttpStatus.CREATED.getCode());
var body = response.body();
assertThat(body).isNotNull();
assertThat(body.getId()).isNotNull();
assertThat(body.getName()).isEqualTo("Honda");
assertThat(body.getCountry()).isEqualTo("Japan");
}
#Test
public void testUpdateAutomakerWhenBodyIsValid() {
var responseCreated = client.create(new AutomakerSaveRequest("Chvrolet", "Canada"));
assertThat(responseCreated.code()).isEqualTo(HttpStatus.CREATED.getCode());
var itemCreated = responseCreated.body();
assertThat(itemCreated).isNotNull();
var responseUpdated = client.update(itemCreated.getId(), new AutomakerSaveRequest("Chevrolet", "United States"));
assertThat(responseUpdated.code()).isEqualTo(HttpStatus.OK.getCode());
var itemUpdated = responseUpdated.body();
assertThat(itemUpdated).isNotNull();
assertThat(itemUpdated.getName()).isEqualTo("Chevrolet");
assertThat(itemUpdated.getCountry()).isEqualTo("United States");
}
}
I could use a method annotated with #Before to populate all the data I need but it would really be nice to be able to use *.sql scripts the way it is possible with Spring. Is there a way to provide such *.sql scripts before the tests are executed ?
TL;DR — Use Flyway.
With Flyway, you can set up and maintain a given database schema (extremely) easily. To your case, any migration script you put under ../test/resources/db/migration/ (or any other default location you set) will be only visible to your tests, and can be executed/run automatically (if configured) any time you run your tests.
Another solution would be to use an in-memory database (but I would stay away from that for real applications). For instance, H2 have a way to specify an "initialization" script and another for data seeding (and such).

RestController with GET + POST on same method?

I'd like to create a single method and configure both GET + POST on it, using spring-mvc:
#RestController
public class MyServlet {
#RequestMapping(value = "test", method = {RequestMethod.GET, RequestMethod.POST})
public void test(#Valid MyReq req) {
//MyReq contains some params
}
}
Problem: with the code above, any POST request leads to an empty MyReq object.
If I change the method signature to #RequestBody #Valid MyReq req, then the post works, but the GET request fails.
So isn't is possible to just use get and post together on the same method, if a bean is used as input parameters?
The best solution to your problem seems to be something like this:
#RestController
public class MyServlet {
#RequestMapping(value = "test", method = {RequestMethod.GET})
public void testGet(#Valid #RequestParam("foo") String foo) {
doStuff(foo)
}
#RequestMapping(value = "test", method = {RequestMethod.POST})
public void testPost(#Valid #RequestBody MyReq req) {
doStuff(req.getFoo());
}
}
You can process the request data in different ways depending on how you receive it and call the same method to do the business logic.
#RequestMapping(value = "/test", method = { RequestMethod.POST, RequestMethod.GET })
public void test(#ModelAttribute("xxxx") POJO pojo) {
//your code
}
This will work for both POST and GET. (make sure the order first POST and then GET)
For GET your POJO has to contain the attribute which you're using in request parameter
like below
public class POJO {
private String parameter1;
private String parameter2;
//getters and setters
URl should be like below
/test?parameter1=blah
Like this way u can use it for both GET and POST
I was unable to get this working on the same method and I'd like to know a solution, but this is my workaround, which differs from luizfzs's in that you take the same request object and not use #RequestParam
#RestController
public class Controller {
#GetMapping("people")
public void getPeople(MyReq req) {
//do it...
}
#PostMapping("people")
public void getPeoplePost(#RequestBody MyReq req) {
getPeople(req);
}
}

How to mix Spring Data Repositories and Spring Rest Controllers

Currently I am exposing a few Spring Data Repositories as RESTful services by annotating them with #RepositoryRestResource like this:
#RepositoryRestResource(collectionResourceRel = "thing1", path = "thing1")
public interface Thing1Repository extends PagingAndSortingRepository<Thing1, String> {}
#RepositoryRestResource(collectionResourceRel = "thing2", path = "thing2")
public interface Thing2Repository extends CrudRepository<Thing2, String> {}
This all works great. When you hit my first endpoint is also shows all the Spring Data Repositories I have exposed, like this:
{
_links: {
thing1: {
href: "http://localhost:8080/thing1{?page,size,sort}",
templated: true
},
thing2: {
href: "http://localhost:8080/thing2"
}
}
}
Now I have some endpoints I want to expose that cannot be represented by Spring Data Repositories, so I am using a RestController.
Here is a simple example:
#RestController
#ExposesResourceFor(Thing3.class)
#RequestMapping("/thing3")
public class Thing3Controller {
#Autowired
EntityLinks entityLinks;
#Autowired
Thing3DAO thing3DAO;
//just assume Thing3.class extends ResourceSupport. I know this is wrong, but it makes the example shorter
#RequestMapping(value = "/{id}", produces = "application/json")
Thing3 thing3(#PathVariable("id") String id)
{
Thing3 thing3 = thing3DAO.findOne(id);
Link link = entityLinks.linkToSingleResource(Thing3.class, id);
thing3.add(link);
return thing3;
}
}
Now if I run this app and go to:
http://localhost:8080/thing3/{id}
I do get a JSON representation of the Thing3 with a link to itself, that works as expected.
What I want to figure out how to do is have the first endpoint also describe this controller. I basically want this:
{
_links: {
thing1: {
href: "http://localhost:8080/thing1{?page,size,sort}",
templated: true
},
thing2: {
href: "http://localhost:8080/thing2"
},
thing3: {
href: "http://localhost:8080/thing3"
}
}
}
What do I need to do to get my base endpoint to have a link to this controller?
You could override RepositoryLinkResource, and add a resource pointing to your thing3:
resource.add(ControllerLinkBuilder.linkTo(Thing3Controller.class).withRel("thing3"));
Check this question: Custom response for root request int the Spring REST HATEOAS with both RepositoryRestResource-s and regular controllers

How use PUT method in Springboot Restcontroller?

Am developing an application using Spring boot.I tried with all representations verbs like GET, POST , DELETE all are working fine too. By using PUT method, it's not supporting in spring boot. Whether I need to add any new configuration.
Put method works only the request not have any parameters. If i add any query parameter or form data it doesnt work. Kindly any expertize will help me to solve this issue.
#RequestMapping("/student/info")
#RequestMapping(method = RequestMethod.PUT)
public #ResponseBody String updateStudent(#RequestParam(value = "stdName")String stdName){
LOG.info(stdName);
return "ok";
}
Request method 'PUT' not supported
This code will work fine. You must specify request mapping in class level or in function
level.
#RequestMapping(value = "/student/info", method = RequestMethod.PUT)
public #ResponseBody String updateStudent(#RequestBody Student student){
LOG.info(student.toString());
return "ok";
}
Have you tried the following Request Mapping:
#RequestMapping(value = "/student/info", method = RequestMethod.PUT)
There's no need to separate the value and the Request Method for the URI.
Since Spring 4.3 you can use #PutMapping("url") : https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/PutMapping.html
In this case it will be:
#PutMapping("/student/info")
public #ResponseBody String updateStudent(#RequestParam(value = "stdName")String stdName){
LOG.info(stdName);
return "ok";
}
I meet the same issue with spring boot 1.5.*,I fixed it by follow:
#RequestMapping(value = "/nick", method = RequestMethod.PUT)
public Result updateNick(String nick) {
return resultOk();
}
Add this bean
#Bean
public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
return new TomcatEmbeddedServletContainerFactory(){
#Override
protected void customizeConnector(Connector connector) {
super.customizeConnector(connector);
connector.setParseBodyMethods("POST,PUT,DELETE");
}
};
}
see also
https://stackoverflow.com/a/25383378/4639921
https://stackoverflow.com/a/47300174/4639921
you can add #RestController annotation before your class.
#RestController
#RequestMapping(value = "/v1/range")
public class RangeRestController {
}

Categories

Resources