My delete api takes a boolean query params /users?force=true. In my controller, I added the following for apipie documentation
param :force, [true, false], required: false, desc: "query parameter."
When I make the api call, I get
Apipie::ParamInvalid (Invalid parameter 'force' value "true": Must be one of: <code>true</code>, <code>false</code>.).
I tried passing /users?force=1, /users?force, but 1 is treated as "1" and not passing anything is treated as nil and both calls fail. How do I make the validation pass?
Note: I am aware that the api definition is not restful.
The param you are passing through ends up being "true" as in string, not a boolean, that's why it's failing. Without any type casting, rails has got no idea that you're trying to pass boolean, not a string.
You should whitelist "true", "false" as strings in valid options like:
param :force, [true, false, "true", "false"], required: false, desc: "query parameter."
Related
I have a parameter defined as such:
param :purchased, [true, false]
This leads to the following error:
Invalid parameter 'purchased' value true: Must be one of: true, false.
I have also tried:
param :purchased, [true, false, 'true', 'false']
but it didn't change anything.
Unfortunately also I cannot cover this in the tests as there the sent boolean is always cast into a String. Then the error looks like this:
Invalid parameter 'purchased' value "true": Must be one of: true, false.
Can anybody help here? Also I would be happy to just disable the validation of Apiepie as I am only interested in the documentation part.
In my Rails controller I'd like to require a single parameter, emails. It must be an array.
This is allowed.
emails[]=foo#bar.com&emails[]=up#down.com
This is not allowed.
emails=foo#bar.com
params.require(:emails) will allow both. The former comes as an Array. The latter as a String. This is a problem.
params.permit(emails: []) does not require an emails parameter.
params.require(:emails).permit([]) does not work because params.require(:emails) gets clever and returns an Array, not an ActionController::Parameters.
How do I allow emails[]=... and disallow emails=...?
Like what tadman said, Screening is what Strong Parameters does mostly.
You can look into Grape, perhaps it can show you some directions for your concerns
desc "API demo", {}
params do
requires :id, type: Integer, desc: 'ID'
requires :name, type: Boolean, desc: 'xxx'
end
Error msg would be received if you pass it an unexpected type of parameters
So I'm working on trying to learn GraphQL for ruby for a project.
I've almost got some parts of it up and running, but I'm having issues with other parts. There are plenty of tutorials out there that cover ultra-basics, but none of them seem to expand in the right directions.
I have a mutation to update my user. So far so good. I can look up the user by their ID, and update a single specific field. I can extend that to updating two fields.
What I cannot do, and this is looking insane, is generalize those fields -- at all. My user model will wind up with over 20 fields attached to it -- phone numbers, addresses, job title, etc etc.
When I create the mutation, I have to define the arguments that go into the resolve method. So far so good. I then define the fields the mutation can return. Again, so far so good.
Then I get to the actual resolve method.
The initial syntax isn't bad. def resolve(user_id:, name:, email:). Then you discover that despite setting required to false, you have to include all the values. You need to specify default values for the optional variables. So it becomes def resolve(user_id:, name: null, email: null) -- but that actually nulls out those values, you can't do partial updates. Worse yet, imagine having 20 fields you have to set this way. You can play games by trying to convert the arguments into a dictionary and rejecting null values -- but then you can't set properties to nil if they need to be nil again.
The solution: a double splat operator. Your syntax becomes def resolve(user_id:, **args). From what I can tell, it turns all remaining named arguments into a dictionary -- and I think unnamed arguments would become an array. Not sure how it would react with a mix of the two.
Full model becomes:
argument :user_id, ID, required: true#, loads: Types::UserType
argument :name, String, required: false
argument :email, String, required: false
field :user, Types::UserType, null: true
field :errors, Types::UserType, null: true
def resolve(user_id:, **args)
user = User.find(user_id)
if user.update(args)
{
user: user,
errors: []
}
else
{
user: nil,
errors: user.errors.full_messages
}
end
end
end
My app is a social platform and I want to have the support-team being able to maintain a blacklist for nicknames. Therefore I have a model Blacklist that contains the different names.
Now I tried with the validates_exclusion_of to map these into the in: but it raises the following error,
wrong number of arguments (given 1, expected 0)
validates_exclusion_of :nickname, in: -> {
where(Blacklist.select(:name).map(&:name).uniq.to_s)
}, message: "This nickname is not allowed"
You don't need to wrap the values to exclude within a lambda, it'll work as is if you pass just the Blacklist AR query result.
validates_exclusion_of :nickname,
in: Blacklist.select(:name).map(&:name).uniq.to_s,
message: 'This nickname is not allowed'
Notice, you can use the ActiveRecord::Calculations#pluck in order to get just the names from Blacklist, getting rid of the select and map combination, and use ActiveRecord::QueryMethod#distinct to get non-repeated values.
With that you don't need the uniq and to_s step. The last one, because the validation is waiting for an enumerable object, and you'd be passing a string.
Something like:
validates_exclusion_of :nickname,
in: Blacklist.distinct.pluck(:name),
message: 'This nickname is not allowed'
The where you're using on the validation won't work, as it's applying a WHERE statement without passing a column name to check, so you'll get an ActiveRecord::StatementInvalid.
I want to create a route for the Grape gem so that a route accepts either an array of strings or a single string with a specific pre-defined value. From the documentation it's not clear how to do that.
Your suggestions?
UPDATE:
I want status to be passed either a single value status1 or as a array where values can be arbitrary and unknown. I combine these?
params do
requires :status, type: Symbol, values: [:status1]
requires :status, type: Array[String]
end
A parameter must be declared only once in the params block. If you declare it twice then only one will be used by Grape. In your case, there're two options to solve your problem.
First option: declare two parameters and define them as mutually exclusive. It means that the user will be able to inform only one of them.
params do
requires :status1, type: Symbol, values: [:status1]
requires :status2, type: Array[String]
mutually_exclusive :status1, :status2
end
Second option: declare only one parameter and set its type to Object. In the method's body, check if it's an Array or a String. If it's a String, verify if it has the correct values.
params do
requires :status, type: Object
end
get 'testing' do
if params[:status].class.name.eql? "Array" then
elsif params[:status].class.name.eql? "String" then
end
end