graphiti_gql 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
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