auction_fun_core 0.8.5

Sign up to get free protection for your applications and to get access to all the features.
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,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Contracts
5
+ module UserContext
6
+ # Contract class responsible for validating the confirmation token for phone number.
7
+ class PhoneConfirmationContract < ApplicationContract
8
+ I18N_SCOPE = "contracts.errors.custom.default"
9
+
10
+ option :user_repository, default: proc { Repos::UserContext::UserRepository.new }
11
+
12
+ params do
13
+ required(:phone_confirmation_token)
14
+
15
+ before(:value_coercer) do |result|
16
+ result.to_h.compact
17
+ end
18
+ end
19
+
20
+ # Validation for token_confirmation_token.
21
+ # Searches for the user in the database from the phone_confirmation_token
22
+ rule do |context:|
23
+ next if schema_error?(:phone_confirmation_token)
24
+
25
+ context[:user] ||= user_repository.by_phone_confirmation_token(values[:phone_confirmation_token])
26
+
27
+ next if context[:user].present?
28
+
29
+ key(:phone_confirmation_token).failure(I18n.t("not_found", scope: I18N_SCOPE))
30
+ end
31
+
32
+ rule do |context:|
33
+ next if context[:user].blank? || context[:user].active?
34
+
35
+ key(:base).failure(I18n.t("inactive_account", scope: I18N_SCOPE))
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Contracts
5
+ module UserContext
6
+ # Contract class to create new users.
7
+ class RegistrationContract < ApplicationContract
8
+ I18N_SCOPE = "contracts.errors.custom.default"
9
+
10
+ option :user_repository, default: proc { Repos::UserContext::UserRepository.new }
11
+
12
+ # @param [Hash] opts Sets an allowed list of parameters, as well as some initial validations.
13
+ params do
14
+ required(:name)
15
+ required(:email)
16
+ required(:phone)
17
+ required(:password)
18
+ required(:password_confirmation)
19
+
20
+ before(:value_coercer) do |result|
21
+ result.to_h.compact
22
+ end
23
+
24
+ # Normalize and add default values
25
+ after(:value_coercer) do |result|
26
+ result.update(email: result[:email].strip.downcase) if result[:email]
27
+ result.update(phone: result[:phone].tr_s("^0-9", "")) if result[:phone]
28
+ end
29
+ end
30
+
31
+ rule(:name).validate(:name_format)
32
+
33
+ # Validation for email.
34
+ # It must validate the format and uniqueness in the database.
35
+ rule(:email).validate(:email_format)
36
+ rule(:email) do
37
+ # Email should be unique on database
38
+ if !rule_error?(:email) && user_repository.exists?(email: value)
39
+ key.failure(I18n.t(:taken, scope: I18N_SCOPE))
40
+ end
41
+ end
42
+
43
+ # Validation for phone.
44
+ # It must validate the format and uniqueness in the database.
45
+ rule(:phone).validate(:phone_format)
46
+ rule(:phone) do
47
+ if !rule_error?(:phone) && user_repository.exists?(phone: value)
48
+ key.failure(I18n.t(:taken, scope: I18N_SCOPE))
49
+ end
50
+ end
51
+
52
+ # Validation for password.
53
+ # Check if the confirmation matches the password.
54
+ rule(:password).validate(:password_format)
55
+ rule(:password, :password_confirmation) do
56
+ if !rule_error?(:password) && values[:password] != values[:password_confirmation]
57
+ key(:password_confirmation).failure(I18n.t(:password_confirmation, scope: I18N_SCOPE))
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Entities
5
+ # Auction Relations class. This return simple objects with attribute readers
6
+ # to represent data in your auction.
7
+ class Auction < ROM::Struct
8
+ def initial_bid
9
+ Money.new(initial_bid_cents, initial_bid_currency)
10
+ end
11
+
12
+ def minimal_bid
13
+ Money.new(minimal_bid_cents, minimal_bid_currency)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Entities
5
+ # Bid Relations class. This return simple objects with attribute readers
6
+ # to represent data in your bid.
7
+ class Bid < ROM::Struct
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Entities
5
+ # Staff Relations class. This return simple objects with attribute readers
6
+ # to represent data in your staff.
7
+ class Staff < ROM::Struct
8
+ def active?
9
+ active
10
+ end
11
+
12
+ def inactive?
13
+ !active
14
+ end
15
+
16
+ def info
17
+ attributes.except(:password_digest)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Entities
5
+ # User Relations class. This return simple objects with attribute readers
6
+ # to represent data in your user.
7
+ class User < ROM::Struct
8
+ def active?
9
+ active
10
+ end
11
+
12
+ def inactive?
13
+ !active
14
+ end
15
+
16
+ def confirmed?
17
+ confirmed_at.present?
18
+ end
19
+
20
+ def email_confirmed?
21
+ email_confirmation_at.present?
22
+ end
23
+
24
+ def phone_confirmed?
25
+ phone_confirmation_at.present?
26
+ end
27
+
28
+ def info
29
+ attributes.except(:password_digest)
30
+ end
31
+
32
+ def balance
33
+ Money.new(balance_cents, balance_currency)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Events
5
+ # Event class to register business events on system.
6
+ # @see https://dry-rb.org/gems/dry-events/main/
7
+ class App
8
+ # @!parser include Dry::Events::Publisher[:app]
9
+ include Dry::Events::Publisher[:app]
10
+
11
+ register_event("auctions.created")
12
+ register_event("auctions.started")
13
+ register_event("auctions.finished")
14
+ register_event("auctions.paused")
15
+ register_event("auctions.unpaused")
16
+
17
+ register_event("bids.created")
18
+
19
+ register_event("staffs.authentication")
20
+ register_event("staffs.registration")
21
+
22
+ register_event("users.authentication")
23
+ register_event("users.registration")
24
+ register_event("users.confirmation")
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Events
5
+ # Event class that can listen business events.
6
+ # @see https://dry-rb.org/gems/dry-events/main/#event-listeners
7
+ class Listener
8
+ # Listener for to *auctions.created* event.
9
+ # @param auction [ROM::Struct::Auction] the auction object
10
+ def on_auctions_created(auction)
11
+ logger("Create auction with: #{auction.to_h}")
12
+ end
13
+
14
+ # Listener for to *auctions.started* event.
15
+ # @param auction [ROM::Struct::Auction] the auction object
16
+ def on_auctions_started(auction)
17
+ logger("Started auction with: #{auction.to_h}")
18
+ end
19
+
20
+ # Listener for to *auctions.finished* event.
21
+ # @param auction [ROM::Struct::Auction] the auction object
22
+ def on_auctions_finished(auction)
23
+ logger("Finished auction: #{auction.to_h}")
24
+ end
25
+
26
+ # Listener for to *auctions.paused* event.
27
+ # @param event [ROM::Struct::Auction] the auction object
28
+ def on_auctions_paused(auction)
29
+ logger("Paused auction with: #{auction.to_h}")
30
+ end
31
+
32
+ # Listener for to *auctions.unpaused* event.
33
+ # @param event [ROM::Struct::Auction] the auction object
34
+ def on_auctions_unpaused(auction)
35
+ logger("Unpaused auction with: #{auction.to_h}")
36
+ end
37
+
38
+ # Listener for to *bids.created* event.
39
+ # @param event [Integer] Auction ID
40
+ def on_bids_created(bid)
41
+ logger("Create bid with: #{bid.to_h}")
42
+ end
43
+
44
+ # Listener for to *staffs.authentication* event.
45
+ # @param attributes [Hash] Authentication attributes
46
+ # @option staff_id [Integer] Staff ID
47
+ # @option time [DateTime] Authentication time
48
+ def on_staffs_authentication(attributes)
49
+ logger("Staff #{attributes[:staff_id]} authenticated on: #{attributes[:time].iso8601}")
50
+ end
51
+
52
+ # Listener for to *staffs.registration* event.
53
+ # @param user [ROM::Struct::Staff] the staff object
54
+ def on_staffs_registration(staff)
55
+ logger("New registered staff: #{staff.to_h}")
56
+ end
57
+
58
+ # Listener for to *users.registration* event.
59
+ # @param user [ROM::Struct::User] the user object
60
+ def on_users_registration(user)
61
+ logger("New registered user: #{user.to_h}")
62
+ end
63
+
64
+ # Listener for to *users.authentication* event.
65
+ # @param attributes [Hash] Authentication attributes
66
+ # @option user_id [Integer] User ID
67
+ # @option time [DateTime] Authentication time
68
+ def on_users_authentication(attributes)
69
+ logger("User #{attributes[:user_id]} authenticated on: #{attributes[:time].iso8601}")
70
+ end
71
+
72
+ # Listener for to *users.confirmation* event.
73
+ # @param attributes [Hash] Confirmation attributes
74
+ # @option user_id [Integer] User ID
75
+ # @option time [DateTime] Authentication time
76
+ def on_users_confirmation(attributes)
77
+ logger("User #{attributes[:user_id]} confirmed at: #{attributes[:time].iso8601}")
78
+ end
79
+
80
+ private
81
+
82
+ # Append message to system log.
83
+ # @param message [String] the message
84
+ def logger(message)
85
+ Application[:logger].info(message)
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Operations
5
+ module AuctionContext
6
+ ##
7
+ # Operation class for creating auctions.
8
+ #
9
+ class CreateOperation < AuctionFunCore::Operations::Base
10
+ include Import["repos.auction_context.auction_repository"]
11
+ include Import["contracts.auction_context.create_contract"]
12
+ include Import["workers.operations.auction_context.processor.start_operation_job"]
13
+
14
+ # @todo Add custom doc
15
+ def self.call(attributes, &block)
16
+ operation = new.call(attributes)
17
+
18
+ return operation unless block
19
+
20
+ Dry::Matcher::ResultMatcher.call(operation, &block)
21
+ end
22
+
23
+ def call(attributes)
24
+ values = yield validate(attributes)
25
+
26
+ auction_repository.transaction do |_t|
27
+ @auction = yield persist(values)
28
+ yield scheduled_start_auction(@auction)
29
+ yield publish_auctions_created(@auction)
30
+ end
31
+
32
+ Success(@auction)
33
+ end
34
+
35
+ private
36
+
37
+ # Calls the auction creation contract class to perform the validation
38
+ # of the informed attributes.
39
+ # @param attrs [Hash] auction attributes
40
+ # @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure]
41
+ def validate(attrs)
42
+ contract = create_contract.call(attrs)
43
+
44
+ return Failure(contract.errors.to_h) if contract.failure?
45
+
46
+ Success(contract.to_h)
47
+ end
48
+
49
+ # Calls the auction repository class to persist the attributes in the database.
50
+ # @param result [Hash] Auction validated attributes
51
+ # @return [ROM::Struct::Auction]
52
+ def persist(result)
53
+ Success(auction_repository.create(result))
54
+ end
55
+
56
+ # Triggers the publication of event *auctions.created*.
57
+ # @param auction [Hash] Auction persisted attributes
58
+ # @return [Dry::Monads::Result::Success]
59
+ def publish_auctions_created(auction)
60
+ Application[:event].publish("auctions.created", auction.to_h)
61
+
62
+ Success()
63
+ end
64
+
65
+ # Calls the background job class that will schedule the start of the auction.
66
+ # Added a small delay to perform operations (such as sending broadcasts and/or other operations).
67
+ # @param auction [ROM::Struct::Auction]
68
+ # @return [String] sidekiq jid
69
+ def scheduled_start_auction(auction)
70
+ perform_at = auction.started_at
71
+
72
+ Success(start_operation_job.class.perform_at(perform_at, auction.id))
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Operations
5
+ module AuctionContext
6
+ module Processor
7
+ ##
8
+ # Operation class for dispatch finish auction.
9
+ # By default, this change auction status from 'running' to 'finished'.
10
+ #
11
+ class FinishOperation < AuctionFunCore::Operations::Base
12
+ include Import["repos.auction_context.auction_repository"]
13
+ include Import["contracts.auction_context.processor.finish_contract"]
14
+
15
+ # @todo Add custom doc
16
+ def self.call(auction_id, &block)
17
+ operation = new.call(auction_id)
18
+
19
+ return operation unless block
20
+
21
+ Dry::Matcher::ResultMatcher.call(operation, &block)
22
+ end
23
+
24
+ # It only performs the basic processing of completing an auction.
25
+ # It just changes the status at the database level and triggers the finished event.
26
+ # @param auction_id [Integer] Auction ID
27
+ # @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure]
28
+ def call(auction_id)
29
+ yield validate(auction_id: auction_id)
30
+
31
+ auction_repository.transaction do |_t|
32
+ @auction, _ = auction_repository.update(auction_id, status: "finished")
33
+
34
+ publish_auction_finish_event(@auction)
35
+ end
36
+
37
+ Success(@auction)
38
+ end
39
+
40
+ private
41
+
42
+ # Calls the finish contract class to perform the validation
43
+ # of the informed attributes.
44
+ # @param attributes [Hash] auction attributes
45
+ # @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure]
46
+ def validate(attributes)
47
+ contract = finish_contract.call(attributes)
48
+
49
+ return Failure(contract.errors.to_h) if contract.failure?
50
+
51
+ Success(contract.to_h)
52
+ end
53
+
54
+ def publish_auction_finish_event(auction)
55
+ Application[:event].publish("auctions.finished", auction.to_h)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Operations
5
+ module AuctionContext
6
+ module Processor
7
+ ##
8
+ # Operation class for dispatch pause auction.
9
+ # By default, this change auction status from 'running' to 'paused'.
10
+ #
11
+ class PauseOperation < AuctionFunCore::Operations::Base
12
+ include Import["repos.auction_context.auction_repository"]
13
+ include Import["contracts.auction_context.processor.pause_contract"]
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
+ def call(attributes)
25
+ attrs = yield validate(attributes)
26
+
27
+ auction_repository.transaction do |_t|
28
+ @auction, _ = auction_repository.update(attrs[:auction_id], status: "paused")
29
+
30
+ publish_auction_pause_event(@auction)
31
+ end
32
+
33
+ Success(attrs[:auction_id])
34
+ end
35
+
36
+ private
37
+
38
+ # Calls the finish contract class to perform the validation
39
+ # of the informed attributes.
40
+ # @param attrs [Hash] auction attributes
41
+ # @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure]
42
+ def validate(attributes)
43
+ contract = pause_contract.call(attributes)
44
+
45
+ return Failure(contract.errors.to_h) if contract.failure?
46
+
47
+ Success(contract.to_h)
48
+ end
49
+
50
+ def publish_auction_pause_event(auction)
51
+ Application[:event].publish("auctions.paused", auction.to_h)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Operations
5
+ module AuctionContext
6
+ module Processor
7
+ ##
8
+ # Operation class for dispatch start auction.
9
+ # By default, this change auction status from 'scheduled' to 'running'
10
+ # and schedule a job to execute the auction finalization when auction is not penny.
11
+ class StartOperation < AuctionFunCore::Operations::Base
12
+ include Import["repos.auction_context.auction_repository"]
13
+ include Import["contracts.auction_context.processor.start_contract"]
14
+ include Import["workers.operations.auction_context.processor.finish_operation_job"]
15
+
16
+ # @todo Add custom doc
17
+ def self.call(attributes, &block)
18
+ operation = new.call(attributes)
19
+
20
+ return operation unless block
21
+
22
+ Dry::Matcher::ResultMatcher.call(operation, &block)
23
+ end
24
+
25
+ # @param [Hash] attributes needed to start a auction
26
+ # @option opts [String] :auction_id auction id
27
+ # @option opts [String] :auction_kind auction kind
28
+ # @option opts [Integer] :stopwatch auction stopwatch
29
+ # @return [ROM::Struct::Auction] auction object
30
+ def call(attributes)
31
+ attrs = yield validate(attributes)
32
+
33
+ auction_repository.transaction do |_t|
34
+ @auction, _ = auction_repository.update(attrs[:auction_id], update_params(attrs))
35
+
36
+ yield publish_auction_start_event(@auction)
37
+ yield scheduled_finished_auction(@auction)
38
+ end
39
+
40
+ Success(@auction)
41
+ end
42
+
43
+ private
44
+
45
+ # Calls the start contract class to perform the validation
46
+ # of the informed attributes.
47
+ # @param attributes [Hash] auction attributes
48
+ # @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure]
49
+ def validate(attributes)
50
+ contract = start_contract.call(attributes)
51
+
52
+ return Failure(contract.errors.to_h) if contract.failure?
53
+
54
+ Success(contract.to_h)
55
+ end
56
+
57
+ # Updates the status of the auction and depending on the type of auction,
58
+ # it already sets the final date.
59
+ # @param attrs [Hash] auction attributes
60
+ # @return [Hash]
61
+ def update_params(attrs)
62
+ return {status: "running"} unless attrs[:kind] == "penny"
63
+
64
+ {status: "running", finished_at: attrs[:stopwatch].seconds.from_now}
65
+ end
66
+
67
+ def publish_auction_start_event(auction)
68
+ Success(Application[:event].publish("auctions.started", auction.to_h))
69
+ end
70
+
71
+ # Calls the background job class that will schedule the finish of the auction.
72
+ # Added a small delay to perform operations (such as sending broadcasts and/or other operations).
73
+ # @param auction [ROM::Struct::Auction]
74
+ # @return [String] sidekiq jid
75
+ def scheduled_finished_auction(auction)
76
+ return Success() if auction.kind == "penny"
77
+
78
+ Success(finish_operation_job.class.perform_at(auction.finished_at, auction.id))
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Operations
5
+ module AuctionContext
6
+ module Processor
7
+ ##
8
+ # Operation class for dispatch unpause auction.
9
+ # By default, this change auction status from 'paused' to 'running'.
10
+ #
11
+ class UnpauseOperation < AuctionFunCore::Operations::Base
12
+ include Import["repos.auction_context.auction_repository"]
13
+ include Import["contracts.auction_context.processor.unpause_contract"]
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
+ def call(attributes)
25
+ attrs = yield validate(attributes)
26
+
27
+ auction_repository.transaction do |_t|
28
+ @auction, _ = auction_repository.update(attrs[:auction_id], status: "running")
29
+
30
+ publish_auction_unpause_event(@auction)
31
+ end
32
+
33
+ Success(attrs[:auction_id])
34
+ end
35
+
36
+ private
37
+
38
+ # Calls the unpause contract class to perform the validation
39
+ # of the informed attributes.
40
+ # @param attrs [Hash] auction attributes
41
+ # @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure]
42
+ def validate(attributes)
43
+ contract = unpause_contract.call(attributes)
44
+
45
+ return Failure(contract.errors.to_h) if contract.failure?
46
+
47
+ Success(contract.to_h)
48
+ end
49
+
50
+ def publish_auction_unpause_event(auction)
51
+ Application[:event].publish("auctions.unpaused", auction.to_h)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Operations
5
+ # Abstract base class for operations.
6
+ # @abstract
7
+ class Base
8
+ include Dry::Monads[:do, :maybe, :result, :try]
9
+ end
10
+ end
11
+ end