This question isn't about REST, but about using the returned value from an invocation made in #When in the subsequent #Then.
I am looking at using JBehave to test some calls to a REST api. First there is a post to create the user
When I create a user with name Charles Darwin
As I understand REST, and this is what the Atom api does, the id is returned in the location header, e.g. /user/22. So then I want to assert something about the response.
Then user was created with a valid Id
I can do this by creating a member variable in the Steps class and storing the response there, and I have used this approach before, but is this the correct way?
Yes. One needs to store data that can be asserted on in your #Then methods. The simplest way to do this is to have a member variable - but that means that your #When/#Then need to be in the same Steps class. Another way to do it is to have a shared data object that all your Steps use and you can then set it in one method and get it in another. If you just want something generic, you can do a Map<String,? extends Object> as your generic data object. And then if you run with multiple threads, then wrap the data object in a ThreadLocal.
That's what I've seen - and the data object should be setup/cleared with a #BeforeScenario/#AfterScenario method.
Related
I have a command object that I want to convert into a domain object.
However, the object I want to convert the command object into may be one of two domain classes (they're both derived classes), and I need to do it in a service (which is where, based on other data, I decide which type of object it should be bound to). Is this possible and what's the best way to do this? bindData() only exists in a controller.
Do I just have to manually map command object parameters to the appropriate domain object properties? Or is there a faster/better way?
If the parameters have the same name, then you can use this question to copy the values over. A quick summary can be as follows.
Using the Grails API
You can cycle through the properties in a class by accessing the properties field in the class.
object.properties.each { property ->
// Do something
}
You can then check to see if the property is present in the other object.
if(otherObject.hasProperty(property) && !(key in ['class', 'metaClass']))
Then you can copy it from one object to the other.
Using Commons
Spring has a really good utility class called BeanUtils that provides a generic copy method that means you can do a simlple oneliner.
BeanUtils.copyProperties(object, otherObject);
That will copy values over where the name is the same. You can check out the docs here.
Otherwise..
If there is no mapping between them, then you're kind of stuck because the engine has no idea how to compare them, so you'll need to do it manually.
This really powerful feature of grails
def save(MyDomain model) {
model.save()
render ''
}
will parse the request body or params, run MyDomain.get(id), fill in the properties from the request body or params and save. That's a lot for this little bit of code.
How do I limit the properties to bind to model? Say I have an accountBalance property that is read only and I don't want a malicious user to be able to change their account balance.
Also, I want to have multiple actions that save a different subset of properties of MyDomain... say one action could be for a bank teller user that is making a deposit for the account holder. In this case the teller should be able to set accountBalance but not password.
I realize that an actual banking app wouldn't work like this, it's just an example.
I had other problems that led me to use command objects to bind data (see Grails fails to parse request when content type is specified during post). Any solution would also have to address that post. I imagine if the solution uses command objects then it will work, but if command objects aren't in the solution, then the request body problem has to be addressed.
Have not tried on an actual domain class but can you try using bindData instead of implicitly binding where you can particularly specify which property to exclude?
def save() {
//params - A Map of source parameters
//It can be params or any other representation of request body
//request.JSON, request.XML
MyDomain model = MyDomain.get(params.id?.toLong())
bindData(model, params, [exclude: ['accountBalance']])
model.save()
render ''
}
I suggest you take a look at the documentation about binding. There is a lot of information, in particular the section about security which is similar to your concerns. Looking at the fact bindData() allows you include/exclude properties you should be able to write any variation of your binding you need.
Based on the information here http://docs.valence.desire2learn.com/res/course.html#actions I would expect that to 'update' a courseOffering I would specify a PUT with a CourseOfferingInfo block, which only contains a few attributes. Every time I try this, I get a 404, not found - even using the same route for a successful GET (404 says org doesn't exist OR org is not an offering - neither is true). However, if I specify a CreateCourseOffering block (directly from a previous GET), the PUT works fine. Is this correct and the documentation not? Or are there other things I should look for in this scenario? The documentation says use CreateCourseOffering for the POST to create an offering… I simply want to update one attribute of that offering and as such thought the PUT was the way to go.
If you use the "create" POST route with a CreateCourseOffering block, this will create a new course offering, and send back the CourseOffering block for the newly created course offering (this will include the org unit ID value for the new org unit you've built).
If you want to update an existing course offering, you should, as you suspected, use the "update" PUT route with a CourseOfferingInfo block. Note that you must provide valid information for all the fields in this block, since when used successfully, the LMS will use all the properties you specify in that block for new values for the org unit. The StartDate and EndDate fields are particularly finicky: you must provide either a valid UTCDateTime value (notice that the three-digit millisecond specifier in these values is mandatory) or a JSON null value if the field is not applicable.
Why a 404? What you're seeing with the 404s and the data you're passing is likely down to the way the back-end service is doing data binding. It tries to de-serialize your provided JSON data (and query parameters) into data objects it can read/manipulate -- if you provide a JSON block that contains a superset of the properties it's expecting, then this may work (for example, if you provide a CourseOffering block when you're expected to provide a CourseOfferingInfo) as the binding layer may ignore fields it doesn't need. If the binding process fails, because you provide a value for a property that can't be bound to the data type expected, or because you fail to provide a JSON property field it expects, then this can cause the service to return a 404 (because binding/de-serializing incoming parameterized data happens at the same time as matching the URL route to an underlying service handler).
If you provide a JSON structure (and query parameters) that the web-service can bind to its expected data objects, but the values you provide are invalid or nonsensical, then this can cause the underlying service handler to respond with a 400 (signalling an Invalid Request). But in order to get this far, your parameterized data still needs to get properly deserialized and bound into data objects for the underlying service to examine.
We'll be updating the documentation to more explicitly draw out this fact. The safest policy from the calling client perspective is to pass valid JSON structures that are exactly what's expected by the individual routes, especially since the underlying back-end service implementation might change how it handles incoming requests.
I am writing some webservices using spring. I wanna know what's the argument to the service methods: domain objects or request parameters? for example a "User" object or a bunch of strings containing name, e-mail, etc.
Depending on your configuration (and the method signature) you will receive unmarshalled objects (Jaxb for instance), the MessageContext and so on.
Take a look in the documentation, you'll find some examples and everything you need to know about the service methods and parameters.
I'm using Openrasta for my RESTful webservice and I've a small doubt with regards to the method parameters and URI
For example: I've following Setup for user entity.
Configuration:
ResourceSpace.Has.ResourcesOfType<User>()
.AtUri("/user")
.And.AtUri("/user/{userId}")
.HandledBy<UserHandler>()
.AsJsonDataContract()
.And.AsXmlDataContract();
Handler method for PUT:
public OperationResult Put(long userId, User user){}
URI for the same will be http://localhost/User/1
Request body will contain a JSON as below:
{
"userId":1,
"userName":"FirstName"
}
Here, my question is: Defining the PUT method with two parameters is correct or not? If it is right way to do that, then userId parameter in the PUT method will contain same value as User entity property UserId.
And, in the PUT method I need to verify whether these two values are same or not and if they are not same I return BadRequest stating that URI doesn't match with the entity provided in request. Why should we do this explicitly why not it can be handled while processing the request and have PUT method take only User entity as parameter? Am I missing anything drastically or is my understanding about this design completely wrong? Any thoughts or opinions please?
There's a few reasons for it.
First, it's a technical limitation of how URI parameters are processed and matched to inputs one variable at a time. The same gets applied to key/values codecs, so that ought to let you have one User object. but when you use a json codec, we get back a full object, so that would end up overriding User alltogether.
The second one is that I never tried to fix that problem, mostly because combining uri parameters and response bodies leads to a whole bunch of hidden security issues you probably want to stay well clear of.
Last and not least, from a modeling perspective a ReST API ought to use URIs as identifiers and links instead of foreign keys, so if you already have your identifier (the URI), there's little reason why that should be modeled in your entity body.