Class: ForestAdminDatasourceGraphqlHasura::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/forest_admin_datasource_graphql_hasura/client.rb

Overview

GraphQL-over-HTTP client for Hasura (queries, mutations and the metadata API), on Net::HTTP so the gem needs no HTTP dependency.

Constant Summary collapse

TRANSPORT_ERRORS =

Wrapped in TransportError (503) so they reach the user as an actionable message without masquerading as a client mistake: errors Hasura itself returns are the only ones raised as GraphqlError (400).

[
  Net::OpenTimeout, Net::ReadTimeout, Net::WriteTimeout, Net::HTTPBadResponse, IOError, SocketError,
  SystemCallError, OpenSSL::SSL::SSLError, JSON::ParserError
].freeze

Instance Method Summary collapse

Constructor Details

#initialize(configuration) ⇒ Client

Returns a new instance of Client.



10
11
12
# File 'lib/forest_admin_datasource_graphql_hasura/client.rb', line 10

def initialize(configuration)
  @configuration = configuration
end

Instance Method Details

#execute(query, variables = {}) ⇒ Object



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
# File 'lib/forest_admin_datasource_graphql_hasura/client.rb', line 22

def execute(query, variables = {})
  body = JSON.generate({ query: query, variables: variables })
  response = post(@configuration.uri, body)

  raise TransportError, "GraphQL endpoint returned HTTP #{response.code}" unless response.is_a?(Net::HTTPSuccess)
  # A 204 is a success with a nil body, which JSON.parse would turn into an
  # unwrapped TypeError.
  raise TransportError, 'GraphQL endpoint returned an empty body' if response.body.nil? || response.body.empty?

  payload = JSON.parse(response.body)
  raise TransportError, 'GraphQL endpoint returned an unexpected body' unless payload.is_a?(Hash)

  if payload['errors']&.any?
    messages = payload['errors'].map { |e| e['message'] }.join('; ')
    raise GraphqlError, messages
  end

  # A 200 with neither data nor errors is malformed, and letting a nil out
  # would crash the caller with an opaque NoMethodError.
  data = payload['data']
  raise TransportError, 'GraphQL endpoint returned no data' if data.nil?

  data
rescue *TRANSPORT_ERRORS => e
  raise TransportError, "Could not reach the GraphQL endpoint (#{e.class}): #{e.message}"
end

#fetch_metadataObject

Returns nil when the endpoint is unreachable or forbidden, which is common in production: introspection then falls back to the configuration and to naming conventions. Every fallback branch warns — losing the metadata silently disables polymorphism detection and custom root field resolution, and each cause deserves something to grep for.



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/forest_admin_datasource_graphql_hasura/client.rb', line 54

def 
  if @configuration..nil?
    return ("no metadata endpoint could be derived from uri (no '/v1/graphql' " \
                             "segment); set the 'metadata_uri' option")
  end

  body = JSON.generate({ type: 'export_metadata', version: 2, args: {} })
  response = post(@configuration., body)

  return ("the metadata endpoint answered HTTP #{response.code}") unless
    response.is_a?(Net::HTTPSuccess)

  (response.body)
rescue *TRANSPORT_ERRORS => e
  ("the metadata endpoint is not reachable (#{e.class})")
end