From 0681e5f766381fc8a1dada7304232b7edb80234b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Geonizeli?= Date: Thu, 21 Jul 2022 10:55:38 -0300 Subject: [PATCH] graphql setup --- Gemfile | 3 ++ Gemfile.lock | 9 +++++ app/controllers/graphql_controller.rb | 50 ++++++++++++++++++++++++++ app/graphql/mutations/.keep | 0 app/graphql/mutations/base_mutation.rb | 8 +++++ app/graphql/progress_test_schema.rb | 37 +++++++++++++++++++ app/graphql/types/.keep | 0 app/graphql/types/base_argument.rb | 4 +++ app/graphql/types/base_connection.rb | 6 ++++ app/graphql/types/base_edge.rb | 6 ++++ app/graphql/types/base_enum.rb | 4 +++ app/graphql/types/base_field.rb | 5 +++ app/graphql/types/base_input_object.rb | 5 +++ app/graphql/types/base_interface.rb | 9 +++++ app/graphql/types/base_object.rb | 7 ++++ app/graphql/types/base_scalar.rb | 4 +++ app/graphql/types/base_union.rb | 6 ++++ app/graphql/types/mutation_type.rb | 10 ++++++ app/graphql/types/node_type.rb | 7 ++++ app/graphql/types/query_type.rb | 17 +++++++++ config/routes.rb | 5 +++ 21 files changed, 202 insertions(+) create mode 100644 app/controllers/graphql_controller.rb create mode 100644 app/graphql/mutations/.keep create mode 100644 app/graphql/mutations/base_mutation.rb create mode 100644 app/graphql/progress_test_schema.rb create mode 100644 app/graphql/types/.keep create mode 100644 app/graphql/types/base_argument.rb create mode 100644 app/graphql/types/base_connection.rb create mode 100644 app/graphql/types/base_edge.rb create mode 100644 app/graphql/types/base_enum.rb create mode 100644 app/graphql/types/base_field.rb create mode 100644 app/graphql/types/base_input_object.rb create mode 100644 app/graphql/types/base_interface.rb create mode 100644 app/graphql/types/base_object.rb create mode 100644 app/graphql/types/base_scalar.rb create mode 100644 app/graphql/types/base_union.rb create mode 100644 app/graphql/types/mutation_type.rb create mode 100644 app/graphql/types/node_type.rb create mode 100644 app/graphql/types/query_type.rb diff --git a/Gemfile b/Gemfile index 37594a0..f170bb9 100644 --- a/Gemfile +++ b/Gemfile @@ -35,6 +35,8 @@ gem "devise", "~> 4.8" gem "omniauth", "~> 1.9.1" gem "omniauth-google-oauth2", "~> 0.8.2" +gem "graphql", "~> 2.0" + group :development, :test do gem "dotenv-rails", "~> 2.7" gem "rspec-rails", "~> 5.1" @@ -48,6 +50,7 @@ group :development do gem "annotate", "~> 3.2" gem "rails-erd", "~> 1.7" gem "web-console" + gem "graphql_playground-rails" end group :test do diff --git a/Gemfile.lock b/Gemfile.lock index 82bf802..61ffda1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -124,6 +124,12 @@ GEM formtastic_i18n (0.7.0) globalid (1.0.0) activesupport (>= 5.0) + graphiql-rails (1.8.0) + railties + sprockets-rails + graphql (2.0.12) + graphql_playground-rails (2.1.0) + rails (>= 5.1.0) has_scope (0.8.0) actionpack (>= 5.2) activesupport (>= 5.2) @@ -340,6 +346,9 @@ DEPENDENCIES enumerize (~> 2.5) factory_bot_rails (~> 6.2) faker (~> 2.21) + graphiql-rails + graphql (~> 2.0) + graphql_playground-rails image_processing (~> 1.2) importmap-rails jbuilder diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb new file mode 100644 index 0000000..016713e --- /dev/null +++ b/app/controllers/graphql_controller.rb @@ -0,0 +1,50 @@ +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 = ProgressTestSchema.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 diff --git a/app/graphql/mutations/.keep b/app/graphql/mutations/.keep new file mode 100644 index 0000000..e69de29 diff --git a/app/graphql/mutations/base_mutation.rb b/app/graphql/mutations/base_mutation.rb new file mode 100644 index 0000000..0749ec0 --- /dev/null +++ b/app/graphql/mutations/base_mutation.rb @@ -0,0 +1,8 @@ +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 diff --git a/app/graphql/progress_test_schema.rb b/app/graphql/progress_test_schema.rb new file mode 100644 index 0000000..0179a68 --- /dev/null +++ b/app/graphql/progress_test_schema.rb @@ -0,0 +1,37 @@ +class ProgressTestSchema < GraphQL::Schema + mutation(Types::MutationType) + 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 + + # 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 diff --git a/app/graphql/types/.keep b/app/graphql/types/.keep new file mode 100644 index 0000000..e69de29 diff --git a/app/graphql/types/base_argument.rb b/app/graphql/types/base_argument.rb new file mode 100644 index 0000000..c1bfdab --- /dev/null +++ b/app/graphql/types/base_argument.rb @@ -0,0 +1,4 @@ +module Types + class BaseArgument < GraphQL::Schema::Argument + end +end diff --git a/app/graphql/types/base_connection.rb b/app/graphql/types/base_connection.rb new file mode 100644 index 0000000..df16c17 --- /dev/null +++ b/app/graphql/types/base_connection.rb @@ -0,0 +1,6 @@ +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 diff --git a/app/graphql/types/base_edge.rb b/app/graphql/types/base_edge.rb new file mode 100644 index 0000000..e3c4bf7 --- /dev/null +++ b/app/graphql/types/base_edge.rb @@ -0,0 +1,6 @@ +module Types + class BaseEdge < Types::BaseObject + # add `node` and `cursor` fields, as well as `node_type(...)` override + include GraphQL::Types::Relay::EdgeBehaviors + end +end diff --git a/app/graphql/types/base_enum.rb b/app/graphql/types/base_enum.rb new file mode 100644 index 0000000..b45a845 --- /dev/null +++ b/app/graphql/types/base_enum.rb @@ -0,0 +1,4 @@ +module Types + class BaseEnum < GraphQL::Schema::Enum + end +end diff --git a/app/graphql/types/base_field.rb b/app/graphql/types/base_field.rb new file mode 100644 index 0000000..7142ef7 --- /dev/null +++ b/app/graphql/types/base_field.rb @@ -0,0 +1,5 @@ +module Types + class BaseField < GraphQL::Schema::Field + argument_class Types::BaseArgument + end +end diff --git a/app/graphql/types/base_input_object.rb b/app/graphql/types/base_input_object.rb new file mode 100644 index 0000000..c97c479 --- /dev/null +++ b/app/graphql/types/base_input_object.rb @@ -0,0 +1,5 @@ +module Types + class BaseInputObject < GraphQL::Schema::InputObject + argument_class Types::BaseArgument + end +end diff --git a/app/graphql/types/base_interface.rb b/app/graphql/types/base_interface.rb new file mode 100644 index 0000000..a330aef --- /dev/null +++ b/app/graphql/types/base_interface.rb @@ -0,0 +1,9 @@ +module Types + module BaseInterface + include GraphQL::Schema::Interface + edge_type_class(Types::BaseEdge) + connection_type_class(Types::BaseConnection) + + field_class Types::BaseField + end +end diff --git a/app/graphql/types/base_object.rb b/app/graphql/types/base_object.rb new file mode 100644 index 0000000..ed6a321 --- /dev/null +++ b/app/graphql/types/base_object.rb @@ -0,0 +1,7 @@ +module Types + class BaseObject < GraphQL::Schema::Object + edge_type_class(Types::BaseEdge) + connection_type_class(Types::BaseConnection) + field_class Types::BaseField + end +end diff --git a/app/graphql/types/base_scalar.rb b/app/graphql/types/base_scalar.rb new file mode 100644 index 0000000..c0aa38b --- /dev/null +++ b/app/graphql/types/base_scalar.rb @@ -0,0 +1,4 @@ +module Types + class BaseScalar < GraphQL::Schema::Scalar + end +end diff --git a/app/graphql/types/base_union.rb b/app/graphql/types/base_union.rb new file mode 100644 index 0000000..d6dd3ac --- /dev/null +++ b/app/graphql/types/base_union.rb @@ -0,0 +1,6 @@ +module Types + class BaseUnion < GraphQL::Schema::Union + edge_type_class(Types::BaseEdge) + connection_type_class(Types::BaseConnection) + end +end diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb new file mode 100644 index 0000000..28516a5 --- /dev/null +++ b/app/graphql/types/mutation_type.rb @@ -0,0 +1,10 @@ +module Types + class MutationType < Types::BaseObject + # TODO: remove me + field :test_field, String, null: false, + description: "An example field added by the generator" + def test_field + "Hello World" + end + end +end diff --git a/app/graphql/types/node_type.rb b/app/graphql/types/node_type.rb new file mode 100644 index 0000000..8daea84 --- /dev/null +++ b/app/graphql/types/node_type.rb @@ -0,0 +1,7 @@ +module Types + module NodeType + include Types::BaseInterface + # Add the `id` field + include GraphQL::Types::Relay::NodeBehaviors + end +end diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb new file mode 100644 index 0000000..e884f8b --- /dev/null +++ b/app/graphql/types/query_type.rb @@ -0,0 +1,17 @@ +module Types + class QueryType < Types::BaseObject + # Add `node(id: ID!) and `nodes(ids: [ID!]!)` + include GraphQL::Types::Relay::HasNodeField + include GraphQL::Types::Relay::HasNodesField + + # Add root-level fields here. + # They will be entry points for queries on your schema. + + # TODO: remove me + field :test_field, String, null: false, + description: "An example field added by the generator" + def test_field + "Hello World!" + end + end +end diff --git a/config/routes.rb b/config/routes.rb index 3980ef9..c0157cd 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,10 @@ Rails.application.routes.draw do devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' } + post "/graphql", to: "graphql#execute" ActiveAdmin.routes(self) + + if Rails.env.development? + mount GraphqlPlayground::Rails::Engine, at: "/playground", graphql_path: "/graphql" + end end