auction_fun_core 0.8.5

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 (102) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +12 -0
  3. data/.env.development.template +7 -0
  4. data/.env.test.template +7 -0
  5. data/.rspec +3 -0
  6. data/.standard.yml +3 -0
  7. data/.tool-versions +5 -0
  8. data/CHANGELOG.md +145 -0
  9. data/CODE_OF_CONDUCT.md +84 -0
  10. data/LICENSE.txt +21 -0
  11. data/Procfile +4 -0
  12. data/README.md +141 -0
  13. data/Rakefile +13 -0
  14. data/auction_fun_core.gemspec +57 -0
  15. data/config/app.rb +5 -0
  16. data/config/application.rb +37 -0
  17. data/config/boot.rb +15 -0
  18. data/db/migrate/20240216200926_enable_postgres_extensions.rb +13 -0
  19. data/db/migrate/20240217115734_create_users.rb +28 -0
  20. data/db/migrate/20240220140151_create_custom_types.rb +11 -0
  21. data/db/migrate/20240220140254_create_staffs.rb +20 -0
  22. data/db/migrate/20240229142933_create_custom_types.rb +13 -0
  23. data/db/migrate/20240229143000_create_auctions.rb +29 -0
  24. data/db/migrate/20240304144422_create_bids.rb +19 -0
  25. data/db/seeds.rb +116 -0
  26. data/i18n/en-US/contracts/contracts.en-US.yml +72 -0
  27. data/i18n/en-US/mail/user_context/registration.en-US.yml +11 -0
  28. data/i18n/pt-BR/contracts/contracts.pt-BR.yml +72 -0
  29. data/i18n/pt-BR/mail/user_context/registration.pt-BR.yml +12 -0
  30. data/lib/auction_fun_core/business/configuration.rb +18 -0
  31. data/lib/auction_fun_core/business/token_generator.rb +17 -0
  32. data/lib/auction_fun_core/commands/auction_context/create_auction.rb +19 -0
  33. data/lib/auction_fun_core/commands/auction_context/delete_auction.rb +15 -0
  34. data/lib/auction_fun_core/commands/auction_context/update_auction.rb +18 -0
  35. data/lib/auction_fun_core/commands/bid_context/create_bid.rb +19 -0
  36. data/lib/auction_fun_core/commands/bid_context/delete_bid.rb +15 -0
  37. data/lib/auction_fun_core/commands/bid_context/update_bid.rb +18 -0
  38. data/lib/auction_fun_core/commands/staff_context/create_staff.rb +19 -0
  39. data/lib/auction_fun_core/commands/user_context/create_user.rb +19 -0
  40. data/lib/auction_fun_core/contracts/application_contract.rb +55 -0
  41. data/lib/auction_fun_core/contracts/auction_context/create_contract.rb +101 -0
  42. data/lib/auction_fun_core/contracts/auction_context/processor/finish_contract.rb +27 -0
  43. data/lib/auction_fun_core/contracts/auction_context/processor/pause_contract.rb +34 -0
  44. data/lib/auction_fun_core/contracts/auction_context/processor/start_contract.rb +46 -0
  45. data/lib/auction_fun_core/contracts/auction_context/processor/unpause_contract.rb +34 -0
  46. data/lib/auction_fun_core/contracts/bid_context/create_bid_closed_contract.rb +79 -0
  47. data/lib/auction_fun_core/contracts/bid_context/create_bid_penny_contract.rb +69 -0
  48. data/lib/auction_fun_core/contracts/bid_context/create_bid_standard_contract.rb +68 -0
  49. data/lib/auction_fun_core/contracts/staff_context/authentication_contract.rb +47 -0
  50. data/lib/auction_fun_core/contracts/staff_context/registration_contract.rb +52 -0
  51. data/lib/auction_fun_core/contracts/user_context/authentication_contract.rb +47 -0
  52. data/lib/auction_fun_core/contracts/user_context/email_confirmation_contract.rb +40 -0
  53. data/lib/auction_fun_core/contracts/user_context/phone_confirmation_contract.rb +40 -0
  54. data/lib/auction_fun_core/contracts/user_context/registration_contract.rb +63 -0
  55. data/lib/auction_fun_core/entities/auction.rb +17 -0
  56. data/lib/auction_fun_core/entities/bid.rb +10 -0
  57. data/lib/auction_fun_core/entities/staff.rb +21 -0
  58. data/lib/auction_fun_core/entities/user.rb +37 -0
  59. data/lib/auction_fun_core/events/app.rb +27 -0
  60. data/lib/auction_fun_core/events/listener.rb +89 -0
  61. data/lib/auction_fun_core/operations/auction_context/create_operation.rb +77 -0
  62. data/lib/auction_fun_core/operations/auction_context/processor/finish_operation.rb +61 -0
  63. data/lib/auction_fun_core/operations/auction_context/processor/pause_operation.rb +57 -0
  64. data/lib/auction_fun_core/operations/auction_context/processor/start_operation.rb +84 -0
  65. data/lib/auction_fun_core/operations/auction_context/processor/unpause_operation.rb +57 -0
  66. data/lib/auction_fun_core/operations/base.rb +11 -0
  67. data/lib/auction_fun_core/operations/bid_context/create_bid_closed_operation.rb +64 -0
  68. data/lib/auction_fun_core/operations/bid_context/create_bid_penny_operation.rb +64 -0
  69. data/lib/auction_fun_core/operations/bid_context/create_bid_standard_operation.rb +74 -0
  70. data/lib/auction_fun_core/operations/staff_context/authentication_operation.rb +52 -0
  71. data/lib/auction_fun_core/operations/staff_context/registration_operation.rb +76 -0
  72. data/lib/auction_fun_core/operations/user_context/authentication_operation.rb +52 -0
  73. data/lib/auction_fun_core/operations/user_context/email_confirmation_operation.rb +67 -0
  74. data/lib/auction_fun_core/operations/user_context/phone_confirmation_operation.rb +67 -0
  75. data/lib/auction_fun_core/operations/user_context/registration_operation.rb +105 -0
  76. data/lib/auction_fun_core/relations/auctions.rb +119 -0
  77. data/lib/auction_fun_core/relations/bids.rb +28 -0
  78. data/lib/auction_fun_core/relations/staffs.rb +27 -0
  79. data/lib/auction_fun_core/relations/users.rb +26 -0
  80. data/lib/auction_fun_core/repos/auction_context/auction_repository.rb +42 -0
  81. data/lib/auction_fun_core/repos/bid_context/bid_repository.rb +27 -0
  82. data/lib/auction_fun_core/repos/staff_context/staff_repository.rb +64 -0
  83. data/lib/auction_fun_core/repos/user_context/user_repository.rb +78 -0
  84. data/lib/auction_fun_core/services/mail/templates/layout.html.erb +72 -0
  85. data/lib/auction_fun_core/services/mail/templates/user_context/registration.html.erb +170 -0
  86. data/lib/auction_fun_core/services/mail/user_context/registration_mailer.rb +25 -0
  87. data/lib/auction_fun_core/version.rb +8 -0
  88. data/lib/auction_fun_core/workers/application_job.rb +22 -0
  89. data/lib/auction_fun_core/workers/operations/auction_context/processor/finish_operation_job.rb +32 -0
  90. data/lib/auction_fun_core/workers/operations/auction_context/processor/start_operation_job.rb +32 -0
  91. data/lib/auction_fun_core/workers/services/mail/user_context/registration_mailer_job.rb +40 -0
  92. data/lib/auction_fun_core.rb +21 -0
  93. data/lib/tasks/database.rake +87 -0
  94. data/system/providers/background_job.rb +15 -0
  95. data/system/providers/core.rb +35 -0
  96. data/system/providers/db.rb +26 -0
  97. data/system/providers/events.rb +13 -0
  98. data/system/providers/logger.rb +11 -0
  99. data/system/providers/mail.rb +25 -0
  100. data/system/providers/persistence.rb +18 -0
  101. data/system/providers/settings.rb +26 -0
  102. metadata +400 -0
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Operations
5
+ module BidContext
6
+ ##
7
+ # Operation class for create new bids for closed auctions.
8
+ #
9
+ class CreateBidClosedOperation < AuctionFunCore::Operations::Base
10
+ include Import["contracts.bid_context.create_bid_closed_contract"]
11
+ include Import["repos.bid_context.bid_repository"]
12
+
13
+ # @todo Add custom doc
14
+ def self.call(attributes, &block)
15
+ operation = new.call(attributes)
16
+
17
+ return operation unless block
18
+
19
+ Dry::Matcher::ResultMatcher.call(operation, &block)
20
+ end
21
+
22
+ # @todo Add custom doc
23
+ def call(attributes)
24
+ values = yield validate(attributes)
25
+
26
+ bid_repository.transaction do |_t|
27
+ @bid = yield persist(values)
28
+ yield publish_bid_created(@bid)
29
+ end
30
+
31
+ Success(@bid)
32
+ end
33
+
34
+ # Calls the bid creation contract class to perform the validation
35
+ # of the informed attributes.
36
+ # @param attrs [Hash] bid attributes
37
+ # @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure]
38
+ def validate(attrs)
39
+ contract = create_bid_closed_contract.call(attrs)
40
+
41
+ return Failure(contract.errors.to_h) if contract.failure?
42
+
43
+ Success(contract.to_h)
44
+ end
45
+
46
+ # Calls the bid repository class to persist the attributes in the database.
47
+ # @param result [Hash] Bid validated attributes
48
+ # @return [ROM::Struct::Bid]
49
+ def persist(result)
50
+ Success(bid_repository.create(result))
51
+ end
52
+
53
+ # Triggers the publication of event *bids.created*.
54
+ # @param bid [ROM::Struct::Bid] Bid object
55
+ # @return [Dry::Monads::Result::Success]
56
+ def publish_bid_created(bid)
57
+ Application[:event].publish("bids.created", bid.to_h)
58
+
59
+ Success()
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Operations
5
+ module BidContext
6
+ ##
7
+ # Operation class for create new bids for penny auctions.
8
+ #
9
+ class CreateBidPennyOperation < AuctionFunCore::Operations::Base
10
+ include Import["contracts.bid_context.create_bid_penny_contract"]
11
+ include Import["repos.bid_context.bid_repository"]
12
+
13
+ # @todo Add custom doc
14
+ def self.call(attributes, &block)
15
+ operation = new.call(attributes)
16
+
17
+ return operation unless block
18
+
19
+ Dry::Matcher::ResultMatcher.call(operation, &block)
20
+ end
21
+
22
+ # @todo Add custom doc
23
+ def call(attributes)
24
+ values = yield validate(attributes)
25
+
26
+ bid_repository.transaction do |_t|
27
+ @bid = yield persist(values)
28
+ yield publish_bid_created(@bid)
29
+ end
30
+
31
+ Success(@bid)
32
+ end
33
+
34
+ # Calls the bid creation contract class to perform the validation
35
+ # of the informed attributes.
36
+ # @param attrs [Hash] bid attributes
37
+ # @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure]
38
+ def validate(attrs)
39
+ contract = create_bid_penny_contract.call(attrs)
40
+
41
+ return Failure(contract.errors.to_h) if contract.failure?
42
+
43
+ Success(contract.to_h)
44
+ end
45
+
46
+ # Calls the bid repository class to persist the attributes in the database.
47
+ # @param result [Hash] Bid validated attributes
48
+ # @return [ROM::Struct::Bid]
49
+ def persist(result)
50
+ Success(bid_repository.create(result))
51
+ end
52
+
53
+ # Triggers the publication of event *bids.created*.
54
+ # @param bid [ROM::Struct::Bid] Bid Object
55
+ # @return [Dry::Monads::Result::Success]
56
+ def publish_bid_created(bid)
57
+ Application[:event].publish("bids.created", bid.to_h)
58
+
59
+ Success()
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Operations
5
+ module BidContext
6
+ ##
7
+ # Operation class for create new bids for standard auctions.
8
+ #
9
+ class CreateBidStandardOperation < AuctionFunCore::Operations::Base
10
+ INCREASE = 0.1
11
+ include Import["contracts.bid_context.create_bid_standard_contract"]
12
+ include Import["repos.auction_context.auction_repository"]
13
+ include Import["repos.bid_context.bid_repository"]
14
+
15
+ # @todo Add custom doc
16
+ def self.call(attributes, &block)
17
+ operation = new.call(attributes)
18
+
19
+ return operation unless block
20
+
21
+ Dry::Matcher::ResultMatcher.call(operation, &block)
22
+ end
23
+
24
+ # @todo Add custom doc
25
+ def call(attributes)
26
+ values = yield validate(attributes)
27
+
28
+ bid_repository.transaction do |_t|
29
+ @bid = yield persist(values)
30
+ yield calculate_standard_auction_new_minimal_bid_value(@bid.auction_id, @bid.value_cents)
31
+ yield publish_bid_created(@bid)
32
+ end
33
+
34
+ Success(@bid)
35
+ end
36
+
37
+ # Calls the bid creation contract class to perform the validation
38
+ # of the informed attributes.
39
+ # @param attrs [Hash] bid attributes
40
+ # @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure]
41
+ def validate(attrs)
42
+ contract = create_bid_standard_contract.call(attrs)
43
+
44
+ return Failure(contract.errors.to_h) if contract.failure?
45
+
46
+ Success(contract.to_h)
47
+ end
48
+
49
+ # Calculates the new minimum next bid amount.
50
+ def calculate_standard_auction_new_minimal_bid_value(auction_id, bid_cents)
51
+ minimal_bid_cents = (bid_cents + (bid_cents * INCREASE))
52
+
53
+ Success(auction_repository.update(auction_id, minimal_bid_cents: minimal_bid_cents))
54
+ end
55
+
56
+ # Calls the bid repository class to persist the attributes in the database.
57
+ # @param result [Hash] Bid validated attributes
58
+ # @return [ROM::Struct::Bid]
59
+ def persist(result)
60
+ Success(bid_repository.create(result))
61
+ end
62
+
63
+ # Triggers the publication of event *bids.created*.
64
+ # @param bid [ROM::Struct::Bid] Bid Object
65
+ # @return [Dry::Monads::Result::Success]
66
+ def publish_bid_created(bid)
67
+ Application[:event].publish("bids.created", bid.to_h)
68
+
69
+ Success()
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Operations
5
+ module StaffContext
6
+ ##
7
+ # Operation class for authenticate staff members.
8
+ #
9
+ class AuthenticationOperation < AuctionFunCore::Operations::Base
10
+ include Import["contracts.staff_context.authentication_contract"]
11
+
12
+ def self.call(attributes, &block)
13
+ operation = new.call(attributes)
14
+
15
+ return operation unless block
16
+
17
+ Dry::Matcher::ResultMatcher.call(operation, &block)
18
+ end
19
+
20
+ # @todo Add custom doc
21
+ def call(attributes)
22
+ staff = yield validate_contract(attributes)
23
+
24
+ yield publish_staff_authentication(staff.id)
25
+
26
+ Success(staff)
27
+ end
28
+
29
+ # Calls the authentication contract class to perform the validation
30
+ # and authentication of the informed attributes.
31
+ # @param attrs [Hash] Staff attributes
32
+ # @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure]
33
+ def validate_contract(attrs)
34
+ contract = authentication_contract.call(attrs)
35
+
36
+ return Failure(contract.errors.to_h) if contract.failure?
37
+
38
+ Success(contract.context[:staff])
39
+ end
40
+
41
+ # Triggers the publication of event *staffs.registration*.
42
+ # @param staff_id [Integer] Staff ID
43
+ # @return [Dry::Monads::Result::Success]
44
+ def publish_staff_authentication(staff_id, time = Time.current)
45
+ Success(
46
+ Application[:event].publish("staffs.authentication", {staff_id: staff_id, time: time})
47
+ )
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Operations
5
+ module StaffContext
6
+ ##
7
+ # Operation class for create new staff member.
8
+ #
9
+ class RegistrationOperation < AuctionFunCore::Operations::Base
10
+ include Import["contracts.staff_context.registration_contract"]
11
+ include Import["repos.staff_context.staff_repository"]
12
+
13
+ def self.call(attributes, &block)
14
+ operation = new.call(attributes)
15
+
16
+ return operation unless block
17
+
18
+ Dry::Matcher::ResultMatcher.call(operation, &block)
19
+ end
20
+
21
+ # @todo Add custom doc
22
+ def call(attributes)
23
+ values = yield validate_contract(attributes)
24
+ values_with_encrypt_password = yield encrypt_password(values)
25
+
26
+ staff_repository.transaction do |_t|
27
+ @staff = yield persist(values_with_encrypt_password)
28
+
29
+ yield publish_staff_registration(@staff.id)
30
+ end
31
+
32
+ Success(@staff)
33
+ end
34
+
35
+ # Calls registration contract class to perform the validation
36
+ # of the informed attributes.
37
+ # @param attrs [Hash] staff attributes
38
+ # @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure]
39
+ def validate_contract(attrs)
40
+ contract = registration_contract.call(attrs)
41
+
42
+ return Failure(contract.errors.to_h) if contract.failure?
43
+
44
+ Success(contract.to_h)
45
+ end
46
+
47
+ # Transforms the password attribute, encrypting it to be saved in the database.
48
+ # @param result [Hash] Staff valid contract attributes
49
+ # @return [Hash] Valid staff database
50
+ def encrypt_password(attrs)
51
+ attributes = attrs.to_h.except(:password)
52
+
53
+ Success(
54
+ {**attributes, password_digest: BCrypt::Password.create(attrs[:password])}
55
+ )
56
+ end
57
+
58
+ # Calls the staff repository class to persist the attributes in the database.
59
+ # @param result [Hash] Staff validated attributes
60
+ # @return [ROM::Struct::Staff]
61
+ def persist(result)
62
+ Success(staff_repository.create(result))
63
+ end
64
+
65
+ # Triggers the publication of event *staffs.registration*.
66
+ # @param staff_id [Integer] Staff ID
67
+ # @return [Dry::Monads::Result::Success]
68
+ def publish_staff_registration(staff_id)
69
+ staff = staff_repository.by_id!(staff_id)
70
+
71
+ Success(Application[:event].publish("staffs.registration", staff.info))
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Operations
5
+ module UserContext
6
+ ##
7
+ # Operation class for authenticate users.
8
+ #
9
+ class AuthenticationOperation < AuctionFunCore::Operations::Base
10
+ include Import["contracts.user_context.authentication_contract"]
11
+
12
+ def self.call(attributes, &block)
13
+ operation = new.call(attributes)
14
+
15
+ return operation unless block
16
+
17
+ Dry::Matcher::ResultMatcher.call(operation, &block)
18
+ end
19
+
20
+ # @todo Add custom doc
21
+ def call(attributes)
22
+ user = yield validate_contract(attributes)
23
+
24
+ yield publish_user_authentication(user.id)
25
+
26
+ Success(user)
27
+ end
28
+
29
+ # Calls the authentication contract class to perform the validation
30
+ # and authentication of the informed attributes.
31
+ # @param attrs [Hash] user attributes
32
+ # @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure]
33
+ def validate_contract(attrs)
34
+ contract = authentication_contract.call(attrs)
35
+
36
+ return Failure(contract.errors.to_h) if contract.failure?
37
+
38
+ Success(contract.context[:user])
39
+ end
40
+
41
+ # Triggers the publication of event *users.registration*.
42
+ # @param user_id [Integer] User ID
43
+ # @return [Dry::Monads::Result::Success]
44
+ def publish_user_authentication(user_id, time = Time.current)
45
+ Success(
46
+ Application[:event].publish("users.authentication", {user_id: user_id, time: time})
47
+ )
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Operations
5
+ module UserContext
6
+ ##
7
+ # Operation class for confirm users by token via email.
8
+ #
9
+ class EmailConfirmationOperation < AuctionFunCore::Operations::Base
10
+ include Import["repos.user_context.user_repository"]
11
+ include Import["contracts.user_context.email_confirmation_contract"]
12
+
13
+ def self.call(attributes, &block)
14
+ operation = new.call(attributes)
15
+
16
+ return operation unless block
17
+
18
+ Dry::Matcher::ResultMatcher.call(operation, &block)
19
+ end
20
+
21
+ # @todo Add custom doc
22
+ def call(attributes)
23
+ user = yield validate_contract(attributes)
24
+
25
+ user_repository.transaction do |_t|
26
+ user = yield persist(user.id)
27
+
28
+ yield publish_user_confirmation(user.id, user.confirmed_at)
29
+ end
30
+
31
+ Success(user)
32
+ end
33
+
34
+ # Calls the authentication contract class to perform the validation
35
+ # and authentication of the informed attributes.
36
+ # @param attrs [Hash] user attributes
37
+ # @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure]
38
+ def validate_contract(attrs)
39
+ contract = email_confirmation_contract.call(attrs)
40
+
41
+ return Failure(contract.errors.to_h) if contract.failure?
42
+
43
+ Success(contract.context[:user])
44
+ end
45
+
46
+ # Calls the user repository class to update database fields.
47
+ # @param user_id [Integer] User ID
48
+ # @param time [Time] Confirmation time
49
+ # @return [ROM::Struct::User]
50
+ def persist(user_id, time = Time.current)
51
+ result = {email_confirmation_token: nil, email_confirmed_at: time, confirmed_at: time}
52
+
53
+ Success(user_repository.update(user_id, result))
54
+ end
55
+
56
+ # Triggers the publication of event *users.registration*.
57
+ # @param user_id [Integer] User ID
58
+ # @return [Dry::Monads::Result::Success]
59
+ def publish_user_confirmation(user_id, time = Time.current)
60
+ Success(
61
+ Application[:event].publish("users.confirmation", {user_id: user_id, time: time})
62
+ )
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Operations
5
+ module UserContext
6
+ ##
7
+ # Operation class for confirm users by token via phone.
8
+ #
9
+ class PhoneConfirmationOperation < AuctionFunCore::Operations::Base
10
+ include Import["repos.user_context.user_repository"]
11
+ include Import["contracts.user_context.phone_confirmation_contract"]
12
+
13
+ def self.call(attributes, &block)
14
+ operation = new.call(attributes)
15
+
16
+ return operation unless block
17
+
18
+ Dry::Matcher::ResultMatcher.call(operation, &block)
19
+ end
20
+
21
+ # @todo Add custom doc
22
+ def call(attributes)
23
+ user = yield validate_contract(attributes)
24
+
25
+ user_repository.transaction do |_t|
26
+ user = yield persist(user.id)
27
+
28
+ yield publish_user_confirmation(user.id, user.confirmed_at)
29
+ end
30
+
31
+ Success(user)
32
+ end
33
+
34
+ # Calls the authentication contract class to perform the validation
35
+ # and authentication of the informed attributes.
36
+ # @param attrs [Hash] user attributes
37
+ # @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure]
38
+ def validate_contract(attrs)
39
+ contract = phone_confirmation_contract.call(attrs)
40
+
41
+ return Failure(contract.errors.to_h) if contract.failure?
42
+
43
+ Success(contract.context[:user])
44
+ end
45
+
46
+ # Calls the user repository class to update database fields.
47
+ # @param user_id [Integer] User ID
48
+ # @param time [Time] Confirmation time
49
+ # @return [ROM::Struct::User]
50
+ def persist(user_id, time = Time.current)
51
+ result = {phone_confirmation_token: nil, phone_confirmed_at: time, confirmed_at: time}
52
+
53
+ Success(user_repository.update(user_id, result))
54
+ end
55
+
56
+ # Triggers the publication of event *users.registration*.
57
+ # @param user_id [Integer] User ID
58
+ # @return [Dry::Monads::Result::Success]
59
+ def publish_user_confirmation(user_id, time = Time.current)
60
+ Success(
61
+ Application[:event].publish("users.confirmation", {user_id: user_id, time: time})
62
+ )
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Operations
5
+ module UserContext
6
+ ##
7
+ # Operation class for create new users.
8
+ #
9
+ class RegistrationOperation < AuctionFunCore::Operations::Base
10
+ include Import["repos.user_context.user_repository"]
11
+ include Import["contracts.user_context.registration_contract"]
12
+
13
+ def self.call(attributes, &block)
14
+ operation = new.call(attributes)
15
+
16
+ return operation unless block
17
+
18
+ Dry::Matcher::ResultMatcher.call(operation, &block)
19
+ end
20
+
21
+ # @todo Add custom doc
22
+ def call(attributes)
23
+ values = yield validate_contract(attributes)
24
+ values_with_encrypt_password = yield encrypt_password(values)
25
+ values_with_confirmation_attributes = yield confirmation_attributes(values_with_encrypt_password)
26
+
27
+ user_repository.transaction do |_t|
28
+ @user = yield persist(values_with_confirmation_attributes)
29
+
30
+ yield publish_user_registration(@user.id)
31
+ yield send_welcome_email(@user.id)
32
+ end
33
+
34
+ Success(@user)
35
+ end
36
+
37
+ # Calls registration contract class to perform the validation
38
+ # of the informed attributes.
39
+ # @param attrs [Hash] user attributes
40
+ # @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure]
41
+ def validate_contract(attrs)
42
+ contract = registration_contract.call(attrs)
43
+
44
+ return Failure(contract.errors.to_h) if contract.failure?
45
+
46
+ Success(contract.to_h)
47
+ end
48
+
49
+ # Transforms the password attribute, encrypting it to be saved in the database.
50
+ # @param result [Hash] User valid contract attributes
51
+ # @return [Hash] Valid user database
52
+ def encrypt_password(attrs)
53
+ attributes = attrs.to_h.except(:password, :password_confirmation)
54
+
55
+ Success(
56
+ {**attributes, password_digest: BCrypt::Password.create(attrs[:password])}
57
+ )
58
+ end
59
+
60
+ # Adds confirmation parameters to the given attributes.
61
+ # @param result [Hash] User valid contract attributes with password
62
+ # @return [Hash] Valid user database
63
+ def confirmation_attributes(attrs)
64
+ Success({**attrs, email_confirmation_token: generate_email_token})
65
+ end
66
+
67
+ # Calls the user repository class to persist the attributes in the database.
68
+ # @param result [Hash] User validated attributes
69
+ # @return [ROM::Struct::User]
70
+ def persist(result)
71
+ Success(user_repository.create(result))
72
+ end
73
+
74
+ # Triggers the publication of event *users.registration*.
75
+ # @param user_id [Integer] User ID
76
+ # @return [Dry::Monads::Result::Success]
77
+ def publish_user_registration(user_id)
78
+ user = user_repository.by_id!(user_id)
79
+
80
+ Success(Application[:event].publish("users.registration", user.info))
81
+ end
82
+
83
+ # Schedule the asynchronous sending of a welcome email.
84
+ # @param user_id [Integer] User ID
85
+ # @return [Dry::Monads::Result::Success]
86
+ def send_welcome_email(user_id)
87
+ Success(registration_mailer_job.perform_async(user_id))
88
+ end
89
+
90
+ # Since the shipping code structure does not follow project conventions,
91
+ # making the default injection dependency would be more complicated.
92
+ # Therefore, here I directly explain the class to be called.
93
+ private
94
+
95
+ def registration_mailer_job
96
+ AuctionFunCore::Workers::Services::Mail::UserContext::RegistrationMailerJob
97
+ end
98
+
99
+ def generate_email_token
100
+ ::AuctionFunCore::Business::TokenGenerator.generate_email_token
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end