module GraphitiGql class Schema class ListArguments class SortDirType < GraphQL::Schema::Enum graphql_name "SortDir" value "asc", "Ascending" value "desc", "Descending" end def initialize(resource, sideload = nil) @resource = resource @sideload = sideload end def apply(field) define_filters(field) unless @resource.filters.empty? define_sorts(field) unless @resource.sorts.empty? end private def registry Registry.instance end def define_filters(field) filter_type = generate_filter_type(field) required = @resource.filters.any? { |name, config| value = !!config[:required] if @sideload value && @sideload.foreign_key != name else value end } field.argument :filter, filter_type, required: required end def generate_filter_type(field) type_name = "#{registry.key_for(@resource)}Filter" if (registered = registry[type_name]) return registered[:type] end klass = Class.new(GraphQL::Schema::InputObject) klass.graphql_name type_name @resource.filters.each_pair do |name, config| attr_type = generate_filter_attribute_type(type_name, name, config) klass.argument name.to_s.camelize(:lower), attr_type, required: !!config[:required] end registry[type_name] = { type: klass } klass end def generate_filter_attribute_type(type_name, filter_name, filter_config) klass = Class.new(GraphQL::Schema::InputObject) filter_graphql_name = "#{type_name}Filter#{filter_name.to_s.camelize(:lower)}" klass.graphql_name(filter_graphql_name) filter_config[:operators].keys.each do |operator| graphiti_type = Graphiti::Types[filter_config[:type]] type = graphiti_type[:graphql_type] if !type canonical_graphiti_type = Graphiti::Types .name_for(filter_config[:type]) type = GQL_TYPE_MAP[canonical_graphiti_type] type = String if filter_name == :id end if (allowlist = filter_config[:allow]) type = define_allowlist_type(filter_graphql_name, allowlist) end type = [type] unless !!filter_config[:single] klass.argument operator, type, required: false end klass end def define_allowlist_type(filter_graphql_name, allowlist) name = "#{filter_graphql_name}Allow" if (registered = registry[name]) return registered[:type] end klass = Class.new(GraphQL::Schema::Enum) klass.graphql_name(name) allowlist.each do |allowed| klass.value(allowed) end registry[name] = { type: klass } klass end def define_sorts(field) sort_type = generate_sort_type field.argument :sort, [sort_type], required: false end def generate_sort_att_type type_name = "#{registry.key_for(@resource)}SortAtt" if (registered = registry[type_name]) return registered[:type] end klass = Class.new(GraphQL::Schema::Enum) { graphql_name(type_name) } @resource.sorts.each_pair do |name, config| klass.value name.to_s.camelize(:lower), "Sort by #{name}" end registry[type_name] = { type: klass } klass end def generate_sort_type type_name = "#{registry.key_for(@resource)}Sort" if (registered = registry[type_name]) return registered[:type] end att_type = generate_sort_att_type klass = Class.new(GraphQL::Schema::InputObject) { graphql_name type_name argument :att, att_type, required: true argument :dir, SortDirType, required: true } registry[type_name] = { type: klass } klass end end end end