In Sinatra, how to make filters dependent of request method? - rest

I've been using filters in Sinatra the way it has been declared in the documentation: with no match string, with a match string or with a match regexp. It has been working fine til now. Now, I have a particular use case. Let's say I have this route:
/resources/1
According to REST, and depending of the request method, this can either be a GET method, PUT method or DELETE method. First question is: How to write filters that are only called when it is a GET request? (currently I'm letting all of them get filtered and only then I test the method. It works, but I don't like it). Second question, and more important: let's say a PUT request like this is triggered:
/resources/
This is of course wrong, because the PUT request has no resource id associated. I would like to know if there is something in Sinatra that enables me to do something like this:
before "/resources/", :method => :put do
error_message
end
just this possibility does not exist (before accepts only one argument). How could I achieve this result at best?

Actually, filters do take conditions. You don't have to use a condition though, you could use a conditional within the filter:
before "/path/" do
if request.request_method == "PUT"
# do something
end
end
If you want to use a condition, I think you'll need to write one, perhaps something like this:
set(:accepted_verbs) {|*verbs|
condition {
verbs.any?{|v| v == request.request_method }
}
}
before "/path/", :accepted_verbs => ["GET","POST"] do
# do something
end
before "/path/", :accepted_verbs => ["PUT"] do
# do something else
end
See conditions for more.

Related

What is the correct way to express "select all when nothing is specified in parameter"?

Let's say we have an HTTP endpoint to get all elements by name?
GET /elements?name={name}
{name} can have a value of CSV or be absent
valid:
GET /elements?name=Bill,Mary,Ann
GET /elements?name=Mike
GET /elements
invalid:
GET /elements?name=
Somehow we find out in controller that name is not passed. We know that the contract implies to return all values for elements. Possible decisions on further actions (I've seen in different projects) are:
using a NULL or a "dummy" substitution like a secret char sequence "!#$#%#$" and juggling them in database while building a query
using if (present) { executeQueryA } else { executeQueryB } logic
I am not sure I like either of these approaches, because when there is more than one optional filter these designs become unmaintainable. Something makes me believe that there is a better way to handle the situation.
What would be a proper design on back-end and in database query to handle the case "select all" when nothing is given? Just a general idea and some pseudo-code will be much appreciated.

How to use object filter with softlayer rest api?

I read this article and have some problems trying to follow the examples. The following is one of the examples given in that article. The first parameter in the object filter is virtualGuests. This object filter can be used in api https://api.softlayer.com/rest/v3/SoftLayer_Account/VirtualGuests.
object_filter = {
'virtualGuests': {
'datacenter': {
'name': {'operation': 'dal05'}
}
}
}
I want to use the object filter in other api methods, like SoftLayer_Account/getBlockDeviceTemplateGroups for example. My question is how to get/set the first parameter like virtualGuests? I tried several times but failed.
Try to follow these recomendations: Getting first parameter through Service Datatype or How to define the first parameter as simple way?
Getting first parameter through Service Datatype
You are trying to get
SoftLayer_Account::getBlockDeviceTemplateGroups
As you see, you are using SoftLayer_Account service, you need to open its datatype from this service:
You can go here:
http://sldn.softlayer.com/reference/services/SoftLayer_Account and
click on "datatypes" label/option
Or go directly here:
SoftLayer_Account
So, you need to start here, the method that you are using is getBlockDeviceTemplateGroups, if you want to get this information in the datatypes, you should skip the word "get" and looking for "BlockDeviceTemplateGroups" property, so you will have the correct parameter that you need to set at first.
How to define the first parameter as simple way?
If you notice, the only changes were: skip "get" word from the method, in this case is "getBlockDeviceTemplateGroups", so it will be:
"BlockDeviceTemplateGroups"
The next step should be set the first char in lowercase like:
"blockDeviceTemplateGroups"
So, it should be the filter:
object_filter = {
'blockDeviceTemplateGroups': {
'datacenter': {
'name': {'operation': 'dal05'}
}
}
}
References:
Object Filters
Going Further with the SoftLayer API Python Client - Part
1

Codeception - how to check response doesn't match xpath

I have the following problem - I want to test REST api with Codeception.
When I want to make sure in JSON response I have array:
'data' => [
'sth' => 'whathever'
]
I can use seeResponseJsonMatchesXpath this way:
$I->seeResponseJsonMatchesXpath('//data/sth');
But what to do if I want to make sure I don't have in response sth? I would like to use for example:
$I->seeResponseJsonMatchesXpath('//data');
$I->dontSeeResponseJsonMatchesXpath('//data/sth');
but obviously there is no dontSeeResponseJsonMatchesXpath method in Codeception.
I also don't know what will be exact value in sth so probably I cannot use here dontSeeResponseContainsJson.
The question is - how to make the following check?
(Disclaimer: Entirely unfamiliar with codeception, only with XPath)
JSON is not supposed to be checked against XPath. But the check you could probably do is
$I->seeResponseJsonMatchesXpath('not(//data/sth)');
Which would return true or 1 if there is no data and sth - and false or 0 if there is.
EDIT: As a response to your comments:
Probably this is something close I need but I want to make sure only sth is not present inside data whereas data should exist (but I don't need to check it in this expression)
The expression already does exactly that - not(//data/sth) returns false for a document like
<data>
<sth/>
</data>
but returns true for a document containing data only:
<data/>
or anything else. But it seems to me there could be pitfalls with converting JSON to XML.
I checked both solutions - yours and //data/not(sth) but it doesn't seem to work
Yes, that does not work because you are using an XPath 1.0 engine. Your attempt would be a valid XPath 2.0 expression. Use the following expression to independently test data and sth:
//data and not(//data/sth)
This only returns true if at least one data element exists and if there is no data element that has an sth element as a child.

If else condition inside check in gatling

I need to implement an if-else condition in my gatling check.
For example:- Below, I might get items array in JSON response as empty. So I need to check if items is empty. I need to execute another set of action. Can you please let me know how can I achieve this?
.check(
jsonPath("$..items[(#.length-1)].id").saveAs("MyAlbumId")
)
As explained on the Gatling ML:
When using a check, the default verify step is exists, so you have to use whatever instead if having your check find nothing is acceptable:
https://github.com/excilys/gatling/wiki/Checks#wiki-verifying
Then, you can use a doIf in order to perform a chain of actions on certain conditions:
https://github.com/excilys/gatling/wiki/Structure-Elements#wiki-do-if
For example:
doIf(session => !session.isAttributeDefined("MyAlbumId")) {
...
}

Perl Dancer trailing slash

Using the Perl web application framework Dancer, I am having some problems with trailing slashes in the URL matching.
Say for example, I want to match the following URL, with an optional Id parameter:
get '/users/:id?' => sub
{
#Do something
}
Both /users/morgan and /users/ match. Though /users will not. Which does not seem very uniform. Since I would prefer, only matching the URL:s without the trailing slash:
/users/morgan and /users. How would I achieve that?
Another approach is to use a named sub - all the examples of Dancer code tend to use anonymous subs, but there's nothing that says it has to be anonymous.
get '/users' => \&show_users;
get '/users/:id' => \&show_users;
sub show_users
{
#Do something
}
Note that, due to the way Dancer does the route matching, this is order-dependent and, in my experience, I've had to list the routes with fewer elements first.
id will contains everything from /user/ on until an optional slash.
get qr{^/users/?(?<id>[^/]+)?$} => sub {
my $captures = captures;
if ( defined $captures->{id} ) {
return sprintf 'the id is: %s', $captures->{id};
}
else {
return 'global user page'
}
};
I know this is an old question, but I've recently solved this problem by using a Plack middleware. There are two of them you can choose from depending on whether you prefer URLs with trailing slashes or not:
Plack::Middleware::TrailingSlash
Plack::Middleware::TrailingSlashKiller
Using any of the middleware above should greatly simplify your core Dancer application code and unit tests since you do not need to handle both cases.
In addition, as mentioned by Dave Sherohman, you should definitely arrange your routes with the fewer elements first in order to match those first, especially if you use the TrailingSlash middleware to force trailing slashes.