How to validate mutual exclusivity of params in Grape API (Ruby) - ruby

When defining an API using Grape, there is a very convenient way of validating the presence and type of parameters, e.g.:
params do
requires :param1, type: String
optional :param1, type: Integer
end
However I can't see a convenient way of specifying that two parameters are mutually exclusive. EG it would be something like:
params do
requires :creatureName, type: String
requires
either :scaleType, type: String
or :furType, type: String
end
I'm interested in suggestions for the most convenient way to get around this.

You can also use exactly_one_of:
params do
optional :scale_type
optional :fur_type
exactly_one_of :scale_type, :fur_type
end
at_least_one_of, all_or_none_of are also available.
More about it here

Parameters can be defined as mutually_exclusive, ensuring that they aren't present at the same time in a request.
params do
optional :beer
optional :wine
mutually_exclusive :beer, :wine
end
Warning: Never define mutually exclusive sets with any required params. Two mutually exclusive required params will mean params are never valid, thus making the endpoint useless. One required param mutually exclusive with an optional param will mean the latter is never valid.

Looking for the same thing. Your question got me to open the hood and see if it could be done. Here's the pull request

Related

Is it possible to return an error for an extra query parameter shows up in light-4j request

I have a question about the light-rest-4j URL validation, for example, if I have a POST request path /party, if I type the path as /party11, I will get error: No handler defined for path /party11, but if I put /party?qqqq, It will pass through, and system treat it as /party should we add validation for this? Our QA team creates this as a defect, in case user input it by mistake, they expect to have error message return.
The light-rest-4j framework validates the request/response based on the OpenAPI specification during the runtime; however, it only validates based on the spec — nothing more and nothing less. In most cases, the spec will define the type of headers, query parameters, path parameters, and cookies, as well as if they are required. We make sure these are validated as defined. For anything that is not defined in the spec, we are doing nothing. For example, an extra query parameter or an extra header in the request will be ignored as they are not defined in the spec. We cannot do any negative validation as we don't know if any client will add additional headers or query parameters for tracing, auditing, etc. A request that comes from one client might be different than another one comes from the same client through a gateway or proxy.

How to pass multiple arguments to golang net rpc call

I am using net library in go and I want to make RPC call:
Client.Call("action", []string{"arg1", "arg2"}, &response)
But in JSON I see:
{"method":"action","params":[["arg1","arg2"]],"id":0}
Notice that arguments are enclosed with double square brackets.
In my case I need params to be a simple list:
{"method":"action","params":["arg1","arg2"],"id":0}
Any ideas how to accomplish this?
The codec that Go's JSON RPC uses on top of the rpc.Client will take whatever param you send and encode that as the first element of the array it uses for the params.
So the encoded request will always have a top level array with just one element, which will contain the params you sent, as you already noted.
See the WriteRequest function here:
https://golang.org/src/net/rpc/jsonrpc/client.go#L57
To achieve what you want, you can implement a custom rpc.ClientCodec.
The interface is documented here:
https://golang.org/pkg/net/rpc/#ClientCodec
You can borrow almost all of the implementation for the default JSON codec here:
https://golang.org/src/net/rpc/jsonrpc/client.go
And modify the params attribute of the request to read:
Params interface{} `json:"params"`
Then when writing your WriteRequest based on the standard one, you can just assign your params to the request params:
c.req.Params[0] = param
You can then use the rpc.NewClientWithCodec to create a client using your custom codec:
https://golang.org/pkg/net/rpc/#NewClientWithCodec

Swagger, YAML: Request and Response object models are same except one field

I'm defining swagger definitions for an API and I came across this use case.
The request and the response object model definitions look exactly the same.
However, there is one field in the object which returns more enum values during the get operation but restricts to minimal enum values for the put operation. Is it possible to reference different enum values for the same field conditionally thus avoiding duplicate definitions. I do not want to recreate the entire model definitions for request and response for the sake of overcoming this limitation.
Here is my example,
definitions:
EntryRequest:
properties:
entries:
$ref: '#/definitions/EntityResponse/properties/entries'
EntryResponse:
properties:
entries:
type: array
items:
$ref: '#/definitions/Entry'
Entry:
properties:
entryStatus:
type: string
enum:
- ENABLE
- DISABLE
- IN_PROGRESS
In the above, there are two things that I'm worried about.
1) For EntryRequest, the API accepts only ENABLE/DISABLE for PUT operation while the API returns all of them in the GET operation. I would like to create a reference to the entryStatus property conditionally. Is it possible?
2) Also, everything except the entryStatus is same for both the EntryRequest and EntryResponse object model. I do not want to duplicate that as well for the sake of representing the differention of entryStatus field.
Is there a way to do it?
EDIT:
As I learn more on this, I get a feeling that this is more an yaml related query. But I would like to have it here to see if anyone has faced a similar situation and how they have handled it. Or to see if I get any recommendations on how to handle this.
For tagging different enums to the same field, I think I can do like this,
RequestEntryStatus:
type: string
enum: &requestStatus
- ENABLE
- DISABLE
ResponseEntryStatus:
type: string
enum: &responseStatus
- ENABLE
- DISABLE
- IN_PROGRESS
Entry:
properties:
entryStatus: *requestStatus
But still this would enforce me to create duplicates of request and response objects with different mapping to the entryStatus field. I would like to know if there is a better way to handle this.
The request and the response object model definitions look exactly the same. However, there is one field in the object which returns more enum values during the get operation but restricts to minimal enum values for the put operation. Is it possible to reference different enum values for the same field conditionally thus avoiding duplicate definitions. I do not want to recreate the entire model definitions for request and response for the sake of overcoming this limitation.
No, it's not possible.
If you want to avoid duplication, you can use a single model with all the enum values, but document verbally in the description that certain values are only used in the responses but not the requests.

Passing extra parameter through GetAll method of webapi

How to pass an extra parameter through a Get method of webapi because when i pass
GetALL(int page,int limit,int start) it works fine but when in passed one more parameters that is optional and may be null it throws error.
GetAll(int page,int limit,int start,string ? search)
What is the best way to make it working
In Web API optional parameters are those which can be nulled.
If you have type values like int or DateTime, you need to make them nullableby using the ? syntax.
But when they're classes instead of value type, they are directly convertible to null, so you don't need to, and can not, mark them as nullable. SO, your method signature must be simply this:
GetAll(int page,int limit,int start,string search)
If you wanted page, limit or start to be nullable, you should declare them as int?. So, int he signature above this 3 parameters are compulsory, and the last one optional.
EDIT, for OP comment
When you use the default routing for Web API the only way to choose the right method is by parameter matching, i.e. the parameters in the request must match the parameters in the action including the optional parameters. So, there are two ways to make it work:
post the optional parameters as empty parameters. For your case, provided you're using the query string, include a &search= in the URL
modify the routes, so that the parameters are provided as route parameters, and define the search parameter as optional
You can also completely modify the web API routing by including the action in the route. In that case, you have to specify the action in the URLs to invoke the action, but the method can be chosen by action name (given by method name or Action attribute), and not by parameter matching. In that case you don't need to provide the optional parameters. It will work like MVC routing.

How can request params be validated to ensure they include required params and don’t include unsupported params?

This is particularly in the context of a REST API built with Ruby and Sinatra.
It's easy enough to manually check to make sure that the required params are not nil. And it's easy to iterate through a flat params hash to see if it's allowed in a whitelist.
However, when the params hash also include hashes it becomes more difficult.
One way of handling this I've thought of is converting the params hash to JSON and using a library to validate it against a JSON schema.
I have come across the sinatra-param gem but I haven't had a chance to see if it can validate sub-hashes or check for unsupported params.
Edit: Another possible way, that might make more sense is passing params directly to the model (I'm using DataMapper) and using its validation and errors instead of rewriting validations.
If each of your routes are going to take the same 4 params (IE :one, :two, :three, :four), you could set up a before filter, store an array of those four params as an instance variable in the before (which is accessible to all routes) and use a sexy little method from class Enumerable called all?:
before do
#base_params = [params[:one], params[:two], params[:three], params[:four]]
unless #base_params.all?
redirect '/error_route'
end
end
Enumerable#all? will return true only if all values in your 'collection' are not false or nil. Documentation can be found here for Ruby 1.9
Additionally if you find that you have different sets of params, you can create a hash instead of just an array of #base_params where they keys are the string value of request.request_method:
before do
#base_params = {"GET" => [params[:one], params[:two], params[:three], params[:four]],
"POST" => [params[:five], params[:six], params[:seven]],
"PUT" => [params[:one], params[:five], params[:six]]}
unless #base_params[request.request_method].all?
redirect '/error_route'
end
end

Resources