Class: ForestAdminDatasourceGraphqlHasura::Collection

Inherits:
ForestAdminDatasourceToolkit::Collection
  • Object
show all
Defined in:
lib/forest_admin_datasource_graphql_hasura/collection.rb

Constant Summary collapse

ForestException =
ForestAdminDatasourceToolkit::Exceptions::ForestException
Projection =
ForestAdminDatasourceToolkit::Components::Query::Projection
PLACEHOLDER_REFERENCE =
{ '*' => nil }.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(datasource, table, client, converter) ⇒ Collection

Returns a new instance of Collection.



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/forest_admin_datasource_graphql_hasura/collection.rb', line 10

def initialize(datasource, table, client, converter)
  super(datasource, converter.collection_name_of(table.name))

  @table = table
  @table_name = table.name
  # root is the select root field; base (the GraphQL type name) is what
  # generated type names derive from; the operation roots carry their own
  # resolved names, custom or derived. See Query::QueryBuilder.
  @names = { root: table.name, base: table.type_name }.merge(table.root_fields || {})
  @client = client
  @converter = converter

  add_fields(converter.build_fields(table))
  enable_count
  schema[:aggregation_capabilities][:supported_date_operations] = []
end

Instance Attribute Details

#namesObject (readonly)

Returns the value of attribute names.



8
9
10
# File 'lib/forest_admin_datasource_graphql_hasura/collection.rb', line 8

def names
  @names
end

#table_nameObject (readonly)

Returns the value of attribute table_name.



8
9
10
# File 'lib/forest_admin_datasource_graphql_hasura/collection.rb', line 8

def table_name
  @table_name
end

Instance Method Details

#aggregate(_caller, filter, aggregation, limit = nil) ⇒ Object



59
60
61
# File 'lib/forest_admin_datasource_graphql_hasura/collection.rb', line 59

def aggregate(_caller, filter, aggregation, limit = nil)
  Query::Aggregator.new(self).run(filter, aggregation, limit)
end

#constraint_backed?(relation_name) ⇒ Boolean

Whether a relationship rests on a real foreign key constraint — only the Hasura metadata knows, and only introspection saw it. False when manual or when the metadata was unreachable (constraint unproven).

Returns:

  • (Boolean)


66
67
68
# File 'lib/forest_admin_datasource_graphql_hasura/collection.rb', line 66

def constraint_backed?(relation_name)
  @table.relationships.any? { |rel| rel.name == relation_name && rel.manual == false }
end

#create(_caller, data) ⇒ Object

Raises:



35
36
37
38
39
40
41
42
# File 'lib/forest_admin_datasource_graphql_hasura/collection.rb', line 35

def create(_caller, data)
  operation = Query::QueryBuilder.create(@names, [writable_columns(data)], column_names)
  returning = execute(:create, operation).dig(@names[:insert], 'returning')

  raise GraphqlError, "No record returned by #{@names[:insert]}" if returning.nil? || returning.empty?

  returning.first
end

#delete(_caller, filter) ⇒ Object



54
55
56
57
# File 'lib/forest_admin_datasource_graphql_hasura/collection.rb', line 54

def delete(_caller, filter)
  operation = Query::QueryBuilder.delete(@names, filter)
  execute(:delete, operation)
end

#execute(operation_name, operation) ⇒ Object

Wraps every Hasura call so the failing operation is named in the error, keeping the class (GraphqlError or TransportError) and thus the status.



72
73
74
75
76
# File 'lib/forest_admin_datasource_graphql_hasura/collection.rb', line 72

def execute(operation_name, operation)
  @client.execute(operation[:query], operation[:variables])
rescue GraphqlError, TransportError => e
  raise e.class, "GraphQL #{operation_name} failed on '#{name}': #{e.message}"
end

#list(_caller, filter, projection) ⇒ Object



27
28
29
30
31
32
33
# File 'lib/forest_admin_datasource_graphql_hasura/collection.rb', line 27

def list(_caller, filter, projection)
  selection = build_selection(projection)
  operation = Query::QueryBuilder.list(@names, filter, selection)
  records = execute(:list, operation)[@names[:root]] || []

  records.map { |record| materialize_polymorphics(record, projection) }
end

#update(_caller, filter, data) ⇒ Object



44
45
46
47
48
49
50
51
52
# File 'lib/forest_admin_datasource_graphql_hasura/collection.rb', line 44

def update(_caller, filter, data)
  if empty_condition?(filter)
    raise ForestException,
          "Refusing to update every row of '#{name}': the filter carries no condition."
  end

  operation = Query::QueryBuilder.update(@names, filter, writable_columns(data))
  execute(:update, operation)
end