api-blocks 0.5.4 → 0.9.0

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