-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #11 from MITLibraries/engx-234-implement-graphql
Initial GraphQL implementation
- Loading branch information
Showing
23 changed files
with
307 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
# frozen_string_literal: true | ||
|
||
class GraphqlController < ApplicationController | ||
# If accessing from outside this domain, nullify the session | ||
# This allows for outside API access while preventing CSRF attacks, | ||
# but you'll have to authenticate your user separately | ||
# protect_from_forgery with: :null_session | ||
|
||
def execute | ||
variables = prepare_variables(params[:variables]) | ||
query = params[:query] | ||
operation_name = params[:operationName] | ||
context = { | ||
# Query context goes here, for example: | ||
# current_user: current_user, | ||
} | ||
result = TacosSchema.execute(query, variables: variables, context: context, operation_name: operation_name) | ||
render json: result | ||
rescue StandardError => e | ||
raise e unless Rails.env.development? | ||
handle_error_in_development(e) | ||
end | ||
|
||
private | ||
|
||
# Handle variables in form data, JSON body, or a blank value | ||
def prepare_variables(variables_param) | ||
case variables_param | ||
when String | ||
if variables_param.present? | ||
JSON.parse(variables_param) || {} | ||
else | ||
{} | ||
end | ||
when Hash | ||
variables_param | ||
when ActionController::Parameters | ||
variables_param.to_unsafe_hash # GraphQL-Ruby will validate name and type of incoming variables. | ||
when nil | ||
{} | ||
else | ||
raise ArgumentError, "Unexpected parameter: #{variables_param}" | ||
end | ||
end | ||
|
||
def handle_error_in_development(e) | ||
logger.error e.message | ||
logger.error e.backtrace.join("\n") | ||
|
||
render json: { errors: [{ message: e.message, backtrace: e.backtrace }], data: {} }, status: 500 | ||
end | ||
end |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# frozen_string_literal: true | ||
|
||
module Mutations | ||
class BaseMutation < GraphQL::Schema::RelayClassicMutation | ||
argument_class Types::BaseArgument | ||
field_class Types::BaseField | ||
input_object_class Types::BaseInputObject | ||
object_class Types::BaseObject | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# frozen_string_literal: true | ||
|
||
class TacosSchema < GraphQL::Schema | ||
query(Types::QueryType) | ||
|
||
# For batch-loading (see https://graphql-ruby.org/dataloader/overview.html) | ||
use GraphQL::Dataloader | ||
|
||
# GraphQL-Ruby calls this when something goes wrong while running a query: | ||
def self.type_error(err, context) | ||
# if err.is_a?(GraphQL::InvalidNullError) | ||
# # report to your bug tracker here | ||
# return nil | ||
# end | ||
super | ||
end | ||
|
||
# Union and Interface Resolution | ||
def self.resolve_type(abstract_type, obj, ctx) | ||
# TODO: Implement this method | ||
# to return the correct GraphQL object type for `obj` | ||
raise(GraphQL::RequiredImplementationMissingError) | ||
end | ||
|
||
# Stop validating when it encounters this many errors: | ||
validate_max_errors(100) | ||
|
||
# Relay-style Object Identification: | ||
|
||
# Return a string UUID for `object` | ||
def self.id_from_object(object, type_definition, query_ctx) | ||
# For example, use Rails' GlobalID library (https://github.com/rails/globalid): | ||
object.to_gid_param | ||
end | ||
|
||
# Given a string UUID, find the object | ||
def self.object_from_id(global_id, query_ctx) | ||
# For example, use Rails' GlobalID library (https://github.com/rails/globalid): | ||
GlobalID.find(global_id) | ||
end | ||
end |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# frozen_string_literal: true | ||
|
||
module Types | ||
class BaseArgument < GraphQL::Schema::Argument | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# frozen_string_literal: true | ||
|
||
module Types | ||
class BaseConnection < Types::BaseObject | ||
# add `nodes` and `pageInfo` fields, as well as `edge_type(...)` and `node_nullable(...)` overrides | ||
include GraphQL::Types::Relay::ConnectionBehaviors | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# frozen_string_literal: true | ||
|
||
module Types | ||
class BaseEdge < Types::BaseObject | ||
# add `node` and `cursor` fields, as well as `node_type(...)` override | ||
include GraphQL::Types::Relay::EdgeBehaviors | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# frozen_string_literal: true | ||
|
||
module Types | ||
class BaseEnum < GraphQL::Schema::Enum | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# frozen_string_literal: true | ||
|
||
module Types | ||
class BaseField < GraphQL::Schema::Field | ||
argument_class Types::BaseArgument | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# frozen_string_literal: true | ||
|
||
module Types | ||
class BaseInputObject < GraphQL::Schema::InputObject | ||
argument_class Types::BaseArgument | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# frozen_string_literal: true | ||
|
||
module Types | ||
module BaseInterface | ||
include GraphQL::Schema::Interface | ||
edge_type_class(Types::BaseEdge) | ||
connection_type_class(Types::BaseConnection) | ||
|
||
field_class Types::BaseField | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# frozen_string_literal: true | ||
|
||
module Types | ||
class BaseObject < GraphQL::Schema::Object | ||
edge_type_class(Types::BaseEdge) | ||
connection_type_class(Types::BaseConnection) | ||
field_class Types::BaseField | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# frozen_string_literal: true | ||
|
||
module Types | ||
class BaseScalar < GraphQL::Schema::Scalar | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# frozen_string_literal: true | ||
|
||
module Types | ||
class BaseUnion < GraphQL::Schema::Union | ||
edge_type_class(Types::BaseEdge) | ||
connection_type_class(Types::BaseConnection) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
module Types | ||
class MutationType < Types::BaseObject | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# frozen_string_literal: true | ||
|
||
module Types | ||
module NodeType | ||
include Types::BaseInterface | ||
# Add the `id` field | ||
include GraphQL::Types::Relay::NodeBehaviors | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# frozen_string_literal: true | ||
|
||
module Types | ||
class QueryType < Types::BaseObject | ||
field :node, Types::NodeType, null: true, description: 'Fetches an object given its ID.' do | ||
argument :id, ID, required: true, description: 'ID of the object.' | ||
end | ||
|
||
def node(id:) | ||
context.schema.object_from_id(id, context) | ||
end | ||
|
||
field :nodes, [Types::NodeType, { null: true }], null: true, | ||
description: 'Fetches a list of objects given a list of IDs.' do | ||
argument :ids, [ID], required: true, description: 'IDs of the objects.' | ||
end | ||
|
||
def nodes(ids:) | ||
ids.map { |id| context.schema.object_from_id(id, context) } | ||
end | ||
|
||
# Add root-level fields here. | ||
# They will be entry points for queries on your schema. | ||
|
||
field :log_search_event, SearchEventType, null: false, | ||
description: 'Log a search and return information about it.' do | ||
argument :search_term, String, required: true | ||
argument :source_system, String, required: true | ||
end | ||
|
||
def log_search_event(search_term:, source_system:) | ||
term = Term.create_or_find_by!(phrase: search_term) | ||
term.search_events.create!(source: source_system) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# frozen_string_literal: true | ||
|
||
module Types | ||
class SearchEventType < Types::BaseObject | ||
field :id, ID, null: false | ||
field :term_id, Integer | ||
field :source, String | ||
field :created_at, GraphQL::Types::ISO8601DateTime, null: false | ||
field :updated_at, GraphQL::Types::ISO8601DateTime, null: false | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
require 'test_helper' | ||
|
||
class GraphqlControllerTest < ActionDispatch::IntegrationTest | ||
test 'search event query returns relevant data' do | ||
post '/graphql', params: { query: '{ | ||
logSearchEvent(sourceSystem: "bento", searchTerm: "range life") { | ||
termId | ||
source | ||
createdAt | ||
updatedAt | ||
} | ||
}' } | ||
assert_equal(200, response.status) | ||
json = JSON.parse(response.body) | ||
term_id = Term.last.id | ||
|
||
assert_equal 'bento', json['data']['logSearchEvent']['source'] | ||
assert_equal term_id, json['data']['logSearchEvent']['termId'] | ||
assert_equal Date.today, json['data']['logSearchEvent']['createdAt'].to_date | ||
assert_equal Date.today, json['data']['logSearchEvent']['updatedAt'].to_date | ||
end | ||
|
||
test 'search event query creates a new term if one does not exist' do | ||
initial_term_count = Term.count | ||
post '/graphql', params: { query: '{ | ||
logSearchEvent(sourceSystem: "bento", searchTerm: "range life") { | ||
termId | ||
source | ||
createdAt | ||
updatedAt | ||
} | ||
}' } | ||
assert_equal(200, response.status) | ||
assert_equal Term.count, (initial_term_count + 1) | ||
assert_equal 'range life', Term.last.phrase | ||
end | ||
|
||
test 'search event query does not create a new term if phrase is already stored' do | ||
initial_term_count = Term.count | ||
post '/graphql', params: { query: '{ | ||
logSearchEvent(sourceSystem: "timdex", searchTerm: "Super cool search") { | ||
termId | ||
source | ||
createdAt | ||
updatedAt | ||
} | ||
}' } | ||
assert_equal(200, response.status) | ||
assert_equal Term.count, initial_term_count | ||
end | ||
end |