Skip to content

Commit

Permalink
Prepare changes for lesson 12 (fs#130)
Browse files Browse the repository at this point in the history
* prepare changes for lesson 12

* fix cops

* fix cops 2

* add information for second lesson

* add enum status
  • Loading branch information
DenisZackharov authored Nov 28, 2023
1 parent ff6ec56 commit 4865c60
Show file tree
Hide file tree
Showing 36 changed files with 379 additions and 2 deletions.
12 changes: 10 additions & 2 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,16 @@ Screenshots/screencasts of the pull request introduced functionality.

List of steps to manually test introduced functionality:

- Log in
- Go to http://localhost:3000
- Go to http://localhost:3000/graphiql
- Make request using schema:
```graphql
query {
me {
id
name
}
}
```

### Deploy notes

Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,7 @@ jobs:

- name: Run rubocop
run: bin/rubocop

- name: Run tests
run: bin/rspec
...
8 changes: 8 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ require:

AllCops:
NewCops: enable
Exclude:
- bin/**/*
- db/**/*
- vendor/**/*
- tmp/**/*
- app/controllers/graphql_controller.rb
- app/graphql/task_tracker_itis_schema.rb
- app/graphql/types/mutation_type.rb

I18n/GetText/DecorateString:
Enabled: false
Expand Down
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ ruby "2.7.4"
gem "action_policy"
gem "active_model_serializers"
gem "enumerize"
gem "graphql"
gem "interactor", "~> 3.0"
# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
gem "rails", "~> 6.1.7"
Expand Down Expand Up @@ -60,6 +61,7 @@ group :development do
gem "listen", "~> 3.3"
gem "rack-mini-profiler", "~> 2.0"
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem "graphiql-rails"
gem "spring"
end

Expand Down
7 changes: 7 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ GEM
ffi (1.15.5)
globalid (1.2.1)
activesupport (>= 6.1)
graphiql-rails (1.9.0)
railties
sprockets-rails
graphql (2.1.6)
racc (~> 1.4)
i18n (1.14.1)
concurrent-ruby (~> 1.0)
interactor (3.1.2)
Expand Down Expand Up @@ -346,6 +351,8 @@ DEPENDENCIES
enumerize
factory_bot_rails
faker
graphiql-rails
graphql
interactor (~> 3.0)
jbuilder (~> 2.7)
jwt
Expand Down
53 changes: 53 additions & 0 deletions app/controllers/graphql_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# 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

skip_verify_authorized

def execute
variables = prepare_variables(params[:variables])
query = params[:query]
operation_name = params[:operationName]
context = {
current_user: current_user
}
result = TaskTrackerItisSchema.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
11 changes: 11 additions & 0 deletions app/graphql/concerns/graphql_errors.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module GraphqlErrors
def formatted_errors(model)
model.errors.map do |error|
path = ["attributes", error.attribute.to_s.camelize(:lower)]
{
path: path,
message: error.message
}
end
end
end
Empty file added app/graphql/mutations/.keep
Empty file.
15 changes: 15 additions & 0 deletions app/graphql/mutations/base_mutation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

module Mutations
class BaseMutation < GraphQL::Schema::Mutation
include GraphqlErrors

argument_class Types::BaseArgument
field_class Types::BaseField
object_class Types::BaseObject

def current_user
@context[:current_user]
end
end
end
13 changes: 13 additions & 0 deletions app/graphql/mutations/create_project.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Mutations
class CreateProject < BaseMutation
argument :input, Types::Inputs::CreateProjectInput, required: true

type Types::Payloads::CreateProjectPayload

def resolve(input:)
result = Projects::Create.call(project_params: input.to_h, user: current_user)

result.to_h.merge(errors: formatted_errors(result.project))
end
end
end
9 changes: 9 additions & 0 deletions app/graphql/resolvers/base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Resolvers
class Base < GraphQL::Schema::Resolver
argument_class Types::BaseArgument

def current_user
@context[:current_user]
end
end
end
11 changes: 11 additions & 0 deletions app/graphql/resolvers/project.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Resolvers
class Project < Resolvers::Base
argument :id, ID, required: true

type Types::ProjectType, null: true

def resolve(**options)
::Project.find_by(id: options[:id])
end
end
end
9 changes: 9 additions & 0 deletions app/graphql/resolvers/projects.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Resolvers
class Projects < Resolvers::Base
type [Types::ProjectType], null: false

def resolve(**_options)
::Project.all
end
end
end
42 changes: 42 additions & 0 deletions app/graphql/task_tracker_itis_schema.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# frozen_string_literal: true

class TaskTrackerItisSchema < 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

# 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 added app/graphql/types/.keep
Empty file.
6 changes: 6 additions & 0 deletions app/graphql/types/base_argument.rb
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
8 changes: 8 additions & 0 deletions app/graphql/types/base_connection.rb
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
8 changes: 8 additions & 0 deletions app/graphql/types/base_edge.rb
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
6 changes: 6 additions & 0 deletions app/graphql/types/base_enum.rb
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
7 changes: 7 additions & 0 deletions app/graphql/types/base_field.rb
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
7 changes: 7 additions & 0 deletions app/graphql/types/base_input_object.rb
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
11 changes: 11 additions & 0 deletions app/graphql/types/base_interface.rb
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
9 changes: 9 additions & 0 deletions app/graphql/types/base_object.rb
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
6 changes: 6 additions & 0 deletions app/graphql/types/base_scalar.rb
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
8 changes: 8 additions & 0 deletions app/graphql/types/base_union.rb
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
8 changes: 8 additions & 0 deletions app/graphql/types/inputs/create_project_input.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module Types
module Inputs
class CreateProjectInput < Types::BaseInputObject
argument :name, String, required: true
argument :description, String, required: false
end
end
end
7 changes: 7 additions & 0 deletions app/graphql/types/mutation_type.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

module Types
class MutationType < Types::BaseObject
field :create_project, mutation: Mutations::CreateProject
end
end
9 changes: 9 additions & 0 deletions app/graphql/types/node_type.rb
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
8 changes: 8 additions & 0 deletions app/graphql/types/payloads/create_project_payload.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module Types
module Payloads
class CreateProjectPayload < Types::BaseObject
field :project, ProjectType, null: true
field :errors, [Types::UserError], null: true
end
end
end
10 changes: 10 additions & 0 deletions app/graphql/types/project_type.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module Types
class ProjectType < Types::BaseObject
field :id, ID, null: false
field :name, String, null: false
field :description, String, null: false
field :created_at, GraphQL::Types::ISO8601DateTime, null: false
field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
field :tasks, [TaskType], null: false
end
end
8 changes: 8 additions & 0 deletions app/graphql/types/query_type.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true

module Types
class QueryType < Types::BaseObject
field :project, resolver: Resolvers::Project
field :projects, resolver: Resolvers::Projects
end
end
7 changes: 7 additions & 0 deletions app/graphql/types/task_status_type.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Types
class TaskStatusType < Types::BaseEnum
value "UNSTARTED", value: "unstarted", description: "Unstarted"
value "STARTED", value: "started", description: "Started"
value "FINISHED", value: "finished", description: "Finished"
end
end
Loading

0 comments on commit 4865c60

Please sign in to comment.