api-blocks 0.7.0 → 0.8.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.
@@ -8,27 +8,33 @@ require 'rails/generators/active_record'
8
8
  #
9
9
  # @private
10
10
  #
11
- class ApiBlocks::Doorkeeper::Invitations::MigrationGenerator < ::Rails::Generators::Base # rubocop:disable Metrics/LineLength
12
- include ::Rails::Generators::Migration
11
+ module ApiBlocks
12
+ module Doorkeeper
13
+ module Invitations
14
+ class MigrationGenerator < ::Rails::Generators::Base
15
+ include ::Rails::Generators::Migration
13
16
 
14
- source_root File.expand_path('templates', __dir__)
15
- desc 'Installs doorkeeper invitations api migrations'
17
+ source_root File.expand_path('templates', __dir__)
18
+ desc 'Installs doorkeeper invitations api migrations'
16
19
 
17
- def install
18
- migration_template(
19
- 'migration.rb.erb',
20
- 'db/migrate/add_invitation_uri_to_doorkeeper_applications.rb',
21
- migration_version: migration_version
22
- )
23
- end
20
+ def install
21
+ migration_template(
22
+ 'migration.rb.erb',
23
+ 'db/migrate/add_invitation_uri_to_doorkeeper_applications.rb',
24
+ migration_version: migration_version
25
+ )
26
+ end
24
27
 
25
- def self.next_migration_number(dirname)
26
- ActiveRecord::Generators::Base.next_migration_number(dirname)
27
- end
28
+ def self.next_migration_number(dirname)
29
+ ActiveRecord::Generators::Base.next_migration_number(dirname)
30
+ end
28
31
 
29
- private
32
+ private
30
33
 
31
- def migration_version
32
- "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
34
+ def migration_version
35
+ "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
36
+ end
37
+ end
38
+ end
33
39
  end
34
40
  end
@@ -1,10 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # ApiBlocks::Doorkeeper::Passwords implements an API reset password workflow.
4
- module ApiBlocks::Doorkeeper::Passwords
5
- extend ActiveSupport::Autoload
4
+ module ApiBlocks
5
+ module Doorkeeper
6
+ module Passwords
7
+ extend ActiveSupport::Autoload
6
8
 
7
- autoload :Controller
8
- autoload :User
9
- autoload :Application
9
+ autoload :Controller
10
+ autoload :User
11
+ autoload :Application
12
+ end
13
+ end
10
14
  end
@@ -8,10 +8,16 @@
8
8
  #
9
9
  # @private
10
10
  #
11
- module ApiBlocks::Doorkeeper::Passwords::Application
12
- extend ActiveSupport::Concern
11
+ module ApiBlocks
12
+ module Doorkeeper
13
+ module Passwords
14
+ module Application
15
+ extend ActiveSupport::Concern
13
16
 
14
- included do
15
- validates :reset_password_uri, "doorkeeper/redirect_uri": true
17
+ included do
18
+ validates :reset_password_uri, "doorkeeper/redirect_uri": true
19
+ end
20
+ end
21
+ end
16
22
  end
17
23
  end
@@ -53,107 +53,113 @@
53
53
  # end
54
54
  # end
55
55
  #
56
- module ApiBlocks::Doorkeeper::Passwords::Controller
57
- extend ActiveSupport::Concern
58
-
59
- included do # rubocop:disable Metrics/BlockLength
60
- # Skip pundit after action hooks because there is no authorization to
61
- # perform.
62
- skip_after_action :verify_authorized
63
- skip_after_action :verify_policy_scoped
64
-
65
- # Initialize the reset password workflow, sends a reset password email to
66
- # the user.
67
- def create
68
- user = user_model.send_reset_password_instructions(
69
- create_params, application: oauth_application
70
- )
71
-
72
- if successfully_sent?(user)
73
- render(status: :no_content)
74
- else
75
- respond_with(user)
56
+ module ApiBlocks
57
+ module Doorkeeper
58
+ module Passwords
59
+ module Controller
60
+ extend ActiveSupport::Concern
61
+
62
+ included do # rubocop:disable Metrics/BlockLength
63
+ # Skip pundit after action hooks because there is no authorization to
64
+ # perform.
65
+ skip_after_action :verify_authorized
66
+ skip_after_action :verify_policy_scoped
67
+
68
+ # Initialize the reset password workflow, sends a reset password email to
69
+ # the user.
70
+ def create
71
+ user = user_model.send_reset_password_instructions(
72
+ create_params, application: oauth_application
73
+ )
74
+
75
+ if successfully_sent?(user)
76
+ render(status: :no_content)
77
+ else
78
+ respond_with(user)
79
+ end
80
+ end
81
+
82
+ # Handles the redirection from the email towards the application's
83
+ # `redirect_uri`.
84
+ def callback
85
+ query = {
86
+ reset_password_token: params[:reset_password_token]
87
+ }.to_query
88
+
89
+ redirect_to(
90
+ "#{oauth_application.reset_password_uri}?#{query}"
91
+ )
92
+ end
93
+
94
+ # Updates the user password and returns a new Doorkeeper::AccessToken.
95
+ def update
96
+ user = user_model.reset_password_by_token(update_params)
97
+
98
+ return respond_with(user) unless user.errors.empty?
99
+
100
+ user.unlock_access! if unlockable?(user)
101
+
102
+ respond_with(Doorkeeper::OAuth::TokenResponse.new(
103
+ access_token(oauth_application, user)
104
+ ).body)
105
+ end
106
+
107
+ private
108
+
109
+ # Create permitted parameters
110
+ def create_params
111
+ params.require(:user).permit(:email)
112
+ end
113
+
114
+ # Update permitted parameters
115
+ def update_params
116
+ params.require(:user).permit(
117
+ :reset_password_token, :password
118
+ )
119
+ end
120
+
121
+ # Copied over from devise base controller in order to clear user errors if
122
+ # `Devise.paranoid` is active.
123
+ def successfully_sent?(user)
124
+ if Devise.paranoid
125
+ user.errors.clear
126
+ true
127
+ elsif user.errors.empty?
128
+ true
129
+ end
130
+ end
131
+
132
+ # Copied over from devise base controller in order to determine wether a ser
133
+ # is unlockable or not.
134
+ def unlockable?(resource)
135
+ resource.respond_to?(:unlock_access!) &&
136
+ resource.respond_to?(:unlock_strategy_enabled?) &&
137
+ resource.unlock_strategy_enabled?(:email)
138
+ end
139
+
140
+ # Returns a new access token for this user.
141
+ def access_token(application, user)
142
+ Doorkeeper::AccessToken.find_or_create_for(
143
+ application,
144
+ user.id,
145
+ Doorkeeper.configuration.default_scopes,
146
+ Doorkeeper.configuration.access_token_expires_in,
147
+ true
148
+ )
149
+ end
150
+
151
+ def oauth_application
152
+ @oauth_application ||= Doorkeeper::Application.find_by!(
153
+ uid: params[:client_id]
154
+ )
155
+ end
156
+
157
+ # Returns the user model class.
158
+ def user_model
159
+ raise 'the method `user_model` must be implemented on your password controller'
160
+ end
161
+ end
76
162
  end
77
163
  end
78
-
79
- # Handles the redirection from the email towards the application's
80
- # `redirect_uri`.
81
- def callback
82
- query = {
83
- reset_password_token: params[:reset_password_token]
84
- }.to_query
85
-
86
- redirect_to(
87
- "#{oauth_application.reset_password_uri}?#{query}"
88
- )
89
- end
90
-
91
- # Updates the user password and returns a new Doorkeeper::AccessToken.
92
- def update
93
- user = user_model.reset_password_by_token(update_params)
94
-
95
- return respond_with(user) unless user.errors.empty?
96
-
97
- user.unlock_access! if unlockable?(user)
98
-
99
- respond_with(Doorkeeper::OAuth::TokenResponse.new(
100
- access_token(oauth_application, user)
101
- ).body)
102
- end
103
-
104
- private
105
-
106
- # Create permitted parameters
107
- def create_params
108
- params.require(:user).permit(:email)
109
- end
110
-
111
- # Update permitted parameters
112
- def update_params
113
- params.require(:user).permit(
114
- :reset_password_token, :password
115
- )
116
- end
117
-
118
- # Copied over from devise base controller in order to clear user errors if
119
- # `Devise.paranoid` is active.
120
- def successfully_sent?(user)
121
- if Devise.paranoid
122
- user.errors.clear
123
- true
124
- elsif user.errors.empty?
125
- true
126
- end
127
- end
128
-
129
- # Copied over from devise base controller in order to determine wether a ser
130
- # is unlockable or not.
131
- def unlockable?(resource)
132
- resource.respond_to?(:unlock_access!) &&
133
- resource.respond_to?(:unlock_strategy_enabled?) &&
134
- resource.unlock_strategy_enabled?(:email)
135
- end
136
-
137
- # Returns a new access token for this user.
138
- def access_token(application, user)
139
- Doorkeeper::AccessToken.find_or_create_for(
140
- application,
141
- user.id,
142
- Doorkeeper.configuration.default_scopes,
143
- Doorkeeper.configuration.access_token_expires_in,
144
- true
145
- )
146
- end
147
-
148
- def oauth_application
149
- @oauth_application ||= Doorkeeper::Application.find_by!(
150
- uid: params[:client_id]
151
- )
152
- end
153
-
154
- # Returns the user model class.
155
- def user_model
156
- raise 'the method `user_model` must be implemented on your password controller' # rubocop:disable Metrics/LineLength
157
- end
158
164
  end
159
165
  end
@@ -8,27 +8,33 @@ require 'rails/generators/active_record'
8
8
  #
9
9
  # @private
10
10
  #
11
- class ApiBlocks::Doorkeeper::Passwords::MigrationGenerator < ::Rails::Generators::Base # rubocop:disable Metrics/LineLength
12
- include ::Rails::Generators::Migration
11
+ module ApiBlocks
12
+ module Doorkeeper
13
+ module Passwords
14
+ class MigrationGenerator < ::Rails::Generators::Base
15
+ include ::Rails::Generators::Migration
13
16
 
14
- source_root File.expand_path('templates', __dir__)
15
- desc 'Installs doorkeeper passwords api migrations'
17
+ source_root File.expand_path('templates', __dir__)
18
+ desc 'Installs doorkeeper passwords api migrations'
16
19
 
17
- def install
18
- migration_template(
19
- 'migration.rb.erb',
20
- 'db/migrate/add_reset_password_uri_to_doorkeeper_applications.rb',
21
- migration_version: migration_version
22
- )
23
- end
20
+ def install
21
+ migration_template(
22
+ 'migration.rb.erb',
23
+ 'db/migrate/add_reset_password_uri_to_doorkeeper_applications.rb',
24
+ migration_version: migration_version
25
+ )
26
+ end
24
27
 
25
- def self.next_migration_number(dirname)
26
- ActiveRecord::Generators::Base.next_migration_number(dirname)
27
- end
28
+ def self.next_migration_number(dirname)
29
+ ActiveRecord::Generators::Base.next_migration_number(dirname)
30
+ end
28
31
 
29
- private
32
+ private
30
33
 
31
- def migration_version
32
- "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
34
+ def migration_version
35
+ "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
36
+ end
37
+ end
38
+ end
33
39
  end
34
40
  end
@@ -9,42 +9,46 @@
9
9
  # include ApiBlocks::Doorkeeper::Passwords::User
10
10
  # end
11
11
  #
12
- module ApiBlocks::Doorkeeper::Passwords::User
13
- extend ActiveSupport::Concern
12
+ module ApiBlocks
13
+ module Doorkeeper
14
+ module Passwords
15
+ module User
16
+ extend ActiveSupport::Concern
14
17
 
15
- included do
16
- # Resets reset password token and send reset password instructions by email.
17
- # Returns the token sent in the e-mail.
18
- def send_reset_password_instructions(application = nil)
19
- token = set_reset_password_token
20
- send_reset_password_instructions_notification(token, application)
18
+ included do
19
+ # Resets reset password token and send reset password instructions by email.
20
+ # Returns the token sent in the e-mail.
21
+ def send_reset_password_instructions(application = nil)
22
+ token = set_reset_password_token
23
+ send_reset_password_instructions_notification(token, application)
21
24
 
22
- token
23
- end
25
+ token
26
+ end
24
27
 
25
- protected
28
+ protected
26
29
 
27
- def send_reset_password_instructions_notification(token, application = nil)
28
- send_devise_notification(
29
- :reset_password_instructions, token, application
30
- )
31
- end
32
- end
30
+ def send_reset_password_instructions_notification(token, application = nil)
31
+ send_devise_notification(
32
+ :reset_password_instructions, token, application
33
+ )
34
+ end
35
+ end
33
36
 
34
- class_methods do
35
- # Attempt to find a user by its email. If a record is found, send new
36
- # password instructions to it. If user is not found, returns a new user
37
- # with an email not found error.
38
- # Attributes must contain the user's email
39
- def send_reset_password_instructions(attributes = {}, application: nil)
40
- recoverable = find_or_initialize_with_errors(
41
- reset_password_keys, attributes, :not_found
42
- )
37
+ class_methods do
38
+ # Attempt to find a user by its email. If a record is found, send new
39
+ # password instructions to it. If user is not found, returns a new user
40
+ # with an email not found error.
41
+ # Attributes must contain the user's email
42
+ def send_reset_password_instructions(attributes = {}, application: nil)
43
+ recoverable = find_or_initialize_with_errors(
44
+ reset_password_keys, attributes, :not_found
45
+ )
43
46
 
44
- if recoverable.persisted?
45
- recoverable.send_reset_password_instructions(application)
47
+ recoverable.send_reset_password_instructions(application) if recoverable.persisted?
48
+ recoverable
49
+ end
50
+ end
46
51
  end
47
- recoverable
48
52
  end
49
53
  end
50
54
  end
@@ -36,88 +36,90 @@ require 'dry/validation'
36
36
  # end
37
37
  # end
38
38
  #
39
- class ApiBlocks::Interactor
40
- include Dry::Transaction
39
+ module ApiBlocks
40
+ class Interactor
41
+ include Dry::Transaction
41
42
 
42
- class << self
43
- attr_accessor :input_schema
44
- end
43
+ class << self
44
+ attr_accessor :input_schema
45
+ end
45
46
 
46
- # Define a contract for the input of this interactor using
47
- # `dry-validation`
48
- #
49
- # @example
50
- #
51
- # class FooInteractor < ApiBlocks::Interactor
52
- # input do
53
- # schema do
54
- # required(:bar).filled
55
- # end
56
- # end
57
- #
58
- # step :validate_input!
59
- # end
60
- #
61
- def self.input(&block)
62
- @input_schema = Class.new(Dry::Validation::Contract, &block).new
63
- end
47
+ # Define a contract for the input of this interactor using
48
+ # `dry-validation`
49
+ #
50
+ # @example
51
+ #
52
+ # class FooInteractor < ApiBlocks::Interactor
53
+ # input do
54
+ # schema do
55
+ # required(:bar).filled
56
+ # end
57
+ # end
58
+ #
59
+ # step :validate_input!
60
+ # end
61
+ #
62
+ def self.input(&block)
63
+ @input_schema = Class.new(Dry::Validation::Contract, &block).new
64
+ end
64
65
 
65
- # Call the interactor with its arguments.
66
- #
67
- # @example
68
- #
69
- # InviteUser.call(
70
- # email: "foo@example.com",
71
- # first_name: "Foo",
72
- # last_name: "Bar"
73
- # )
74
- #
75
- def self.call(*args)
76
- new.call(*args)
77
- end
66
+ # Call the interactor with its arguments.
67
+ #
68
+ # @example
69
+ #
70
+ # InviteUser.call(
71
+ # email: "foo@example.com",
72
+ # first_name: "Foo",
73
+ # last_name: "Bar"
74
+ # )
75
+ #
76
+ def self.call(*args)
77
+ new.call(*args)
78
+ end
78
79
 
79
- # Call the interactor with additional step arguments.
80
- #
81
- # @example
82
- #
83
- # InviteUser.with_step_args(deliver_invitation: [mailer: UserMailer])
84
- #
85
- def self.with_step_args(*args)
86
- new.with_step_args(*args)
87
- end
80
+ # Call the interactor with additional step arguments.
81
+ #
82
+ # @example
83
+ #
84
+ # InviteUser.with_step_args(deliver_invitation: [mailer: UserMailer])
85
+ #
86
+ def self.with_step_args(*args)
87
+ new.with_step_args(*args)
88
+ end
88
89
 
89
- protected
90
+ protected
90
91
 
91
- # Validates input with the class attribute `schema` if it is
92
- # defined.
93
- #
94
- # Add this step to your interactor if you want to validate its input.
95
- #
96
- def validate_input!(input)
97
- return Success(input) unless self.class.input_schema
92
+ # Validates input with the class attribute `schema` if it is
93
+ # defined.
94
+ #
95
+ # Add this step to your interactor if you want to validate its input.
96
+ #
97
+ def validate_input!(input)
98
+ return Success(input) unless self.class.input_schema
98
99
 
99
- result = self.class.input_schema.call(input)
100
+ result = self.class.input_schema.call(input)
100
101
 
101
- if result.success?
102
- Success(result.values)
103
- else
104
- Failure(result)
102
+ if result.success?
103
+ Success(result.values)
104
+ else
105
+ Failure(result)
106
+ end
105
107
  end
106
- end
107
108
 
108
- # Wraps the steps inside an AR transaction.
109
- #
110
- # Add this step to your interactor if you want to wrap its operations inside a
111
- # database transaction
112
- #
113
- def database_transaction!(input)
114
- result = nil
109
+ # Wraps the steps inside an AR transaction.
110
+ #
111
+ # Add this step to your interactor if you want to wrap its operations inside a
112
+ # database transaction
113
+ #
114
+ def database_transaction!(input)
115
+ result = nil
115
116
 
116
- ActiveRecord::Base.transaction do
117
- result = yield(Success(input))
118
- raise ActiveRecord::Rollback if result.failure?
119
- end
117
+ ActiveRecord::Base.transaction do
118
+ result = yield(Success(input))
119
+ raise ActiveRecord::Rollback if result.failure?
120
+ end
120
121
 
121
- result
122
+ result
123
+ end
122
124
  end
123
125
  end