13493: Update CORS OPTIONS test.
[arvados.git] / services / api / lib / record_filters.rb
index ce9fe6b13af20258987e3e2e4ca04b24aaefbebc..dc427c12c1f82cfc76d8b53a13ad1d7b8a88c032 100644 (file)
@@ -63,11 +63,17 @@ module RecordFilters
       attrs.each do |attr|
         subproperty = attr.split(".", 2)
 
-        if !model_class.searchable_columns(operator).index subproperty[0]
-          raise ArgumentError.new("Invalid attribute '#{subproperty[0]}' in filter")
-        end
+        col = model_class.columns.select { |c| c.name == subproperty[0] }.first
 
         if subproperty.length == 2
+          if col.nil? or col.type != :jsonb
+            raise ArgumentError.new("Invalid attribute '#{subproperty[0]}' for subproperty filter")
+          end
+
+          if subproperty[1][0] == "<" and subproperty[1][-1] == ">"
+            subproperty[1] = subproperty[1][1..-2]
+          end
+
         # jsonb search
           case operator.downcase
           when '=', '!='
@@ -84,32 +90,48 @@ module RecordFilters
               raise ArgumentError.new("Invalid operand type '#{operand.class}' "\
                                       "for '#{operator}' operator in filters")
             end
-          when '<', '<=', '>', '>='
-            cond_out << "#{ar_table_name}.#{subproperty[0]}->? #{operator} ?::jsonb"
-            param_out << subproperty[1]
-            param_out << SafeJSON.dump(operand)
+          when '<', '<=', '>', '>='
+            cond_out << "#{ar_table_name}.#{subproperty[0]}->? #{operator} ?::jsonb"
+            param_out << subproperty[1]
+            param_out << SafeJSON.dump(operand)
           when 'like', 'ilike'
             cond_out << "#{ar_table_name}.#{subproperty[0]}->>? #{operator} ?"
+            param_out << subproperty[1]
             param_out << operand
           when 'not in'
             if operand.is_a? Array
-              cond_out << "#{ar_table_name}.#{subproperty[0]}->>? NOT IN (?)"
+              cond_out << "#{ar_table_name}.#{subproperty[0]}->>? NOT IN (?) OR #{ar_table_name}.#{subproperty[0]}->>? IS NULL"
               param_out << subproperty[1]
               param_out << operand
+              param_out << subproperty[1]
             else
               raise ArgumentError.new("Invalid operand type '#{operand.class}' "\
                                       "for '#{operator}' operator in filters")
             end
-          when '?'
-          if operand
-            raise ArgumentError.new("Invalid operand for subproperty existence filter, should be empty or null")
+          when 'exists'
+          if operand == true
+            cond_out << "jsonb_exists(#{ar_table_name}.#{subproperty[0]}, ?)"
+          elsif operand == false
+            cond_out << "(NOT jsonb_exists(#{ar_table_name}.#{subproperty[0]}, ?)) OR #{ar_table_name}.#{subproperty[0]} is NULL"
+          else
+            raise ArgumentError.new("Invalid operand '#{operand}' for '#{operator}' must be true or false")
           end
-          cond_out << "jsonb_exists(#{ar_table_name}.#{subproperty[0]}, ?)"
           param_out << subproperty[1]
           else
             raise ArgumentError.new("Invalid operator for subproperty search '#{operator}'")
           end
+        elsif operator.downcase == "exists"
+          if col.type != :jsonb
+            raise ArgumentError.new("Invalid attribute '#{subproperty[0]}' for operator '#{operator}' in filter")
+          end
+
+          cond_out << "jsonb_exists(#{ar_table_name}.#{subproperty[0]}, ?)"
+          param_out << operand
         else
+          if !model_class.searchable_columns(operator).index subproperty[0]
+            raise ArgumentError.new("Invalid attribute '#{subproperty[0]}' in filter")
+          end
+
           case operator.downcase
           when '=', '<', '<=', '>', '>=', '!=', 'like', 'ilike'
             attr_type = model_class.attribute_column(attr).type
@@ -150,6 +172,9 @@ module RecordFilters
                  [true, false].include?(operand)
               cond_out << "#{ar_table_name}.#{attr} #{operator} ?"
               param_out << operand
+            elsif (attr_type == :integer)
+              cond_out << "#{ar_table_name}.#{attr} #{operator} ?"
+              param_out << operand
             else
               raise ArgumentError.new("Invalid operand type '#{operand.class}' "\
                                       "for '#{operator}' operator in filters")