ibrain-core 0.1.6 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f097ac075d8648653298078113ae7d36b8b9058b8a06dd93d450c6c1126ea1fc
4
- data.tar.gz: 9dcdccbb0202e258a67d1f7373e2abb329f47fc6e2ad94faccd832e489a19c82
3
+ metadata.gz: 1a04fdcab0c02b1f8a8d58a25dae8cd1bda74a4b296e2aeb219861df4464f008
4
+ data.tar.gz: baae07824103dcc3c80ab8228b154009242bca6648f3706ab9d5e18f743c9303
5
5
  SHA512:
6
- metadata.gz: 49610235d7470b0634732e8f47c40d12d6e80cfeff70fa676b586883e251b2a125367ee34232e49c79456910e679827b1597a16b8aabe1a4dc4266c6d10bbf81
7
- data.tar.gz: 137dbd8406dc08d92624cf78e0c8732b31b89507a39372cd0215d2ed6e3514d990f4ab90da097a45861cf351228e9c33af3bb9373561a2a9deab2790c2300e4c
6
+ metadata.gz: 4e1d2b42216cdffccbc7e5b1b103f9370c5dc8486a2a30ee5a902295a72c5d606e23881d110bb63715e105c2270834515c931ad49abbd6888437cf34ec97dda3
7
+ data.tar.gz: cdbc90d0ddaed7d02d76ad69061e84cbd7bc4b6dcfaf82ce4035f994189fbeece3c5452098393e4efdf85a4700b120ed1df8d25fea612fdcd4ce93b2e0b47694
data/README.md CHANGED
@@ -54,20 +54,34 @@ bundle exec rails generate ibrain:graphql:resolvers users --model=User
54
54
  For pagination please using aggregate body query, something like
55
55
  ```
56
56
  query users($offset: Int, $limit: Int, $filter: Filter) {
57
- users(offset: $offset, limit: $limit, filter: $filter) {
58
- id
59
- first_name
60
- }
57
+ users(offset: $offset, limit: $limit, filter: $filter) {
58
+ id
59
+ first_name
60
+ }
61
61
 
62
- users_aggregate(filter: $filter) {
63
- total_count
64
- }
62
+ users_aggregate(filter: $filter) {
63
+ total_count
64
+ }
65
65
  }
66
66
  ```
67
67
  To generate graphql mutation to insert, update, delete user
68
68
  ```bash
69
69
  bundle exec rails generate ibrain:graphql:mutation insert_user --model=User
70
70
  ```
71
+
72
+ Default all operation will be rejected if you not have Authorization Token at request header, so to skip authenticate please change `parent_controller` at `ibrain.rb`
73
+ ```
74
+ config.parent_controller = "<Your parent controller>"
75
+ ```
76
+ then create method skip_operations at this parent_controller
77
+ ```
78
+ class ApplicationController < BaseController::API
79
+ def skip_operations
80
+ %w[sign_in].include?(operation_name)
81
+ end
82
+ end
83
+ ```
84
+
71
85
  ## Contributing
72
86
  Contribution directions go here.
73
87
 
@@ -12,6 +12,7 @@ module IbrainHandler
12
12
  rescue_from IbrainErrors::UnknownError, with: :bad_request_handler
13
13
  rescue_from ActionController::InvalidAuthenticityToken, with: :unauthorized_handler
14
14
  rescue_from ActiveSupport::MessageVerifier::InvalidSignature, with: :unauthorized_handler
15
+ rescue_from GraphQL::ExecutionError, with: :bad_request_handler
15
16
  end
16
17
 
17
18
  private
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ibrain
4
- class BaseController < ActionController::API
4
+ class BaseController < Ibrain::Config.parent_controller.constantize
5
5
  include ActionController::Helpers
6
6
  include Ibrain::Core::ControllerHelpers::Response
7
7
  include Ibrain::Core::ControllerHelpers::StrongParameters
@@ -18,7 +18,7 @@ module Ibrain
18
18
  end
19
19
 
20
20
  def skip_operations
21
- []
21
+ super || ['IntrospectionQuery'].include?(operation_name)
22
22
  end
23
23
 
24
24
  def cryptor
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibrain
4
+ module Core
5
+ class GraphqlController < ::Ibrain::BaseController
6
+ include Devise::Controllers::ScopedViews
7
+
8
+ before_action :authenticate_user!, unless: :skip_operations
9
+ before_action :map_user_class_to_request
10
+
11
+ helpers = %w(resource scope_name resource_name signed_in_resource
12
+ resource_class resource_params devise_mapping)
13
+ helper_method(*helpers)
14
+
15
+ def execute
16
+ query, variables, operation_name = normalize_entity
17
+
18
+ result = schema.execute(
19
+ query,
20
+ variables: variables,
21
+ context: {
22
+ session: session,
23
+ current_user: try_ibrain_current_user,
24
+ controller: self,
25
+ request: request
26
+ },
27
+ operation_name: operation_name
28
+ )
29
+
30
+ render_json_ok(result['data'], nil, result['errors'])
31
+ end
32
+
33
+ protected
34
+
35
+ def normalize_entity
36
+ query = params[:query]
37
+ operation_name = params[:operationName]
38
+ variables = prepare_variables(params[:variables])
39
+
40
+ [query, variables, operation_name]
41
+ end
42
+
43
+ # Handle variables in form data, JSON body, or a blank value
44
+ def prepare_variables(variables_param)
45
+ case variables_param
46
+ when String
47
+ if variables_param.present?
48
+ JSON.parse(variables_param) || {}
49
+ else
50
+ {}
51
+ end
52
+ when Hash
53
+ variables_param
54
+ when ActionController::Parameters
55
+ variables_param.to_unsafe_hash # GraphQLRuby will validate name and type of incoming variables.
56
+ when nil
57
+ {}
58
+ else
59
+ raise ArgumentError, "Unexpected parameter: #{variables_param}"
60
+ end
61
+ end
62
+
63
+ def schema
64
+ Ibrain::Config.graphql_schema.safe_constantize
65
+ end
66
+
67
+ def map_user_class_to_request
68
+ return if request.env['devise.mapping'].present?
69
+
70
+ request.env['devise.mapping'] = Ibrain.user_class
71
+ end
72
+ end
73
+ end
74
+ end
@@ -6,7 +6,7 @@ module Ibrain
6
6
 
7
7
  use GraphQL::Guard.new(
8
8
  policy_object: ::Ibrain::Config.graphql_policy.safe_constantize,
9
- not_authorized: ->(type, field) { GraphQL::ExecutionError.new("Not authorized to access #{type}.#{field}") }
9
+ not_authorized: ->(type, field) { raise IbrainErrors::UnknownError.new("Not authorized to access #{type}.#{field}") }
10
10
  )
11
11
 
12
12
  # Union and Interface Resolution
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Ibrain
4
4
  module Lazy
5
- class Base < GraphQL::Function
5
+ class Base < GraphQL::Schema::Resolver
6
6
  end
7
7
  end
8
8
  end
@@ -30,6 +30,18 @@ module Ibrain
30
30
  def current_user
31
31
  context[:current_user]
32
32
  end
33
+
34
+ def controller
35
+ context[:controller]
36
+ end
37
+
38
+ def session
39
+ context[:session]
40
+ end
41
+
42
+ def request
43
+ context[:request]
44
+ end
33
45
  end
34
46
  end
35
47
  end
@@ -3,26 +3,32 @@
3
3
  module Ibrain
4
4
  module Policies
5
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
6
  class << self
7
+ def query_rules
8
+ {
9
+ '*': {
10
+ guard: ->(_obj, _args, _ctx) { false }
11
+ }
12
+ }
13
+ end
14
+
15
+ def mutation_rules
16
+ {
17
+ '*': {
18
+ guard: ->(_obj, _args, _ctx) { false }
19
+ }
20
+ }
21
+ end
22
+
23
+ def rules
24
+ {
25
+ 'Types::QueryType' => query_rules,
26
+ 'Types::MutationType' => mutation_rules
27
+ }.freeze
28
+ end
29
+
24
30
  def roles
25
- Ibrain::Config.ibrain_roles
31
+ Ibrain.user_class.roles.keys
26
32
  end
27
33
 
28
34
  def has_permission?(current_user, resource)
@@ -33,11 +39,11 @@ module Ibrain
33
39
  end
34
40
 
35
41
  def guard(type, field)
36
- RULES.dig(type.name, field, :guard)
42
+ rules.dig(type.name, field, :guard)
37
43
  end
38
44
 
39
45
  def not_authorized_handler(type, field)
40
- RULES.dig(type, field, :not_authorized) || RULES.dig(type, :*, :not_authorized)
46
+ rules.dig(type, field, :not_authorized) || rules.dig(type, :*, :not_authorized)
41
47
  end
42
48
  end
43
49
  end
@@ -10,6 +10,10 @@ module Ibrain
10
10
  def current_user
11
11
  context.fetch(:current_user)
12
12
  end
13
+
14
+ def controller
15
+ context[:controller]
16
+ end
13
17
  end
14
18
  end
15
19
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Ibrain::Types::BaseNode < Types::BaseObject
3
+ class Ibrain::Types::BaseNode < Ibrain::Types::BaseObject
4
4
  include ::GraphQL::Relay::Node
5
5
  implements ::Ibrain::Interfaces::RecordInterface
6
6
 
@@ -5,10 +5,8 @@ module Ibrain
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  include Ibrain::UserApiAuthentication
8
- include Ibrain::UserReporting
9
8
 
10
9
  included do
11
- extend Ibrain::DisplayMoney
12
10
  after_create :auto_generate_ibrain_api_key
13
11
 
14
12
  include Ibrain::RansackableAttributes unless included_modules.include?(Ibrain::RansackableAttributes)
@@ -6,7 +6,7 @@ module Ibrain
6
6
  # @note This class is intended to be modified by extensions (ex.
7
7
  # ibrain-auth)
8
8
  class LegacyUser < Ibrain::Base
9
- include UserMethods
9
+ include Ibrain::UserMethods
10
10
 
11
11
  self.table_name = 'ibrain_users'
12
12
 
data/config/routes.rb CHANGED
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  Ibrain::Core::Engine.routes.draw do
4
- if ::Ibrain::Config.api_version.blank?
5
- post '/api/graphql', to: 'graphql#execute'
6
- else
7
- post "/api/#{::Ibrain::Config.api_version.downcase}/graphql", controller: 'graphql', action: 'execute'
8
- end
4
+ post "/", controller: 'graphql', action: 'execute'
9
5
  end
@@ -7,7 +7,7 @@ module Resolvers
7
7
  # description
8
8
 
9
9
  # TODO: define return fields
10
- type Types::Objects::<%= model_name.capitalize %>Type, null: false, description: 'Define data type will be response to client'
10
+ type Types::Objects::<%= model_name.capitalize %>Type, null: false
11
11
 
12
12
  argument :id, ID, required: false, description: 'TODO: describe about this argument'
13
13
 
@@ -7,7 +7,7 @@ module Resolvers
7
7
  # description
8
8
 
9
9
  # TODO: define return fields
10
- type [Types::Objects::<%= model_name.capitalize %>Type], null: false, description: 'Define data type will be response to client'
10
+ type [Types::Objects::<%= model_name.capitalize %>Type], null: false
11
11
 
12
12
  # TODO: define resolve method
13
13
  def resolve(args)
@@ -41,6 +41,9 @@ Ibrain.config do |config|
41
41
 
42
42
  # Graphql Encryptor key
43
43
  # config.ibrain_encryptor_key = Rails.application.secrets.secret_key_base.byteslice(0..31)
44
+
45
+ # Parent controller
46
+ # config.parent_controller = 'ActionController::API'
44
47
  end
45
48
 
46
49
  <% if defined?(Ibrain::Api::Engine) -%>
@@ -48,6 +48,9 @@ module Ibrain
48
48
  # Graphql Encryptor key
49
49
  preference :ibrain_encryptor_key, :string, default: nil
50
50
 
51
+ # Parent controller
52
+ preference :parent_controller, :string, default: 'ActionController::API'
53
+
51
54
  def static_model_preferences
52
55
  @static_model_preferences ||= Ibrain::Preferences::StaticModelPreferences.new
53
56
  end
@@ -16,6 +16,7 @@ module Ibrain
16
16
  def render_json_error(error, status)
17
17
  e_message = error.try(:record).try(:errors).try(:full_messages).try(:first)
18
18
  e_message = error.try(:message) if e_message.blank?
19
+ e_message = error.try(:details) if e_message.blank?
19
20
 
20
21
  backtrace = error.try(:backtrace).try(:join, "\n")
21
22
 
@@ -5,7 +5,7 @@ require 'ibrain/config'
5
5
  module Ibrain
6
6
  module Core
7
7
  class Engine < ::Rails::Engine
8
- isolate_namespace Ibrain
8
+ isolate_namespace Ibrain::Core
9
9
  config.generators.api_only = true
10
10
 
11
11
  initializer "ibrain.environment", before: :load_config_initializers do |app|
@@ -1,14 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ibrain
4
- VERSION = "0.1.6"
4
+ VERSION = "0.2.0"
5
5
 
6
6
  def self.ibrain_version
7
7
  VERSION
8
8
  end
9
9
 
10
10
  def self.previous_ibrain_minor_version
11
- '0.1.5'
11
+ '0.1.9'
12
12
  end
13
13
 
14
14
  def self.ibrain_gem_version
data/lib/ibrain/core.rb CHANGED
@@ -14,6 +14,7 @@ require 'friendly_id'
14
14
  require 'kaminari/activerecord'
15
15
  require 'rack/cors'
16
16
  require 'ransack'
17
+ require 'discard'
17
18
 
18
19
  module Ibrain
19
20
  mattr_accessor :user_class
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ibrain-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tai Nguyen Van
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-30 00:00:00.000000000 Z
11
+ date: 2022-01-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord-session_store
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: 1.15.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: discard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.2'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.2'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: friendly_id
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -169,7 +183,7 @@ files:
169
183
  - app/controllers/concerns/ibrain_errors.rb
170
184
  - app/controllers/concerns/ibrain_handler.rb
171
185
  - app/controllers/ibrain/base_controller.rb
172
- - app/controllers/ibrain/graphql_controller.rb
186
+ - app/controllers/ibrain/core/graphql_controller.rb
173
187
  - app/graphql/ibrain/base_schema.rb
174
188
  - app/graphql/ibrain/extentions/default_value.rb
175
189
  - app/graphql/ibrain/interfaces/base_interface.rb
@@ -200,14 +214,13 @@ files:
200
214
  - app/graphql/ibrain/types/node_type.rb
201
215
  - app/graphql/ibrain/util/field_combiner.rb
202
216
  - app/graphql/ibrain/util/query_combiner.rb
203
- - app/graphql/mutations/insert_user.rb
217
+ - app/models/concerns/ibrain/ransackable_attributes.rb
218
+ - app/models/concerns/ibrain/soft_deletable.rb
219
+ - app/models/concerns/ibrain/user_api_authentication.rb
220
+ - app/models/concerns/ibrain/user_methods.rb
204
221
  - app/models/ibrain/ability.rb
205
222
  - app/models/ibrain/application_record.rb
206
223
  - app/models/ibrain/base.rb
207
- - app/models/ibrain/concerns/ransackable_attributes.rb
208
- - app/models/ibrain/concerns/soft_deletable.rb
209
- - app/models/ibrain/concerns/user_api_authentication.rb
210
- - app/models/ibrain/concerns/user_methods.rb
211
224
  - app/models/ibrain/legacy_user.rb
212
225
  - app/models/ibrain/role.rb
213
226
  - app/models/ibrain/role_user.rb
@@ -1,55 +0,0 @@
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: try_ibrain_current_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
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Ibrain
4
- module Mutations
5
- class InsertUser < Ibrain::Mutations::BaseMutation
6
- # TODO: define return fields
7
- # field :post, Types::PostType, null: false
8
-
9
- # TODO: define arguments
10
- # argument :name, String, required: true
11
-
12
- # TODO: define resolve method
13
- # def resolve(name:)
14
- # { post: ... }
15
- # end
16
- end
17
- end
18
- end