auction_fun_core 0.8.5 → 0.8.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/auction_fun_core.gemspec +1 -0
  4. data/db/migrate/20240229143000_create_auctions.rb +1 -0
  5. data/db/seeds.rb +87 -36
  6. data/i18n/en-US/contracts/contracts.en-US.yml +9 -0
  7. data/i18n/en-US/mail/application.en-US.yml +59 -0
  8. data/i18n/en-US/mail/auction_context/post_auction/participant.en-US.yml +13 -0
  9. data/i18n/en-US/mail/auction_context/post_auction/winner.en-US.yml +13 -0
  10. data/i18n/pt-BR/contracts/contracts.pt-BR.yml +9 -0
  11. data/i18n/pt-BR/mail/application.pt-BR.yml +59 -0
  12. data/i18n/pt-BR/mail/auction_context/post_auction/participant.pt-BR.yml +13 -0
  13. data/i18n/pt-BR/mail/auction_context/post_auction/winner.pt-BR.yml +13 -0
  14. data/i18n/pt-BR/mail/user_context/registration.pt-BR.yml +0 -4
  15. data/lib/auction_fun_core/contracts/auction_context/post_auction/participant_contract.rb +42 -0
  16. data/lib/auction_fun_core/contracts/auction_context/post_auction/winner_contract.rb +41 -0
  17. data/lib/auction_fun_core/contracts/auction_context/processor/finish/closed_contract.rb +47 -0
  18. data/lib/auction_fun_core/contracts/auction_context/processor/finish/penny_contract.rb +48 -0
  19. data/lib/auction_fun_core/contracts/auction_context/processor/finish/standard_contract.rb +48 -0
  20. data/lib/auction_fun_core/entities/auction.rb +6 -0
  21. data/lib/auction_fun_core/operations/auction_context/create_operation.rb +12 -2
  22. data/lib/auction_fun_core/operations/auction_context/post_auction/participant_operation.rb +52 -0
  23. data/lib/auction_fun_core/operations/auction_context/post_auction/winner_operation.rb +51 -0
  24. data/lib/auction_fun_core/operations/auction_context/processor/finish/closed_operation.rb +100 -0
  25. data/lib/auction_fun_core/operations/auction_context/processor/finish/penny_operation.rb +100 -0
  26. data/lib/auction_fun_core/operations/auction_context/processor/finish/standard_operation.rb +102 -0
  27. data/lib/auction_fun_core/operations/auction_context/processor/start_operation.rb +23 -11
  28. data/lib/auction_fun_core/operations/bid_context/create_bid_penny_operation.rb +57 -7
  29. data/lib/auction_fun_core/operations/staff_context/registration_operation.rb +10 -0
  30. data/lib/auction_fun_core/relations/auctions.rb +167 -26
  31. data/lib/auction_fun_core/relations/staffs.rb +1 -1
  32. data/lib/auction_fun_core/services/mail/auction_context/post_auction/participant_mailer.rb +31 -0
  33. data/lib/auction_fun_core/services/mail/auction_context/post_auction/winner_mailer.rb +31 -0
  34. data/lib/auction_fun_core/services/mail/templates/auction_context/post_auction/participant.html.erb +173 -0
  35. data/lib/auction_fun_core/services/mail/templates/auction_context/post_auction/winner.html.erb +174 -0
  36. data/lib/auction_fun_core/services/mail/templates/user_context/registration.html.erb +2 -2
  37. data/lib/auction_fun_core/version.rb +1 -1
  38. data/lib/auction_fun_core/workers/application_job.rb +2 -0
  39. data/lib/auction_fun_core/workers/operations/auction_context/post_auction/participant_operation_job.rb +33 -0
  40. data/lib/auction_fun_core/workers/operations/auction_context/post_auction/winner_operation_job.rb +33 -0
  41. data/lib/auction_fun_core/workers/operations/auction_context/processor/finish/closed_operation_job.rb +34 -0
  42. data/lib/auction_fun_core/workers/operations/auction_context/processor/finish/penny_operation_job.rb +37 -0
  43. data/lib/auction_fun_core/workers/operations/auction_context/processor/finish/standard_operation_job.rb +34 -0
  44. data/lib/auction_fun_core/workers/services/mail/auction_context/post_auction/participant_mailer_job.rb +51 -0
  45. data/lib/auction_fun_core/workers/services/mail/auction_context/post_auction/winner_mailer_job.rb +51 -0
  46. data/system/providers/background_job.rb +19 -0
  47. metadata +43 -5
  48. data/lib/auction_fun_core/contracts/auction_context/processor/finish_contract.rb +0 -27
  49. data/lib/auction_fun_core/operations/auction_context/processor/finish_operation.rb +0 -61
  50. data/lib/auction_fun_core/workers/operations/auction_context/processor/finish_operation_job.rb +0 -32
@@ -5,6 +5,8 @@ module AuctionFunCore
5
5
  # Auction Relations class. This return simple objects with attribute readers
6
6
  # to represent data in your auction.
7
7
  class Auction < ROM::Struct
8
+ INQUIRER_ATTRIBUTES = Relations::Auctions::STATUSES.values.freeze
9
+
8
10
  def initial_bid
9
11
  Money.new(initial_bid_cents, initial_bid_currency)
10
12
  end
@@ -12,6 +14,10 @@ module AuctionFunCore
12
14
  def minimal_bid
13
15
  Money.new(minimal_bid_cents, minimal_bid_currency)
14
16
  end
17
+
18
+ def winner?
19
+ winner_id.present?
20
+ end
15
21
  end
16
22
  end
17
23
  end
@@ -21,7 +21,8 @@ module AuctionFunCore
21
21
  end
22
22
 
23
23
  def call(attributes)
24
- values = yield validate(attributes)
24
+ values = yield validate_contract(attributes)
25
+ values = yield assign_default_values(values)
25
26
 
26
27
  auction_repository.transaction do |_t|
27
28
  @auction = yield persist(values)
@@ -38,7 +39,7 @@ module AuctionFunCore
38
39
  # of the informed attributes.
39
40
  # @param attrs [Hash] auction attributes
40
41
  # @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure]
41
- def validate(attrs)
42
+ def validate_contract(attrs)
42
43
  contract = create_contract.call(attrs)
43
44
 
44
45
  return Failure(contract.errors.to_h) if contract.failure?
@@ -46,6 +47,15 @@ module AuctionFunCore
46
47
  Success(contract.to_h)
47
48
  end
48
49
 
50
+ # By default, the auction status is set to 'scheduled'.
51
+ # @todo Refactor this method in the future to consider the status as of the auction start date.
52
+ # @param attrs [Hash] auction attributes
53
+ # @return [Dry::Monads::Result::Success]
54
+ def assign_default_values(attrs)
55
+ attrs[:status] = "scheduled"
56
+ Success(attrs)
57
+ end
58
+
49
59
  # Calls the auction repository class to persist the attributes in the database.
50
60
  # @param result [Hash] Auction validated attributes
51
61
  # @return [ROM::Struct::Auction]
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Operations
5
+ module AuctionContext
6
+ module PostAuction
7
+ ##
8
+ # Operation class for finish auctions.
9
+ #
10
+ class ParticipantOperation < AuctionFunCore::Operations::Base
11
+ include Import["repos.user_context.user_repository"]
12
+ include Import["contracts.auction_context.post_auction.participant_contract"]
13
+ include Import["workers.services.mail.auction_context.post_auction.participant_mailer_job"]
14
+
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
+ ## @todo Add more actions
24
+ # Send email to participant with auction statistics.
25
+ def call(attributes)
26
+ auction, participant = yield validate_contract(attributes)
27
+
28
+ user_repository.transaction do |_t|
29
+ yield send_participant_email_with_statistics_and_payment_instructions(auction.id, participant.id)
30
+ end
31
+
32
+ Success([auction, participant])
33
+ end
34
+
35
+ private
36
+
37
+ def validate_contract(attributes)
38
+ contract = participant_contract.call(attributes)
39
+
40
+ return Failure(contract.errors.to_h) if contract.failure?
41
+
42
+ Success([contract.context[:auction], contract.context[:participant]])
43
+ end
44
+
45
+ def send_participant_email_with_statistics_and_payment_instructions(auction_id, participant_id)
46
+ Success(participant_mailer_job.class.perform_async(auction_id, participant_id))
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Operations
5
+ module AuctionContext
6
+ module PostAuction
7
+ ##
8
+ # Operation class for finish auctions.
9
+ #
10
+ class WinnerOperation < AuctionFunCore::Operations::Base
11
+ include Import["repos.user_context.user_repository"]
12
+ include Import["contracts.auction_context.post_auction.winner_contract"]
13
+ include Import["workers.services.mail.auction_context.post_auction.winner_mailer_job"]
14
+
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
+ ## @todo Add doc
24
+ def call(attributes)
25
+ auction, winner = yield validate_contract(attributes)
26
+
27
+ user_repository.transaction do |_t|
28
+ send_winner_email_with_statistics_and_payment_instructions(auction.id, winner.id)
29
+ end
30
+
31
+ Success([auction, winner])
32
+ end
33
+
34
+ private
35
+
36
+ def validate_contract(attributes)
37
+ contract = winner_contract.call(attributes)
38
+
39
+ return Failure(contract.errors.to_h) if contract.failure?
40
+
41
+ Success([contract.context[:auction], contract.context[:winner]])
42
+ end
43
+
44
+ def send_winner_email_with_statistics_and_payment_instructions(auction_id, winner_id)
45
+ Success(winner_mailer_job.class.perform_async(auction_id, winner_id))
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Operations
5
+ module AuctionContext
6
+ module Processor
7
+ module Finish
8
+ ##
9
+ # Operation class for finalizing a closed auction.
10
+ # By default, this change auction status from 'running' to 'finished'.
11
+ #
12
+ class ClosedOperation < AuctionFunCore::Operations::Base
13
+ include Import["repos.auction_context.auction_repository"]
14
+ include Import["contracts.auction_context.processor.finish.closed_contract"]
15
+ include Import["workers.operations.auction_context.post_auction.winner_operation_job"]
16
+ include Import["workers.operations.auction_context.post_auction.participant_operation_job"]
17
+
18
+ # @todo Add custom doc
19
+ def self.call(attributes, &block)
20
+ operation = new.call(attributes)
21
+
22
+ return operation unless block
23
+
24
+ Dry::Matcher::ResultMatcher.call(operation, &block)
25
+ end
26
+
27
+ # It only performs the basic processing of completing an auction.
28
+ # It just changes the status at the database level and triggers the finished event.
29
+ # @param auction_id [Integer] Auction ID
30
+ # @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure]
31
+ def call(attributes)
32
+ auction = yield validate_contract(attributes)
33
+ summary = yield load_closed_auction_winners_and_participants(auction.id)
34
+ update_auction_attributes = yield update_finished_auction(auction, summary)
35
+
36
+ auction_repository.transaction do |_t|
37
+ auction, _ = auction_repository.update(auction.id, update_auction_attributes)
38
+
39
+ yield winner_operation(auction.id, summary.winner_id)
40
+
41
+ summary.participant_ids.each do |participant_id|
42
+ yield participant_operation(auction.id, participant_id)
43
+ end
44
+
45
+ publish_auction_finish_event(auction)
46
+ end
47
+
48
+ Success(auction)
49
+ end
50
+
51
+ private
52
+
53
+ # Calls the finish contract class to perform the validation
54
+ # of the informed attributes.
55
+ # @param attributes [Hash] auction attributes
56
+ # @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure]
57
+ def validate_contract(attributes)
58
+ contract = closed_contract.call(attributes)
59
+
60
+ return Failure(contract.errors.to_h) if contract.failure?
61
+
62
+ Success(contract.context[:auction])
63
+ end
64
+
65
+ def load_closed_auction_winners_and_participants(auction_id)
66
+ summary = relation.load_closed_auction_winners_and_participants(auction_id).first
67
+
68
+ Success(summary)
69
+ end
70
+
71
+ def update_finished_auction(auction, summary)
72
+ attrs = {status: "finished"}
73
+ attrs[:winner_id] = summary.winner_id if summary.winner_id.present?
74
+
75
+ Success(attrs)
76
+ end
77
+
78
+ def relation
79
+ AuctionFunCore::Application[:container].relations[:auctions]
80
+ end
81
+
82
+ def winner_operation(auction_id, winner_id)
83
+ return Success() if winner_id.blank?
84
+
85
+ Success(winner_operation_job.class.perform_async(auction_id, winner_id))
86
+ end
87
+
88
+ def participant_operation(auction_id, participant_id)
89
+ Success(participant_operation_job.class.perform_async(auction_id, participant_id))
90
+ end
91
+
92
+ def publish_auction_finish_event(auction)
93
+ Application[:event].publish("auctions.finished", auction.to_h)
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Operations
5
+ module AuctionContext
6
+ module Processor
7
+ module Finish
8
+ ##
9
+ # Operation class for finalizing a penny auction.
10
+ # By default, this change auction status from 'running' to 'finished'.
11
+ #
12
+ class PennyOperation < AuctionFunCore::Operations::Base
13
+ include Import["repos.auction_context.auction_repository"]
14
+ include Import["contracts.auction_context.processor.finish.penny_contract"]
15
+ include Import["workers.operations.auction_context.post_auction.winner_operation_job"]
16
+ include Import["workers.operations.auction_context.post_auction.participant_operation_job"]
17
+
18
+ # @todo Add custom doc
19
+ def self.call(attributes, &block)
20
+ operation = new.call(attributes)
21
+
22
+ return operation unless block
23
+
24
+ Dry::Matcher::ResultMatcher.call(operation, &block)
25
+ end
26
+
27
+ # It only performs the basic processing of completing an auction.
28
+ # It just changes the status at the database level and triggers the finished event.
29
+ # @param auction_id [Integer] Auction ID
30
+ # @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure]
31
+ def call(attributes)
32
+ auction = yield validate_contract(attributes)
33
+ summary = yield load_penny_auction_winners_and_participants(auction.id)
34
+ update_auction_attributes = yield update_finished_auction(auction, summary)
35
+
36
+ auction_repository.transaction do |_t|
37
+ auction, _ = auction_repository.update(auction.id, update_auction_attributes)
38
+
39
+ yield winner_operation(auction.id, summary.winner_id)
40
+
41
+ summary.participant_ids.each do |participant_id|
42
+ yield participant_operation(auction.id, participant_id)
43
+ end
44
+
45
+ publish_auction_finish_event(auction)
46
+ end
47
+
48
+ Success(auction)
49
+ end
50
+
51
+ private
52
+
53
+ # Calls the finish contract class to perform the validation
54
+ # of the informed attributes.
55
+ # @param attributes [Hash] auction attributes
56
+ # @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure]
57
+ def validate_contract(attributes)
58
+ contract = penny_contract.call(attributes)
59
+
60
+ return Failure(contract.errors.to_h) if contract.failure?
61
+
62
+ Success(contract.context[:auction])
63
+ end
64
+
65
+ def load_penny_auction_winners_and_participants(auction_id)
66
+ summary = relation.load_penny_auction_winners_and_participants(auction_id).first
67
+
68
+ Success(summary)
69
+ end
70
+
71
+ def update_finished_auction(auction, summary)
72
+ attrs = {status: "finished"}
73
+ attrs[:winner_id] = summary.winner_id if summary.winner_id.present?
74
+
75
+ Success(attrs)
76
+ end
77
+
78
+ def relation
79
+ AuctionFunCore::Application[:container].relations[:auctions]
80
+ end
81
+
82
+ def winner_operation(auction_id, winner_id)
83
+ return Success() if winner_id.blank?
84
+
85
+ Success(winner_operation_job.class.perform_async(auction_id, winner_id))
86
+ end
87
+
88
+ def participant_operation(auction_id, participant_id)
89
+ Success(participant_operation_job.class.perform_async(auction_id, participant_id))
90
+ end
91
+
92
+ def publish_auction_finish_event(auction)
93
+ Application[:event].publish("auctions.finished", auction.to_h)
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Operations
5
+ module AuctionContext
6
+ module Processor
7
+ module Finish
8
+ ##
9
+ # Operation class for finalizing a standard auction.
10
+ # By default, this change auction status from 'running' to 'finished'.
11
+ #
12
+ class StandardOperation < AuctionFunCore::Operations::Base
13
+ include Import["repos.auction_context.auction_repository"]
14
+ include Import["contracts.auction_context.processor.finish.standard_contract"]
15
+ include Import["workers.operations.auction_context.post_auction.winner_operation_job"]
16
+ include Import["workers.operations.auction_context.post_auction.participant_operation_job"]
17
+
18
+ # @todo Add custom doc
19
+ def self.call(attributes, &block)
20
+ operation = new.call(attributes)
21
+
22
+ return operation unless block
23
+
24
+ Dry::Matcher::ResultMatcher.call(operation, &block)
25
+ end
26
+
27
+ # TODO: update doc
28
+ # It only performs the basic processing of completing an auction.
29
+ # It just changes the status at the database level and triggers the finished event.
30
+ # @param attrs [Hash] auction attributes
31
+ # @option auction_id [Integer] Auction ID
32
+ # @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure]
33
+ def call(attributes)
34
+ auction = yield validate_contract(attributes)
35
+ summary = yield load_standard_auction_winners_and_participants(auction.id)
36
+ update_auction_attributes = yield update_finished_auction(auction, summary)
37
+
38
+ auction_repository.transaction do |_t|
39
+ auction, _ = auction_repository.update(auction.id, update_auction_attributes)
40
+
41
+ yield winner_operation(auction.id, summary.winner_id)
42
+
43
+ summary.participant_ids.each do |participant_id|
44
+ yield participant_operation(auction.id, participant_id)
45
+ end
46
+
47
+ publish_auction_finish_event(auction)
48
+ end
49
+
50
+ Success(auction)
51
+ end
52
+
53
+ private
54
+
55
+ # Calls the finish standard contract class to perform the validation
56
+ # of the informed attributes.
57
+ # @param attributes [Hash] auction attributes
58
+ # @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure]
59
+ def validate_contract(attributes)
60
+ contract = standard_contract.call(attributes)
61
+
62
+ return Failure(contract.errors.to_h) if contract.failure?
63
+
64
+ Success(contract.context[:auction])
65
+ end
66
+
67
+ def load_standard_auction_winners_and_participants(auction_id)
68
+ summary = relation.load_standard_auction_winners_and_participants(auction_id).first
69
+
70
+ Success(summary)
71
+ end
72
+
73
+ def update_finished_auction(auction, summary)
74
+ attrs = {status: "finished"}
75
+ attrs[:winner_id] = summary.winner_id if summary.winner_id.present?
76
+
77
+ Success(attrs)
78
+ end
79
+
80
+ def relation
81
+ AuctionFunCore::Application[:container].relations[:auctions]
82
+ end
83
+
84
+ def winner_operation(auction_id, winner_id)
85
+ return Success() if winner_id.blank?
86
+
87
+ Success(winner_operation_job.class.perform_async(auction_id, winner_id))
88
+ end
89
+
90
+ def participant_operation(auction_id, participant_id)
91
+ Success(participant_operation_job.class.perform_async(auction_id, participant_id))
92
+ end
93
+
94
+ def publish_auction_finish_event(auction)
95
+ Application[:event].publish("auctions.finished", auction.to_h)
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -11,7 +11,9 @@ module AuctionFunCore
11
11
  class StartOperation < AuctionFunCore::Operations::Base
12
12
  include Import["repos.auction_context.auction_repository"]
13
13
  include Import["contracts.auction_context.processor.start_contract"]
14
- include Import["workers.operations.auction_context.processor.finish_operation_job"]
14
+ include Import["workers.operations.auction_context.processor.finish.closed_operation_job"]
15
+ include Import["workers.operations.auction_context.processor.finish.penny_operation_job"]
16
+ include Import["workers.operations.auction_context.processor.finish.standard_operation_job"]
15
17
 
16
18
  # @todo Add custom doc
17
19
  def self.call(attributes, &block)
@@ -28,10 +30,10 @@ module AuctionFunCore
28
30
  # @option opts [Integer] :stopwatch auction stopwatch
29
31
  # @return [ROM::Struct::Auction] auction object
30
32
  def call(attributes)
31
- attrs = yield validate(attributes)
33
+ auction, attrs = yield validate_contract(attributes)
32
34
 
33
35
  auction_repository.transaction do |_t|
34
- @auction, _ = auction_repository.update(attrs[:auction_id], update_params(attrs))
36
+ @auction, _ = auction_repository.update(auction.id, update_params(auction, attrs))
35
37
 
36
38
  yield publish_auction_start_event(@auction)
37
39
  yield scheduled_finished_auction(@auction)
@@ -46,36 +48,46 @@ module AuctionFunCore
46
48
  # of the informed attributes.
47
49
  # @param attributes [Hash] auction attributes
48
50
  # @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure]
49
- def validate(attributes)
51
+ def validate_contract(attributes)
50
52
  contract = start_contract.call(attributes)
51
53
 
52
54
  return Failure(contract.errors.to_h) if contract.failure?
53
55
 
54
- Success(contract.to_h)
56
+ Success([contract.context[:auction], contract.to_h])
55
57
  end
56
58
 
57
59
  # Updates the status of the auction and depending on the type of auction,
58
60
  # it already sets the final date.
59
61
  # @param attrs [Hash] auction attributes
60
62
  # @return [Hash]
61
- def update_params(attrs)
62
- return {status: "running"} unless attrs[:kind] == "penny"
63
+ def update_params(auction, attrs)
64
+ return {kind: auction.kind, status: "running"} unless attrs[:kind] == "penny"
63
65
 
64
- {status: "running", finished_at: attrs[:stopwatch].seconds.from_now}
66
+ {kind: auction.kind, status: "running", finished_at: attrs[:stopwatch].seconds.from_now}
65
67
  end
66
68
 
67
69
  def publish_auction_start_event(auction)
68
70
  Success(Application[:event].publish("auctions.started", auction.to_h))
69
71
  end
70
72
 
73
+ # TODO: Added a small delay to perform operations (such as sending broadcasts and/or other operations).
71
74
  # 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).
75
+ # In the case of the penny auction, the end of the auction occurs after the first timer resets.
73
76
  # @param auction [ROM::Struct::Auction]
74
77
  # @return [String] sidekiq jid
75
78
  def scheduled_finished_auction(auction)
76
- return Success() if auction.kind == "penny"
79
+ perform_at = auction.finished_at
77
80
 
78
- Success(finish_operation_job.class.perform_at(auction.finished_at, auction.id))
81
+ case auction.kind
82
+ in "penny"
83
+ perform_at = auction.started_at + auction.stopwatch.seconds
84
+
85
+ Success(penny_operation_job.class.perform_at(perform_at, auction.id))
86
+ in "standard"
87
+ Success(standard_operation_job.class.perform_at(perform_at, auction.id))
88
+ in "closed"
89
+ Success(closed_operation_job.class.perform_at(perform_at, auction.id))
90
+ end
79
91
  end
80
92
  end
81
93
  end
@@ -7,8 +7,10 @@ module AuctionFunCore
7
7
  # Operation class for create new bids for penny auctions.
8
8
  #
9
9
  class CreateBidPennyOperation < AuctionFunCore::Operations::Base
10
- include Import["contracts.bid_context.create_bid_penny_contract"]
11
10
  include Import["repos.bid_context.bid_repository"]
11
+ include Import["repos.auction_context.auction_repository"]
12
+ include Import["contracts.bid_context.create_bid_penny_contract"]
13
+ include Import["workers.operations.auction_context.processor.finish.penny_operation_job"]
12
14
 
13
15
  # @todo Add custom doc
14
16
  def self.call(attributes, &block)
@@ -21,10 +23,13 @@ module AuctionFunCore
21
23
 
22
24
  # @todo Add custom doc
23
25
  def call(attributes)
24
- values = yield validate(attributes)
26
+ auction, values = yield validate_contract(attributes)
25
27
 
26
28
  bid_repository.transaction do |_t|
27
29
  @bid = yield persist(values)
30
+ updated_auction = yield update_end_auction(auction)
31
+
32
+ yield reschedule_end_auction(updated_auction)
28
33
  yield publish_bid_created(@bid)
29
34
  end
30
35
 
@@ -35,19 +40,42 @@ module AuctionFunCore
35
40
  # of the informed attributes.
36
41
  # @param attrs [Hash] bid attributes
37
42
  # @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure]
38
- def validate(attrs)
43
+ def validate_contract(attrs)
39
44
  contract = create_bid_penny_contract.call(attrs)
40
45
 
41
46
  return Failure(contract.errors.to_h) if contract.failure?
42
47
 
43
- Success(contract.to_h)
48
+ Success([contract.context[:auction], contract.to_h])
49
+ end
50
+
51
+ # Updates the end time of an auction if it has already started.
52
+ #
53
+ # This method checks whether an auction is currently running. If the auction is running,
54
+ # it calculates a new end time based on the current time and the duration specified
55
+ # by the auction's stopwatch. The auction's finish time is then updated in the repository.
56
+ # If the auction has not started, it returns the auction as is without any modifications.
57
+ #
58
+ # @param auction [ROM::Struct::Auction] An instance of Auction to be checked and potentially updated.
59
+ # @return [Dry::Monads::Result::Success<ROM::Struct::Auction>, Dry::Monads::Result::Failure]
60
+ def update_end_auction(auction)
61
+ return Success(auction) unless started_auction?(auction)
62
+
63
+ updated_attributes = {
64
+ finished_at: Time.current + auction.stopwatch.seconds,
65
+ kind: auction.kind,
66
+ status: auction.status
67
+ }
68
+
69
+ updated_auction, _ = auction_repository.update(auction.id, updated_attributes)
70
+
71
+ Success(updated_auction)
44
72
  end
45
73
 
46
74
  # Calls the bid repository class to persist the attributes in the database.
47
75
  # @param result [Hash] Bid validated attributes
48
- # @return [ROM::Struct::Bid]
49
- def persist(result)
50
- Success(bid_repository.create(result))
76
+ # @return [Dry::Monads::Result::Success<ROM::Struct::Bid>, Dry::Monads::Result::Failure]
77
+ def persist(values)
78
+ Success(bid_repository.create(values))
51
79
  end
52
80
 
53
81
  # Triggers the publication of event *bids.created*.
@@ -58,6 +86,28 @@ module AuctionFunCore
58
86
 
59
87
  Success()
60
88
  end
89
+
90
+ # TODO: Added a small delay to perform operations (such as sending broadcasts and/or other operations).
91
+ # Reschedules the end time of an auction's background job if the auction has already started.
92
+ #
93
+ # This method checks if the auction is running. If so, it schedules a background job to
94
+ # execute at the auction's current finish time using the job class defined by the
95
+ # penny_operation_job attribute of the auction. If the auction has not started, it
96
+ # simply returns a Success object with no parameters.
97
+ #
98
+ # @return [Dry::Monads::Result::Success<ROM::Struct::Auction>, Dry::Monads::Result::Success<String>]
99
+ def reschedule_end_auction(auction)
100
+ # binding.pry
101
+ return Success(auction) unless started_auction?(auction)
102
+
103
+ perform_at = auction.finished_at
104
+
105
+ Success(penny_operation_job.class.perform_at(perform_at, auction.id))
106
+ end
107
+
108
+ def started_auction?(auction)
109
+ auction.status == "running"
110
+ end
61
111
  end
62
112
  end
63
113
  end
@@ -21,6 +21,7 @@ module AuctionFunCore
21
21
  # @todo Add custom doc
22
22
  def call(attributes)
23
23
  values = yield validate_contract(attributes)
24
+ values = yield assign_default_values(values)
24
25
  values_with_encrypt_password = yield encrypt_password(values)
25
26
 
26
27
  staff_repository.transaction do |_t|
@@ -44,6 +45,15 @@ module AuctionFunCore
44
45
  Success(contract.to_h)
45
46
  end
46
47
 
48
+ # By default, there can only be a single root staff. All other members have their type set to 'common'.
49
+ # @param attrs [Hash] user attributes
50
+ # @return [Dry::Monads::Result::Success]
51
+ def assign_default_values(attrs)
52
+ attrs[:kind] = "common"
53
+
54
+ Success(attrs)
55
+ end
56
+
47
57
  # Transforms the password attribute, encrypting it to be saved in the database.
48
58
  # @param result [Hash] Staff valid contract attributes
49
59
  # @return [Hash] Valid staff database