Added ArvadosModel.search_for_user to perform full-text search on a model.
[arvados.git] / services / api / app / models / arvados_model.rb
index 991755b48a67f9803bbcdf415c1552c09dec1ba2..4e406de9d5c55b538b7f41ca1b3f3271ea3e4234 100644 (file)
@@ -1,3 +1,4 @@
+require 'assign_uuid'
 class ArvadosModel < ActiveRecord::Base
   self.abstract_class = true
 
@@ -36,6 +37,39 @@ class ArvadosModel < ActiveRecord::Base
     "#{current_api_base}/#{self.class.to_s.pluralize.underscore}/#{self.uuid}"
   end
 
+  def self.searchable_columns
+    self.columns.collect do |col|
+      if [:string, :text].index(col.type) && col.name != 'owner_uuid'
+        col.name
+      end
+    end.compact
+  end
+
+  # searchable? indicates whether a model is subject to full-text
+  # search through the workbench.  Models which include data that is
+  # interesting to a full-text search (e.g. Collection, Specimen, Trait)
+  # should override this to return 'true'.
+  def self.searchable?
+    false
+  end
+
+  # search all "searchable" columns of this table for query_str.
+  def self.search_for_user(user, query_str)
+    return [] unless self.searchable?
+    ilikes = []
+    ilike_params = []
+    self.searchable_columns.each do |column|
+      ilikes << "#{table_name}.#{column} ilike ?"
+      ilike_params << "%#{query_str}%"
+    end
+    if ilikes.empty?
+      return []
+    else
+      query_conditions = [ ilikes.join(' or ') ] + ilike_params
+      return self.readable_by(user).where(query_conditions)
+    end
+  end
+
   def eager_load_associations
     self.class.columns.each do |col|
       re = col.name.match /^(.*)_kind$/
@@ -49,6 +83,21 @@ class ArvadosModel < ActiveRecord::Base
     end
   end
 
+  def self.readable_by user
+    uuid_list = [user.uuid, *user.groups_i_can(:read)]
+    sanitized_uuid_list = uuid_list.
+      collect { |uuid| sanitize(uuid) }.join(', ')
+    or_references_me = ''
+    if self == Link and user
+      or_references_me = "OR (#{table_name}.link_class in (#{sanitize 'permission'}, #{sanitize 'resources'}) AND #{sanitize user.uuid} IN (#{table_name}.head_uuid, #{table_name}.tail_uuid))"
+    end
+    joins("LEFT JOIN links permissions ON permissions.head_uuid in (#{table_name}.owner_uuid, #{table_name}.uuid) AND permissions.tail_uuid in (#{sanitized_uuid_list}) AND permissions.link_class='permission'").
+      where("?=? OR #{table_name}.owner_uuid in (?) OR #{table_name}.uuid=? OR permissions.head_uuid IS NOT NULL #{or_references_me}",
+            true, user.is_admin,
+            uuid_list,
+            user.uuid)
+  end
+
   protected
 
   def ensure_permission_to_create
@@ -132,4 +181,30 @@ class ArvadosModel < ActiveRecord::Base
       end
     end
   end
+
+  def self.resource_class_for_uuid(uuid)
+    if uuid.is_a? ArvadosModel
+      return uuid.class
+    end
+    unless uuid.is_a? String
+      return nil
+    end
+    if uuid.match /^[0-9a-f]{32}(\+[^,]+)*(,[0-9a-f]{32}(\+[^,]+)*)*$/
+      return Collection
+    end
+    resource_class = nil
+
+    Rails.application.eager_load!
+    uuid.match /^[0-9a-z]{5}-([0-9a-z]{5})-[0-9a-z]{15}$/ do |re|
+      ActiveRecord::Base.descendants.reject(&:abstract_class?).each do |k|
+        if k.respond_to?(:uuid_prefix)
+          if k.uuid_prefix == re[1]
+            return k
+          end
+        end
+      end
+    end
+    nil
+  end
+
 end