Ruby GraphQL: запуск фильтров и вызов пользовательских методов для встроенных объектов - PullRequest
3 голосов
/ 17 марта 2019

Я следовал абсолютно звездному руководству по созданию API GraphQL на https://www.howtographql.com/graphql-ruby/0-introduction/

Моя структура API - Grape, а Mongoid - моя база данных, но она очень похожа на rails, поскольку использует ActiveRecord

После того, как моя конечная точка API заработала и определив некоторые модели в существующем API REST, я понял, что не могу писать методы и фильтры для дочерних записей (массив).Мне кажется, что если я добавлю аргумент function в поле cost_codes в классе JOBDATA::Types::JobType, он будет разрешать аргументы, однако из-за порядка загрузки это невозможно.В других средах для решения такого рода проблем вы можете заключить ваш класс в строку, чтобы он загружался по требованию после загрузки приложения, но это не поддерживается в GraphQL.

updated: я могу разрешить загрузкупроблема с requirelative, но тогда мой класс поиска больше не фильтрует дочерние элементы родителя, вместо этого он вызывает JOBDATA::CostCode.all и пытается загрузить всю коллекцию

## this works on the parent, I have a custom method "orderBy"

{
  allJobs(orderBy: ["jobNumber:asc"]) {
    id,
    createdAt,
    updatedAt,
    jobNumber,
    description,
    status,
    costCodes { ## <-- by default returns the order of cost codes on the database
      id,
      createdAt,
      updatedAt,
      costCode,
      description
    }
  }
}

что мне нужно сделать

{
  allJobs(orderBy: ["jobNumber:asc"]) {
    id,
    createdAt,
    updatedAt,
    jobNumber,
    description,
    status,
    costCodes(orderBy: ["costCode:asc"]) {
      id,
      createdAt,
      updatedAt,
      costCode,
      description
    }
  }
}

Код:

Файлы загружаются в следующем порядке

module JOBDATA
  ## . . .

  ## GraphQL Files
  Dir.glob('./graphql/types/*.rb') { |file| load file }
  Dir.glob('./graphql/search_filters/*.rb') { |file| load file }
  Dir.glob('./graphql/resolvers/*.rb') { |file| load file }
  Dir.glob('./graphql/schemas/*.rb') { |file| load file }
end

Сначала загружается './graphql/types/cost_code_type.rb'

module JOBDATA
  module Types
    class CostCodeType < GraphQL::Introspection::BaseObject
      field :id, GraphQL::Types::ID, null: false
      field :created_at, GraphQL::Types::ISO8601DateTime, null: false
      field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
      field :cost_code, GraphQL::Types::String, null: false
      field :description, GraphQL::Types::String, null: true
      field :status, GraphQL::Types::String, null: false
    end
  end
end

Второй'./graphql/types/job_type.rb'

module JOBDATA
  module Types
    class JobType < GraphQL::Introspection::BaseObject
      field :id, GraphQL::Types::ID, null: false
      field :created_at, GraphQL::Types::ISO8601DateTime, null: false
      field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
      field :job_number, GraphQL::Types::String, null: false
      field :description, GraphQL::Types::String, null: true
      field :status, GraphQL::Types::String, null: false
      field :cost_codes, [JOBDATA::Types::CostCodeType], null: false

      ## this seems like it should work - but because of the required load order I 
      ## I can force it using require relative, but rather than returning only the 
      ## parents children it returns everything for that collection agnostic to the 
      ## parent (its connected via has_many relationship on the parent) 
      # field :cost_codes, [JOBDATA::Types::CostCodeType], null: false, function: JOBDATA::SearchFilters::CostCode
    end
  end
end

Третий './graphql/search_filters/cost_code_search_filter.rb'

module JOBDATA
  module SearchFilters
    class CostCode
      include SearchObject.module(:graphql)
      scope { JOBDATA::CostCode.all }
      type types[JOBDATA::Types::CostCodeType]

      option :orderBy, type: types[types.String], with: :apply_order_by, default: ['cost_code:asc']

      def apply_order_by(scope, value)
        order_by = []
        value.each do |field|
          pair = field.split(':')
          order_by.push([pair[0].underscore, pair[1].downcase])
        end
        scope.order_by(order_by)
      end
    end
  end
end

Четвертый './graphql/search_filters/job_search_filter.rb'

module JOBDATA
  module SearchFilters
    class Job
      include SearchObject.module(:graphql)
      scope { JOBDATA::Job.all }
      type types[JOBDATA::Types::JobType]

      option :orderBy, type: types[types.String], with: :apply_order_by, default: ['job_number:asc']

      def apply_order_by(scope, value)
        order_by = []
        value.each do |field|
          pair = field.split(':')
          order_by.push([pair[0].underscore, pair[1].downcase])
        end
        scope.order_by(order_by)
      end
    end
  end
end
...