module Ecoportal module API class GraphQL module Logic class BaseQuery include Ecoportal::API::Common::GraphQL::ClassHelpers inheritable_attrs :accepted_params, :param_defaults class << self def param_defaults @param_defaults ||= {} end def accepted_params(*keys, default: :unused) @accepted_params ||= [] return @accepted_params if keys.empty? keys.map(&:to_sym).uniq.each do |key| @accepted_params |= [key] param_defaults[key] = default unless default == :unused end @accepted_params.push(*keys).tap(&:uniq!) @accepted_params end def clear_accepted_params @param_defaults = {} @accepted_params = [] end def slice_params(kargs) kargs.slice(*accepted_params) end # Used to obtain the full `path` in the GraphQL query by using `base_path` # @note it is meant for reusability of queries from different end-points def field_name(str = nil) return @field_name unless str @field_name = nil @field_name = str.to_s if str end def base_path(path = :unused) return @base_path if path == :unused path ||= [] path = path.to_s.split('.') if path.is_a?(String) path = path.map(&:to_s).compact @base_path = path end end include Ecoportal::API::Common::Concerns::Benchmarkable attr_reader :client attr_reader :base_path def initialize(client, path: nil, base_path: self.class.base_path) @path = path @base_path = base_path @client = client end # Resolves the `path` by using `path` or `base_path` + `class.field_name`. def path(field_name = self.class.field_name) result = @path result ||= default_path if respond_to?(:default_path, true) result ||= (base_path + [field_name]) if base_path && field_name result ||= [field_name] result end # Query rely that manages the different blocks. # @return [Class] an object of `response_class` with the results hanging from `path`. def query(path: self.path, **kargs, &block) benchmarking("#{self.class}##{__method__}", print: true) do graphql_query(path: path, **kargs, &basic_block(&block)) end end def response_class raise "You should override this method in #{self.class}" end def access_point(path = []) path.last end private def basic_block raise "This method should be implemented in the child class #{self.class}" end def graphql_query(path: self.path, raw_response: {}, **kargs, &block) default_params = self.class.param_defaults.dup query_params = self.class.slice_params(default_params.merge(kargs)) request(*path) do raw_response[:data] = client.query(query_params, &block) end rescue Faraday::ParsingError, Graphlient::Errors::GraphQLError => _err puts "Internal Error with these params:" pp kargs pp block raise end def request(*path) response = yield wrap_response(response, path) end def wrap_response(response, path = []) unless (res = response.to_h.dig(*path.dup.unshift("data"))) msg = "Complete failure on request. Path: #{path}\n#{response.to_h.pretty_inspect}" raise msg end data = Ecoportal::API::Common::GraphQL::HashHelpers.deep_dup(res) response_class.new(data) end end end end end end