graphiti_gql 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ff76a3032a8378019cc0034c48cc1bd33302a8d662515fd722d970e642ee355e
4
- data.tar.gz: 532980890cd827d260bceebd5f944eeb999a561cde6df3e14cff5ddbfaa5f146
3
+ metadata.gz: 899580a6d3577a31dc7356d894eab26756c2c9876636db69ce5ece2d5eeee3ce
4
+ data.tar.gz: faf261a7614a0565c9dc3e45b4a9d14d75962aa6665b7302752733584abb5bdc
5
5
  SHA512:
6
- metadata.gz: 8a67a1e359377aa6682b2f23fd0461b2547a05df41c14739d4abf691d879682dd8cd83a4e4deda386855ae6c7a587feb453b2aff5490d2e0d44998955ff6bba9
7
- data.tar.gz: f5f83d5859fc24f3215723d3abd7d1105dcefc3f02002853f829a4d954923444df0d1a37c0cbbb736dbb79df9745dd1be1c197c6151b07febb117272eb12e8e7
6
+ metadata.gz: becebe36d5aabea0a75ba3e617f9f62d0830d3f4a35e5becdacef1fbe10f4991621f061521f10bc4c0e527bdc834d5380e9f38ff0b9cc2388a66a7cf25bb33b8
7
+ data.tar.gz: 37a9a4221261f112100e32f06832a948c30cbef5a06ffcb786f080538c1b9f3894bd3f7f8fc32394f0c2cb122e94c1b1e58cd6b8c38b6e0feab08a30d55bbfb5
@@ -0,0 +1,210 @@
1
+ module GraphitiGql
2
+ module ActiveResource
3
+ extend ActiveSupport::Concern
4
+
5
+ class Node < OpenStruct
6
+ def initialize(resource, hash)
7
+ @resource = resource
8
+ hash.each_pair do |key, value|
9
+ if value.is_a?(Hash)
10
+ if (sideload = resource.sideload(key))
11
+ if value.key?(:edges)
12
+ hash[key] = value[:edges].map { |v| Node.new(sideload.resource.class, v[:node]) }
13
+ else
14
+ hash[key] = Node.new(sideload.resource.class, value)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ super(hash)
20
+ end
21
+
22
+ def decoded_id
23
+ Base64.decode64(self.id)
24
+ end
25
+
26
+ def int_id
27
+ decoded_id.to_i
28
+ end
29
+ end
30
+
31
+ class Proxy
32
+ def initialize(resource, params, ctx)
33
+ @resource = resource
34
+ @ctx = ctx
35
+ @params = params.deep_transform_keys { |key| key.to_s.camelize(:lower).to_sym }
36
+ (@params[:sort] || []).each do |sort|
37
+ sort[:att] = sort[:att].to_s.camelize(:lower)
38
+ sort[:dir] = sort[:dir].to_s
39
+ end
40
+ end
41
+
42
+ def to_h(symbolize_keys: true)
43
+ result = GraphitiGql.run(query, @params, @ctx)
44
+ result = result.deep_symbolize_keys if symbolize_keys
45
+ @response = result
46
+ result
47
+ end
48
+
49
+ def nodes
50
+ return [] unless data
51
+ nodes = edges.map { |e| underscore(e[:node]) }
52
+ nodes.map { |n| Node.new(@resource, n) }
53
+ end
54
+ alias :to_a :nodes
55
+
56
+ def response
57
+ @response ||= to_h
58
+ end
59
+
60
+ def data
61
+ if response.key?(:data)
62
+ response[:data]
63
+ else
64
+ raise "Tried to access 'data', but these errors were returned instead: #{error_messages.join(". ")}."
65
+ end
66
+ end
67
+
68
+ def errors
69
+ response[:errors]
70
+ end
71
+
72
+ def error_messages
73
+ response[:errors].map { |e| e[:message] }
74
+ end
75
+
76
+ def edges
77
+ data[data.keys.first][:edges]
78
+ end
79
+
80
+ def stats
81
+ underscore(data[data.keys.first][:stats])
82
+ end
83
+
84
+ def page_info
85
+ underscore(data[data.keys.first][:pageInfo])
86
+ end
87
+
88
+ def query
89
+ name = Schema.registry.key_for(@resource)
90
+ filter_bang = "!" if @resource.filters.values.any? { |f| f[:required] }
91
+ sortvar = "$sort: [#{name}Sort!]," if @resource.sorts.any?
92
+
93
+ if !(fields = @params[:fields])
94
+ fields = []
95
+ @resource.attributes.each_pair do |name, config|
96
+ (fields << name) if config[:readable]
97
+ end
98
+ end
99
+
100
+ q = %|
101
+ query #{name} (
102
+ $filter: #{name}Filter#{filter_bang},
103
+ #{sortvar}
104
+ $first: Int,
105
+ $last: Int,
106
+ $before: String,
107
+ $after: String,
108
+ ) {
109
+ #{@resource.graphql_entrypoint} (
110
+ filter: $filter,
111
+ #{ 'sort: $sort,' if sortvar }
112
+ first: $first,
113
+ last: $last,
114
+ before: $before,
115
+ after: $after,
116
+ ) {
117
+ edges {
118
+ node {|
119
+
120
+ fields.each do |name|
121
+ q << %|
122
+ #{name.to_s.camelize(:lower)}|
123
+ end
124
+
125
+ if @params[:include]
126
+ includes = Array(@params[:include])
127
+ # NB HASH (?)
128
+ includes.each do |inc|
129
+ sideload = @resource.sideload(inc.to_sym)
130
+ to_one = [:belongs_to, :has_one, :polymorphic_belongs_to].include?(sideload.type)
131
+ indent = " " if !to_one
132
+ q << %|
133
+ #{inc.to_s.camelize(:lower)} {|
134
+ if !to_one
135
+ q << %|
136
+ edges {
137
+ node {|
138
+ end
139
+
140
+ r = @resource.sideload(inc.to_sym).resource
141
+ r.attributes.each_pair do |name, config|
142
+ next unless config[:readable]
143
+ q << %|
144
+ #{indent}#{name.to_s.camelize(:lower)}|
145
+ end
146
+
147
+ if to_one
148
+ q << %|
149
+ }|
150
+ else
151
+ q << %|
152
+ }
153
+ }
154
+ }|
155
+ end
156
+ end
157
+ end
158
+
159
+ q << %|
160
+ }
161
+ }
162
+ pageInfo {
163
+ startCursor
164
+ endCursor
165
+ hasNextPage
166
+ hasPreviousPage
167
+ }|
168
+
169
+ if @params[:stats]
170
+ q << %|
171
+ stats {|
172
+ @params[:stats].each_pair do |name, calculations|
173
+ q << %|
174
+ #{name.to_s.camelize(:lower)} {|
175
+ Array(calculations).each do |calc|
176
+ q << %|
177
+ #{calc.to_s.camelize(:lower)}|
178
+ end
179
+
180
+ q << %|
181
+ }|
182
+ end
183
+ q << %|
184
+ }|
185
+ end
186
+
187
+ q << %|
188
+ }
189
+ }
190
+ |
191
+
192
+ q
193
+ end
194
+
195
+ private
196
+
197
+ def underscore(hash)
198
+ hash.deep_transform_keys { |k| k.to_s.underscore.to_sym }
199
+ end
200
+ end
201
+
202
+ class_methods do
203
+ def gql(params = {}, ctx = {})
204
+ Proxy.new(self, params, ctx)
205
+ end
206
+ end
207
+ end
208
+ end
209
+
210
+ Graphiti::Resource.send(:include, GraphitiGql::ActiveResource)
@@ -9,9 +9,18 @@ module GraphitiGql
9
9
  GraphitiGql.schema!
10
10
  end
11
11
 
12
+ module ControllerContext
13
+ def graphql_context
14
+ ctx = { controller: self }
15
+ ctx[:current_user] = current_user if respond_to?(:current_user)
16
+ ctx
17
+ end
18
+ end
19
+
12
20
  initializer "graphiti_gql.define_controller" do
13
21
  require "#{Rails.root}/app/controllers/application_controller"
14
22
  app_controller = GraphitiGql.config.application_controller || ::ApplicationController
23
+ app_controller.send(:include, ControllerContext)
15
24
 
16
25
  # rubocop:disable Lint/ConstantDefinitionInBlock(Standard)
17
26
  class GraphitiGql::ExecutionController < app_controller
@@ -20,9 +29,15 @@ module GraphitiGql
20
29
  variables = params[:variables] || {}
21
30
  result = GraphitiGql.run params[:query],
22
31
  params[:variables],
23
- { current_user: current_user }
32
+ graphql_context
24
33
  render json: result
25
34
  end
35
+
36
+ private
37
+
38
+ def default_context
39
+ defined?(:current_user)
40
+ end
26
41
  end
27
42
  end
28
43
  end
@@ -29,7 +29,7 @@ module GraphitiGql
29
29
  name = Registry.instance.key_for(@resource)
30
30
  stat_graphql_name = "#{name}Stats"
31
31
  return Registry.instance[stat_graphql_name][:type] if Registry.instance[stat_graphql_name]
32
- klass = Class.new(GraphQL::Schema::Object)
32
+ klass = Class.new(Schema.base_object)
33
33
  klass.graphql_name(stat_graphql_name)
34
34
  @resource.stats.each_pair do |name, config|
35
35
  calc_class = build_calc_class(stat_graphql_name, name, config.calculations.keys)
@@ -41,7 +41,7 @@ module GraphitiGql
41
41
 
42
42
  def build_calc_class(stat_graphql_name, stat_name, calculations)
43
43
  name = "#{stat_graphql_name}#{stat_name}Calculations"
44
- klass = Class.new(GraphQL::Schema::Object)
44
+ klass = Class.new(Schema.base_object)
45
45
  klass.graphql_name(name)
46
46
  calculations.each do |calc|
47
47
  klass.field calc, Float, null: false
@@ -3,9 +3,8 @@ module GraphitiGql
3
3
  class Query
4
4
  def initialize(resources, existing_query: nil)
5
5
  @resources = resources
6
- @query_class = Class.new(existing_query || ::GraphQL::Schema::Object)
6
+ @query_class = Class.new(existing_query || Schema.base_object)
7
7
  @query_class.graphql_name "Query"
8
- @query_class.field_class ::GraphQL::Schema::Field
9
8
  end
10
9
 
11
10
  def build
@@ -68,7 +68,7 @@ module GraphitiGql
68
68
  Registry.instance[registry_name][:type]
69
69
  end
70
70
  else
71
- klass = Class.new(GraphQL::Schema::Object)
71
+ klass = Class.new(Schema.base_object)
72
72
  end
73
73
 
74
74
  klass
@@ -32,6 +32,21 @@ module GraphitiGql
32
32
  end
33
33
  end
34
34
 
35
+ def self.base_object
36
+ klass = Class.new(GraphQL::Schema::Object)
37
+ # TODO make this config maybe
38
+ if defined?(ActionView)
39
+ klass.send(:include, ActionView::Helpers::TranslationHelper)
40
+ klass.class_eval do
41
+ def initialize(*)
42
+ super
43
+ @virtual_path = "."
44
+ end
45
+ end
46
+ end
47
+ klass
48
+ end
49
+
35
50
  def self.registry
36
51
  Registry.instance
37
52
  end
@@ -2,149 +2,44 @@ module GraphitiGql
2
2
  module SpecHelper
3
3
  extend ActiveSupport::Concern
4
4
 
5
- class Node < OpenStruct
6
- def decoded_id
7
- Base64.decode64(self.id)
8
- end
9
-
10
- def int_id
11
- decoded_id.to_i
12
- end
13
- end
5
+ included do
6
+ extend Forwardable
7
+ def_delegators :result,
8
+ :page_info,
9
+ :errors,
10
+ :error_messages,
11
+ :nodes,
12
+ :stats
14
13
 
15
- class Util
16
- def self.underscore(hash)
17
- hash.deep_transform_keys { |k| k.to_s.underscore.to_sym }
14
+ if defined?(RSpec)
15
+ let(:params) { {} }
16
+ let(:resource) { described_class }
17
+ let(:ctx) { {} }
18
18
  end
19
19
  end
20
20
 
21
- def query
22
- name = Schema.registry.key_for(resource)
23
- q = %|
24
- query #{name} (
25
- $filter: #{name}Filter,
26
- $sort: [#{name}Sort!],
27
- $first: Int,
28
- $last: Int,
29
- $before: String,
30
- $after: String,
31
- ) {
32
- #{resource.graphql_entrypoint} (
33
- filter: $filter,
34
- sort: $sort,
35
- first: $first,
36
- last: $last,
37
- before: $before,
38
- after: $after,
39
- ) {
40
- edges {
41
- node {|
42
-
43
- fields.each do |name|
44
- q << %|
45
- #{name.to_s.camelize(:lower)}|
46
- end
47
-
48
- q << %|
49
- }
50
- }
51
- pageInfo {
52
- startCursor
53
- endCursor
54
- hasNextPage
55
- hasPreviousPage
56
- }|
57
-
58
- if params[:stats]
59
- q << %|
60
- stats {|
61
- params[:stats].each_pair do |name, calculations|
62
- q << %|
63
- #{name.to_s.camelize(:lower)} {|
64
- Array(calculations).each do |calc|
65
- q << %|
66
- #{calc.to_s.camelize(:lower)}|
67
- end
68
-
69
- q << %|
70
- }|
71
- end
72
- q << %|
73
- }|
21
+ def gql_datetime(timestamp, precise = false)
22
+ if precise
23
+ timestamp.utc.round(10).iso8601(6)
24
+ else
25
+ DateTime.parse(timestamp.to_s).iso8601
74
26
  end
75
-
76
- q << %|
77
- }
78
- }
79
- |
80
-
81
- q
82
27
  end
83
28
 
84
29
  def run
85
30
  lambda do
86
- gql_params = params.deep_transform_keys { |key| key.to_s.camelize(:lower).to_sym }
87
- (gql_params[:sort] || []).each do |sort|
88
- sort[:att] = sort[:att].to_s.camelize(:lower)
89
- sort[:dir] = sort[:dir].to_s
90
- end
91
- GraphitiGql.run(query, gql_params, ctx).deep_symbolize_keys
31
+ proxy = resource.gql(params, ctx)
32
+ proxy.to_h
33
+ proxy
92
34
  end
93
35
  end
94
36
 
95
37
  def run!
96
- @response = run.call
97
- end
98
-
99
- def response
100
- @response ||= run!
101
- end
102
-
103
- def errors
104
- response[:errors]
105
- end
106
-
107
- def error_messages
108
- response[:errors].map { |e| e[:message] }
38
+ @result = run.call
109
39
  end
110
40
 
111
- def nodes
112
- return [] unless data
113
- nodes = edges.map { |e| Util.underscore(e[:node]) }
114
- nodes.map { |n| ::GraphitiGql::SpecHelper::Node.new(n) }
115
- end
116
-
117
- def data
118
- if response.key?(:data)
119
- response[:data]
120
- else
121
- raise "Tried to access 'data', but these errors were returned instead: #{error_messages.join(". ")}."
122
- end
123
- end
124
-
125
- def edges
126
- data[data.keys.first][:edges]
127
- end
128
-
129
- def stats
130
- Util.underscore(data[data.keys.first][:stats])
131
- end
132
-
133
- def page_info
134
- Util.underscore(data[data.keys.first][:pageInfo])
135
- end
136
-
137
- included do
138
- let(:params) { {} }
139
- let(:resource) { described_class }
140
- let(:ctx) { {} }
141
- let(:fields) do
142
- fields = []
143
- resource.attributes.each_pair do |name, config|
144
- (fields << name) if config[:readable]
145
- end
146
- fields
147
- end
41
+ def result
42
+ @result ||= run!
148
43
  end
149
44
  end
150
45
  end
@@ -1,3 +1,3 @@
1
1
  module GraphitiGql
2
- VERSION = "0.2.1"
2
+ VERSION = "0.2.2"
3
3
  end
data/lib/graphiti_gql.rb CHANGED
@@ -24,6 +24,7 @@ require "graphiti_gql/schema/fields/to_many"
24
24
  require "graphiti_gql/schema/fields/to_one"
25
25
  require "graphiti_gql/schema/fields/attribute"
26
26
  require "graphiti_gql/schema/fields/stats"
27
+ require "graphiti_gql/active_resource"
27
28
  require "graphiti_gql/engine" if defined?(Rails)
28
29
 
29
30
  module GraphitiGql
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphiti_gql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lee Richmond
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-06-23 00:00:00.000000000 Z
11
+ date: 2022-06-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: graphql
@@ -137,6 +137,7 @@ files:
137
137
  - config/routes.rb
138
138
  - graphiti_gql.gemspec
139
139
  - lib/graphiti_gql.rb
140
+ - lib/graphiti_gql/active_resource.rb
140
141
  - lib/graphiti_gql/engine.rb
141
142
  - lib/graphiti_gql/errors.rb
142
143
  - lib/graphiti_gql/graphiti_hax.rb