ibrain-core 0.4.6 → 0.4.7
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/app/controllers/ibrain/base_controller.rb +14 -12
- data/app/controllers/ibrain/core/graphql_controller.rb +83 -79
- data/app/graphql/ibrain/base_schema.rb +52 -50
- data/app/graphql/ibrain/extentions/default_value.rb +11 -7
- data/app/graphql/ibrain/extentions/roles.rb +20 -15
- data/app/graphql/ibrain/extentions/session_required.rb +14 -10
- data/app/graphql/ibrain/lazy/base.rb +6 -2
- data/app/graphql/ibrain/loaders/association_loader.rb +55 -51
- data/app/graphql/ibrain/mutations/base_mutation.rb +56 -52
- data/app/graphql/ibrain/policies/base_policy.rb +47 -43
- data/app/graphql/ibrain/policies/graphql_policy.rb +6 -2
- data/app/graphql/ibrain/resolvers/base_aggregate.rb +10 -6
- data/app/graphql/ibrain/resolvers/base_resolver.rb +12 -8
- data/app/graphql/ibrain/types/aggregate_type.rb +8 -4
- data/app/graphql/ibrain/types/attribute_type.rb +5 -2
- data/app/graphql/ibrain/types/base_api_connection.rb +10 -6
- data/app/graphql/ibrain/types/base_api_edge.rb +8 -4
- data/app/graphql/ibrain/types/base_api_field.rb +11 -7
- data/app/graphql/ibrain/types/base_api_object.rb +10 -6
- data/app/graphql/ibrain/types/base_argument.rb +7 -3
- data/app/graphql/ibrain/types/base_connection.rb +11 -7
- data/app/graphql/ibrain/types/base_edge.rb +7 -3
- data/app/graphql/ibrain/types/base_enum.rb +5 -1
- data/app/graphql/ibrain/types/base_field.rb +10 -6
- data/app/graphql/ibrain/types/base_input_object.rb +10 -6
- data/app/graphql/ibrain/types/base_interface.rb +9 -5
- data/app/graphql/ibrain/types/base_object.rb +16 -12
- data/app/graphql/ibrain/types/base_scalar.rb +5 -1
- data/app/graphql/ibrain/types/base_type.rb +10 -4
- data/app/graphql/ibrain/types/base_union.rb +7 -3
- data/app/graphql/ibrain/types/filter_type.rb +5 -1
- data/app/graphql/ibrain/types/node_type.rb +8 -4
- data/app/graphql/ibrain/util/field_combiner.rb +9 -5
- data/app/graphql/ibrain/util/query_combiner.rb +8 -4
- data/app/models/concerns/ibrain/soft_deletable.rb +8 -6
- data/app/models/concerns/ibrain/user_api_authentication.rb +16 -14
- data/app/models/concerns/ibrain/user_methods.rb +13 -11
- data/app/models/ibrain/ability.rb +32 -31
- data/app/models/ibrain/aggregate.rb +7 -5
- data/app/models/ibrain/application_record.rb +4 -2
- data/app/models/ibrain/legacy_user.rb +13 -7
- data/app/models/ibrain/role.rb +8 -6
- data/app/models/ibrain/role_user.rb +10 -8
- data/app/repositories/ibrain/base_repository.rb +11 -9
- data/lib/generators/ibrain/core/model_generator.rb +21 -17
- data/lib/generators/ibrain/graphql/core.rb +59 -55
- data/lib/generators/ibrain/graphql/mutation_generator.rb +66 -58
- data/lib/generators/ibrain/graphql/object_generator.rb +70 -58
- data/lib/generators/ibrain/graphql/resolver_generator.rb +25 -17
- data/lib/generators/ibrain/graphql/resolvers_generator.rb +67 -59
- data/lib/generators/ibrain/graphql/templates/aggregate.erb +7 -5
- data/lib/generators/ibrain/graphql/templates/input.erb +9 -5
- data/lib/generators/ibrain/graphql/templates/mutation.erb +26 -24
- data/lib/generators/ibrain/graphql/templates/object.erb +10 -6
- data/lib/generators/ibrain/graphql/templates/resolver.erb +11 -9
- data/lib/generators/ibrain/graphql/templates/resolvers.erb +10 -8
- data/lib/generators/ibrain/graphql/type_generator.rb +88 -84
- data/lib/generators/ibrain/install/install_generator.rb +140 -137
- data/lib/generators/ibrain/install/templates/graphql/types/mutation_type.rb.tt +15 -13
- data/lib/generators/ibrain/install/templates/graphql/types/query_type.rb.tt +13 -11
- data/lib/generators/ibrain/install/templates/rubocop.yml.tt +30 -83
- data/lib/ibrain/app_configuration.rb +36 -34
- data/lib/ibrain/core/class_constantizer.rb +33 -30
- data/lib/ibrain/core/controller_helpers/auth.rb +59 -53
- data/lib/ibrain/core/controller_helpers/current_host.rb +11 -5
- data/lib/ibrain/core/controller_helpers/response.rb +49 -43
- data/lib/ibrain/core/controller_helpers/strong_parameters.rb +15 -9
- data/lib/ibrain/core/engine.rb +9 -5
- data/lib/ibrain/core/environment.rb +9 -5
- data/lib/ibrain/core/environment_extension.rb +17 -13
- data/lib/ibrain/core/role_configuration.rb +54 -52
- data/lib/ibrain/core/validators/email.rb +17 -15
- data/lib/ibrain/core/version.rb +2 -2
- data/lib/ibrain/core/versioned_value.rb +65 -61
- data/lib/ibrain/encryptor.rb +20 -18
- data/lib/ibrain/logger.rb +15 -13
- data/lib/ibrain/permission_sets/base.rb +26 -22
- data/lib/ibrain/permission_sets/super_user.rb +7 -3
- data/lib/ibrain/permitted_attributes.rb +20 -18
- data/lib/ibrain/preferences/configuration.rb +137 -135
- data/lib/ibrain/preferences/preferable.rb +162 -158
- data/lib/ibrain/preferences/preferable_class_methods.rb +114 -112
- data/lib/ibrain/user_class_handle.rb +24 -22
- data/lib/ibrain_core.rb +1 -7
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '09278662f5dace559b2b93ed9a45f39ef9ca822643b31e0e50b44e923e1027dc'
|
|
4
|
+
data.tar.gz: 8f19875c9383f8235aaf53596978f7ab0fb568924a312d018e867fe5b4ae2957
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b68fc74e862182d9295200edb26eced011eccd9f7e6b93b994a8392127a9ecb29e1a4e26405238ce4e450e76b1b86ccbf54a5c3f334752fdae6a04999dc708a2
|
|
7
|
+
data.tar.gz: 31d6e746ca29c99508950469e01e36b98279098cd7d6b97220a1a77de568c0e10da90fa9b0d877fa16375d48852e386f2b25db7f80b1aeb02b2bb60266402de7
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
module Ibrain
|
|
4
|
+
class BaseController < Ibrain::Config.parent_controller.constantize
|
|
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
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
include IbrainErrors
|
|
12
|
+
include IbrainHandler
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
protected
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
def cryptor
|
|
17
|
+
Ibrain::Encryptor.new
|
|
18
|
+
end
|
|
17
19
|
end
|
|
18
|
-
end
|
|
20
|
+
end
|
|
@@ -1,99 +1,103 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
3
|
+
module Ibrain
|
|
4
|
+
module Core
|
|
5
|
+
class GraphqlController < ::Ibrain::BaseController
|
|
6
|
+
include Devise::Controllers::ScopedViews
|
|
7
|
+
|
|
8
|
+
before_action :map_user_class_to_request
|
|
9
|
+
|
|
10
|
+
helpers = %w(resource scope_name resource_name signed_in_resource
|
|
11
|
+
resource_class resource_params devise_mapping)
|
|
12
|
+
helper_method(*helpers)
|
|
13
|
+
|
|
14
|
+
def execute
|
|
15
|
+
query, variables, operation_name = normalize_entity
|
|
16
|
+
|
|
17
|
+
result = schema.execute(
|
|
18
|
+
query,
|
|
19
|
+
variables: variables,
|
|
20
|
+
context: {
|
|
21
|
+
session: session,
|
|
22
|
+
current_user: try_ibrain_current_user,
|
|
23
|
+
controller: self,
|
|
24
|
+
request: request
|
|
25
|
+
},
|
|
26
|
+
operation_name: operation_name,
|
|
27
|
+
max_depth: max_depth(operation_name)
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
render_json_ok(result['data'], nil, result['errors'])
|
|
31
|
+
end
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
protected
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
def normalize_entity
|
|
36
|
+
return [params[:query], prepare_variables(params[:variables]), params[:operationName]] if params[:variables].present?
|
|
37
|
+
return [params[:query], prepare_variables(params[:variables]), 'IntrospectionQuery'] if params[:query].try(:include?, 'IntrospectionQuery')
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
operations = prepare_variables(params[:operations])
|
|
40
|
+
query = operations['query'] || params[:query]
|
|
41
|
+
variables = operations['variables'] || params[:variables]
|
|
42
|
+
operation_name = operations['operationName'] || params[:operationName]
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
if params[:map].present?
|
|
45
|
+
JSON.parse(params[:map]).each do |k, arguments|
|
|
46
|
+
argument_name = arguments.try(:first).try(:split, '.').try(:second)
|
|
47
|
+
next if argument_name.blank?
|
|
46
48
|
|
|
47
|
-
|
|
49
|
+
file = params[k]
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
if variables[argument_name].blank?
|
|
52
|
+
variables[argument_name] = variables[argument_name].is_a?(Array) ? [file] : file
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
next
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
unless variables[argument_name].is_a?(Array)
|
|
58
|
+
variables[argument_name] = [variables[argument_name]]
|
|
59
|
+
end
|
|
54
60
|
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
variables[argument_name] = variables[argument_name].concat([file]).compact
|
|
62
|
+
end
|
|
57
63
|
end
|
|
58
64
|
|
|
59
|
-
|
|
65
|
+
[query, variables, operation_name]
|
|
60
66
|
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
[query, variables, operation_name]
|
|
64
|
-
end
|
|
65
67
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
# Handle variables in form data, JSON body, or a blank value
|
|
69
|
+
def prepare_variables(variables_param)
|
|
70
|
+
case variables_param
|
|
71
|
+
when String
|
|
72
|
+
if variables_param.present?
|
|
73
|
+
JSON.parse(variables_param) || {}
|
|
74
|
+
else
|
|
75
|
+
{}
|
|
76
|
+
end
|
|
77
|
+
when Hash
|
|
78
|
+
variables_param
|
|
79
|
+
when ActionController::Parameters
|
|
80
|
+
variables_param.to_unsafe_hash # GraphQLRuby will validate name and type of incoming variables.
|
|
81
|
+
when nil
|
|
82
|
+
{}
|
|
83
|
+
else
|
|
84
|
+
raise ArgumentError, "Unexpected parameter: #{variables_param}"
|
|
85
|
+
end
|
|
74
86
|
end
|
|
75
|
-
when Hash
|
|
76
|
-
variables_param
|
|
77
|
-
when ActionController::Parameters
|
|
78
|
-
variables_param.to_unsafe_hash # GraphQLRuby will validate name and type of incoming variables.
|
|
79
|
-
when nil
|
|
80
|
-
{}
|
|
81
|
-
else
|
|
82
|
-
raise ArgumentError, "Unexpected parameter: #{variables_param}"
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
87
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
def schema
|
|
89
|
+
Ibrain::Config.graphql_schema.safe_constantize
|
|
90
|
+
end
|
|
89
91
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
def map_user_class_to_request
|
|
93
|
+
return if request.env['devise.mapping'].present?
|
|
92
94
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
+
request.env['devise.mapping'] = Ibrain.user_class
|
|
96
|
+
end
|
|
95
97
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
+
def max_depth(operation_name)
|
|
99
|
+
operation_name == 'IntrospectionQuery' ? nil : Ibrain::Config.graphql_max_depth
|
|
100
|
+
end
|
|
101
|
+
end
|
|
98
102
|
end
|
|
99
|
-
end
|
|
103
|
+
end
|
|
@@ -1,53 +1,55 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
3
|
+
module Ibrain
|
|
4
|
+
class BaseSchema < ::GraphQL::Schema
|
|
5
|
+
use GraphQL::Batch
|
|
6
|
+
|
|
7
|
+
max_depth Ibrain::Config.graphql_max_depth
|
|
8
|
+
query_analyzer(Ibrain::LogQueryDepth)
|
|
9
|
+
|
|
10
|
+
# use GraphQL::Guard.new(
|
|
11
|
+
# policy_object: ::Ibrain::Config.graphql_policy.safe_constantize,
|
|
12
|
+
# not_authorized: ->(type, field) { raise IbrainErrors::PermissionError.new("You not have permission to access #{type}.#{field}") }
|
|
13
|
+
# )
|
|
14
|
+
|
|
15
|
+
# Union and Interface Resolution
|
|
16
|
+
def self.resolve_type(_abstract_type, _obj, _ctx)
|
|
17
|
+
# TODO: Implement this function
|
|
18
|
+
# to return the correct object type for `obj`
|
|
19
|
+
raise(GraphQL::RequiredImplementationMissingError)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Relay-style Object Identification:
|
|
23
|
+
|
|
24
|
+
# Return a string UUID for `object`
|
|
25
|
+
def self.id_from_object(object, type_definition, query_ctx)
|
|
26
|
+
# Here's a simple implementation which:
|
|
27
|
+
# - joins the type name & object.id
|
|
28
|
+
# - encodes it with base64:
|
|
29
|
+
# GraphQL::Schema::UniqueWithinType.encode(type_definition.name, object.id)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Given a string UUID, find the object
|
|
33
|
+
def self.object_from_id(id, query_ctx)
|
|
34
|
+
# For example, to decode the UUIDs generated above:
|
|
35
|
+
# type_name, item_id = GraphQL::Schema::UniqueWithinType.decode(id)
|
|
36
|
+
#
|
|
37
|
+
# Then, based on `type_name` and `id`
|
|
38
|
+
# find an object in your application
|
|
39
|
+
# ...
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.field(*args, camelize: false, **kwargs, &block)
|
|
43
|
+
# if camelize == false
|
|
44
|
+
# # Also make a camelized field:
|
|
45
|
+
# field(*args, camelize: false, **kwargs, &block)
|
|
46
|
+
# end
|
|
47
|
+
super
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
rescue_from(ActiveRecord::RecordNotFound) do |_err, _obj, _args, _ctx, field|
|
|
51
|
+
# Raise a graphql-friendly error with a custom message
|
|
52
|
+
raise GraphQL::ExecutionError, "#{field.type.unwrap.graphql_name} not found"
|
|
53
|
+
end
|
|
19
54
|
end
|
|
20
|
-
|
|
21
|
-
# Relay-style Object Identification:
|
|
22
|
-
|
|
23
|
-
# Return a string UUID for `object`
|
|
24
|
-
def self.id_from_object(object, type_definition, query_ctx)
|
|
25
|
-
# Here's a simple implementation which:
|
|
26
|
-
# - joins the type name & object.id
|
|
27
|
-
# - encodes it with base64:
|
|
28
|
-
# GraphQL::Schema::UniqueWithinType.encode(type_definition.name, object.id)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# Given a string UUID, find the object
|
|
32
|
-
def self.object_from_id(id, query_ctx)
|
|
33
|
-
# For example, to decode the UUIDs generated above:
|
|
34
|
-
# type_name, item_id = GraphQL::Schema::UniqueWithinType.decode(id)
|
|
35
|
-
#
|
|
36
|
-
# Then, based on `type_name` and `id`
|
|
37
|
-
# find an object in your application
|
|
38
|
-
# ...
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def self.field(*args, camelize: false, **kwargs, &block)
|
|
42
|
-
# if camelize == false
|
|
43
|
-
# # Also make a camelized field:
|
|
44
|
-
# field(*args, camelize: false, **kwargs, &block)
|
|
45
|
-
# end
|
|
46
|
-
super
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
rescue_from(ActiveRecord::RecordNotFound) do |_err, _obj, _args, _ctx, field|
|
|
50
|
-
# Raise a graphql-friendly error with a custom message
|
|
51
|
-
raise GraphQL::ExecutionError, "#{field.type.unwrap.graphql_name} not found"
|
|
52
|
-
end
|
|
53
|
-
end
|
|
55
|
+
end
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
|
9
13
|
end
|
|
10
14
|
end
|
|
11
|
-
end
|
|
15
|
+
end
|
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
class Ibrain::Extentions::Roles < GraphQL::Schema::FieldExtension
|
|
3
|
-
def after_resolve(object:, value:, **_rest)
|
|
4
|
-
raise IbrainErrors::PermissionError.new("You not have permission to access #{field&.name}") if is_invalid_role(object)
|
|
5
2
|
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
module Ibrain
|
|
4
|
+
module Extentions
|
|
5
|
+
class Roles < GraphQL::Schema::FieldExtension
|
|
6
|
+
def after_resolve(object:, value:, **_rest)
|
|
7
|
+
raise IbrainErrors::PermissionError.new("You not have permission to access #{field&.name}") if is_invalid_role(object)
|
|
8
|
+
|
|
9
|
+
value
|
|
10
|
+
end
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
private
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
def is_invalid_role(object)
|
|
15
|
+
roles = options.try(:fetch, :roles, [])
|
|
16
|
+
current_user = object.try(:context).try(:fetch, :current_user, nil)
|
|
17
|
+
role = current_user.try(:role) || current_user.try(:graphql_role)
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
return if roles.blank?
|
|
20
|
+
return true if current_user.blank?
|
|
21
|
+
return false if roles.include?(role)
|
|
19
22
|
|
|
20
|
-
|
|
23
|
+
true
|
|
24
|
+
end
|
|
25
|
+
end
|
|
21
26
|
end
|
|
22
|
-
end
|
|
27
|
+
end
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
module Ibrain
|
|
4
|
+
module Extentions
|
|
5
|
+
class SessionRequired < GraphQL::Schema::FieldExtension
|
|
6
|
+
def resolve(object:, arguments:, **rest)
|
|
7
|
+
raise ActionController::InvalidAuthenticityToken, I18n.t('ibrain.errors.session.invalid_session') if is_invalid_session(object)
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
# yield the current time as `memo`
|
|
10
|
+
yield(object, arguments, rest)
|
|
11
|
+
end
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
private
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
def is_invalid_session(object)
|
|
16
|
+
object.try(:context).try(:fetch, :current_user, nil).blank? && options.try(:fetch, :session_required, false)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
15
19
|
end
|
|
16
|
-
end
|
|
20
|
+
end
|
|
@@ -2,56 +2,60 @@
|
|
|
2
2
|
|
|
3
3
|
# ref: https://zenn.dev/necocoa/articles/setup-graphql-batch
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
|
22
59
|
end
|
|
23
|
-
return Promise.resolve(read_association(record)) if association_loaded?(record)
|
|
24
|
-
|
|
25
|
-
super
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# We want to load the associations on all records, even if they have the same id
|
|
29
|
-
def cache_key(record)
|
|
30
|
-
record.object_id
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def perform(records)
|
|
34
|
-
preload_association(records)
|
|
35
|
-
records.each { |record| fulfill(record, read_association(record)) }
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
private
|
|
39
|
-
|
|
40
|
-
def validate
|
|
41
|
-
return if @model.reflect_on_association(@association_name)
|
|
42
|
-
|
|
43
|
-
raise ArgumentError, "No association #{@association_name} on #{@model}"
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def preload_association(records)
|
|
47
|
-
::ActiveRecord::Associations::Preloader.new.preload(records, @association_name)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def read_association(record)
|
|
51
|
-
record.public_send(@association_name)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def association_loaded?(record)
|
|
55
|
-
record.association(@association_name).loaded?
|
|
56
60
|
end
|
|
57
|
-
end
|
|
61
|
+
end
|