hq-graphql 2.1.12 → 2.2.0
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 +4 -4
- data/README.md +25 -9
- data/lib/hq/graphql.rb +17 -8
- data/lib/hq/graphql/association_loader.rb +1 -0
- data/lib/hq/graphql/comparator.rb +3 -8
- data/lib/hq/graphql/enum/sort_by.rb +8 -4
- data/lib/hq/graphql/enum/sort_order.rb +8 -4
- data/lib/hq/graphql/ext.rb +8 -0
- data/lib/hq/graphql/ext/active_record_extensions.rb +148 -0
- data/lib/hq/graphql/ext/enum_extensions.rb +81 -0
- data/lib/hq/graphql/ext/input_object_extensions.rb +110 -0
- data/lib/hq/graphql/ext/mutation_extensions.rb +24 -0
- data/lib/hq/graphql/ext/object_extensions.rb +122 -0
- data/lib/hq/graphql/ext/schema_extensions.rb +50 -0
- data/lib/hq/graphql/field.rb +1 -1
- data/lib/hq/graphql/paginated_association_loader.rb +1 -0
- data/lib/hq/graphql/record_loader.rb +1 -0
- data/lib/hq/graphql/resource.rb +38 -13
- data/lib/hq/graphql/resource/auto_mutation.rb +8 -5
- data/lib/hq/graphql/root_mutation.rb +1 -1
- data/lib/hq/graphql/root_query.rb +3 -3
- data/lib/hq/graphql/scalars.rb +0 -2
- data/lib/hq/graphql/types.rb +1 -2
- data/lib/hq/graphql/version.rb +1 -1
- metadata +24 -38
- data/lib/hq/graphql/active_record_extensions.rb +0 -143
- data/lib/hq/graphql/enum.rb +0 -78
- data/lib/hq/graphql/input_object.rb +0 -104
- data/lib/hq/graphql/mutation.rb +0 -15
- data/lib/hq/graphql/object.rb +0 -120
- data/lib/hq/graphql/schema.rb +0 -22
- data/lib/hq/graphql/types/object.rb +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a158f615a83d3743a038f248bc82c04285631e6575f62b5124bbb5767ab6ede9
|
4
|
+
data.tar.gz: 630bea394ce6e073b1f4ee2bd62047fcf46ba433357d1c759b99d7ecf350bfec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b6076f33bc12f04a7e701f2deeb7420fc44792af3e99727029196d24d68280276a15c7c8d70d875de82c3ec48e41afdc13b5578a45fbcff2ddcc0edfa8971628
|
7
|
+
data.tar.gz: c85f1b1af47ddee4f049a431277ca63bb1728ab74738fe8bc1bbc0f7b31f9dedd14f12fb850045f82ff301d83729d3f896628154e0fcaecea17c117dfd6140d5
|
data/README.md
CHANGED
@@ -8,6 +8,7 @@ OneHQ GraphQL interface to [Ruby Graphql](https://github.com/rmosolgo/graphql-ru
|
|
8
8
|
## Configuration
|
9
9
|
|
10
10
|
### Default Scope
|
11
|
+
|
11
12
|
Define a global default scope.
|
12
13
|
|
13
14
|
```ruby
|
@@ -19,6 +20,7 @@ end
|
|
19
20
|
```
|
20
21
|
|
21
22
|
### Excluded Inputs
|
23
|
+
|
22
24
|
Define a global excluded input fields. Useful for excluding (autogenerated | auto set) fields like below.
|
23
25
|
|
24
26
|
```ruby
|
@@ -28,9 +30,11 @@ end
|
|
28
30
|
```
|
29
31
|
|
30
32
|
## GraphQL Resource
|
33
|
+
|
31
34
|
Connect to ActiveRecord to auto generate queries and mutations.
|
32
35
|
|
33
36
|
### Queries
|
37
|
+
|
34
38
|
Include `::HQ::GraphQL::Resource` and set `self.model_name` to start using queries.
|
35
39
|
Fields will be created for all active record columns. Association fields will be created if the association
|
36
40
|
is also a GraphQL Resource.
|
@@ -45,6 +49,7 @@ end
|
|
45
49
|
```
|
46
50
|
|
47
51
|
#### Customize
|
52
|
+
|
48
53
|
```ruby
|
49
54
|
class AdvisorResource
|
50
55
|
include ::HQ::GraphQL::Resource
|
@@ -66,6 +71,7 @@ end
|
|
66
71
|
```
|
67
72
|
|
68
73
|
### Mutations
|
74
|
+
|
69
75
|
Mutations will not be created by default. Add `mutations` to a resource to build mutations for create, update, and destroy.
|
70
76
|
|
71
77
|
```ruby
|
@@ -88,34 +94,40 @@ end
|
|
88
94
|
```
|
89
95
|
|
90
96
|
### Enums
|
97
|
+
|
91
98
|
Auto generate enums from the database using ActiveRecord
|
92
99
|
This comes in handy when we have constants that we want represented as enums.
|
93
100
|
|
94
101
|
#### Example
|
102
|
+
|
95
103
|
Let's assume we're saving data into a user types table
|
104
|
+
|
96
105
|
```postgresl
|
97
106
|
# select * from user_types;
|
98
|
-
id | name
|
107
|
+
id | name
|
99
108
|
--- +-------------
|
100
|
-
1 | Admin
|
109
|
+
1 | Admin
|
101
110
|
2 | Support User
|
102
111
|
(2 rows)
|
103
112
|
```
|
104
113
|
|
105
114
|
```ruby
|
106
|
-
class Enums::UserType < ::
|
115
|
+
class Enums::UserType < ::GraphQL::Schema::Enum
|
107
116
|
with_model
|
108
117
|
end
|
109
118
|
```
|
119
|
+
|
110
120
|
This class automatically uses the UserType ActiveRecord model to generate an enum:
|
121
|
+
|
111
122
|
```graphql
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
123
|
+
enum UserType {
|
124
|
+
Admin
|
125
|
+
SupportUser
|
126
|
+
}
|
116
127
|
```
|
117
128
|
|
118
129
|
### Root Mutations
|
130
|
+
|
119
131
|
Add mutations to your schema
|
120
132
|
|
121
133
|
```ruby
|
@@ -127,7 +139,9 @@ end
|
|
127
139
|
```
|
128
140
|
|
129
141
|
### Default Root Queries
|
142
|
+
|
130
143
|
Create a root query:
|
144
|
+
|
131
145
|
```ruby
|
132
146
|
class AdvisorResource
|
133
147
|
include ::HQ::GraphQL::Resource
|
@@ -144,6 +158,7 @@ end
|
|
144
158
|
```
|
145
159
|
|
146
160
|
### Custom Root Queries
|
161
|
+
|
147
162
|
```ruby
|
148
163
|
class AdvisorResource
|
149
164
|
include ::HQ::GraphQL::Resource
|
@@ -163,9 +178,10 @@ class AdvisorResource
|
|
163
178
|
end
|
164
179
|
```
|
165
180
|
|
166
|
-
## Create a new ::
|
181
|
+
## Create a new ::GraphQL::Schema::Object
|
182
|
+
|
167
183
|
```ruby
|
168
|
-
class AdvisorType < ::
|
184
|
+
class AdvisorType < ::GraphQL::Schema::Object
|
169
185
|
# Supports graphql-ruby functionality
|
170
186
|
field :id, Int, null: false
|
171
187
|
|
data/lib/hq/graphql.rb
CHANGED
@@ -51,6 +51,7 @@ module HQ
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def self.reset!
|
54
|
+
@lazy_load_classes = nil
|
54
55
|
@root_queries = nil
|
55
56
|
@enums = nil
|
56
57
|
@resources = nil
|
@@ -58,16 +59,28 @@ module HQ
|
|
58
59
|
::HQ::GraphQL::Types.reset!
|
59
60
|
end
|
60
61
|
|
62
|
+
def self.load_types!
|
63
|
+
lazy_load_classes.pop.lazy_load! while lazy_load_classes.length > 0
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.lazy_load(klass)
|
67
|
+
lazy_load_classes << klass unless lazy_load_classes.include?(klass)
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.lazy_load_classes
|
71
|
+
@lazy_load_classes ||= []
|
72
|
+
end
|
73
|
+
|
61
74
|
def self.root_queries
|
62
|
-
@root_queries ||=
|
75
|
+
@root_queries ||= []
|
63
76
|
end
|
64
77
|
|
65
78
|
def self.enums
|
66
|
-
@enums ||=
|
79
|
+
@enums ||= []
|
67
80
|
end
|
68
81
|
|
69
82
|
def self.resources
|
70
|
-
@resources ||=
|
83
|
+
@resources ||= []
|
71
84
|
end
|
72
85
|
end
|
73
86
|
end
|
@@ -75,16 +88,12 @@ end
|
|
75
88
|
require "hq/graphql/association_loader"
|
76
89
|
require "hq/graphql/scalars"
|
77
90
|
require "hq/graphql/comparator"
|
78
|
-
require "hq/graphql/
|
91
|
+
require "hq/graphql/ext"
|
79
92
|
require "hq/graphql/inputs"
|
80
|
-
require "hq/graphql/input_object"
|
81
|
-
require "hq/graphql/mutation"
|
82
|
-
require "hq/graphql/object"
|
83
93
|
require "hq/graphql/paginated_association_loader"
|
84
94
|
require "hq/graphql/record_loader"
|
85
95
|
require "hq/graphql/resource"
|
86
96
|
require "hq/graphql/root_mutation"
|
87
97
|
require "hq/graphql/root_query"
|
88
|
-
require "hq/graphql/schema"
|
89
98
|
require "hq/graphql/types"
|
90
99
|
require "hq/graphql/engine"
|
@@ -15,12 +15,13 @@ module HQ
|
|
15
15
|
|
16
16
|
class << self
|
17
17
|
def compare(old_schema, new_schema, criticality: :breaking)
|
18
|
+
old_schema.load_types! if old_schema < ::GraphQL::Schema
|
19
|
+
new_schema.load_types! if old_schema < ::GraphQL::Schema
|
18
20
|
level = CRITICALITY[criticality]
|
19
21
|
raise ::ArgumentError, "Invalid criticality. Possible values are #{CRITICALITY.keys.join(", ")}" unless level
|
20
22
|
|
21
|
-
result = ::GraphQL::SchemaComparator.compare(
|
23
|
+
result = ::GraphQL::SchemaComparator.compare(old_schema, new_schema)
|
22
24
|
return nil if result.identical?
|
23
|
-
|
24
25
|
changes = {}
|
25
26
|
changes[:breaking] = result.breaking_changes
|
26
27
|
if level >= CRITICALITY[:dangerous]
|
@@ -33,12 +34,6 @@ module HQ
|
|
33
34
|
|
34
35
|
changes
|
35
36
|
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
def convert_schema_to_string(schema)
|
40
|
-
schema.is_a?(::String) ? schema : schema.to_definition
|
41
|
-
end
|
42
37
|
end
|
43
38
|
end
|
44
39
|
end
|
@@ -1,10 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "hq/graphql/
|
3
|
+
require "hq/graphql/ext/enum_extensions"
|
4
4
|
|
5
5
|
module HQ
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
module GraphQL
|
7
|
+
module Enum
|
8
|
+
class SortBy < ::GraphQL::Schema::Enum
|
9
|
+
value "CreatedAt", value: :created_at
|
10
|
+
value "UpdatedAt", value: :updated_at
|
11
|
+
end
|
12
|
+
end
|
9
13
|
end
|
10
14
|
end
|
@@ -1,10 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "hq/graphql/
|
3
|
+
require "hq/graphql/ext/enum_extensions"
|
4
4
|
|
5
5
|
module HQ
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
module GraphQL
|
7
|
+
module Enum
|
8
|
+
class SortOrder < ::GraphQL::Schema::Enum
|
9
|
+
value "ASC", value: :asc
|
10
|
+
value "DESC", value: :desc
|
11
|
+
end
|
12
|
+
end
|
9
13
|
end
|
10
14
|
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "hq/graphql/ext/active_record_extensions"
|
4
|
+
require "hq/graphql/ext/enum_extensions"
|
5
|
+
require "hq/graphql/ext/input_object_extensions"
|
6
|
+
require "hq/graphql/ext/mutation_extensions"
|
7
|
+
require "hq/graphql/ext/object_extensions"
|
8
|
+
require "hq/graphql/ext/schema_extensions"
|
@@ -0,0 +1,148 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HQ
|
4
|
+
module GraphQL
|
5
|
+
module Ext
|
6
|
+
module ActiveRecordExtensions
|
7
|
+
class ActiveRecordError < StandardError
|
8
|
+
MISSING_MODEL_MSG = "Your GraphQL object must be connected to a model: `::GraphQL::Schema::Object.with_model 'User'`"
|
9
|
+
MISSING_ATTR_MSG = "Can't find attr %{model}.%{attr}'`"
|
10
|
+
MISSING_ASSOC_MSG = "Can't find association %{model}.%{assoc}'`"
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.included(klass)
|
14
|
+
klass.extend(ClassMethods)
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
attr_accessor :model_name,
|
19
|
+
:authorize_action,
|
20
|
+
:auto_load_attributes,
|
21
|
+
:auto_load_associations,
|
22
|
+
:auto_load_enums
|
23
|
+
|
24
|
+
def lazy_load(&block)
|
25
|
+
@lazy_load ||= []
|
26
|
+
if block
|
27
|
+
::HQ::GraphQL.lazy_load(self)
|
28
|
+
@lazy_load << block
|
29
|
+
end
|
30
|
+
@lazy_load
|
31
|
+
end
|
32
|
+
|
33
|
+
def lazy_load!
|
34
|
+
lazy_load.shift.call while lazy_load.length > 0
|
35
|
+
@lazy_load = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def model_columns
|
39
|
+
model_columns =
|
40
|
+
if auto_load_attributes
|
41
|
+
model_klass.columns
|
42
|
+
else
|
43
|
+
added_attributes.map { |attr| column_from_model(attr) }
|
44
|
+
end
|
45
|
+
|
46
|
+
# validate removed_attributes exist
|
47
|
+
removed_attributes.each { |attr| column_from_model(attr) }
|
48
|
+
|
49
|
+
model_columns.reject { |c| removed_attributes.include?(c.name.to_sym) }.sort_by(&:name)
|
50
|
+
end
|
51
|
+
|
52
|
+
def model_associations
|
53
|
+
model_associations = []
|
54
|
+
enums = model_klass.reflect_on_all_associations.select { |a| is_enum?(a) }
|
55
|
+
associatons = model_klass.reflect_on_all_associations - enums
|
56
|
+
|
57
|
+
if auto_load_enums
|
58
|
+
model_associations.concat(enums)
|
59
|
+
end
|
60
|
+
|
61
|
+
if auto_load_associations
|
62
|
+
model_associations.concat(associatons)
|
63
|
+
end
|
64
|
+
|
65
|
+
model_associations.concat(added_associations.map { |association| association_from_model(association) }).uniq
|
66
|
+
|
67
|
+
# validate removed_associations exist
|
68
|
+
removed_associations.each { |association| association_from_model(association) }
|
69
|
+
|
70
|
+
model_associations.reject { |a| removed_associations.include?(a.name.to_sym) }.sort_by(&:name)
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def add_attributes(*attrs)
|
76
|
+
validate_model!
|
77
|
+
added_attributes.concat attrs.map(&:to_sym)
|
78
|
+
end
|
79
|
+
alias_method :add_attribute, :add_attributes
|
80
|
+
alias_method :add_attrs, :add_attributes
|
81
|
+
alias_method :add_attr, :add_attributes
|
82
|
+
|
83
|
+
def remove_attributes(*attrs)
|
84
|
+
validate_model!
|
85
|
+
removed_attributes.concat attrs.map(&:to_sym)
|
86
|
+
end
|
87
|
+
alias_method :remove_attribute, :remove_attributes
|
88
|
+
alias_method :remove_attrs, :remove_attributes
|
89
|
+
alias_method :remove_attr, :remove_attributes
|
90
|
+
|
91
|
+
def add_associations(*associations)
|
92
|
+
validate_model!
|
93
|
+
added_associations.concat associations.map(&:to_sym)
|
94
|
+
end
|
95
|
+
alias_method :add_association, :add_associations
|
96
|
+
|
97
|
+
def remove_associations(*associations)
|
98
|
+
validate_model!
|
99
|
+
removed_associations.concat associations.map(&:to_sym)
|
100
|
+
end
|
101
|
+
alias_method :remove_association, :remove_associations
|
102
|
+
|
103
|
+
def model_klass
|
104
|
+
@model_klass ||= model_name.constantize
|
105
|
+
end
|
106
|
+
|
107
|
+
def column_from_model(attr)
|
108
|
+
model_klass.columns_hash[attr.to_s] || raise(ActiveRecordError, ActiveRecordError::MISSING_ATTR_MSG % { model: model_name, attr: attr })
|
109
|
+
end
|
110
|
+
|
111
|
+
def association_from_model(association)
|
112
|
+
model_klass.reflect_on_association(association) || raise(ActiveRecordError, ActiveRecordError::MISSING_ASSOC_MSG % { model: model_name, assoc: association })
|
113
|
+
end
|
114
|
+
|
115
|
+
def added_attributes
|
116
|
+
@added_attributes ||= []
|
117
|
+
end
|
118
|
+
|
119
|
+
def removed_attributes
|
120
|
+
@removed_attributes ||= []
|
121
|
+
end
|
122
|
+
|
123
|
+
def added_associations
|
124
|
+
@added_associations ||= []
|
125
|
+
end
|
126
|
+
|
127
|
+
def removed_associations
|
128
|
+
@removed_associations ||= []
|
129
|
+
end
|
130
|
+
|
131
|
+
def camelize(name)
|
132
|
+
name.to_s.camelize(:lower)
|
133
|
+
end
|
134
|
+
|
135
|
+
def is_enum?(association)
|
136
|
+
::HQ::GraphQL.enums.include?(association.klass)
|
137
|
+
end
|
138
|
+
|
139
|
+
def validate_model!
|
140
|
+
lazy_load do
|
141
|
+
model_name || raise(ActiveRecordError, ActiveRecordError::MISSING_MODEL_MSG)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "hq/graphql/types"
|
4
|
+
|
5
|
+
module HQ::GraphQL
|
6
|
+
module Ext
|
7
|
+
module EnumExtensions
|
8
|
+
## Auto generate enums from the database using ActiveRecord
|
9
|
+
# This comes in handy when we have constants that we want represented as enums.
|
10
|
+
#
|
11
|
+
# == Example
|
12
|
+
# Let's assume we're saving data into a user types table
|
13
|
+
# # select * from user_types;
|
14
|
+
# id | name
|
15
|
+
# --- +-------------
|
16
|
+
# 1 | Admin
|
17
|
+
# 2 | Support User
|
18
|
+
# (2 rows)
|
19
|
+
#
|
20
|
+
# ```ruby
|
21
|
+
# class Enums::UserType < ::HQ::GraphQL::Enum
|
22
|
+
# with_model
|
23
|
+
# end
|
24
|
+
# ```
|
25
|
+
#
|
26
|
+
# Creates the following enum:
|
27
|
+
# ```graphql
|
28
|
+
# enum UserType {
|
29
|
+
# Admin
|
30
|
+
# SupportUser
|
31
|
+
# }
|
32
|
+
# ```
|
33
|
+
def with_model(
|
34
|
+
klass = default_model_name.safe_constantize,
|
35
|
+
prefix: nil,
|
36
|
+
register: true,
|
37
|
+
scope: nil,
|
38
|
+
strip: /(^[^_a-zA-Z])|([^_a-zA-Z0-9]*)/,
|
39
|
+
value_method: :name
|
40
|
+
)
|
41
|
+
raise ArgumentError.new(<<~ERROR) if !klass
|
42
|
+
`::HQ::GraphQL::Enum.with_model {...}' had trouble automatically inferring the class name.
|
43
|
+
Avoid this by manually passing in the class name: `::HQ::GraphQL::Enum.with_model(#{default_model_name}) {...}`
|
44
|
+
ERROR
|
45
|
+
|
46
|
+
if register
|
47
|
+
::HQ::GraphQL.enums << klass
|
48
|
+
::HQ::GraphQL::Types.register(klass, self)
|
49
|
+
end
|
50
|
+
|
51
|
+
lazy_load do
|
52
|
+
records = scope ? klass.instance_exec(&scope) : klass.all
|
53
|
+
records.each do |record|
|
54
|
+
value "#{prefix}#{record.send(value_method).gsub(strip, "")}", value: record
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def lazy_load(&block)
|
60
|
+
@lazy_load ||= []
|
61
|
+
if block
|
62
|
+
::HQ::GraphQL.lazy_load(self)
|
63
|
+
@lazy_load << block
|
64
|
+
end
|
65
|
+
@lazy_load
|
66
|
+
end
|
67
|
+
|
68
|
+
def lazy_load!
|
69
|
+
lazy_load.shift.call while lazy_load.length > 0
|
70
|
+
@lazy_load = []
|
71
|
+
end
|
72
|
+
|
73
|
+
def default_model_name
|
74
|
+
to_s.sub(/^((::)?\w+)::/, "")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
::GraphQL::Schema::Enum.extend ::HQ::GraphQL::Ext::EnumExtensions
|