morpho 1.2.0 → 1.2.1

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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/app/api/morpho/entities/user.rb +0 -1
  3. data/app/api/morpho/resources/activations.rb +9 -4
  4. data/app/api/morpho/resources/externals.rb +8 -2
  5. data/app/api/morpho/resources/passwords.rb +9 -4
  6. data/app/api/morpho/resources/tokens.rb +19 -5
  7. data/app/api/morpho/resources/unlocks.rb +10 -5
  8. data/app/api/morpho/resources/users.rb +8 -3
  9. data/app/concepts/morpho/contracts/user/deliver_email.rb +13 -0
  10. data/app/concepts/morpho/contracts/user/external_sign_in.rb +17 -0
  11. data/app/concepts/morpho/contracts/user/refresh_token.rb +13 -0
  12. data/app/concepts/morpho/contracts/user/resend_activation_email.rb +8 -0
  13. data/app/concepts/morpho/contracts/user/resend_unlock_email.rb +8 -0
  14. data/app/concepts/morpho/contracts/user/send_reset_password_email.rb +8 -0
  15. data/app/concepts/morpho/contracts/user/sign_in.rb +15 -0
  16. data/app/concepts/morpho/contracts/user/sign_up.rb +25 -0
  17. data/app/concepts/morpho/operations/base/create.rb +14 -0
  18. data/app/concepts/morpho/operations/base/delete.rb +16 -0
  19. data/app/concepts/morpho/operations/base/fetch.rb +106 -0
  20. data/app/concepts/morpho/operations/base/find.rb +55 -0
  21. data/app/concepts/morpho/operations/base/form.rb +84 -0
  22. data/app/concepts/morpho/operations/base/update.rb +19 -0
  23. data/app/concepts/morpho/operations/user/deliver_email.rb +38 -0
  24. data/app/concepts/morpho/operations/user/external_sign_in.rb +38 -0
  25. data/app/concepts/morpho/operations/user/generate_token.rb +33 -0
  26. data/app/concepts/morpho/operations/user/refresh_token.rb +41 -0
  27. data/app/concepts/morpho/operations/user/resend_activation_email.rb +31 -0
  28. data/app/concepts/morpho/operations/user/resend_unlock_email.rb +31 -0
  29. data/app/concepts/morpho/operations/user/send_reset_password_email.rb +30 -0
  30. data/app/concepts/morpho/operations/user/sign_in.rb +49 -0
  31. data/app/concepts/morpho/operations/user/sign_up.rb +8 -0
  32. data/app/models/morpho/user.rb +6 -2
  33. data/config/locales/morpho.en.yml +2 -0
  34. data/config/locales/morpho.es.yml +2 -0
  35. data/lib/generators/morpho/install/templates/app/api/morpho/api.rb +12 -2
  36. data/lib/generators/morpho/install/templates/config/initializers/morpho.rb +2 -1
  37. data/lib/morpho/configuration.rb +2 -0
  38. data/lib/morpho/engine.rb +1 -6
  39. data/lib/morpho/version.rb +1 -1
  40. metadata +26 -17
  41. data/app/concepts/morpho/user/contract/activate.rb +0 -8
  42. data/app/concepts/morpho/user/contract/external_sign_in.rb +0 -12
  43. data/app/concepts/morpho/user/contract/refresh_token.rb +0 -8
  44. data/app/concepts/morpho/user/contract/reset_password.rb +0 -8
  45. data/app/concepts/morpho/user/contract/sign_in.rb +0 -10
  46. data/app/concepts/morpho/user/contract/sign_up.rb +0 -22
  47. data/app/concepts/morpho/user/contract/unlock.rb +0 -8
  48. data/app/concepts/morpho/user/operation/activate.rb +0 -42
  49. data/app/concepts/morpho/user/operation/external_sign_in.rb +0 -70
  50. data/app/concepts/morpho/user/operation/refresh_token.rb +0 -37
  51. data/app/concepts/morpho/user/operation/reset_password.rb +0 -40
  52. data/app/concepts/morpho/user/operation/sign_in.rb +0 -73
  53. data/app/concepts/morpho/user/operation/sign_up.rb +0 -32
  54. data/app/concepts/morpho/user/operation/unlock.rb +0 -42
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 82024952461a4e2bd7809b6490d89a95ea14ffbdb09bf0eb48f38293195a77d3
4
- data.tar.gz: 3f7ddb7bdb31f77544040cc6b6b7576fd6ec2536f88cc66bcbe9e2c4b8ca0065
3
+ metadata.gz: 031b5db81031a0b5580c14f0d5d2090d52232d4a2bef373f3b96252b02649701
4
+ data.tar.gz: 98beadb9189542547bf913eec75540898ebffafaeb4a14e8cf8a9cf34259fec4
5
5
  SHA512:
6
- metadata.gz: 9accfa9064d5a4d9cfcc2110c107007a8fe511c6847bdb368206d2f57caf5e8549c08e1fbed88b47ed61661ee3eb2deae199196a14955db7f0f829382e469202
7
- data.tar.gz: 60ed2c1e0f7fed8363d114ff1d98801455b02ad5f9c627e2b6f098b0443dfdbd6352c9001f565257924a7cd91992a6637bc542b7d325f4bede7533daf4ad7254
6
+ metadata.gz: a7635d8bc511ae7ecc7924604de502506e442a949cfa787ce317b62c1d040d9ffb6ccc5aac5a5854abaef0de0b028ca5e343e3eb5ffadd4f7aa65e8d0d0847ca
7
+ data.tar.gz: fbf30803bf591c3029b79e53d57b0102409874e43d0b3075e516c1c29db2c71619c6c722800f50c42816ccd71f2868cfca75453f56ba6cb3955db124f6262f4d
@@ -1,7 +1,6 @@
1
1
  module Morpho
2
2
  module Entities
3
3
  class User < ::Morpho::Entities::Base
4
- root 'data', 'data'
5
4
  expose :email, documentation: { type: 'string', desc: 'User email address' }
6
5
  end
7
6
  end
@@ -4,7 +4,7 @@ module Morpho
4
4
  helpers Morpho::Helpers::HTTPResponses
5
5
 
6
6
  namespace :activations do
7
- desc 'Request user activation token' do
7
+ desc 'Resend activation instructions email' do
8
8
  detail ''
9
9
  success Morpho::Grape::DataWrapper.new(Morpho::Entities::User)
10
10
  failure [
@@ -16,11 +16,16 @@ module Morpho
16
16
  params do
17
17
  requires :data, type: Morpho::Entities::UserEmail
18
18
  end
19
- post do
20
- result = Morpho::User::Operation::Activate.call(params)
19
+ post :resend_activation_email do
20
+ result = Morpho::Operations::User::ResendActivationEmail.call(
21
+ 'params' => params,
22
+ 'model.class' => Morpho::User,
23
+ 'contract.class' => Morpho::Contracts::User::ResendActivationEmail,
24
+ 'presenter.class' => Morpho::Entities::User
25
+ )
21
26
 
22
27
  if result.success?
23
- present result['model'], with: Morpho::Entities::User
28
+ present result['response']
24
29
  end
25
30
  end
26
31
  end
@@ -15,10 +15,16 @@ module Morpho
15
15
  requires :data, type: Morpho::Entities::External
16
16
  end
17
17
  post do
18
- result = Morpho::User::Operation::ExternalSignIn.call(params, ip: request.ip)
18
+ result = Morpho::Operations::User::ExternalSignIn.call(
19
+ 'params' => params,
20
+ 'model.class' => Morpho::User,
21
+ 'contract.class' => Morpho::Contracts::User::ExternalSignIn,
22
+ 'presenter.class' => Morpho::Entities::AuthenticationToken,
23
+ 'ip_address' => request.ip
24
+ )
19
25
 
20
26
  if result.success?
21
- present result['token'], with: Morpho::Entities::AuthenticationToken
27
+ present result['response']
22
28
  end
23
29
  end
24
30
  end
@@ -4,7 +4,7 @@ module Morpho
4
4
  helpers Morpho::Helpers::HTTPResponses
5
5
 
6
6
  namespace :passwords do
7
- desc 'Request user reset password token' do
7
+ desc 'Send reset password instructions email' do
8
8
  detail ''
9
9
  success Morpho::Grape::DataWrapper.new(Morpho::Entities::User)
10
10
  failure [
@@ -16,11 +16,16 @@ module Morpho
16
16
  params do
17
17
  requires :data, type: Morpho::Entities::UserEmail
18
18
  end
19
- post do
20
- result = Morpho::User::Operation::ResetPassword.call(params)
19
+ post :send_reset_password_email do
20
+ result = Morpho::Operations::User::SendResetPasswordEmail.call(
21
+ 'params' => params,
22
+ 'model.class' => Morpho::User,
23
+ 'contract.class' => Morpho::Contracts::User::SendResetPasswordEmail,
24
+ 'presenter.class' => Morpho::Entities::User
25
+ )
21
26
 
22
27
  if result.success?
23
- present result['model'], with: Morpho::Entities::User
28
+ present result['response']
24
29
  end
25
30
  end
26
31
  end
@@ -19,10 +19,16 @@ module Morpho
19
19
  requires :data, type: Morpho::Entities::Credentials
20
20
  end
21
21
  post do
22
- result = Morpho::User::Operation::SignIn.call(params, ip: request.ip)
22
+ result = Morpho::Operations::User::SignIn.call(
23
+ 'params' => params,
24
+ 'model.class' => Morpho::User,
25
+ 'contract.class' => Morpho::Contracts::User::SignIn,
26
+ 'presenter.class' => Morpho::Entities::AuthenticationToken,
27
+ 'ip_address' => request.ip
28
+ )
23
29
 
24
30
  if result.success?
25
- present result['token'], with: Morpho::Entities::AuthenticationToken
31
+ present result['response']
26
32
  end
27
33
  end
28
34
 
@@ -30,18 +36,26 @@ module Morpho
30
36
  detail ''
31
37
  success Morpho::Grape::DataWrapper.new(Morpho::Entities::AuthenticationToken)
32
38
  failure [
39
+ [ 403, I18n.t('morpho.api.messages.general.403'), Morpho::Entities::Error ],
33
40
  [ 404, I18n.t('morpho.api.messages.general.404'), Morpho::Entities::Error ],
34
- [ 422, I18n.t('morpho.api.messages.general.422'), Morpho::Entities::Error ]
41
+ [ 422, I18n.t('morpho.api.messages.general.422'), Morpho::Entities::Error ],
42
+ [ 423, I18n.t('morpho.api.messages.general.423'), Morpho::Entities::Error ]
35
43
  ]
36
44
  end
37
45
  params do
38
46
  requires :data, type: Morpho::Entities::RefreshToken
39
47
  end
40
48
  post :refresh do
41
- result = Morpho::User::Operation::RefreshToken.call(params)
49
+ result = Morpho::Operations::User::RefreshToken.call(
50
+ 'params' => params,
51
+ 'model.class' => Morpho::User,
52
+ 'contract.class' => Morpho::Contracts::User::RefreshToken,
53
+ 'presenter.class' => Morpho::Entities::AuthenticationToken,
54
+ 'ip_address' => request.ip
55
+ )
42
56
 
43
57
  if result.success?
44
- present result['token'], with: Morpho::Entities::AuthenticationToken
58
+ present result['response']
45
59
  end
46
60
  end
47
61
  end
@@ -4,9 +4,9 @@ module Morpho
4
4
  helpers Morpho::Helpers::HTTPResponses
5
5
 
6
6
  namespace :unlocks do
7
- desc 'Request user unlock token' do
7
+ desc 'Resend unlock instructions email' do
8
8
  detail ''
9
- success Morpho::Grape::DataWrapper.new(Morpho::Entities::UserEmail)
9
+ success Morpho::Grape::DataWrapper.new(Morpho::Entities::User)
10
10
  failure [
11
11
  [ 404, I18n.t('morpho.api.messages.general.404'), Morpho::Entities::Error ],
12
12
  [ 405, I18n.t('morpho.api.messages.general.405'), Morpho::Entities::Error ],
@@ -16,11 +16,16 @@ module Morpho
16
16
  params do
17
17
  requires :data, type: Morpho::Entities::UserEmail
18
18
  end
19
- post do
20
- result = Morpho::User::Operation::Unlock.call(params)
19
+ post :resend_unlock_email do
20
+ result = Morpho::Operations::User::ResendUnlockEmail.call(
21
+ 'params' => params,
22
+ 'model.class' => Morpho::User,
23
+ 'contract.class' => Morpho::Contracts::User::ResendActivationEmail,
24
+ 'presenter.class' => Morpho::Entities::User
25
+ )
21
26
 
22
27
  if result.success?
23
- present result['model'], with: Morpho::Entities::User
28
+ present result['response']
24
29
  end
25
30
  end
26
31
  end
@@ -4,7 +4,7 @@ module Morpho
4
4
  helpers Morpho::Helpers::HTTPResponses
5
5
 
6
6
  namespace :users do
7
- desc 'User registration' do
7
+ desc 'User sign up' do
8
8
  detail ''
9
9
  success Morpho::Grape::DataWrapper.new(Morpho::Entities::User)
10
10
  failure [
@@ -15,10 +15,15 @@ module Morpho
15
15
  requires :data, type: Morpho::Entities::UserSignUp
16
16
  end
17
17
  post do
18
- result = Morpho::User::Operation::SignUp.call(params)
18
+ result = Morpho::Operations::User::SignUp.call(
19
+ 'params' => params,
20
+ 'model.class' => Morpho::User,
21
+ 'contract.class' => Morpho::Contracts::User::SignUp,
22
+ 'presenter.class' => Morpho::Entities::User
23
+ )
19
24
 
20
25
  if result.success?
21
- present result['model'], with: Morpho::Entities::User
26
+ present result['response']
22
27
  end
23
28
  end
24
29
  end
@@ -0,0 +1,13 @@
1
+ module Morpho
2
+ module Contracts
3
+ module User
4
+ class DeliverEmail < Reform::Form
5
+ include Reform::Form::ActiveRecord
6
+
7
+ property :email
8
+
9
+ validates :email, presence: true, email_format: true
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ module Morpho
2
+ module Contracts
3
+ module User
4
+ class ExternalSignIn < Reform::Form
5
+ include Reform::Form::ActiveRecord
6
+
7
+ property :email
8
+ property :provider
9
+ property :uid
10
+
11
+ validates :email, presence: true, email_format: true
12
+ validates :provider, presence: true
13
+ validates :uid, presence: true
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ module Morpho
2
+ module Contracts
3
+ module User
4
+ class RefreshToken < Reform::Form
5
+ include Reform::Form::ActiveRecord
6
+
7
+ property :refresh_token
8
+
9
+ validates :refresh_token, presence: true
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ module Morpho
2
+ module Contracts
3
+ module User
4
+ class ResendActivationEmail < Morpho::Contracts::User::DeliverEmail
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module Morpho
2
+ module Contracts
3
+ module User
4
+ class ResendUnlockEmail < Morpho::Contracts::User::DeliverEmail
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module Morpho
2
+ module Contracts
3
+ module User
4
+ class SendResetPasswordEmail < Morpho::Contracts::User::DeliverEmail
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,15 @@
1
+ module Morpho
2
+ module Contracts
3
+ module User
4
+ class SignIn < Reform::Form
5
+ include Reform::Form::ActiveRecord
6
+
7
+ property :email
8
+ property :password
9
+
10
+ validates :email, presence: true, email_format: true
11
+ validates :password, presence: true
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ require 'reform/form/validation/unique_validator'
2
+
3
+ module Morpho
4
+ module Contracts
5
+ module User
6
+ class SignUp < Reform::Form
7
+ include Reform::Form::ActiveRecord
8
+
9
+ property :email
10
+ property :password
11
+ property :password_confirmation, virtual: true
12
+
13
+ validates :email, presence: true, unique: true, email_format: true
14
+ validates :password,
15
+ presence: true,
16
+ confirmation: true,
17
+ length: { minimum: Morpho.config.auth.password_minimum_length },
18
+ :'morpho/validators/contain_number' => true,
19
+ :'morpho/validators/contain_uppercase' => true,
20
+ :'morpho/validators/contain_symbol' => true
21
+ validates :password_confirmation, presence: true
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Morpho
4
+ module Operations
5
+ module Base
6
+ # Base create operation
7
+ class Create < Morpho::Operations::Base::Form
8
+ def build_model!(options, **)
9
+ options['model'] = options['model.class'].new
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Morpho
4
+ module Operations
5
+ module Base
6
+ # Base delete operation
7
+ class Delete < Morpho::Operations::Base::Find
8
+ pass :destroy!, after: :present!
9
+
10
+ def destroy!(options, model:, **)
11
+ model.destroy
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Morpho
4
+ module Operations
5
+ module Base
6
+ # Base fetch operation
7
+ class Fetch < Trailblazer::Operation
8
+ pass :init!
9
+ pass :params!
10
+ pass :meta!
11
+ pass :model_class!
12
+ pass :build_build_models!
13
+ pass :filter!
14
+ pass :page!
15
+ pass :paginate!
16
+ pass :search!
17
+ pass :update_meta!
18
+ pass :present!
19
+
20
+ def init!(options, **)
21
+ options['default.page.size'] ||= 25
22
+ end
23
+
24
+ def params!(options, **)
25
+ options['params'] ||= {}
26
+ end
27
+
28
+ def meta!(options, params:, **)
29
+ options['meta'] = params.fetch('meta', {})
30
+ end
31
+
32
+ def update_meta!(options, **)
33
+ if options['models'].try(:current_page)
34
+ options['meta'].merge!(
35
+ 'current_page' => options['models'].current_page,
36
+ 'from' => options['models'].prev_page,
37
+ 'last_page' => options['models'].total_pages,
38
+ 'per_page' => options['models'].limit_value,
39
+ 'to' => options['models'].next_page,
40
+ 'total' => options['models'].total_count
41
+ )
42
+ end
43
+ options
44
+ end
45
+
46
+ def model_class!(options, **)
47
+ model_class = options['model.class']
48
+ unless model_class.is_a?(Class) && model_class.ancestors
49
+ .include?(
50
+ ::ActiveRecord::Base
51
+ )
52
+ raise ArgumentError,
53
+ 'Supply a valid model.class option'
54
+ end
55
+ end
56
+
57
+ def build_models!(options, **)
58
+ options['models'] = options['model.class'].all
59
+ end
60
+
61
+ def filter!(options, params:, **)
62
+ options['filter'] = params.fetch('filter', {})
63
+ end
64
+
65
+ def page!(options, params:, **)
66
+ options['page'] = params.fetch(
67
+ 'page',
68
+ 'number' => 1,
69
+ 'size' => options['default.page.size']
70
+ )
71
+ end
72
+
73
+ def paginate!(options, page:, **)
74
+ page_size = page.fetch('size', 25)
75
+ page_number = page.fetch('number', 1)
76
+ if page &&
77
+ page_size.to_i.positive?
78
+ options['models'] = options['models']
79
+ .page(page_number)
80
+ .per(page_size)
81
+ end
82
+ options
83
+ end
84
+
85
+ def search!(options, **)
86
+ options['models'] = options['models']
87
+ .ransack(options['filter'])
88
+ .result(distinct: options['ransack.distinct'])
89
+ end
90
+
91
+ def present!(options, meta:, models:, **)
92
+ data =
93
+ if options['presenter.class']
94
+ options['presenter.class'].represent(models)['data']
95
+ else
96
+ models
97
+ end
98
+ options['response'] = {
99
+ data: data,
100
+ meta: meta
101
+ }
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end