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,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