hq-graphql 2.1.9 → 2.2.1
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 +34 -9
- data/lib/hq/graphql.rb +21 -8
- data/lib/hq/graphql/association_loader.rb +1 -0
- data/lib/hq/graphql/comparator.rb +22 -21
- data/lib/hq/graphql/config.rb +1 -0
- 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 +45 -14
- 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 -139
- data/lib/hq/graphql/enum.rb +0 -78
- data/lib/hq/graphql/input_object.rb +0 -95
- data/lib/hq/graphql/mutation.rb +0 -15
- data/lib/hq/graphql/object.rb +0 -116
- 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: '08dfdf7baf4c44a54bdb3ecb9e0e89baacfaab9e6dda53525a0f8dded456a8c9'
|
4
|
+
data.tar.gz: 2b12d46d565e00710f49618e603c6a88f7fd09cc753d82cb8395b8e2d7f36447
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 157d2faad665955ce59916bf809e265ff95f3910c8f2999fb88d697f9b1e8bacf624ee3750a90cdaf38d1777b848659ded147da5669c46b60febe6c5c05e4679
|
7
|
+
data.tar.gz: 2fa8a1a3b59f900553eb91673dc5b86daf9e25ad35d45ada3ee0cb8799882a4de9da7a3f5e59e82ebd24570f1eb86cb0813ebea959462fd3248495eb61538f13
|
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
|
@@ -18,10 +19,22 @@ Define a global default scope.
|
|
18
19
|
end
|
19
20
|
```
|
20
21
|
|
22
|
+
### Excluded Inputs
|
23
|
+
|
24
|
+
Define a global excluded input fields. Useful for excluding (autogenerated | auto set) fields like below.
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
::HQ::GraphQL.configure do |config|
|
28
|
+
config.excluded_inputs = [:id, :created_at, :updated_at]
|
29
|
+
end
|
30
|
+
```
|
31
|
+
|
21
32
|
## GraphQL Resource
|
33
|
+
|
22
34
|
Connect to ActiveRecord to auto generate queries and mutations.
|
23
35
|
|
24
36
|
### Queries
|
37
|
+
|
25
38
|
Include `::HQ::GraphQL::Resource` and set `self.model_name` to start using queries.
|
26
39
|
Fields will be created for all active record columns. Association fields will be created if the association
|
27
40
|
is also a GraphQL Resource.
|
@@ -36,6 +49,7 @@ end
|
|
36
49
|
```
|
37
50
|
|
38
51
|
#### Customize
|
52
|
+
|
39
53
|
```ruby
|
40
54
|
class AdvisorResource
|
41
55
|
include ::HQ::GraphQL::Resource
|
@@ -57,6 +71,7 @@ end
|
|
57
71
|
```
|
58
72
|
|
59
73
|
### Mutations
|
74
|
+
|
60
75
|
Mutations will not be created by default. Add `mutations` to a resource to build mutations for create, update, and destroy.
|
61
76
|
|
62
77
|
```ruby
|
@@ -79,34 +94,40 @@ end
|
|
79
94
|
```
|
80
95
|
|
81
96
|
### Enums
|
97
|
+
|
82
98
|
Auto generate enums from the database using ActiveRecord
|
83
99
|
This comes in handy when we have constants that we want represented as enums.
|
84
100
|
|
85
101
|
#### Example
|
102
|
+
|
86
103
|
Let's assume we're saving data into a user types table
|
104
|
+
|
87
105
|
```postgresl
|
88
106
|
# select * from user_types;
|
89
|
-
id | name
|
107
|
+
id | name
|
90
108
|
--- +-------------
|
91
|
-
1 | Admin
|
109
|
+
1 | Admin
|
92
110
|
2 | Support User
|
93
111
|
(2 rows)
|
94
112
|
```
|
95
113
|
|
96
114
|
```ruby
|
97
|
-
class Enums::UserType < ::
|
115
|
+
class Enums::UserType < ::GraphQL::Schema::Enum
|
98
116
|
with_model
|
99
117
|
end
|
100
118
|
```
|
119
|
+
|
101
120
|
This class automatically uses the UserType ActiveRecord model to generate an enum:
|
121
|
+
|
102
122
|
```graphql
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
123
|
+
enum UserType {
|
124
|
+
Admin
|
125
|
+
SupportUser
|
126
|
+
}
|
107
127
|
```
|
108
128
|
|
109
129
|
### Root Mutations
|
130
|
+
|
110
131
|
Add mutations to your schema
|
111
132
|
|
112
133
|
```ruby
|
@@ -118,7 +139,9 @@ end
|
|
118
139
|
```
|
119
140
|
|
120
141
|
### Default Root Queries
|
142
|
+
|
121
143
|
Create a root query:
|
144
|
+
|
122
145
|
```ruby
|
123
146
|
class AdvisorResource
|
124
147
|
include ::HQ::GraphQL::Resource
|
@@ -135,6 +158,7 @@ end
|
|
135
158
|
```
|
136
159
|
|
137
160
|
### Custom Root Queries
|
161
|
+
|
138
162
|
```ruby
|
139
163
|
class AdvisorResource
|
140
164
|
include ::HQ::GraphQL::Resource
|
@@ -154,9 +178,10 @@ class AdvisorResource
|
|
154
178
|
end
|
155
179
|
```
|
156
180
|
|
157
|
-
## Create a new ::
|
181
|
+
## Create a new ::GraphQL::Schema::Object
|
182
|
+
|
158
183
|
```ruby
|
159
|
-
class AdvisorType < ::
|
184
|
+
class AdvisorType < ::GraphQL::Schema::Object
|
160
185
|
# Supports graphql-ruby functionality
|
161
186
|
field :id, Int, null: false
|
162
187
|
|
data/lib/hq/graphql.rb
CHANGED
@@ -36,6 +36,10 @@ module HQ
|
|
36
36
|
config.extract_class.call(klass)
|
37
37
|
end
|
38
38
|
|
39
|
+
def self.excluded_inputs
|
40
|
+
config.excluded_inputs || []
|
41
|
+
end
|
42
|
+
|
39
43
|
def self.lookup_resource(klass)
|
40
44
|
[klass, klass.base_class, klass.superclass].lazy.map do |k|
|
41
45
|
config.resource_lookup.call(k) || resources.detect { |r| r.model_klass == k }
|
@@ -47,6 +51,7 @@ module HQ
|
|
47
51
|
end
|
48
52
|
|
49
53
|
def self.reset!
|
54
|
+
@lazy_load_classes = nil
|
50
55
|
@root_queries = nil
|
51
56
|
@enums = nil
|
52
57
|
@resources = nil
|
@@ -54,16 +59,28 @@ module HQ
|
|
54
59
|
::HQ::GraphQL::Types.reset!
|
55
60
|
end
|
56
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
|
+
|
57
74
|
def self.root_queries
|
58
|
-
@root_queries ||=
|
75
|
+
@root_queries ||= []
|
59
76
|
end
|
60
77
|
|
61
78
|
def self.enums
|
62
|
-
@enums ||=
|
79
|
+
@enums ||= []
|
63
80
|
end
|
64
81
|
|
65
82
|
def self.resources
|
66
|
-
@resources ||=
|
83
|
+
@resources ||= []
|
67
84
|
end
|
68
85
|
end
|
69
86
|
end
|
@@ -71,16 +88,12 @@ end
|
|
71
88
|
require "hq/graphql/association_loader"
|
72
89
|
require "hq/graphql/scalars"
|
73
90
|
require "hq/graphql/comparator"
|
74
|
-
require "hq/graphql/
|
91
|
+
require "hq/graphql/ext"
|
75
92
|
require "hq/graphql/inputs"
|
76
|
-
require "hq/graphql/input_object"
|
77
|
-
require "hq/graphql/mutation"
|
78
|
-
require "hq/graphql/object"
|
79
93
|
require "hq/graphql/paginated_association_loader"
|
80
94
|
require "hq/graphql/record_loader"
|
81
95
|
require "hq/graphql/resource"
|
82
96
|
require "hq/graphql/root_mutation"
|
83
97
|
require "hq/graphql/root_query"
|
84
|
-
require "hq/graphql/schema"
|
85
98
|
require "hq/graphql/types"
|
86
99
|
require "hq/graphql/engine"
|
@@ -13,31 +13,32 @@ module HQ
|
|
13
13
|
non_breaking: 2
|
14
14
|
}
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
changes[:
|
26
|
-
if level >= CRITICALITY[:dangerous]
|
27
|
-
changes[:dangerous] = result.dangerous_changes
|
28
|
-
end
|
29
|
-
if level >= CRITICALITY[:non_breaking]
|
30
|
-
changes[:non_breaking] = result.non_breaking_changes
|
31
|
-
end
|
32
|
-
return nil unless changes.values.flatten.any?
|
33
|
-
|
34
|
-
changes
|
16
|
+
def self.compare(old_schema, new_schema, criticality: :breaking)
|
17
|
+
level = CRITICALITY[criticality]
|
18
|
+
raise ::ArgumentError, "Invalid criticality. Possible values are #{CRITICALITY.keys.join(", ")}" unless level
|
19
|
+
|
20
|
+
result = ::GraphQL::SchemaComparator.compare(prepare_schema(old_schema), prepare_schema(new_schema))
|
21
|
+
return if result.identical?
|
22
|
+
changes = {}
|
23
|
+
changes[:breaking] = result.breaking_changes
|
24
|
+
if level >= CRITICALITY[:dangerous]
|
25
|
+
changes[:dangerous] = result.dangerous_changes
|
35
26
|
end
|
27
|
+
if level >= CRITICALITY[:non_breaking]
|
28
|
+
changes[:non_breaking] = result.non_breaking_changes
|
29
|
+
end
|
30
|
+
return unless changes.values.flatten.any?
|
31
|
+
|
32
|
+
changes
|
33
|
+
end
|
36
34
|
|
35
|
+
class << self
|
37
36
|
private
|
38
37
|
|
39
|
-
def
|
40
|
-
schema.
|
38
|
+
def prepare_schema(schema)
|
39
|
+
schema = ::GraphQL::Schema.from_definition(schema) if schema.is_a?(String)
|
40
|
+
schema.load_types!
|
41
|
+
schema
|
41
42
|
end
|
42
43
|
end
|
43
44
|
end
|
data/lib/hq/graphql/config.rb
CHANGED
@@ -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
|