4

I was following an absolutely stellar tutorial on building a GraphQL API on https://www.howtographql.com/graphql-ruby/0-introduction/

My API framework is Grape, and Mongoid is my Database framework but works very similar to rails because it uses ActiveRecord

After getting my API endpoint working and defining some models on an existing REST API I realized I am not able to write methods and filters on child records (array). My sense is that if I add a function argument to the cost_codes field in the JOBDATA::Types::JobType class that it will resolve arguments, however because of the load order this is impossible. In other frameworks to resolve this sort of issue, you can wrap your class in a string so it loads on demand after the application has been loaded, but thats not supported by graphql.

updated : I can resolve the load issue with require relative, but then my search class no longer filters the child elements of the parent, instead it calls JOBDATA::CostCode.all and tries to load the entire collection

## 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
    }
  }
}

what I need to do

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

Code:

Files are loaded in the following order

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

First to load is './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

Second './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

Third './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

Fourth './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
alilland
  • 2,039
  • 1
  • 21
  • 42

0 Answers0