Testing @DELETE resources with path param

1,143 views
Skip to first unread message

Reuben Firmin

unread,
Nov 28, 2013, 10:49:36 AM11/28/13
to dropwiz...@googlegroups.com
Hello,

I'm confused by the jersey client API for specifying path params.

My resource method is defined as follows:

    @DELETE
    public void delete(@Auth User user, @PathParam("id") String id) {
        try {
            dao.deleteToDo(user, id);
        } catch (Exception e) {
            throw new WebApplicationException(e);
        }
    }

My test method is:

    @Test
    public void testDelete() throws Exception {
        client().addFilter(new HTTPBasicAuthFilter("reuben", "foo"));
        ToDo t1 = new ToDo();
        t1.setId("12345");
        client().resource("/todo").path("delete").path(t1.getId()).delete();
        verify(dao).deleteToDo(reuben, t1.getId());
    }

When I run this, I get the following console output:

10:45:04.407 [main] INFO  c.s.j.s.i.a.WebApplicationImpl - Initiating Jersey application, version 'Jersey: 1.17.1 02/28/2013 12:47 PM'
10:45:04.762 [main] INFO  c.s.j.a.c.filter.LoggingFilter - 1 * Server in-bound request
1 > DELETE /todo/delete/12345
1 > Authorization: Basic cmV1YmVuOmZvbw==
1 > 

10:45:04.768 [main] INFO  c.s.j.a.c.filter.LoggingFilter - 1 * Server out-bound response
1 < 404
1 < 

10:45:04.775 [main] INFO  c.s.j.t.f.s.c.i.InMemoryTestContainerFactory$InMemoryTestContainer - Stopping low level InMemory test container

com.sun.jersey.api.client.UniformInterfaceException: Client response status: 404
at com.sun.jersey.api.client.WebResource.voidHandle(WebResource.java:707)
at com.sun.jersey.api.client.WebResource.delete(WebResource.java:262)
at com.flavor8.server.db.impl.TodoResourceTest.testDelete(TodoResourceTest.java:87)
...

However if I change it to use a query param (and pass in a queryparam in the test method) it works fine. I had previously attempted to pass in a ToDo to the delete method, but got an even more obscure error from Jersey on that. (While I like Jersey's API brevity, the exceptions can be annoyingly obtuse.) 

Can somebody advise as to how to property get the client to call the resource method above?

Thanks

Reuben Firmin

unread,
Nov 28, 2013, 10:52:49 AM11/28/13
to dropwiz...@googlegroups.com
Quick edit: I inadvertently posted a wrong version of the test code (I reverted from the working queryparam variant to post this)... it should have been .path("id"), not .path("delete"). The problem remains with .path("id").

Nick Telford

unread,
Nov 28, 2013, 11:36:47 AM11/28/13
to dropwiz...@googlegroups.com
Does your resource class contain an @Path("/todo/delete/{id}") annotation?

Reuben Firmin

unread,
Nov 29, 2013, 2:58:40 PM11/29/13
to dropwiz...@googlegroups.com
No, I have the path setup as follows (based on the dropwizard documentation). I guess my question can best be rephrased as: given the following, what's the best way to address the delete method using the client library? :) 


@Path("/todo")
@Produces(MediaType.APPLICATION_JSON)
public class ToDoResource {
 
    @GET
    @Timed
    public List<ToDo> getAll(@Auth User user, @NotNull @QueryParam(value = "sort") SortOrder sortOrder) {
          ..
    }

    @POST
    @Timed
    public ToDo create(@Auth User user, @Valid ToDo todo) {
         ..
    }

    @PUT
    @Timed
    public boolean update(@Auth User user, @Valid ToDo todo) {
         ..
    }

    @DELETE
    public void delete(@Auth User user, @PathParam("id") String id) {
        try {
            dao.deleteToDo(user, id);
        } catch (Exception e) {
            throw new WebApplicationException(e);
        }
    }
}

// test code that returns 404 when the above is a @PathParam:

        client().resource("/todo").path("id").path("12345").delete();

// working test code against POST, with same client setup

        client().resource("/todo").type(MediaType.APPLICATION_JSON_TYPE).post(todo);

// working test code against DELETE if I switch the delete method to @QueryParam

        client().resource("/todo").queryParam("id", t1.getId()).delete();

Thanks 



--
You received this message because you are subscribed to a topic in the Google Groups "dropwizard-user" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/dropwizard-user/jr_t-3sDG0I/unsubscribe.
To unsubscribe from this group and all its topics, send an email to dropwizard-us...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Jacek Jackowiak

unread,
Nov 29, 2013, 6:57:53 PM11/29/13
to dropwiz...@googlegroups.com
Yours delete() method should also have @Path("/{id}") annotation. Without it Jersey dont know where to search for given PathParam.

Reuben Firmin

unread,
Nov 29, 2013, 8:53:53 PM11/29/13
to dropwiz...@googlegroups.com
Thanks, you got me to the right answer:

    @Path("/id/{id}")
    @DELETE

That's nonintuitive to me, but OK (I'd have expected QueryParam and PathParam to work equivalently).

Part b. Originally I was posting an entity to the delete method as follows (as that seemed neatest):

    @DELETE
    public void delete(@Auth User user, @Valid ToDo todo) {
        try {
            dao.deleteToDo(user, todo.getId());
        } catch (Exception e) {
            throw new WebApplicationException(e);
        }
    }

// test code
client().resource("/todo").type(MediaType.APPLICATION_JSON_TYPE).delete(mytodo);

...but that has its own unhelpful exception (note no actual error listed):

com.yammer.dropwizard.validation.InvalidEntityException: The request entity had the following errors:
at com.yammer.dropwizard.jersey.JacksonMessageBodyProvider.validate(JacksonMessageBodyProvider.java:75)
at com.yammer.dropwizard.jersey.JacksonMessageBodyProvider.readFrom(JacksonMessageBodyProvider.java:61)
at com.sun.jersey.spi.container.ContainerRequest.getEntity(ContainerRequest.java:488)
at com.sun.jersey.server.impl.model.method.dispatch.EntityParamDispatchProvider$EntityInjectable.getValue(EntityParamDispatchProvider.java:123)
at com.sun.jersey.server.impl.inject.InjectableValuesProvider.getInjectableValues(InjectableValuesProvider.java:46)

When I debug that stack, I see that it hits line 54 of Validator:
        
        if (o == null) {
            errors.add("request entity required");
        }

In this case, why is o (the value param that's passed into JacksonMessageBodyProvider) null, and why doesn't "request entity required" surface in the exception that's thrown? It seems a lot like JacksonMessageBodyProvider should be scooping up that error message:

       if (classes != null) {
            final ImmutableList<String> errors = validator.validate(value, classes);
            if (!errors.isEmpty()) {
                throw new InvalidEntityException("The request entity had the following errors:",
                                                 errors);
            }
        }

Thanks
Reply all
Reply to author
Forward
0 new messages