hai 0.0.2 → 0.0.3
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/Gemfile +1 -1
- data/Gemfile.lock +14 -2
- data/README.md +186 -10
- data/app/controllers/hai/rest_controller.rb +65 -0
- data/config/routes.rb +7 -0
- data/lib/hai/action_mods.rb +26 -0
- data/lib/hai/create.rb +41 -3
- data/lib/hai/delete.rb +17 -0
- data/lib/hai/graphql/create_mutations.rb +28 -9
- data/lib/hai/graphql/delete_mutations.rb +25 -0
- data/lib/hai/graphql/list_queries.rb +20 -13
- data/lib/hai/graphql/read_queries.rb +11 -3
- data/lib/hai/graphql/types.rb +88 -13
- data/lib/hai/graphql/update_mutations.rb +34 -0
- data/lib/hai/graphql.rb +17 -4
- data/lib/hai/policies.rb +26 -0
- data/lib/hai/railtie.rb +13 -0
- data/lib/hai/read.rb +82 -44
- data/lib/hai/tasks/graphql/filter_type.rake +8 -0
- data/lib/hai/tasks/hai.rake +2 -0
- data/lib/hai/types/arel/boolean_input_type.rb +11 -0
- data/lib/hai/types/arel/float_input_type.rb +10 -0
- data/lib/hai/types/arel/sort_input_type.rb +13 -0
- data/lib/hai/types/base_create.rb +14 -0
- data/lib/hai/types/mutation_error_type.rb +11 -0
- data/lib/hai/types/sort_input_type.rb +10 -0
- data/lib/hai/update.rb +25 -5
- data/lib/hai/version.rb +1 -1
- data/lib/hai.rb +20 -4
- data/todo.md +17 -10
- metadata +34 -3
- data/hai-0.0.1.gem +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd1e9a5e790765b1b2030e4384048a2ad08f420e2078b5d883e2bffd2d8dc8eb
|
4
|
+
data.tar.gz: 4da9e653d8c708d2e01135aa8cbf70234ec73100a0c158cb3dbd1583a47add28
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dda8bcf71ee7a008b7fac3c6ad0f83b25e3d4fb3c50078b7cae6e1d9d896fa1d3d88b18eacced38fe7ce2e7e6c1efd55c02dc6187934376ab03e7f28f7ed7af4
|
7
|
+
data.tar.gz: f125a58684eeba263730397623dd1f3fa58b1168423960c88cb5a0d4f815f6312c18d470d558c8cb8f9a992d542b60637c2086dd4487b0b8737ea48db0c19015
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
hai (0.0.
|
4
|
+
hai (0.0.2)
|
5
5
|
activerecord (~> 7.0)
|
6
|
+
graphql (~> 2.0)
|
6
7
|
pg (~> 1.3.5)
|
7
8
|
|
8
9
|
GEM
|
@@ -26,12 +27,14 @@ GEM
|
|
26
27
|
graphql (2.0.9)
|
27
28
|
i18n (1.10.0)
|
28
29
|
concurrent-ruby (~> 1.0)
|
30
|
+
language_server-protocol (3.16.0.3)
|
29
31
|
method_source (1.0.0)
|
30
32
|
minitest (5.16.0)
|
31
33
|
parallel (1.21.0)
|
32
34
|
parser (3.1.1.0)
|
33
35
|
ast (~> 2.4.1)
|
34
36
|
pg (1.3.5)
|
37
|
+
prettier_print (0.1.0)
|
35
38
|
pry (0.14.1)
|
36
39
|
coderay (~> 1.1)
|
37
40
|
method_source (~> 1.0)
|
@@ -50,23 +53,32 @@ GEM
|
|
50
53
|
unicode-display_width (>= 1.4.0, < 3.0)
|
51
54
|
rubocop-ast (1.16.0)
|
52
55
|
parser (>= 3.1.1.0)
|
56
|
+
ruby-lsp (0.1.0)
|
57
|
+
language_server-protocol
|
58
|
+
rubocop (>= 1.0)
|
59
|
+
sorbet-runtime
|
60
|
+
syntax_tree (>= 2.4)
|
53
61
|
ruby-progressbar (1.11.0)
|
62
|
+
sorbet-runtime (0.5.10139)
|
63
|
+
syntax_tree (2.8.0)
|
64
|
+
prettier_print
|
54
65
|
tzinfo (2.0.4)
|
55
66
|
concurrent-ruby (~> 1.0)
|
56
67
|
unicode-display_width (2.1.0)
|
57
68
|
|
58
69
|
PLATFORMS
|
59
70
|
x86_64-darwin-21
|
71
|
+
x86_64-linux
|
60
72
|
|
61
73
|
DEPENDENCIES
|
62
74
|
activesupport (~> 7.0)
|
63
75
|
factory_bot
|
64
|
-
graphql
|
65
76
|
hai!
|
66
77
|
minitest (~> 5.0)
|
67
78
|
pry
|
68
79
|
rake (~> 13.0)
|
69
80
|
rubocop (~> 1.21)
|
81
|
+
ruby-lsp
|
70
82
|
|
71
83
|
BUNDLED WITH
|
72
84
|
2.3.5
|
data/README.md
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# Hai
|
2
2
|
|
3
|
-
|
3
|
+
The easist way to create a CRUD GraphQL or Rest api with ruby.
|
4
|
+
Heavily inspired by [Ash Elixir](https://www.ash-elixir.org/)
|
4
5
|
|
5
|
-
|
6
|
+
Feedback is welcome and appreciated.
|
6
7
|
|
7
8
|
## Installation
|
8
9
|
|
@@ -16,24 +17,199 @@ And then execute:
|
|
16
17
|
|
17
18
|
$ bundle install
|
18
19
|
|
19
|
-
|
20
|
+
## Usage
|
20
21
|
|
21
|
-
|
22
|
+
Hai is a resource based api and those resources are ActiveRecord models. Keeping with this first principle, let's see how it can be used in your Ruby application.
|
22
23
|
|
23
|
-
##
|
24
|
+
## Action Modifications
|
25
|
+
|
26
|
+
If you want to modify any of the actions, you can add a Actions module to the
|
27
|
+
model that you want to modify.
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
class Post < ApplicationRecord
|
31
|
+
belongs_to :user
|
32
|
+
|
33
|
+
module Actions
|
34
|
+
def self.read(query, context)
|
35
|
+
query.where(user_id: context[:user].id)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.list(query, context)
|
39
|
+
query.where(user_id: context[:user].id)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.create(post, context)
|
43
|
+
post.user = context[:user]
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.update(post, context)
|
47
|
+
post.last_updated_by = context[:user]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
```
|
52
|
+
## Policies
|
53
|
+
Policies are handled in the same manner of Action Modifications. We will use the `Policies` module in the model to handle things like authorization.
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
class Post < ApplicationRecord
|
57
|
+
belongs_to :user
|
58
|
+
|
59
|
+
module Policies
|
60
|
+
def self.read(context)
|
61
|
+
context[:user].can?(:read, context[:model])
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.list(query, context)
|
65
|
+
context[:user].can?(:list, context[:model])
|
66
|
+
end
|
67
|
+
|
68
|
+
# NOTE: create does a create or update
|
69
|
+
def self.create(post, context)
|
70
|
+
if post.persisted?
|
71
|
+
post.user_id == context[:user].id
|
72
|
+
else
|
73
|
+
context[:user].can?(:create, context[:model])
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.update(post, context)
|
78
|
+
post.user_id == context[:user].id
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.delete(post, context)
|
82
|
+
post.user_id == context[:user].id
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
```
|
87
|
+
|
88
|
+
## Graphql
|
89
|
+
|
90
|
+
Hai Graphql depends on `graphql-ruby` so if you don't have that installed and
|
91
|
+
boostrapped, head over to [ their repo and do that now ](https://github.com/rmosolgo/graphql-ruby#installation).
|
92
|
+
|
93
|
+
First, we have to load the Hai Graphql Types with the following snippet of code in your GraphQL::Schema file. Currently, order of operations matters so this needs to be called before the mutation and query class methods.
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
class MyAppSchema < GraphQL::Schema
|
97
|
+
include Hai::GraphQL::Types
|
98
|
+
hai_types(User, Post) # comma list of the models you want to expose
|
99
|
+
|
100
|
+
mutation(Types::MutationType)
|
101
|
+
query(Types::QueryType)
|
102
|
+
# ...
|
103
|
+
end
|
104
|
+
```
|
105
|
+
|
106
|
+
Now, if we want to add read operations (`readUser` and `listUsers`) complete with filtering, pagination, & sorting, we just have to declare it in the `Types::QueryType` file like so:
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
module Types
|
110
|
+
class QueryType < Types::BaseObject
|
111
|
+
# Add `node(id: ID!) and `nodes(ids: [ID!]!)`
|
112
|
+
include GraphQL::Types::Relay::HasNodeField
|
113
|
+
include GraphQL::Types::Relay::HasNodesField
|
114
|
+
|
115
|
+
include Hai::GraphQL
|
116
|
+
hai_query(User)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
```
|
120
|
+
|
121
|
+
Lastly, if you want to add mutations (`createUser`, `updateUser`, & `deleteUser`), you simply declare which models you'd like to expose in the `Types::MutationType` file.
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
module Types
|
125
|
+
class MutationType < Types::BaseObject
|
126
|
+
include Hai::GraphQL
|
127
|
+
hai_mutation(User)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
```
|
131
|
+
|
132
|
+
## Rest
|
133
|
+
|
134
|
+
This is even easier than adding Hai Graphql. Hai Rest is a dynamic engine that can be mounted with any namespace. You just have to mount it in your routes file like this:
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
Rails.application.routes.draw do
|
138
|
+
mount Hai::Rest::Engine => "/rest"
|
139
|
+
end
|
140
|
+
```
|
141
|
+
|
142
|
+
Example queries for rest.
|
143
|
+
#### List all users
|
144
|
+
|
145
|
+
Simple use case
|
146
|
+
|
147
|
+
`GET <base_url>/rest/users`
|
148
|
+
|
149
|
+
You can also filter:
|
150
|
+
|
151
|
+
`GET <base_url>/rest/users?filter[name][eq]=bob`
|
152
|
+
|
153
|
+
Sort
|
154
|
+
|
155
|
+
`GET <base_url>/rest/users?sort[field]=name&sort[direction]=desc`
|
156
|
+
|
157
|
+
Paginate
|
158
|
+
|
159
|
+
`GET <base_url>/rest/users?limit=10&offset=20`
|
160
|
+
|
161
|
+
Or all things combined
|
162
|
+
|
163
|
+
`GET <base_url>/rest/users?filter[name][eq]=bob&sort[field]=name&sort[direction]=desc&limit=10&offset=20`
|
164
|
+
|
165
|
+
#### Read a specific user
|
166
|
+
|
167
|
+
`GET <base_url>/rest/users/1`
|
168
|
+
|
169
|
+
#### Create a user
|
170
|
+
|
171
|
+
`POST <base_url>/rest/users`
|
172
|
+
|
173
|
+
```JSON
|
174
|
+
{
|
175
|
+
"user": {
|
176
|
+
"name": "bob"
|
177
|
+
}
|
178
|
+
}
|
179
|
+
```
|
180
|
+
|
181
|
+
#### Update a user
|
182
|
+
`PUT <base_url>/rest/users/1`
|
183
|
+
|
184
|
+
```JSON
|
185
|
+
{
|
186
|
+
"user": {
|
187
|
+
"name": "bob"
|
188
|
+
}
|
189
|
+
}
|
190
|
+
```
|
24
191
|
|
25
|
-
|
192
|
+
#### Delete a user
|
193
|
+
`DELETE <base_url>/rest/users/1`
|
26
194
|
|
27
195
|
## Development
|
28
196
|
|
29
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
197
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
198
|
+
`rake test` to run the tests. You can also run `bin/console` for an interactive
|
199
|
+
prompt that will allow you to experiment.
|
30
200
|
|
31
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To
|
201
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To
|
202
|
+
release a new version, update the version number in `version.rb`, and then run
|
203
|
+
`bundle exec rake release`, which will create a git tag for the version, push
|
204
|
+
git commits and the created tag, and push the `.gem` file to
|
205
|
+
[rubygems.org](https://rubygems.org).
|
32
206
|
|
33
207
|
## Contributing
|
34
208
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at
|
209
|
+
Bug reports and pull requests are welcome on GitHub at
|
210
|
+
https://github.com/[USERNAME]/hai.
|
36
211
|
|
37
212
|
## License
|
38
213
|
|
39
|
-
The gem is available as open source under the terms of the [MIT
|
214
|
+
The gem is available as open source under the terms of the [MIT
|
215
|
+
License](https://opensource.org/licenses/MIT).
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Hai
|
2
|
+
class RestController < ::ApplicationController
|
3
|
+
skip_forgery_protection
|
4
|
+
def index
|
5
|
+
render json:
|
6
|
+
Hai::Read
|
7
|
+
.new(model_class, context)
|
8
|
+
.list(
|
9
|
+
# TODO: this is a security risk, thanks co-pilot
|
10
|
+
# potenntial use attributes types plus AREL_TYPE_CAST
|
11
|
+
filter: params[:filter]&.to_unsafe_h,
|
12
|
+
limit: params[:limit],
|
13
|
+
offset: params[:offset],
|
14
|
+
sort: params[:sort]&.to_unsafe_h
|
15
|
+
)
|
16
|
+
.to_json
|
17
|
+
end
|
18
|
+
|
19
|
+
def show
|
20
|
+
render json:
|
21
|
+
Hai::Read
|
22
|
+
.new(model_class, context)
|
23
|
+
.read(id: { eq: params[:id] })
|
24
|
+
.to_json
|
25
|
+
end
|
26
|
+
|
27
|
+
def create
|
28
|
+
render json:
|
29
|
+
Hai::Create
|
30
|
+
.new(model_class, context)
|
31
|
+
.execute(**params[params[:model].singularize].permit!)
|
32
|
+
.to_json
|
33
|
+
end
|
34
|
+
|
35
|
+
def update
|
36
|
+
render json:
|
37
|
+
Hai::Update
|
38
|
+
.new(model_class, context)
|
39
|
+
.execute(
|
40
|
+
id: params[:id],
|
41
|
+
attributes: params[params[:model]].permit!
|
42
|
+
)
|
43
|
+
.to_json
|
44
|
+
end
|
45
|
+
|
46
|
+
def destroy
|
47
|
+
render json:
|
48
|
+
Hai::Delete
|
49
|
+
.new(model_class, context)
|
50
|
+
.execute(id: params[:id])
|
51
|
+
.to_json
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def model_class
|
57
|
+
params[:model].classify.constantize
|
58
|
+
end
|
59
|
+
|
60
|
+
def read_params
|
61
|
+
params.permit! || {}
|
62
|
+
# params.permit(:limit, :offset, sort: { :field , :order},filter: {} )
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module Hai
|
2
|
+
module ActionMods
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
def run_action_modification(action, context)
|
8
|
+
return unless (action_mod = self.class.action_mods[action])
|
9
|
+
|
10
|
+
action_mod.call(self, context)
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def action_mods
|
15
|
+
@action_mods ||= {}
|
16
|
+
end
|
17
|
+
|
18
|
+
# TODO: validate CRUD actions
|
19
|
+
def action(action, &block)
|
20
|
+
action_mods[action] = lambda do |instance, context|
|
21
|
+
block.call(instance, context)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/hai/create.rb
CHANGED
@@ -1,13 +1,51 @@
|
|
1
1
|
module Hai
|
2
2
|
class Create
|
3
|
-
attr_accessor :model
|
3
|
+
attr_accessor :model, :context
|
4
4
|
|
5
|
-
def initialize(model)
|
5
|
+
def initialize(model, context)
|
6
6
|
@model = model
|
7
|
+
@context = context
|
8
|
+
@context[:model] = model
|
7
9
|
end
|
8
10
|
|
9
11
|
def execute(**attrs)
|
10
|
-
|
12
|
+
id = attrs.delete(:id)
|
13
|
+
instance = id ? model.find(id) : model.new
|
14
|
+
|
15
|
+
return unauthorized_error unless check_policy(instance)
|
16
|
+
|
17
|
+
instance.assign_attributes(**attrs)
|
18
|
+
|
19
|
+
run_action_modification(instance)
|
20
|
+
|
21
|
+
if instance.save
|
22
|
+
{ errors: [], result: instance }
|
23
|
+
else
|
24
|
+
{ errors: instance.errors.map(&:full_message), result: nil }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def unauthorized_error
|
31
|
+
{ errors: ["UnauthorizedError"], result: nil }
|
32
|
+
end
|
33
|
+
|
34
|
+
def check_policy(instance)
|
35
|
+
if model.const_defined?("Policies") &&
|
36
|
+
model::Policies.respond_to?(:create)
|
37
|
+
model::Policies.create(instance, context)
|
38
|
+
else
|
39
|
+
true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def run_action_modification(instance)
|
44
|
+
if model.const_defined?("Actions") && model::Actions.respond_to?(:create)
|
45
|
+
model::Actions.create(instance, context)
|
46
|
+
else
|
47
|
+
instance
|
48
|
+
end
|
11
49
|
end
|
12
50
|
end
|
13
51
|
end
|
data/lib/hai/delete.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module Hai
|
2
|
+
class Delete
|
3
|
+
attr_reader :model, :context
|
4
|
+
|
5
|
+
def initialize(model, context)
|
6
|
+
@model = model
|
7
|
+
@context = context
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute(id:)
|
11
|
+
record = model.find(id)
|
12
|
+
raise UnauthorizedError if record.respond_to?(:check_hai_policy) && record.check_hai_policy(:delete, context)
|
13
|
+
|
14
|
+
record.destroy
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -1,23 +1,42 @@
|
|
1
|
+
require "hai/types/base_create"
|
2
|
+
|
1
3
|
module Hai
|
2
4
|
module GraphQL
|
3
5
|
class CreateMutations
|
4
6
|
class << self
|
5
7
|
def add(mutation_type, model)
|
8
|
+
define_resolver(model)
|
6
9
|
add_field(mutation_type, model)
|
7
|
-
define_create_method(mutation_type, model)
|
8
10
|
end
|
9
11
|
|
10
|
-
def
|
11
|
-
|
12
|
-
|
13
|
-
|
12
|
+
def define_resolver(model)
|
13
|
+
klass = Class.new(Hai::GraphQL::Types::BaseCreate)
|
14
|
+
klass.send(:graphql_name, "Create#{model}")
|
15
|
+
klass.description("Attributes for creating or updating a #{model}.")
|
16
|
+
model.attribute_types.each do |attr, type|
|
17
|
+
next if %w[id created_at updated_at].include?(attr)
|
18
|
+
|
19
|
+
klass.argument(
|
20
|
+
attr,
|
21
|
+
Hai::GraphQL::TYPE_CAST[type.class] ||
|
22
|
+
Hai::GraphQL::TYPE_CAST[type.class.superclass],
|
23
|
+
required: false
|
24
|
+
)
|
14
25
|
end
|
15
|
-
end
|
16
26
|
|
17
|
-
|
18
|
-
|
19
|
-
|
27
|
+
klass.field(:result, ::Types.const_get("#{model}Type"))
|
28
|
+
|
29
|
+
klass.define_method(:resolve) do |args|
|
30
|
+
Hai::Create.new(model, context).execute(**args)
|
20
31
|
end
|
32
|
+
Hai::GraphQL::Types.const_set("Create#{model}", klass)
|
33
|
+
end
|
34
|
+
|
35
|
+
def add_field(mutation_type, model)
|
36
|
+
mutation_type.field(
|
37
|
+
"create_#{model.name.downcase}",
|
38
|
+
mutation: Hai::GraphQL::Types.const_get("Create#{model}")
|
39
|
+
)
|
21
40
|
end
|
22
41
|
end
|
23
42
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Hai
|
2
|
+
module GraphQL
|
3
|
+
class DeleteMutations
|
4
|
+
class << self
|
5
|
+
def add(mutation_type, model)
|
6
|
+
add_field(mutation_type, model)
|
7
|
+
define_create_method(mutation_type, model)
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_field(mutation_type, model)
|
11
|
+
mutation_type.field("delete_#{model.name.downcase}", "Types::#{model}Type".constantize) do
|
12
|
+
mutation_type.description("Delete a #{model}.")
|
13
|
+
argument(:id, ::GraphQL::Types::ID)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def define_create_method(mutation_type, model)
|
18
|
+
mutation_type.define_method("delete_#{model.name.downcase}") do |id:|
|
19
|
+
Hai::Delete.new(model, context).execute(id: id)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,33 +1,40 @@
|
|
1
|
+
require "hai/types/sort_input_type"
|
2
|
+
|
1
3
|
module Hai
|
2
4
|
module GraphQL
|
3
5
|
class ListQueries
|
4
6
|
class << self
|
5
7
|
def add(query_type, model)
|
6
|
-
define_filter_type(model)
|
7
8
|
add_field(query_type, model)
|
8
9
|
define_list_method(query_type, model)
|
9
10
|
end
|
10
11
|
|
11
|
-
def define_filter_type(model)
|
12
|
-
filter_klass = Class.new(::GraphQL::Schema::InputObject)
|
13
|
-
model.attribute_types.each do |attr, type|
|
14
|
-
filter_klass.send(:argument, attr, AREL_TYPE_CAST[type.class], required: false)
|
15
|
-
end
|
16
|
-
Object.const_set "#{model}FilterInputType", filter_klass
|
17
|
-
end
|
18
|
-
|
19
12
|
def add_field(query_type, model)
|
20
|
-
query_type.field "list_#{model.name.downcase}",
|
13
|
+
query_type.field "list_#{model.name.pluralize.downcase}",
|
14
|
+
["Types::#{model}Type".constantize] do
|
21
15
|
query_type.description "List of #{model}."
|
22
|
-
argument :filter,
|
16
|
+
argument :filter,
|
17
|
+
"#{model}FilterInputType".constantize,
|
18
|
+
required: false
|
23
19
|
argument :limit, ::GraphQL::Types::Int, required: false
|
24
20
|
argument :offset, ::GraphQL::Types::Int, required: false
|
21
|
+
argument :sort, Types::SortInputType, required: false
|
22
|
+
|
23
|
+
(model.try(:arguments) || []).each do |name, type, kwargs|
|
24
|
+
argument(name, type, **kwargs)
|
25
|
+
end
|
25
26
|
end
|
26
27
|
end
|
27
28
|
|
28
29
|
def define_list_method(query_type, model)
|
29
|
-
query_type.define_method(
|
30
|
-
|
30
|
+
query_type.define_method(
|
31
|
+
"list_#{model.name.pluralize.downcase}"
|
32
|
+
) do |**args|
|
33
|
+
Hai::Read.new(model, context).list(
|
34
|
+
**args.transform_values do |v|
|
35
|
+
[Integer, String].include?(v.class) ? v : v.to_h
|
36
|
+
end
|
37
|
+
)
|
31
38
|
end
|
32
39
|
end
|
33
40
|
end
|
@@ -8,17 +8,25 @@ module Hai
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def add_field(query_type, model)
|
11
|
-
query_type.field(
|
11
|
+
query_type.field(
|
12
|
+
"read_#{model.name.downcase}",
|
13
|
+
"Types::#{model}Type".constantize
|
14
|
+
) do
|
12
15
|
query_type.description("List a single #{model}.")
|
13
16
|
model.attribute_types.each do |attr, type|
|
14
|
-
argument(
|
17
|
+
argument(
|
18
|
+
attr,
|
19
|
+
Hai::GraphQL::AREL_TYPE_CAST[type.class] ||
|
20
|
+
Hai::GraphQL::Types::Arel::IntInputType,
|
21
|
+
required: false
|
22
|
+
)
|
15
23
|
end
|
16
24
|
end
|
17
25
|
end
|
18
26
|
|
19
27
|
def define_read_method(query_type, model)
|
20
28
|
query_type.define_method("read_#{model.name.downcase}") do |**args|
|
21
|
-
Hai::Read.new(model).read(args.transform_values(&:to_h))
|
29
|
+
Hai::Read.new(model, context).read(args.transform_values(&:to_h))
|
22
30
|
end
|
23
31
|
end
|
24
32
|
end
|