diff --git a/CHANGELOG.md b/CHANGELOG.md index 1447a22..7ed674e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] * Added/Changed/Deprecated/Removed/Fixed/Security: YOUR CHANGE HERE +* Fixed: multipart adapter raises `Graphlient::Errors::FaradayServerError` on 5xx responses instead of passing HTML error pages on as the body * Added: automatic conversion from symbols to keywords setting * Added: relation.empty?, relation.blank?, relation.present? methods * Added: fields now can be marked as "keywords" diff --git a/lib/active_graphql/client/adapters/graphlient_multipart_adapter.rb b/lib/active_graphql/client/adapters/graphlient_multipart_adapter.rb index 5a3351d..ac7553e 100644 --- a/lib/active_graphql/client/adapters/graphlient_multipart_adapter.rb +++ b/lib/active_graphql/client/adapters/graphlient_multipart_adapter.rb @@ -12,13 +12,20 @@ module Adapters class GraphlientMultipartAdapter < Graphlient::Adapters::HTTP::Adapter def execute(document:, operation_name:, variables:, context:) response = execute_request(document:, operation_name:, variables:, context:) + raise_server_error(response) if response.status >= 500 + response.body - rescue Faraday::ClientError => e + rescue Faraday::ClientError, Faraday::ServerError => e raise Graphlient::Errors::FaradayServerError, e end private + # 4xx bodies may still carry GraphQL errors, so only 5xx are treated as transport failures. + def raise_server_error(response) + raise Faraday::ServerError.new("the server responded with status #{response.status}", response.to_hash) + end + def execute_request(document:, operation_name:, variables:, context:) connection.post do |req| req.headers.merge!(context[:headers] || {}) @@ -32,7 +39,6 @@ def execute_request(document:, operation_name:, variables:, context:) def connection @connection ||= Faraday.new(url:, headers:) do |c| - c.adapter Faraday::Response::RaiseError c.request :multipart c.request :url_encoded c.response :json diff --git a/spec/lib/active_graphql/client/adapters/graphlient_multipart_adapter_spec.rb b/spec/lib/active_graphql/client/adapters/graphlient_multipart_adapter_spec.rb new file mode 100644 index 0000000..d4cc633 --- /dev/null +++ b/spec/lib/active_graphql/client/adapters/graphlient_multipart_adapter_spec.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'active_graphql/client/adapters/graphlient_multipart_adapter' + +class ActiveGraphql::Client + RSpec.describe Adapters::GraphlientMultipartAdapter do + subject(:adapter) { described_class.new(url) } + + let(:url) { 'http://example.com/graphql' } + let(:document) { GraphQL.parse('mutation { createUser { id } }') } + + describe '#execute' do + subject(:execute) { adapter.execute(document:, operation_name: nil, variables: {}, context: {}) } + + context 'when server responds with JSON' do + before do + stub_request(:post, url).to_return( + status: 200, + body: { data: { createUser: { id: 1 } } }.to_json, + headers: { 'Content-Type' => 'application/json' } + ) + end + + it 'returns parsed body' do + expect(execute).to eq('data' => { 'createUser' => { 'id' => 1 } }) + end + end + + context 'when server responds with 503 HTML page' do + before do + stub_request(:post, url).to_return( + status: 503, + body: '

503 Service Unavailable

', + headers: { 'Content-Type' => 'text/html' } + ) + end + + it 'raises FaradayServerError with status code' do + expect { execute }.to raise_error(Graphlient::Errors::FaradayServerError) do |error| + expect(error.status_code).to eq 503 + end + end + end + + context 'when server responds with 4xx containing GraphQL errors' do + before do + stub_request(:post, url).to_return( + status: 422, + body: { errors: [{ message: 'invalid' }] }.to_json, + headers: { 'Content-Type' => 'application/json' } + ) + end + + it 'returns body so GraphQL errors can be handled' do + expect(execute).to eq('errors' => [{ 'message' => 'invalid' }]) + end + end + end + end +end