We have a Rails controller that gets the following data:
params = ActionController::Parameters.new({
"requests": [{
"params": {
"facets": ["user.id", "user.type"],
"facetFilters": [
["user.type:Individual"]
]
}
}, {
"params": {
"facets": "user.type"
}
}]
})
We want to use strong parameters to accept this data, but I haven't yet seen a pattern that will let us accept the 2D array in facetFilters
. I'm tinkering with the following:
params[:requests].each do |request|
request[:permitted] = true
request[:params].each do |o|
if ['facets', 'facetFilters'].include?(o.first)
begin
o[:permitted] = true
rescue
end
end
end
end
As one can see not all attributes are permitted (the permitted attributes don't get passed to children elements), as this returns:
[<ActionController::Parameters {"params"=><ActionController::Parameters {"facets"=>["user.id", "user.type"], "facetFilters"=>[["user.type:Individual"]]} permitted: false>, "permitted"=>true} permitted: false>, <ActionController::Parameters {"params"=><ActionController::Parameters {"facets"=>"user.type"} permitted: false>, "permitted"=>true} permitted: false>]
And there are lots of permitted: false in there...
Is it possible to accomplish this goal? Any pointers would be super helpful!
The best way I've found to get this done is to convert the 2D array to a 1D array, permit the 1D array, and then convert the array back to 2D.
nested_facet_filters = []
(params[:requests] || []).each_with_index do |r, ridx|
if r[:params].key?(:facetFilters) && r[:params][:facetFilters].kind_of?(Array) && r[:params][:facetFilters].first.kind_of?(Array)
# flatten the facet filters into a 1D array because Rails can't permit a 2D array
nested_facet_filters << ridx
r[:params][:facetFilters] = r[:params][:facetFilters].flatten
end
end
permitted = params.permit({
requests: [
:query,
:params => [
:facets => [],
:facetFilters => [],
]
]
}).to_h
# after permitting the params, we need to convert the facetFilters back into a 2D array
if nested_facet_filters.present?
nested_facet_filters.each do |idx|
# create map from facet key to array of values
d = {}
permitted[:requests][idx][:params][:facetFilters].each do |s|
split = s.split(':')
facet = split.first
value = split[1..-1].join(':')
if d.key?(facet)
d[facet] << s
else
d[facet] = [s]
end
end
# mutate facetFilters back to 2D array for posting to Algolia
permitted[:requests][idx][:params][:facetFilters] = d.values
end
end
There's been a merge request to accept 2D arrays for six years open in the Rails issue tracker [thread]...