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