ibrain-core 0.1.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +75 -0
- data/Rakefile +7 -0
- data/app/controllers/concerns/ibrain_errors.rb +23 -0
- data/app/controllers/concerns/ibrain_handler.rb +42 -0
- data/app/controllers/ibrain/base_controller.rb +28 -0
- data/app/controllers/ibrain/graphql_controller.rb +55 -0
- data/app/graphql/ibrain/base_schema.rb +52 -0
- data/app/graphql/ibrain/extentions/default_value.rb +15 -0
- data/app/graphql/ibrain/interfaces/base_interface.rb +5 -0
- data/app/graphql/ibrain/interfaces/person_interface.rb +15 -0
- data/app/graphql/ibrain/interfaces/record_interface.rb +10 -0
- data/app/graphql/ibrain/lazy/base.rb +8 -0
- data/app/graphql/ibrain/loaders/association_loader.rb +61 -0
- data/app/graphql/ibrain/mutations/base_mutation.rb +35 -0
- data/app/graphql/ibrain/policies/base_policy.rb +45 -0
- data/app/graphql/ibrain/policies/graphql_policy.rb +8 -0
- data/app/graphql/ibrain/resolvers/base_aggregate.rb +9 -0
- data/app/graphql/ibrain/resolvers/base_resolver.rb +15 -0
- data/app/graphql/ibrain/types/aggregate_type.rb +9 -0
- data/app/graphql/ibrain/types/base_argument.rb +11 -0
- data/app/graphql/ibrain/types/base_connection.rb +16 -0
- data/app/graphql/ibrain/types/base_edge.rb +10 -0
- data/app/graphql/ibrain/types/base_enum.rb +8 -0
- data/app/graphql/ibrain/types/base_field.rb +15 -0
- data/app/graphql/ibrain/types/base_input_object.rb +9 -0
- data/app/graphql/ibrain/types/base_interface.rb +14 -0
- data/app/graphql/ibrain/types/base_node.rb +13 -0
- data/app/graphql/ibrain/types/base_object.rb +14 -0
- data/app/graphql/ibrain/types/base_query_type.rb +14 -0
- data/app/graphql/ibrain/types/base_scalar.rb +8 -0
- data/app/graphql/ibrain/types/base_union.rb +10 -0
- data/app/graphql/ibrain/types/filter_type.rb +8 -0
- data/app/graphql/ibrain/types/node_type.rb +11 -0
- data/app/graphql/ibrain/util/field_combiner.rb +13 -0
- data/app/graphql/ibrain/util/query_combiner.rb +13 -0
- data/app/graphql/mutations/insert_user.rb +18 -0
- data/app/models/ibrain/ability.rb +51 -0
- data/app/models/ibrain/application_record.rb +7 -0
- data/app/models/ibrain/base.rb +47 -0
- data/app/models/ibrain/concerns/ransackable_attributes.rb +22 -0
- data/app/models/ibrain/concerns/soft_deletable.rb +16 -0
- data/app/models/ibrain/concerns/user_api_authentication.rb +23 -0
- data/app/models/ibrain/concerns/user_methods.rb +25 -0
- data/app/models/ibrain/legacy_user.rb +19 -0
- data/app/models/ibrain/role.rb +14 -0
- data/app/models/ibrain/role_user.rb +18 -0
- data/config/initializers/friendly_id.rb +87 -0
- data/config/locales/en.yml +10 -0
- data/config/locales/jp.yml +10 -0
- data/config/locales/vi.yml +10 -0
- data/config/routes.rb +9 -0
- data/lib/generators/ibrain/graphql/core.rb +75 -0
- data/lib/generators/ibrain/graphql/mutation_generator.rb +58 -0
- data/lib/generators/ibrain/graphql/object_generator.rb +80 -0
- data/lib/generators/ibrain/graphql/resolver_generator.rb +33 -0
- data/lib/generators/ibrain/graphql/resolvers_generator.rb +59 -0
- data/lib/generators/ibrain/graphql/templates/aggregate.erb +10 -0
- data/lib/generators/ibrain/graphql/templates/mutation.erb +16 -0
- data/lib/generators/ibrain/graphql/templates/object.erb +11 -0
- data/lib/generators/ibrain/graphql/templates/resolver.erb +15 -0
- data/lib/generators/ibrain/graphql/templates/resolvers.erb +13 -0
- data/lib/generators/ibrain/graphql/type_generator.rb +101 -0
- data/lib/generators/ibrain/install/install_generator.rb +189 -0
- data/lib/generators/ibrain/install/templates/config/database.tt +23 -0
- data/lib/generators/ibrain/install/templates/config/initializers/cors.tt +25 -0
- data/lib/generators/ibrain/install/templates/config/initializers/ibrain.rb.tt +55 -0
- data/lib/generators/ibrain/install/templates/config/puma.tt +43 -0
- data/lib/generators/ibrain/install/templates/graphql/app_schema.rb.tt +4 -0
- data/lib/generators/ibrain/install/templates/graphql/types/mutation_type.rb.tt +10 -0
- data/lib/generators/ibrain/install/templates/graphql/types/query_type.rb.tt +13 -0
- data/lib/ibrain/app_configuration.rb +66 -0
- data/lib/ibrain/config.rb +5 -0
- data/lib/ibrain/core/class_constantizer.rb +41 -0
- data/lib/ibrain/core/controller_helpers/auth.rb +64 -0
- data/lib/ibrain/core/controller_helpers/current_host.rb +17 -0
- data/lib/ibrain/core/controller_helpers/response.rb +52 -0
- data/lib/ibrain/core/controller_helpers/strong_parameters.rb +21 -0
- data/lib/ibrain/core/engine.rb +16 -0
- data/lib/ibrain/core/environment.rb +17 -0
- data/lib/ibrain/core/environment_extension.rb +27 -0
- data/lib/ibrain/core/role_configuration.rb +72 -0
- data/lib/ibrain/core/validators/email.rb +23 -0
- data/lib/ibrain/core/version.rb +17 -0
- data/lib/ibrain/core/versioned_value.rb +73 -0
- data/lib/ibrain/core.rb +86 -0
- data/lib/ibrain/encryptor.rb +27 -0
- data/lib/ibrain/i18n.rb +17 -0
- data/lib/ibrain/logger.rb +23 -0
- data/lib/ibrain/permission_sets/base.rb +33 -0
- data/lib/ibrain/permission_sets/super_user.rb +11 -0
- data/lib/ibrain/permission_sets.rb +4 -0
- data/lib/ibrain/permitted_attributes.rb +26 -0
- data/lib/ibrain/preferences/configuration.rb +170 -0
- data/lib/ibrain/preferences/preferable.rb +183 -0
- data/lib/ibrain/preferences/preferable_class_methods.rb +140 -0
- data/lib/ibrain/user_class_handle.rb +29 -0
- data/lib/ibrain_core.rb +3 -0
- data/lib/tasks/ibrain/auto_annotate_models.rake +61 -0
- data/lib/tasks/ibrain/core_tasks.rake +5 -0
- data/lib/tasks/ibrain/ridgepole.rake +37 -0
- metadata +293 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: e306b2238fba539d079c52bbb0c6b6bb2ff1092e9f950c4c6fd85065b2325fa3
|
|
4
|
+
data.tar.gz: d9ae60e6b5769f14339bcdba97ed42538523c34aa9d382454496d0ac49f1d320
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 3ae2d1c79c5931769a432d6532f104c88245afb7df5dc3d0af557b3496b215548cbae686203f229c7d5bac7226000e39caad0ffe42fae0361fe19b1ca64f38df
|
|
7
|
+
data.tar.gz: 6b34268ff18f1eb0daafcd47d2b7a32f48360370ddc7367c18d5399048b69ca391758b4e0fdaa137a3a6f9d73d7cf02c84d828fe000a0ba7911d99d934dabe7e
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright 2021 TODO: Write your name
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Ibrain::Core
|
|
2
|
+
This is core plugin for rails api project
|
|
3
|
+
|
|
4
|
+
## Usage
|
|
5
|
+
How to use my plugin.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
Add this line to your application's Gemfile:
|
|
9
|
+
|
|
10
|
+
```ruby
|
|
11
|
+
gem 'ibrain-core'
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
And then execute:
|
|
15
|
+
```bash
|
|
16
|
+
$ bundle
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or install it yourself as:
|
|
20
|
+
```bash
|
|
21
|
+
$ gem install ibrain-core
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Please install plugin after install this gem
|
|
25
|
+
```bash
|
|
26
|
+
$ bundle exec rails generate ibrain:install
|
|
27
|
+
```
|
|
28
|
+
# Migration
|
|
29
|
+
Default migration engine is [ridgepole](https://github.com/ridgepole/ridgepole), using this command line to migrate database:
|
|
30
|
+
```bash
|
|
31
|
+
bundle exec rails ridgpole:apply
|
|
32
|
+
```
|
|
33
|
+
To use default migration plugin, please disable ridgepole
|
|
34
|
+
```bash
|
|
35
|
+
bundle exec rails generate ibrain::install --with-ridgepole=false
|
|
36
|
+
```
|
|
37
|
+
# GraphQL API
|
|
38
|
+
If you use graphql for rails please add this option when install plugin
|
|
39
|
+
```bash
|
|
40
|
+
$ bundle exec rails generate ibrain::install --with-graphql
|
|
41
|
+
```
|
|
42
|
+
To generate graphql type
|
|
43
|
+
```bash
|
|
44
|
+
bundle exec rails generate ibrain:graphql:object user
|
|
45
|
+
```
|
|
46
|
+
To generate graphql resolver single query to get user data
|
|
47
|
+
```bash
|
|
48
|
+
bundle exec rails generate ibrain:graphql:resolver user --model=User
|
|
49
|
+
```
|
|
50
|
+
To generate graphql resolvers query to get users list
|
|
51
|
+
```bash
|
|
52
|
+
bundle exec rails generate ibrain:graphql:resolvers users --model=User
|
|
53
|
+
```
|
|
54
|
+
For pagination please using aggregate body query, something like
|
|
55
|
+
```
|
|
56
|
+
query users($offset: Int, $limit: Int, $filter: Filter) {
|
|
57
|
+
users(offset: $offset, limit: $limit, filter: $filter) {
|
|
58
|
+
id
|
|
59
|
+
first_name
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
users_aggregate(filter: $filter) {
|
|
63
|
+
total_count
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
To generate graphql mutation to insert, update, delete user
|
|
68
|
+
```bash
|
|
69
|
+
bundle exec rails generate ibrain:graphql:mutation insert_user --model=User
|
|
70
|
+
```
|
|
71
|
+
## Contributing
|
|
72
|
+
Contribution directions go here.
|
|
73
|
+
|
|
74
|
+
## License
|
|
75
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module IbrainErrors
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
class PermissionError < StandardError; end
|
|
7
|
+
|
|
8
|
+
class UnknownError < StandardError
|
|
9
|
+
def initialize(message)
|
|
10
|
+
super
|
|
11
|
+
|
|
12
|
+
@message = message
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def details
|
|
16
|
+
message
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
attr_accessor :message
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module IbrainHandler
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
rescue_from StandardError, with: :standard_handler
|
|
8
|
+
rescue_from ActionController::ParameterMissing, with: :parameter_handler
|
|
9
|
+
rescue_from ActiveRecord::RecordNotFound, with: :not_found_handler
|
|
10
|
+
rescue_from ActiveRecord::RecordInvalid, with: :bad_request_handler
|
|
11
|
+
rescue_from IbrainErrors::PermissionError, with: :permission_denied_handler
|
|
12
|
+
rescue_from IbrainErrors::UnknownError, with: :bad_request_handler
|
|
13
|
+
rescue_from ActionController::InvalidAuthenticityToken, with: :unauthorized_handler
|
|
14
|
+
rescue_from ActiveSupport::MessageVerifier::InvalidSignature, with: :unauthorized_handler
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def standard_handler(errors)
|
|
20
|
+
render_json_error(errors, 500)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def bad_request_handler(errors)
|
|
24
|
+
render_json_error(errors, 400)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def parameter_handler(errors)
|
|
28
|
+
render_json_error(errors, 400)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def not_found_handler(errors)
|
|
32
|
+
render_json_error(errors, 404)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def permission_denied_handler(errors)
|
|
36
|
+
render_json_error(errors, 405)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def unauthorized_handler(errors)
|
|
40
|
+
render_json_error(errors, 401)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ibrain
|
|
4
|
+
class BaseController < ActionController::API
|
|
5
|
+
include ActionController::Helpers
|
|
6
|
+
include Ibrain::Core::ControllerHelpers::Response
|
|
7
|
+
include Ibrain::Core::ControllerHelpers::StrongParameters
|
|
8
|
+
include Ibrain::Core::ControllerHelpers::CurrentHost
|
|
9
|
+
include Ibrain::Core::ControllerHelpers::Auth
|
|
10
|
+
|
|
11
|
+
include IbrainErrors
|
|
12
|
+
include IbrainHandler
|
|
13
|
+
|
|
14
|
+
protected
|
|
15
|
+
|
|
16
|
+
def operation_name
|
|
17
|
+
params[:operationName]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def skip_operations
|
|
21
|
+
[]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def cryptor
|
|
25
|
+
Ibrain::Encryptor.new
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ibrain
|
|
4
|
+
class GraphqlController < Ibrain::BaseController
|
|
5
|
+
def execute
|
|
6
|
+
query, variables, operation_name = normalize_entity
|
|
7
|
+
|
|
8
|
+
result = schema.execute(
|
|
9
|
+
query,
|
|
10
|
+
variables: variables,
|
|
11
|
+
context: {
|
|
12
|
+
session: session,
|
|
13
|
+
current_user: current_ibrain_user
|
|
14
|
+
},
|
|
15
|
+
operation_name: operation_name
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
render_json_ok(result['data'], nil, result['errors'])
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
protected
|
|
22
|
+
|
|
23
|
+
def normalize_entity
|
|
24
|
+
query = params[:query]
|
|
25
|
+
operation_name = params[:operationName]
|
|
26
|
+
variables = prepare_variables(params[:variables])
|
|
27
|
+
|
|
28
|
+
[query, variables, operation_name]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Handle variables in form data, JSON body, or a blank value
|
|
32
|
+
def prepare_variables(variables_param)
|
|
33
|
+
case variables_param
|
|
34
|
+
when String
|
|
35
|
+
if variables_param.present?
|
|
36
|
+
JSON.parse(variables_param) || {}
|
|
37
|
+
else
|
|
38
|
+
{}
|
|
39
|
+
end
|
|
40
|
+
when Hash
|
|
41
|
+
variables_param
|
|
42
|
+
when ActionController::Parameters
|
|
43
|
+
variables_param.to_unsafe_hash # GraphQLRuby will validate name and type of incoming variables.
|
|
44
|
+
when nil
|
|
45
|
+
{}
|
|
46
|
+
else
|
|
47
|
+
raise ArgumentError, "Unexpected parameter: #{variables_param}"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def schema
|
|
52
|
+
Ibrain::Config.graphql_schema.safe_constantize
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ibrain
|
|
4
|
+
class BaseSchema < ::GraphQL::Schema
|
|
5
|
+
use GraphQL::Batch
|
|
6
|
+
|
|
7
|
+
use GraphQL::Guard.new(
|
|
8
|
+
policy_object: ::Ibrain::Config.graphql_policy.safe_constantize,
|
|
9
|
+
not_authorized: ->(type, field) { GraphQL::ExecutionError.new("Not authorized to access #{type}.#{field}") }
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
# Union and Interface Resolution
|
|
13
|
+
def self.resolve_type(_abstract_type, _obj, _ctx)
|
|
14
|
+
# TODO: Implement this function
|
|
15
|
+
# to return the correct object type for `obj`
|
|
16
|
+
raise(GraphQL::RequiredImplementationMissingError)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Relay-style Object Identification:
|
|
20
|
+
|
|
21
|
+
# Return a string UUID for `object`
|
|
22
|
+
def self.id_from_object(object, type_definition, query_ctx)
|
|
23
|
+
# Here's a simple implementation which:
|
|
24
|
+
# - joins the type name & object.id
|
|
25
|
+
# - encodes it with base64:
|
|
26
|
+
# GraphQL::Schema::UniqueWithinType.encode(type_definition.name, object.id)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Given a string UUID, find the object
|
|
30
|
+
def self.object_from_id(id, query_ctx)
|
|
31
|
+
# For example, to decode the UUIDs generated above:
|
|
32
|
+
# type_name, item_id = GraphQL::Schema::UniqueWithinType.decode(id)
|
|
33
|
+
#
|
|
34
|
+
# Then, based on `type_name` and `id`
|
|
35
|
+
# find an object in your application
|
|
36
|
+
# ...
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.field(*args, camelize: false, **kwargs, &block)
|
|
40
|
+
# if camelize == false
|
|
41
|
+
# # Also make a camelized field:
|
|
42
|
+
# field(*args, camelize: false, **kwargs, &block)
|
|
43
|
+
# end
|
|
44
|
+
super
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
rescue_from(ActiveRecord::RecordNotFound) do |_err, _obj, _args, _ctx, field|
|
|
48
|
+
# Raise a graphql-friendly error with a custom message
|
|
49
|
+
raise GraphQL::ExecutionError, "#{field.type.unwrap.graphql_name} not found"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ibrain
|
|
4
|
+
module Extentions
|
|
5
|
+
class DefaultValue < GraphQL::Schema::FieldExtension
|
|
6
|
+
def after_resolve(value:, **_rest)
|
|
7
|
+
if value.nil?
|
|
8
|
+
options[:default_value]
|
|
9
|
+
else
|
|
10
|
+
value
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ibrain::Interfaces::PersonInterface
|
|
4
|
+
include Ibrain::Types::BaseInterface
|
|
5
|
+
|
|
6
|
+
description 'Person Interface'
|
|
7
|
+
|
|
8
|
+
field :first_name, String, null: true
|
|
9
|
+
field :last_name, String, null: true
|
|
10
|
+
field :full_name, String, null: true
|
|
11
|
+
|
|
12
|
+
def full_name
|
|
13
|
+
[object.try(:first_name), object.try(:last_name)].compact.join(' ')
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ibrain::Interfaces::RecordInterface
|
|
4
|
+
include Ibrain::Types::BaseInterface
|
|
5
|
+
|
|
6
|
+
description 'Common Interface'
|
|
7
|
+
|
|
8
|
+
field :created_at, ::GraphQL::Types::ISO8601DateTime, null: false
|
|
9
|
+
field :updated_at, ::GraphQL::Types::ISO8601DateTime, null: false
|
|
10
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# ref: https://zenn.dev/necocoa/articles/setup-graphql-batch
|
|
4
|
+
|
|
5
|
+
module Ibrain
|
|
6
|
+
module Loaders
|
|
7
|
+
class AssociationLoader < GraphQL::Batch::Loader
|
|
8
|
+
def self.validate(model, association_name)
|
|
9
|
+
new(model, association_name)
|
|
10
|
+
nil
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def initialize(model, association_name)
|
|
14
|
+
super()
|
|
15
|
+
@model = model
|
|
16
|
+
@association_name = association_name
|
|
17
|
+
validate
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def load(record)
|
|
21
|
+
unless record.is_a?(@model)
|
|
22
|
+
raise TypeError,
|
|
23
|
+
"#{@model} loader can't load association for #{record.class}"
|
|
24
|
+
end
|
|
25
|
+
return Promise.resolve(read_association(record)) if association_loaded?(record)
|
|
26
|
+
|
|
27
|
+
super
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# We want to load the associations on all records, even if they have the same id
|
|
31
|
+
def cache_key(record)
|
|
32
|
+
record.object_id
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def perform(records)
|
|
36
|
+
preload_association(records)
|
|
37
|
+
records.each { |record| fulfill(record, read_association(record)) }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def validate
|
|
43
|
+
return if @model.reflect_on_association(@association_name)
|
|
44
|
+
|
|
45
|
+
raise ArgumentError, "No association #{@association_name} on #{@model}"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def preload_association(records)
|
|
49
|
+
::ActiveRecord::Associations::Preloader.new.preload(records, @association_name)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def read_association(record)
|
|
53
|
+
record.public_send(@association_name)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def association_loaded?(record)
|
|
57
|
+
record.association(@association_name).loaded?
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ibrain
|
|
4
|
+
module Mutations
|
|
5
|
+
class BaseMutation < GraphQL::Schema::RelayClassicMutation
|
|
6
|
+
argument_class Types::BaseArgument
|
|
7
|
+
field_class Types::BaseField
|
|
8
|
+
input_object_class Types::BaseInputObject
|
|
9
|
+
object_class Types::BaseObject
|
|
10
|
+
|
|
11
|
+
argument :attribute, Types::AttributeType, required: false
|
|
12
|
+
argument :attributes, [Types::AttributeType], required: false
|
|
13
|
+
|
|
14
|
+
def resolve(args)
|
|
15
|
+
@params = ::ActionController::Parameters.new(args)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
protected
|
|
19
|
+
|
|
20
|
+
attr_reader :params
|
|
21
|
+
|
|
22
|
+
def upload_permitted
|
|
23
|
+
%i[content_type headers original_filename tempfile]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def cryptor
|
|
27
|
+
::Ibrain::Encryptor.new
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def current_user
|
|
31
|
+
context[:current_user]
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ibrain
|
|
4
|
+
module Policies
|
|
5
|
+
class BasePolicy
|
|
6
|
+
IBRAIN_QUERY_RULES = {
|
|
7
|
+
'*': {
|
|
8
|
+
guard: ->(_obj, _args, _ctx) { true }
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
IBRAIN_MUTATION_RULES = {
|
|
13
|
+
'*': {
|
|
14
|
+
guard: ->(_obj, _args, ctx) { roles.include?(ctx[:current_user].try(:role)) }
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
RULES = {
|
|
19
|
+
'Query' => IBRAIN_QUERY_RULES,
|
|
20
|
+
'Mutation' => IBRAIN_MUTATION_RULES
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
23
|
+
class << self
|
|
24
|
+
def roles
|
|
25
|
+
Ibrain::Config.ibrain_roles
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def has_permission?(current_user, resource)
|
|
29
|
+
return false if current_user.blank?
|
|
30
|
+
return true if current_user.super_admin?
|
|
31
|
+
|
|
32
|
+
current_user.try(:scope).to_s.split(',').include?(resource)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def guard(type, field)
|
|
36
|
+
RULES.dig(type.name, field, :guard)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def not_authorized_handler(type, field)
|
|
40
|
+
RULES.dig(type, field, :not_authorized) || RULES.dig(type, :*, :not_authorized)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ibrain
|
|
4
|
+
module Resolvers
|
|
5
|
+
class BaseResolver < GraphQL::Schema::Resolver
|
|
6
|
+
argument :filter, Ibrain::Types::FilterType, required: false, default_value: nil
|
|
7
|
+
argument :offset, Int, required: false, default_value: 0
|
|
8
|
+
argument :limit, Int, required: false, default_value: 10
|
|
9
|
+
|
|
10
|
+
def current_user
|
|
11
|
+
context.fetch(:current_user)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ibrain
|
|
4
|
+
module Types
|
|
5
|
+
class BaseConnection < Types::BaseObject
|
|
6
|
+
# add `nodes` and `pageInfo` fields, as well as `edge_type(...)` and `node_nullable(...)` overrides
|
|
7
|
+
include GraphQL::Types::Relay::ConnectionBehaviors
|
|
8
|
+
|
|
9
|
+
field :total_count, Integer, null: false, camelize: false
|
|
10
|
+
|
|
11
|
+
def total_count
|
|
12
|
+
object.items.size
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ibrain
|
|
4
|
+
module Types
|
|
5
|
+
class BaseField < GraphQL::Schema::Field
|
|
6
|
+
argument_class ::Ibrain::Types::BaseArgument
|
|
7
|
+
|
|
8
|
+
def initialize(*args, default_value: nil, **kwargs, &block)
|
|
9
|
+
super(*args, camelize: false, **kwargs, &block)
|
|
10
|
+
|
|
11
|
+
extension(::Ibrain::Extentions::DefaultValue, default_value: default_value) unless default_value.nil?
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ibrain
|
|
4
|
+
module Types
|
|
5
|
+
module BaseInterface
|
|
6
|
+
include GraphQL::Schema::Interface
|
|
7
|
+
|
|
8
|
+
edge_type_class(Ibrain::Types::BaseEdge)
|
|
9
|
+
connection_type_class(Ibrain::Types::BaseConnection)
|
|
10
|
+
|
|
11
|
+
field_class Ibrain::Types::BaseField
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Ibrain::Types::BaseNode < Types::BaseObject
|
|
4
|
+
implements ::GraphQL::Relay::Node.interface
|
|
5
|
+
implements ::Ibrain::Interfaces::RecordInterface
|
|
6
|
+
|
|
7
|
+
global_id_field :id
|
|
8
|
+
field_class ::Ibrain::Types::BaseField
|
|
9
|
+
|
|
10
|
+
def self.field(*args, camelize: false, **kwargs, &block)
|
|
11
|
+
super
|
|
12
|
+
end
|
|
13
|
+
end
|