X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/d8de76141cbe9a9af10dfc408b6d656cc727bbe5..dd6e25d53f9cf02f91b419584ff4c331e0618a24:/apps/workbench/app/controllers/application_controller.rb diff --git a/apps/workbench/app/controllers/application_controller.rb b/apps/workbench/app/controllers/application_controller.rb index f9de62d60e..eabf3c58ef 100644 --- a/apps/workbench/app/controllers/application_controller.rb +++ b/apps/workbench/app/controllers/application_controller.rb @@ -1,16 +1,16 @@ class ApplicationController < ActionController::Base + include ArvadosApiClientHelper + respond_to :html, :json, :js protect_from_forgery ERROR_ACTIONS = [:render_error, :render_not_found] around_filter :thread_clear - around_filter(:thread_with_mandatory_api_token, - except: [:index, :show] + ERROR_ACTIONS) + around_filter :thread_with_mandatory_api_token, except: ERROR_ACTIONS around_filter :thread_with_optional_api_token before_filter :check_user_agreements, except: ERROR_ACTIONS before_filter :check_user_notifications, except: ERROR_ACTIONS - around_filter :using_reader_tokens, only: [:index, :show] before_filter :find_object_by_uuid, except: [:index] + ERROR_ACTIONS theme :select_theme @@ -64,19 +64,27 @@ class ApplicationController < ActionController::Base end def index + @limit ||= 200 if params[:limit] - limit = params[:limit].to_i - else - limit = 200 + @limit = params[:limit].to_i end + @offset ||= 0 if params[:offset] - offset = params[:offset].to_i - else - offset = 0 + @offset = params[:offset].to_i + end + + @filters ||= [] + if params[:filters] + filters = params[:filters] + if filters.is_a? String + filters = Oj.load filters + end + @filters += filters end - @objects ||= model_class.limit(limit).offset(offset).all + @objects ||= model_class + @objects = @objects.filter(@filters).limit(@limit).offset(@offset).all respond_to do |f| f.json { render json: @objects } f.html { render } @@ -89,7 +97,7 @@ class ApplicationController < ActionController::Base return render_not_found("object not found") end respond_to do |f| - f.json { render json: @object } + f.json { render json: @object.attributes.merge(href: url_for(@object)) } f.html { if request.method == 'GET' render @@ -112,21 +120,21 @@ class ApplicationController < ActionController::Base end def update - updates = params[@object.class.to_s.underscore.singularize.to_sym] - updates.keys.each do |attr| + @updates ||= params[@object.class.to_s.underscore.singularize.to_sym] + @updates.keys.each do |attr| if @object.send(attr).is_a? Hash - if updates[attr].is_a? String - updates[attr] = Oj.load updates[attr] + if @updates[attr].is_a? String + @updates[attr] = Oj.load @updates[attr] end if params[:merge] || params["merge_#{attr}".to_sym] # Merge provided Hash with current Hash, instead of # replacing. - updates[attr] = @object.send(attr).with_indifferent_access. - deep_merge(updates[attr].with_indifferent_access) + @updates[attr] = @object.send(attr).with_indifferent_access. + deep_merge(@updates[attr].with_indifferent_access) end end end - if @object.update_attributes updates + if @object.update_attributes @updates show else self.render_error status: 422 @@ -134,15 +142,20 @@ class ApplicationController < ActionController::Base end def create - @object ||= model_class.new params[model_class.to_s.underscore.singularize] - @object.save! - - respond_to do |f| - f.json { render json: @object } - f.html { - redirect_to(params[:return_to] || @object) - } - f.js { render } + @new_resource_attrs ||= params[model_class.to_s.underscore.singularize] + @new_resource_attrs ||= {} + @new_resource_attrs.reject! { |k,v| k.to_s == 'uuid' } + @object ||= model_class.new @new_resource_attrs, params["options"] + if @object.save + respond_to do |f| + f.json { render json: @object.attributes.merge(href: url_for(@object)) } + f.html { + redirect_to @object + } + f.js { render } + end + else + self.render_error status: 422 end end @@ -193,7 +206,7 @@ class ApplicationController < ActionController::Base respond_to do |f| f.html { if request.method == 'GET' - redirect_to $arvados_api_client.arvados_login_url(return_to: request.url) + redirect_to arvados_api_client.arvados_login_url(return_to: request.url) else flash[:error] = "Either you are not logged in, or your session has timed out. I can't automatically log you in and re-attempt this request." redirect_to :back @@ -207,23 +220,6 @@ class ApplicationController < ActionController::Base false # For convenience to return from callbacks end - def using_reader_tokens(login_optional=false) - if params[:reader_tokens].is_a?(Array) and params[:reader_tokens].any? - Thread.current[:reader_tokens] = params[:reader_tokens] - end - begin - yield - rescue ArvadosApiClient::NotLoggedInException - if login_optional - raise - else - return redirect_to_login - end - ensure - Thread.current[:reader_tokens] = nil - end - end - def using_specific_api_token(api_token) start_values = {} [:arvados_api_token, :user].each do |key| @@ -242,8 +238,14 @@ class ApplicationController < ActionController::Base if params[:id] and params[:id].match /\D/ params[:uuid] = params.delete :id end - if params[:uuid].is_a? String - @object = model_class.find(params[:uuid]) + if not model_class + @object = nil + elsif params[:uuid].is_a? String + if params[:uuid].empty? + @object = nil + else + @object = model_class.find(params[:uuid]) + end else @object = model_class.where(uuid: params[:uuid]).first end @@ -429,4 +431,198 @@ class ApplicationController < ActionController::Base @notification_count = '' end end + + helper_method :my_folders + def my_folders + return @my_folders if @my_folders + @my_folders = [] + root_of = {} + Group.filter([['group_class','=','folder']]).each do |g| + root_of[g.uuid] = g.owner_uuid + @my_folders << g + end + done = false + while not done + done = true + root_of = root_of.each_with_object({}) do |(child, parent), h| + if root_of[parent] + h[child] = root_of[parent] + done = false + else + h[child] = parent + end + end + end + @my_folders = @my_folders.select do |g| + root_of[g.uuid] == current_user.uuid + end + end + + # helper method to get links for given object or uuid + helper_method :links_for_object + def links_for_object object_or_uuid + raise ArgumentError, 'No input argument' unless object_or_uuid + preload_links_for_objects([object_or_uuid]) + uuid = object_or_uuid.is_a?(String) ? object_or_uuid : object_or_uuid.uuid + @all_links_for[uuid] ||= [] + end + + # helper method to preload links for given objects and uuids + helper_method :preload_links_for_objects + def preload_links_for_objects objects_and_uuids + @all_links_for ||= {} + + raise ArgumentError, 'Argument is not an array' unless objects_and_uuids.is_a? Array + return @all_links_for if objects_and_uuids.empty? + + uuids = objects_and_uuids.collect { |x| x.is_a?(String) ? x : x.uuid } + + # if already preloaded for all of these uuids, return + if not uuids.select { |x| @all_links_for[x].nil? }.any? + return @all_links_for + end + + uuids.each do |x| + @all_links_for[x] = [] + end + + # TODO: make sure we get every page of results from API server + Link.filter([['head_uuid', 'in', uuids]]).each do |link| + @all_links_for[link.head_uuid] << link + end + @all_links_for + end + + # helper method to get a certain number of objects of a specific type + # this can be used to replace any uses of: "dataclass.limit(n)" + helper_method :get_n_objects_of_class + def get_n_objects_of_class dataclass, size + @objects_map_for ||= {} + + raise ArgumentError, 'Argument is not a data class' unless dataclass.is_a? Class + raise ArgumentError, 'Argument is not a valid limit size' unless (size && size>0) + + # if the objects_map_for has a value for this dataclass, and the + # size used to retrieve those objects is equal, return it + size_key = "#{dataclass.name}_size" + if @objects_map_for[dataclass.name] && @objects_map_for[size_key] && + (@objects_map_for[size_key] == size) + return @objects_map_for[dataclass.name] + end + + @objects_map_for[size_key] = size + @objects_map_for[dataclass.name] = dataclass.limit(size) + end + + # helper method to get collections for the given uuid + helper_method :collections_for_object + def collections_for_object uuid + raise ArgumentError, 'No input argument' unless uuid + preload_collections_for_objects([uuid]) + @all_collections_for[uuid] ||= [] + end + + # helper method to preload collections for the given uuids + helper_method :preload_collections_for_objects + def preload_collections_for_objects uuids + @all_collections_for ||= {} + + raise ArgumentError, 'Argument is not an array' unless uuids.is_a? Array + return @all_collections_for if uuids.empty? + + # if already preloaded for all of these uuids, return + if not uuids.select { |x| @all_collections_for[x].nil? }.any? + return @all_collections_for + end + + uuids.each do |x| + @all_collections_for[x] = [] + end + + # TODO: make sure we get every page of results from API server + Collection.where(uuid: uuids).each do |collection| + @all_collections_for[collection.uuid] << collection + end + @all_collections_for + end + + # helper method to get log collections for the given log + helper_method :log_collections_for_object + def log_collections_for_object log + raise ArgumentError, 'No input argument' unless log + + preload_log_collections_for_objects([log]) + + uuid = log + fixup = /([a-f0-9]{32}\+\d+)(\+?.*)/.match(log) + if fixup && fixup.size>1 + uuid = fixup[1] + end + + @all_log_collections_for[uuid] ||= [] + end + + # helper method to preload collections for the given uuids + helper_method :preload_log_collections_for_objects + def preload_log_collections_for_objects logs + @all_log_collections_for ||= {} + + raise ArgumentError, 'Argument is not an array' unless logs.is_a? Array + return @all_log_collections_for if logs.empty? + + uuids = [] + logs.each do |log| + fixup = /([a-f0-9]{32}\+\d+)(\+?.*)/.match(log) + if fixup && fixup.size>1 + uuids << fixup[1] + else + uuids << log + end + end + + # if already preloaded for all of these uuids, return + if not uuids.select { |x| @all_log_collections_for[x].nil? }.any? + return @all_log_collections_for + end + + uuids.each do |x| + @all_log_collections_for[x] = [] + end + + # TODO: make sure we get every page of results from API server + Collection.where(uuid: uuids).each do |collection| + @all_log_collections_for[collection.uuid] << collection + end + @all_log_collections_for + end + + # helper method to get object of a given dataclass and uuid + helper_method :object_for_dataclass + def object_for_dataclass dataclass, uuid + raise ArgumentError, 'No input argument dataclass' unless (dataclass && uuid) + preload_objects_for_dataclass(dataclass, [uuid]) + @objects_for[uuid] + end + + # helper method to preload objects for given dataclass and uuids + helper_method :preload_objects_for_dataclass + def preload_objects_for_dataclass dataclass, uuids + @objects_for ||= {} + + raise ArgumentError, 'Argument is not a data class' unless dataclass.is_a? Class + raise ArgumentError, 'Argument is not an array' unless uuids.is_a? Array + + return @all_collections_for if uuids.empty? + + # if already preloaded for all of these uuids, return + if not uuids.select { |x| @objects_for[x].nil? }.any? + return @objects_for + end + + dataclass.where(uuid: uuids).each do |obj| + @objects_for[obj.uuid] = obj + end + @objects_for + end + end