Skip to content
This repository was archived by the owner on Sep 24, 2019. It is now read-only.

Commit facbf4f

Browse files
committed
Handle errors in filmRate
1 parent 95e4360 commit facbf4f

File tree

8 files changed

+171
-17
lines changed

8 files changed

+171
-17
lines changed

app/controllers/graphql_controller.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ class GraphqlController < ApplicationController
22
before_action :authenticate
33

44
def execute
5+
context = {
6+
user: @user,
7+
}
58
query_string = params[:query].to_s
69
variables = ensure_hash(params[:variables])
710
context = {

app/models/graph.rb

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,21 @@ def find_by_id_field(type, model)
55
type type
66
argument :id, !types.ID
77
resolve ->(_, args, _) do
8-
gid = GlobalID.parse(args[:id])
8+
model_id = Graph.parse_id(args[:id], model)
99

10-
return unless gid
11-
return unless gid.model_name == type.name
12-
13-
Graph::FindLoader.for(model).load(gid.model_id.to_i)
10+
Graph::FindLoader.for(model).load(model_id)
1411
end
1512
end
1613
end
14+
15+
def parse_id(gid, model)
16+
parsed_gid = GlobalID.parse(gid)
17+
18+
return unless parsed_gid
19+
return unless parsed_gid.app == GlobalID.app
20+
return unless parsed_gid.model_name != model.name.downcase
21+
22+
parsed_gid.model_id.to_i
23+
end
1724
end
1825
end

app/models/graph/mutations/film_rate.rb

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,27 @@ module Mutations
77
input_field :rating, !types.Int
88

99
return_field :film, Graph::Types::Film
10+
return_field :errors, !types[!Graph::Types::MutationError]
1011

1112
resolve ->(_, input, ctx) do
12-
# TODO auth error
13-
return unless user = ctx[:user]
13+
raise GraphQL::ExecutionError.new('Authentication required to rate a film.') unless user = ctx[:user]
1414

15-
# TODO user error
16-
return unless film = Film.find_by(id: input["filmId"])
15+
film_id = Graph.parse_id(input['filmId'], Film)
16+
film = Film.find_by(id: film_id) if film_id
17+
raise GraphQL::ExecutionError.new('Invalid filmId.') unless film
1718

18-
rating = user.ratings.build(
19-
film: film,
20-
rating: input["rating"]
21-
)
19+
rating = user.ratings.where(film: film).first_or_initialize
20+
rating.rating = input['rating']
2221

2322
if rating.save
2423
{
25-
film: film
24+
film: film,
25+
errors: []
2626
}
2727
else
28-
# TODO add user errors
2928
{
30-
film: film
29+
film: film,
30+
errors: rating.errors.map { |field, message| MutationError.new(field, message) },
3131
}
3232
end
3333
end
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module Graph
2+
module Mutations
3+
class MutationError < Struct.new(:field, :message)
4+
end
5+
end
6+
end

app/models/graph/schema.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ module Graph
1313

1414
object_from_id ->(id, query_ctx) do
1515
gid = GlobalID.parse(id)
16+
return unless gid
17+
1618
possible_types = query_ctx.warden.possible_types(GraphQL::Relay::Node.interface)
1719

1820
return unless possible_types.map(&:name).include?(gid.model_name)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module Graph
2+
module Types
3+
MutationError = GraphQL::ObjectType.define do
4+
name "MutationError"
5+
6+
field :field, !types.String, "The name of the input field that caused the error."
7+
field :message, !types.String, "The description of the error."
8+
end
9+
end
10+
end

app/models/rating.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ class Rating < ApplicationRecord
22
belongs_to :user
33
belongs_to :film
44

5-
validates_inclusion_of :rating, in: 0..5
5+
validates :rating, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 5 }
66
end
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
require 'test_helper'
2+
3+
class Graph::Mutations::FilmRateTest < ActiveSupport::TestCase
4+
def setup
5+
@context = {
6+
user: @user = User.first
7+
}
8+
9+
@query_string = "
10+
mutation ($input: FilmRateInput!) {
11+
filmRate(input: $input) {
12+
film {
13+
title
14+
}
15+
errors {
16+
field
17+
message
18+
}
19+
}
20+
}
21+
"
22+
23+
@film = Film.first
24+
25+
@variables = {
26+
"input" => {
27+
"filmId" => @film.to_global_id.to_s,
28+
"rating" => 5,
29+
}
30+
}
31+
end
32+
33+
test "raises an execution error when user is not logged in" do
34+
expected = {
35+
"data" => { "filmRate" => nil},
36+
"errors" => [{
37+
"message" => "Authentication required to rate a film.",
38+
"locations" => [{ "line" => 3, "column" => 9}],
39+
"path" => ["filmRate"]}
40+
]
41+
}
42+
43+
result = Graph::Schema.execute(@query_string, variables: @variables, context: {})
44+
assert_equal expected, result
45+
end
46+
47+
test "raises an execution error when filmId is invalid" do
48+
@variables['input']['filmId'] = 'invalid'
49+
expected = {
50+
"data" => { "filmRate" => nil},
51+
"errors" => [{
52+
"message" => "Invalid filmId.",
53+
"locations" => [{ "line" => 3, "column" => 9}],
54+
"path" => ["filmRate"]}
55+
]
56+
}
57+
58+
result = Graph::Schema.execute(@query_string, variables: @variables, context: @context)
59+
assert_equal expected, result
60+
end
61+
62+
test "returns error when an invalid rating is inputted" do
63+
@variables['input']['rating'] = 100
64+
expected = {
65+
"data" => {
66+
"filmRate" => {
67+
"film" => {
68+
"title" => @film.title
69+
},
70+
"errors" => [
71+
{ "field" => "rating", "message" => "must be less than or equal to 5" }
72+
]
73+
}
74+
}
75+
}
76+
77+
result = Graph::Schema.execute(@query_string, variables: @variables, context: @context)
78+
assert_equal expected, result
79+
end
80+
81+
test "creates a new rating on success" do
82+
expected = {
83+
"data" => {
84+
"filmRate" => {
85+
"film" => {
86+
"title" => @film.title
87+
},
88+
"errors" => [],
89+
}
90+
}
91+
}
92+
93+
assert_difference "Rating.count", 1 do
94+
result = Graph::Schema.execute(@query_string, variables: @variables, context: @context)
95+
assert_equal expected, result
96+
end
97+
98+
rating = Rating.last
99+
assert_equal @film, rating.film
100+
assert_equal @variables['input']['rating'], rating.rating
101+
assert_equal @user, rating.user
102+
end
103+
104+
test "updates existing rating if user already rated film" do
105+
expected = {
106+
"data" => {
107+
"filmRate" => {
108+
"film" => {
109+
"title" => @film.title
110+
},
111+
"errors" => [],
112+
}
113+
}
114+
}
115+
116+
rating = @user.ratings.create(film: @film, rating: 1)
117+
118+
assert_difference "Rating.count", 0 do
119+
result = Graph::Schema.execute(@query_string, variables: @variables, context: @context)
120+
assert_equal expected, result
121+
end
122+
123+
rating.reload
124+
assert_equal @variables['input']['rating'], rating.rating
125+
end
126+
end

0 commit comments

Comments
 (0)