api-blocks 0.1.1 → 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: 2e93230922c56b224b46fc9e1dd0c907cae145922f1d33279381b09bcbef3dc0
4
- data.tar.gz: 0e14903ac71f1f4b013065e09a27072e6269ceddf4b065dc12a4b9bd1002796f
3
+ metadata.gz: 6612a52d3c9bd211ec55b833406c4fd53196d257161945ad0ddaad976f4b5ac6
4
+ data.tar.gz: 5f959035fe3f550d408efe650051ccb5869595cce7a999377b5cae688e752c51
5
5
  SHA512:
6
- metadata.gz: f326dc96844750dca45d10408fb9ec5ff9c1f1692b24d20895f84584aae1882eba07374d5c8be6566aff28ce94900a8c8683b42ed934b5be52f6c6a00a19bc52
7
- data.tar.gz: afc90a22ee62c2b3f75e523edf5bd854af9ccac15f916808ea08e8389e7429b9001c6403b7be829719d3ce93f897e385bb6697ea7888f7e943e91d87deb808ac
6
+ metadata.gz: 5a5496696e2e9176e444c0001a3061f71aadede9e8d0b9c8ac86e74d37d62c75a689a42b1e3009d92381b83c73883c91dcc310fb0e1a9afb487ff4a1d68d00df
7
+ data.tar.gz: 3db65f293c0adda35b590e5b49d85ffd04dc4ce1d02c63acb1f0587e5d0324231d7c150857573afaa0b2397d98c9494e0b5778c050fbf8d7e41b239d03d04514
data/lib/api-blocks.rb ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'api_blocks'
@@ -1,11 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # frozen_string_litreal: true
2
4
 
3
- require "pundit"
5
+ require 'pundit'
4
6
 
5
7
  # ApiBlocks::Controller provides a set of default configurations for
6
8
  # Ruby on Rails api controllers.
7
9
  #
8
- # It sets up `ApiBlocks::Responder` as a responder, `Pundit` and controller defaults.
10
+ # It sets up `ApiBlocks::Responder` as a responder, `Pundit` and controller
11
+ # defaults.
9
12
  #
10
13
  # @example
11
14
  #
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ # ApiBlocks::Doorkeeper::Passwords::Application adds `reset_password_uri`
4
+ # validation to `Doorkeeper::Application`.
5
+ #
6
+ # This module is automatically included on rails application startup if the
7
+ # passwords migrations have been ran.
8
+ #
9
+ # @private
10
+ #
11
+ module ApiBlocks::Doorkeeper::Passwords::Application
12
+ extend ActiveSupport::Concern
13
+
14
+ included do
15
+ validates :reset_password_uri, "doorkeeper/redirect_uri": true
16
+ end
17
+ end
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ # ApiBlocks::Doorkeeper::Passwords::Controller implements an API passwords reset
4
+ # workflow.
5
+ #
6
+ # @example
7
+ # # app/controllers/api/v1/passwords_controller.rb
8
+ # class Api::V1::PasswordsController < Api::V1::ApplicationController
9
+ # include ApiBlocks::Doorkeeper::Passwords::Controller
10
+ #
11
+ # private
12
+ #
13
+ # def user_model
14
+ # User
15
+ # end
16
+ # end
17
+ #
18
+ # @example
19
+ # # config/routes.rb
20
+ # Rails.application.routes.draw do
21
+ # scope module: :api do
22
+ # namespace :v1 do
23
+ # resources :passwords, only: %i[create] do
24
+ # get :callback, on: :collection
25
+ # put :update, on: :collection
26
+ # end
27
+ # end
28
+ # end
29
+ # end
30
+ #
31
+ # @example
32
+ # # app/models/user.rb
33
+ # class User < ApplicationRecord
34
+ # include ApiBlocks::Doorkeeper::Passwords::User
35
+ # end
36
+ #
37
+ # @example
38
+ # # config/initializers/devise.rb
39
+ # Devise.setup do |config|
40
+ # # Configure the class responsible to send e-mails.
41
+ # config.mailer = "DeviseMailer"
42
+ # end
43
+ #
44
+ # @example
45
+ # # app/mailers/devise_mailer.rb
46
+ #
47
+ # class DeviseMailer < Devise::Mailer
48
+ # def reset_password_instructions(
49
+ # record, token, application = nil, _opts = {}
50
+ # )
51
+ # @token = token
52
+ # @application = application
53
+ # end
54
+ # end
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
+ application = Doorkeeper::Application.find_by!(uid: params[:client_id])
69
+
70
+ user = user_model.send_reset_password_instructions(
71
+ create_params, application: application
72
+ )
73
+
74
+ if successfully_sent?(user)
75
+ render(status: :no_content)
76
+ else
77
+ respond_with(user)
78
+ end
79
+ end
80
+
81
+ # Handles the redirection from the email towards the application's
82
+ # `redirect_uri`.
83
+ def callback
84
+ application = Doorkeeper::Application.find_by!(uid: params[:client_id])
85
+
86
+ query = {
87
+ reset_password_token: params[:reset_password_token]
88
+ }.to_query
89
+
90
+ redirect_to(
91
+ "#{application.reset_password_uri}?#{query}"
92
+ )
93
+ end
94
+
95
+ # Updates the user password and returns a new Doorkeeper::AccessToken.
96
+ def update
97
+ application = Doorkeeper::Application.find_by!(uid: params[:client_id])
98
+ user = user_model.reset_password_by_token(update_params)
99
+
100
+ if user.errors.empty?
101
+ user.unlock_access! if unlockable?(user)
102
+
103
+ render json: access_token(application, user)
104
+ else
105
+ respond_with(user)
106
+ end
107
+ end
108
+
109
+ private
110
+
111
+ # Create permitted parameters
112
+ def create_params
113
+ params.require(:user).permit(:email)
114
+ end
115
+
116
+ # Update permitted parameters
117
+ def update_params
118
+ params.require(:user).permit(
119
+ :reset_password_token, :password
120
+ )
121
+ end
122
+
123
+ # Copied over from devise base controller in order to clear user errors if
124
+ # `Devise.paranoid` is active.
125
+ def successfully_sent?(user)
126
+ if Devise.paranoid
127
+ user.errors.clear
128
+ true
129
+ elsif user.errors.empty?
130
+ true
131
+ end
132
+ end
133
+
134
+ # Copied over from devise base controller in order to determine wether a ser
135
+ # is unlockable or not.
136
+ def unlockable?(resource)
137
+ resource.respond_to?(:unlock_access!) &&
138
+ resource.respond_to?(:unlock_strategy_enabled?) &&
139
+ resource.unlock_strategy_enabled?(:email)
140
+ end
141
+
142
+ # Returns a new access token for this user.
143
+ def access_token(application, user)
144
+ Doorkeeper::AccessToken.find_or_create_for(
145
+ application,
146
+ user.id,
147
+ Doorkeeper.configuration.default_scopes,
148
+ Doorkeeper.configuration.access_token_expires_in,
149
+ true
150
+ )
151
+ end
152
+
153
+ # Returns the user model class.
154
+ def user_model
155
+ raise 'the method `user_model` must be implemented on your password controller' # rubocop:disable Metrics/LineLength
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'rails/generators/active_record'
5
+
6
+ # ApiBlocks::Doorkeeper::Passwords::MigrationGenerator implements the Rails
7
+ # generator for doorkeeper passwords api migrations.
8
+ #
9
+ # @private
10
+ #
11
+ class ApiBlocks::Doorkeeper::Passwords::MigrationGenerator < ::Rails::Generators::Base # rubocop:disable Metrics/LineLength
12
+ include ::Rails::Generators::Migration
13
+
14
+ source_root File.expand_path('templates', __dir__)
15
+ desc 'Installs doorkeeper passwords api migrations'
16
+
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
24
+
25
+ def self.next_migration_number(dirname)
26
+ ActiveRecord::Generators::Base.next_migration_number(dirname)
27
+ end
28
+
29
+ private
30
+
31
+ def migration_version
32
+ "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
33
+ end
34
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ # ApiBlocks::Doorkeeper::Passwords::User overrides some methods from devise
4
+ # recoverable module to add the dorkeeper application to the mailer.
5
+ #
6
+ # @example
7
+ # # app/models/user.rb
8
+ # class User < ApplicationRecord
9
+ # include ApiBlocks::Doorkeeper::Passwords::User
10
+ # end
11
+ #
12
+ module ApiBlocks::Doorkeeper::Passwords::User
13
+ extend ActiveSupport::Concern
14
+
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
+ puts token
21
+ send_reset_password_instructions_notification(token, application)
22
+
23
+ token
24
+ end
25
+
26
+ protected
27
+
28
+ def send_reset_password_instructions_notification(token, application = nil)
29
+ send_devise_notification(
30
+ :reset_password_instructions, token, application
31
+ )
32
+ end
33
+ end
34
+
35
+ class_methods do
36
+ # Attempt to find a user by its email. If a record is found, send new
37
+ # password instructions to it. If user is not found, returns a new user
38
+ # with an email not found error.
39
+ # Attributes must contain the user's email
40
+ def send_reset_password_instructions(attributes = {}, application: nil)
41
+ recoverable = find_or_initialize_with_errors(
42
+ reset_password_keys, attributes, :not_found
43
+ )
44
+
45
+ if recoverable.persisted?
46
+ recoverable.send_reset_password_instructions(application)
47
+ end
48
+ recoverable
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # ApiBlocks::Doorkeeper::Passwords implements an API reset password workflow.
4
+ module ApiBlocks::Doorkeeper::Passwords
5
+ extend ActiveSupport::Autoload
6
+
7
+ autoload :Controller
8
+ autoload :User
9
+ autoload :Application
10
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # ApiBlocks::Doorkeeper implements API extensions for doorkeeper.
4
+ module ApiBlocks::Doorkeeper
5
+ extend ActiveSupport::Autoload
6
+
7
+ autoload :Passwords
8
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/transaction"
4
- require "dry/validation"
3
+ require 'dry/transaction'
4
+ require 'dry/validation'
5
5
 
6
6
  # ApiBlocks::Interactor implements a base interactor class.
7
7
  #
@@ -10,7 +10,7 @@ require "dry/validation"
10
10
  #
11
11
  # @example
12
12
  #
13
- # class InviteUser < ApplicationInteractor
13
+ # class InviteUser < ApiBlocks::Interactor
14
14
  # input do
15
15
  # schema do
16
16
  # required(:email).filled
@@ -48,7 +48,7 @@ class ApiBlocks::Interactor
48
48
  #
49
49
  # @example
50
50
  #
51
- # class FooInteractor < ApplicationInteractor
51
+ # class FooInteractor < ApiBlocks::Interactor
52
52
  # input do
53
53
  # schema do
54
54
  # required(:bar).filled
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # ApiBlocks::Railtie implements the Rails integration for ApiBlocks.
4
+ #
5
+ # @private
6
+ #
7
+ class ApiBlocks::Railtie < Rails::Railtie
8
+ config.after_initialize do
9
+ next unless defined?(Doorkeeper)
10
+
11
+ ActiveSupport.on_load(:active_record) do
12
+ # do not load the Doorkeeper::Application extensions if migrations have
13
+ # not been setup.
14
+ has_reset_password_uri = Doorkeeper::Application.columns.find do |col|
15
+ col.name == 'reset_password_uri'
16
+ end
17
+
18
+ next unless has_reset_password_uri
19
+
20
+ Doorkeeper::Application.include(
21
+ ApiBlocks::Doorkeeper::Passwords::Application
22
+ )
23
+ end
24
+ end
25
+
26
+ generators do
27
+ require_relative 'doorkeeper/passwords/migration_generator'
28
+ end
29
+ end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "action_controller/responder"
4
- require "responders"
5
- require "dry/monads"
3
+ require 'action_controller/responder'
4
+ require 'responders'
5
+ require 'dry/monads/result'
6
6
 
7
7
  # ApiBlocks::Responder provides a responder with better error handling and
8
8
  # `ApiBlocks::Interactor` through `Dry::Monads::Result` support.
@@ -27,7 +27,8 @@ class ApiBlocks::Responder < ActionController::Responder
27
27
  def to_format
28
28
  return super unless resource.is_a?(Dry::Monads::Result)
29
29
 
30
- # unwrap the result monad so it can be processed by ActionController::Responder
30
+ # unwrap the result monad so it can be processed by
31
+ # ActionController::Responder
31
32
  resource.fmap { |result| @resource = result }.or do |failure|
32
33
  @resource = failure
33
34
  @failure = true
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
- #
2
+
3
3
  module ApiBlocks
4
4
  # Current version of ApiBlocks
5
- VERSION = '0.1.1'.freeze
5
+ VERSION = '0.2.0'
6
6
  end
data/lib/api_blocks.rb CHANGED
@@ -7,7 +7,6 @@ require 'api_blocks/version'
7
7
  require 'active_support/concern'
8
8
  require 'active_support/dependencies/autoload'
9
9
 
10
-
11
10
  # ApiBlocks provides simple and consistent rails api extensions.
12
11
  module ApiBlocks
13
12
  extend ActiveSupport::Autoload
@@ -15,4 +14,7 @@ module ApiBlocks
15
14
  autoload :Controller
16
15
  autoload :Responder
17
16
  autoload :Interactor
17
+ autoload :Doorkeeper
18
18
  end
19
+
20
+ require 'api_blocks/railtie' if defined?(Rails)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: api-blocks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul d'Hubert
@@ -25,35 +25,35 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: 3.0.0
27
27
  - !ruby/object:Gem::Dependency
28
- name: rails
28
+ name: dry-monads
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 6.0.0
33
+ version: '1.3'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 6.0.0
40
+ version: '1.3'
41
41
  - !ruby/object:Gem::Dependency
42
- name: pundit
42
+ name: dry-transaction
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '2.1'
47
+ version: '0.13'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '2.1'
54
+ version: '0.13'
55
55
  - !ruby/object:Gem::Dependency
56
- name: dry-monads
56
+ name: dry-validation
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
@@ -67,33 +67,33 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '1.3'
69
69
  - !ruby/object:Gem::Dependency
70
- name: dry-transaction
70
+ name: pundit
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '0.13'
75
+ version: '2.1'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '0.13'
82
+ version: '2.1'
83
83
  - !ruby/object:Gem::Dependency
84
- name: dry-validation
84
+ name: rails
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - "~>"
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
- version: '1.3'
89
+ version: 6.0.0
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - "~>"
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
- version: '1.3'
96
+ version: 6.0.0
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: responders
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -109,21 +109,21 @@ dependencies:
109
109
  - !ruby/object:Gem::Version
110
110
  version: 3.0.0
111
111
  - !ruby/object:Gem::Dependency
112
- name: rails
112
+ name: bundler
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
- - - "~>"
115
+ - - ">="
116
116
  - !ruby/object:Gem::Version
117
- version: '6.0'
117
+ version: '0'
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - "~>"
122
+ - - ">="
123
123
  - !ruby/object:Gem::Version
124
- version: '6.0'
124
+ version: '0'
125
125
  - !ruby/object:Gem::Dependency
126
- name: bundler
126
+ name: mocha
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
129
  - - ">="
@@ -150,6 +150,20 @@ dependencies:
150
150
  - - ">="
151
151
  - !ruby/object:Gem::Version
152
152
  version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rails
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '6.0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '6.0'
153
167
  - !ruby/object:Gem::Dependency
154
168
  name: rake
155
169
  requirement: !ruby/object:Gem::Requirement
@@ -220,29 +234,23 @@ dependencies:
220
234
  - - ">="
221
235
  - !ruby/object:Gem::Version
222
236
  version: '0'
223
- - !ruby/object:Gem::Dependency
224
- name: mocha
225
- requirement: !ruby/object:Gem::Requirement
226
- requirements:
227
- - - ">="
228
- - !ruby/object:Gem::Version
229
- version: '0'
230
- type: :development
231
- prerelease: false
232
- version_requirements: !ruby/object:Gem::Requirement
233
- requirements:
234
- - - ">="
235
- - !ruby/object:Gem::Version
236
- version: '0'
237
237
  description: Simple and consistent rails api extensions
238
238
  email: dev@tymate.com
239
239
  executables: []
240
240
  extensions: []
241
241
  extra_rdoc_files: []
242
242
  files:
243
+ - lib/api-blocks.rb
243
244
  - lib/api_blocks.rb
244
245
  - lib/api_blocks/controller.rb
246
+ - lib/api_blocks/doorkeeper.rb
247
+ - lib/api_blocks/doorkeeper/passwords.rb
248
+ - lib/api_blocks/doorkeeper/passwords/application.rb
249
+ - lib/api_blocks/doorkeeper/passwords/controller.rb
250
+ - lib/api_blocks/doorkeeper/passwords/migration_generator.rb
251
+ - lib/api_blocks/doorkeeper/passwords/user.rb
245
252
  - lib/api_blocks/interactor.rb
253
+ - lib/api_blocks/railtie.rb
246
254
  - lib/api_blocks/responder.rb
247
255
  - lib/api_blocks/version.rb
248
256
  homepage: https://github.com/tymate/api-blocks