I have two similarly worded steps, but still different enough (I would hope):
#When("I send $method '$url'")
public void sendMethodURL(
#Named("method") final String method,
#Named("url") final String url)
{
// stuff
}
#When("I send $method '$url' with JSON '$body'")
public void sendMethodURLBody(
#Named("method") final String method,
#Named("url") final String url,
#Named("body") final String inputJSON)
{
// stuff
}
However, in my story, I have:
When I send POST '/blah/foo/bar' with JSON '["some", "json"]'
Yet it's calling sendMethodURL where method is "POST '/blah/foo/bar' with JSON" and url is "[\"some\", \"json\"]". Clearly, I'd rather it call sendMethodURLBody instead.
What am I doing wrong here? How can I get JBehave to differentiate between the two steps?
Edit: I realize it's the RegexStepMatcher matching the first #When it finds that matches (which is sendMethodURL)... but how can I keep the grammar as it is, but differentiate the two so it no longer matches both steps? The order of the methods seems to work, but there must be a less fragile way.
We had same problem, but our workaround is to use different wording to distinguish steps from each other. As far as I understand this is not an option for you. In this case I would recomment to look into steps prioritization - you can set higher priority to longer step, so it would be tried first (see JBehave documentation: http://jbehave.org/reference/stable/prioritising-steps.html).
Related
I am playing around with REST services in a Spring Boot environment. I have a question about the URI and naming conventions.
I have (currently) the following three mappings in my controller implementation...
#RequestMapping(method=RequestMethod.GET, value= "/v1/accounts")
public List<Account> getAllAccounts() {
return accountService.getAllAccounts();
}
#RequestMapping(method=RequestMethod.GET, value="/v1/accounts/{accountId}")
public Account getAccount(#PathVariable int accountId) {
return accountService.getAccount(accountId);
}
#RequestMapping(method=RequestMethod.GET, value="/v1/accounts/")
public Account getAccount(#RequestParam("shortName") String shortName) {
return accountService.getAccount(shortName);
}
These currently "work", but I have a question/concern about the getAccount(String) method. If I simply use the path "v1/accounts", the compiler seems to be unable to differentiate this from the URI for getAllAccounts(). So, I added the trailing '/', and the request now looks like...
/v1/accounts/?shortName=foo
However, it seems like the two requests should be...
/v1/accounts
and
/v1/accounts?shortName=foo
But, as already identified, changing the third request mapping to remove the trailing '/' results in compile-time errors.
Any input on either (a) how to eliminate the trailing '/' without running into compile-time errors, or (b) the advisability of incorporating the trailing '/' "just" to have both REST services exposed (I'm concerned about "what happens when the 3rd service is needed")?
After talking with one of our front-end devs, who said it's preferable to only deal with one return type (i.e., List vice List and Account), things simplified to...
#RequestMapping(method=RequestMethod.GET, value="/v1/accounts")
public List<Account> getAccount(#RequestParam("shortName") Optional<String> shortName) {
return (shortName.isPresent()) ? accountService.getAccount(shortName.get())
: accountService.getAccounts();
}
I've seen some concerns about the use of "Optional" as a parameter type, but it does seem to (a) clean up the REST endpoint's URI, and (b) is not horrific in the controller implementation.
Unless someone points out the "big, unanticipated nightmare" associated with this approach, I think I'll run with this
I am a novice with vertx so maybe I am doing something wrong. I am trying to implement the following routes:
router.get("/api/users/").handler(this::getUsers);
router.route("/api/users/:username*").handler(this::checkUsername);
router.get("/api/users/:username/").handler(this::getUser);
router.put("/api/users/:username/").handler(this::addUser);
router.get("/api/users/:username/assignments/").handler(this::getAssignments);
router.post("/api/users/:username/assignments/").handler(this::addAssignment);
router.route("/api/users/:username/assignments/:assignmentId/").handler(this::checkAssignmentId);
router.get("/api/users/:username/assignments/:assignmentId/").handler(this::getAssignment);
Is this the correct way to avoid duplicating this logic in all handlers?
I am trying to chain handlers, where the checkUsername handler reads the username parameter from the path, tries to find a corresponding user, and puts that user in the context. If no user is found, a statuscode 400 is returned. Otherwise the next handler is called. I would like to apply the same principle to the assignmentId parameter.
While trying to implement this, I believe I found a problem with the path, more specifically the trailing slash and star. The documentation states that trailing slashes are ignored. This is not the behavior when there is a parameter in the path. In that case the trailing slash matters. If the path definition contains one and the request does not, vertx returns a 404. It does not make a difference whether or not the parameter is at the end of the path or in the middle.
The same goes for paths ending with a star. This functionality does not work when the path contains a parameter.
You can use a regular expression to avoid duplication of the checkUsername validation check. What I would do is I would have a method like this to check if the username is valid:
private void checkUsername(RoutingContext routingContext){
//The "param0" is the capture group of the regular expression. See the routing config below.
if (isValidUsername(routingContext.request().getParam("param0"))){
routingContext.next();
} else {
routingContext
.response()
.setStatusCode(400)
.end();
}
}
To check the assignment ID I would do something similar:
private void checkAssignmentId(RoutingContext routingContext){
if (isValidAssignmentId(routingContext.request().getParam("assignmentId"))){
routingContext.next();
} else {
routingContext
.response()
.setStatusCode(400)
.end();
}
}
Try to avoid trailing slashes in your paths. I would change the routing handler assignments to be something like this:
router.get("/api/users").handler(this::getUsers);
//By the way, you really want to be using a POST request when adding users just to stick to the principles of REST.
//When you are sending a POST request there is no need to put the username in the URI. You can have it in the request body.
//Please ensure you validate this username using the same validation helper used in your other validations.
router.post("/api/users").handler(this::addUser);
//Use regular expression to match all "/api/users/:username*" URIs
router.routeWithRegex("\\/api\\/users\\/([^\\/]+)").handler(this::checkUsername);
router.get("/api/users/:username").handler(this::getUser);
router.get("/api/users/:username/assignments").handler(this::getAssignments);
router.post("/api/users/:username/assignments").handler(this::addAssignment);
router.route("/api/users/:username/assignments/:assignmentId").handler(this::checkAssignmentId);
router.get("/api/users/:username/assignments/:assignmentId").handler(this::getAssignment);
I'm a beginner with portlets, and I don't understand the difference between request.setAttribute and response.setRenderParameter (for an action). Both save an attribute into the request and allow to access to it after. I think specially about transmission between a processAction and the render which is just after the action method.
I know that with setRenderParameter we cannot "stock" a complex object, but if I just want to transfer a String which one should I use?
In which case should we use the setRenderParameter method or the setAttribute method ?
Well, one sets an attribute on a request. The other sets a parameter on the response. They are different objects, obviously.
response.setRenderParameter is of use if you wish to call different render methods based on your action. For example, imagine your action method sends an email, and you want to show the user a different view on success and failure. In this case, you would do something like this in your ActionMapping
if(sentOK){
response.setRenderParameter("result", "success");
}else{
response.setRenderParameter("result", "fail");
}
And then have two RenderMapping methods:
#RenderMapping(params = "result=success")
public String success(){
#RenderMapping(params = "result=fail")
public String fail(){
I'm having trouble setting something up that I'm pretty sure /should/ be easy, so I thought I'd throw it to the crowd. I can't seem to find what I'm looking for elsewhere on the web or on SE.
I am simplifying my project of course, but basically I have a JAX-WS annontated Jersey resource class that looks something like this:
#Path("myresource")
public class MyResource {
#Autowired
MyComplexObjectDAO daoInstance;
#Path("findObject/{id}")
#GET
public MyComplexObject findObject( #PathParam(value="id") String id ) {
return daoInstance.findObject( id );
}
#Path("saveObject")
#PUT
public MyComplexObject saveObject( MyComplexObject objectToSave ) {
MyComplexObject savedObject = daoInstance.saveObject( objectToSave );
return savedObject;
}
}
So you can see I'm autowiring a DAO object using spring, and then I use the DAO methods in the REST handlers.
The 'findObject' call seems to work fine - so far it works exactly as I expect it to.
The 'saveObject' call is not working the way I want and that's what I need some advice on.
You can see that I'm trying to directly take an instance of my complex object as a parameter to the REST method. Additionally I would like to return an instance of the complex object after it's been saved.
I put together some 'client' code for testing this out.
#Test
public void saveTest() {
WebResource wsClient = createWebServiceClient();
MyComplexObject unsavedInstance = createMyComplexObject();
MyComplexObject savedInstance =
wsClient
.path("saveObject")
.accept(MediaType.APPLICATION_XML)
.put(MyComplexObject.class, unsavedInstance);
assertNotNull(savedIntent);
}
Which is returning the following error:
com.sun.jersey.api.client.UniformInterfaceException: PUT http://localhost:8081/rest/myresource/save returned a response status of 400 Bad Request
I don't see why this isn't working and I think I've tried just about everything I can think of. Any help or direction would be very much appreciated.
Thanks so much!
I see that you call the accept() method in your test client (which means that a "Accept:" header is added to the request, indicating the server what type of representation you would like). However, you don't call the type() method to add a "Content-type:" header and inform the server that you are sending XML data. See http://jersey.java.net/nonav/documentation/latest/client-api.html#d4e644 for examples.
Side remark: your URLs are not RESTful - you should avoid verbs in your path:
So, instead of:
/api/findObject/{id}
/api/saveObject
You should use:
/api/objects/{id}
/api/objects
Last note: to create an object on calling /api/objects, you should do a POST and not a PUT to adhere to REST best practices and widely adopted patterns.
switching to the 'concrete class' solution I alluded to in my earlier comment is what fixed things up for me.
The preamble
We're implementing a MVC2 site that needs to consume an external API via https (We cannot use WCF or even old-style SOAP WebServices, I'm afraid). We're using AsyncController wherever we need to communicate with the API, and everything is running fine so far.
Some scenarios have come up where we need to make multiple API calls in series, using results from one step to perform the next.
The general pattern (simplified for demonstration purposes) so far is as follows:
public class WhateverController : AsyncController
{
public void DoStuffAsync(DoStuffModel data)
{
AsyncManager.OutstandingOperations.Increment();
var apiUri = API.getCorrectServiceUri();
var req = new WebClient();
req.DownloadStringCompleted += (sender, e) =>
{
AsyncManager.Parameters["result"] = e.Result;
AsyncManager.OutstandingOperations.Decrement();
};
req.DownloadStringAsync(apiUri);
}
public ActionResult DoStuffCompleted(string result)
{
return View(result);
}
}
We have several Actions that need to perform API calls in parallel working just fine already; we just perform multiple requests, and ensure that we increment AsyncManager.OutstandingOperations correctly.
The scenario
To perform multiple API service requests in series, we presently are calling the next step within the event handler for the first request's DownloadStringCompleted. eg,
req.DownloadStringCompleted += (sender, e) =>
{
AsyncManager.Parameters["step1"] = e.Result;
OtherActionAsync(e.Result);
AsyncManager.OutstandingOperations.Decrement();
}
where OtherActionAsync is another action defined in this same controller following the same pattern as defined above.
The question
Can calling other async actions from within the event handler cause a possible race when accessing values within AsyncManager?
I tried looking around MSDN but all of the commentary about AsyncManager.Sync() was regarding the BeginMethod/EndMethod pattern with IAsyncCallback. In that scenario, the documentation warns about potential race conditions.
We don't need to actually call another action within the controller, if that is off-putting to you. The code to build another WebClient and call .DownloadStringAsync() on that could just as easily be placed within the event handler of the first request. I have just shown it like that here to make it slightly easier to read.
Hopefully that makes sense! If not, please leave a comment and I'll attempt to clarify anything you like.
Thanks!
It turns out the answer is "No".
(for future reference incase anyone comes across this question via a search)