From 6013f8494577491576a104183330f63f73221e78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Miedes=20Garc=C3=A9s?= Date: Sat, 3 Dec 2016 13:36:14 +0100 Subject: [PATCH] Handle multiple kind of requests in GraphQL controller * Added support for GET requests * Added support for application/graphql content-type for POST requests * Handling query string parsing errors in controller * Wrote specs for all this --- app/controllers/graphql_controller.rb | 26 +++-- config/routes.rb | 1 + spec/controllers/graphql_controller_spec.rb | 110 +++++++------------- 3 files changed, 59 insertions(+), 78 deletions(-) diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb index e3bedceea..be4f51f03 100644 --- a/app/controllers/graphql_controller.rb +++ b/app/controllers/graphql_controller.rb @@ -4,11 +4,25 @@ class GraphqlController < ApplicationController skip_authorization_check def query - # ConsulSchema.execute returns the query result in the shape of a Hash, which - # is sent back to the client rendered in JSON - render json: ConsulSchema.execute( - params[:query], - variables: params[:variables] || {} - ) + + if request.headers["CONTENT_TYPE"] == 'application/graphql' + query_string = request.body.string # request.body.class => StringIO + else + query_string = params[:query] + end + + if query_string.nil? + render json: { message: 'Query string not present' }, status: :bad_request + else + begin + response = ConsulSchema.execute( + query_string, + variables: params[:variables] || {} + ) + render json: response, status: :ok + rescue GraphQL::ParseError + render json: { message: 'Query string is not valid JSON' }, status: :bad_request + end + end end end diff --git a/config/routes.rb b/config/routes.rb index b530df52c..63be98384 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -264,6 +264,7 @@ Rails.application.routes.draw do # GraphQL mount GraphiQL::Rails::Engine, at: '/graphiql', graphql_path: '/graphql' + get '/graphql', to: 'graphql#query' post '/graphql', to: 'graphql#query' if Rails.env.development? diff --git a/spec/controllers/graphql_controller_spec.rb b/spec/controllers/graphql_controller_spec.rb index e0244ee68..e95bdd3c9 100644 --- a/spec/controllers/graphql_controller_spec.rb +++ b/spec/controllers/graphql_controller_spec.rb @@ -1,90 +1,56 @@ require 'rails_helper' -# Hacerlo como los test de controlador de rails +# Useful resource: http://graphql.org/learn/serving-over-http/ describe GraphqlController, type: :request do let(:proposal) { create(:proposal) } - it "answers simple json queries" do - headers = { "CONTENT_TYPE" => "application/json" } - #post "/widgets", '{ "widget": { "name":"My Widget" } }', headers - post '/graphql', { query: "{ proposal(id: #{proposal.id}) { title } }" }.to_json, headers - expect(response).to have_http_status(200) - expect(JSON.parse(response.body)['data']['proposal']['title']).to eq(proposal.title) + describe "handles GET request" do + specify "with query string inside query params" do + get '/graphql', query: "{ proposal(id: #{proposal.id}) { title } }" + expect(response).to have_http_status(:ok) + expect(JSON.parse(response.body)['data']['proposal']['title']).to eq(proposal.title) + end + + specify "with malformed query string" do + get '/graphql', query: 'Malformed query string' + expect(response).to have_http_status(:bad_request) + expect(JSON.parse(response.body)['message']).to eq('Query string is not valid JSON') + end + + specify "without query string" do + get '/graphql' + expect(response).to have_http_status(:bad_request) + expect(JSON.parse(response.body)['message']).to eq('Query string not present') + end end - -end + describe "handles POST request" do + let(:json_headers) { { "CONTENT_TYPE" => "application/json" } } -=begin -describe GraphqlController do - let(:uri) { URI::HTTP.build(host: 'localhost', path: '/graphql', port: 3000) } - let(:query_string) { "" } - let(:body) { {query: query_string}.to_json } - - describe "POST requests" do - let(:author) { create(:user) } - let(:proposal) { create(:proposal, author: author) } - let(:response) { HTTP.headers('Content-Type' => 'application/json').post(uri, body: body) } - let(:response_body) { JSON.parse(response.body) } - - context "when query string is valid" do - let(:query_string) { "{ proposal(id: #{proposal.id}) { title, author { username } } }" } - let(:returned_proposal) { response_body['data']['proposal'] } - - it "returns HTTP 200 OK" do - expect(response.code).to eq(200) - end - - it "returns first-level fields" do - expect(returned_proposal['title']).to eq(proposal.title) - end - - it "returns nested fields" do - expect(returned_proposal['author']['username']).to eq(author.username) - end + specify "with json-encoded query string inside body" do + post '/graphql', { query: "{ proposal(id: #{proposal.id}) { title } }" }.to_json, json_headers + expect(response).to have_http_status(:ok) + expect(JSON.parse(response.body)['data']['proposal']['title']).to eq(proposal.title) end - context "when query string asks for invalid fields" do - let(:query_string) { "{ proposal(id: #{proposal.id}) { missing_field } }" } - - it "returns HTTP 200 OK" do - expect(response.code).to eq(200) - end - - it "doesn't return any data" do - expect(response_body['data']).to be_nil - end - - it "returns error inside body" do - expect(response_body['errors']).to be_present - end + specify "with raw query string inside body" do + graphql_headers = { "CONTENT_TYPE" => "application/graphql" } + post '/graphql', "{ proposal(id: #{proposal.id}) { title } }", graphql_headers + expect(response).to have_http_status(:ok) + expect(JSON.parse(response.body)['data']['proposal']['title']).to eq(proposal.title) end - context "when query string is not valid" do - let(:query_string) { "invalid" } - - it "returns HTTP 400 Bad Request" do - expect(response.code).to eq(400) - end + specify "with malformed query string" do + post '/graphql', { query: "Malformed query string" }.to_json, json_headers + expect(response).to have_http_status(:bad_request) + expect(JSON.parse(response.body)['message']).to eq('Query string is not valid JSON') end - context "when query string is missing" do - let(:query_string) { nil } - - it "returns HTTP 400 Bad Request" do - expect(response.code).to eq(400) - end + it "without query string" do + post '/graphql', json_headers + expect(response).to have_http_status(:bad_request) + expect(JSON.parse(response.body)['message']).to eq('Query string not present') end - - context "when body is missing" do - let(:body) { nil } - - it "returns HTTP 400 Bad Request" do - expect(response.code).to eq(400) - end - end - end end -=end