1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
|
# frozen_string_literal: true
require "json"
require "net/http"
require "uri"
module GraphQL
class Client
# Public: Basic HTTP network adapter.
#
# GraphQL::Client.new(
# execute: GraphQL::Client::HTTP.new("http://graphql-swapi.parseapp.com/")
# )
#
# Assumes GraphQL endpoint follows the express-graphql endpoint conventions.
# https://github.com/graphql/express-graphql#http-usage
#
# Production applications should consider implementing there own network
# adapter. This class exists for trivial stock usage and allows for minimal
# request header configuration.
class HTTP
# Public: Create HTTP adapter instance for a single GraphQL endpoint.
#
# GraphQL::Client::HTTP.new("http://graphql-swapi.parseapp.com/") do
# def headers(context)
# { "User-Agent": "My Client" }
# end
# end
#
# uri - String endpoint URI
# block - Optional block to configure class
def initialize(uri, &block)
@uri = URI.parse(uri)
singleton_class.class_eval(&block) if block_given?
end
# Public: Parsed endpoint URI
#
# Returns URI.
attr_reader :uri
# Public: Extension point for subclasses to set custom request headers.
#
# Returns Hash of String header names and values.
def headers(_context)
{}
end
# Public: Make an HTTP request for GraphQL query.
#
# Implements Client's "execute" adapter interface.
#
# document - The Query GraphQL::Language::Nodes::Document
# operation_name - The String operation definition name
# variables - Hash of query variables
# context - An arbitrary Hash of values which you can access
#
# Returns { "data" => ... , "errors" => ... } Hash.
def execute(document:, operation_name: nil, variables: {}, context: {})
request = Net::HTTP::Post.new(uri.request_uri)
request.basic_auth(uri.user, uri.password) if uri.user || uri.password
request["Accept"] = "application/json"
request["Content-Type"] = "application/json"
headers(context).each { |name, value| request[name] = value }
body = {}
body["query"] = document.to_query_string
body["variables"] = variables if variables.any?
body["operationName"] = operation_name if operation_name
request.body = JSON.generate(body)
response = connection.request(request)
case response
when Net::HTTPOK, Net::HTTPBadRequest
JSON.parse(response.body)
else
{ "errors" => [{ "message" => "#{response.code} #{response.message}" }] }
end
end
# Public: Extension point for subclasses to customize the Net:HTTP client
#
# Returns a Net::HTTP object
def connection
Net::HTTP.new(uri.host, uri.port).tap do |client|
client.use_ssl = uri.scheme == "https"
end
end
end
end
end
|