auction_fun_core 0.8.5 → 0.8.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/.standard.yml +2 -0
  3. data/CHANGELOG.md +48 -0
  4. data/Procfile +1 -0
  5. data/README.md +34 -32
  6. data/Rakefile +1 -1
  7. data/auction_fun_core.gemspec +1 -0
  8. data/db/migrate/20240229143000_create_auctions.rb +1 -0
  9. data/db/seeds.rb +87 -36
  10. data/i18n/en-US/contracts/contracts.en-US.yml +12 -0
  11. data/i18n/en-US/mail/application.en-US.yml +66 -0
  12. data/i18n/en-US/mail/auction_context/post_auction/participant.en-US.yml +13 -0
  13. data/i18n/en-US/mail/auction_context/post_auction/winner.en-US.yml +13 -0
  14. data/i18n/en-US/mail/auction_context/pre_auction/auction_start_reminder.en-US.yml +11 -0
  15. data/i18n/pt-BR/contracts/contracts.pt-BR.yml +12 -0
  16. data/i18n/pt-BR/mail/application.pt-BR.yml +66 -0
  17. data/i18n/pt-BR/mail/auction_context/post_auction/participant.pt-BR.yml +13 -0
  18. data/i18n/pt-BR/mail/auction_context/post_auction/winner.pt-BR.yml +13 -0
  19. data/i18n/pt-BR/mail/auction_context/pre_auction/auction_start_reminder.pt-BR.yml +11 -0
  20. data/i18n/pt-BR/mail/user_context/registration.pt-BR.yml +0 -4
  21. data/lib/auction_fun_core/business/configuration.rb +31 -0
  22. data/lib/auction_fun_core/business/token_generator.rb +19 -1
  23. data/lib/auction_fun_core/contracts/application_contract.rb +9 -1
  24. data/lib/auction_fun_core/contracts/auction_context/create_contract.rb +35 -20
  25. data/lib/auction_fun_core/contracts/auction_context/post_auction/participant_contract.rb +64 -0
  26. data/lib/auction_fun_core/contracts/auction_context/post_auction/winner_contract.rb +62 -0
  27. data/lib/auction_fun_core/contracts/auction_context/pre_auction/auction_start_reminder_contract.rb +48 -0
  28. data/lib/auction_fun_core/contracts/auction_context/processor/finish/closed_contract.rb +59 -0
  29. data/lib/auction_fun_core/contracts/auction_context/processor/finish/penny_contract.rb +60 -0
  30. data/lib/auction_fun_core/contracts/auction_context/processor/finish/standard_contract.rb +60 -0
  31. data/lib/auction_fun_core/contracts/auction_context/processor/pause_contract.rb +16 -4
  32. data/lib/auction_fun_core/contracts/auction_context/processor/start_contract.rb +17 -5
  33. data/lib/auction_fun_core/contracts/auction_context/processor/unpause_contract.rb +16 -4
  34. data/lib/auction_fun_core/contracts/bid_context/create_bid_closed_contract.rb +20 -11
  35. data/lib/auction_fun_core/contracts/bid_context/create_bid_penny_contract.rb +18 -9
  36. data/lib/auction_fun_core/contracts/bid_context/create_bid_standard_contract.rb +19 -10
  37. data/lib/auction_fun_core/contracts/staff_context/authentication_contract.rb +18 -4
  38. data/lib/auction_fun_core/contracts/staff_context/registration_contract.rb +20 -8
  39. data/lib/auction_fun_core/contracts/user_context/authentication_contract.rb +18 -4
  40. data/lib/auction_fun_core/contracts/user_context/email_confirmation_contract.rb +17 -2
  41. data/lib/auction_fun_core/contracts/user_context/phone_confirmation_contract.rb +17 -2
  42. data/lib/auction_fun_core/contracts/user_context/registration_contract.rb +26 -8
  43. data/lib/auction_fun_core/entities/auction.rb +52 -2
  44. data/lib/auction_fun_core/entities/bid.rb +3 -2
  45. data/lib/auction_fun_core/entities/staff.rb +15 -2
  46. data/lib/auction_fun_core/entities/user.rb +31 -2
  47. data/lib/auction_fun_core/events/app.rb +8 -2
  48. data/lib/auction_fun_core/events/listener.rb +19 -16
  49. data/lib/auction_fun_core/operations/auction_context/create_operation.rb +37 -11
  50. data/lib/auction_fun_core/operations/auction_context/post_auction/participant_operation.rb +85 -0
  51. data/lib/auction_fun_core/operations/auction_context/post_auction/winner_operation.rb +85 -0
  52. data/lib/auction_fun_core/operations/auction_context/pre_auction/auction_start_reminder_operation.rb +96 -0
  53. data/lib/auction_fun_core/operations/auction_context/processor/finish/closed_operation.rb +172 -0
  54. data/lib/auction_fun_core/operations/auction_context/processor/finish/penny_operation.rb +171 -0
  55. data/lib/auction_fun_core/operations/auction_context/processor/finish/standard_operation.rb +171 -0
  56. data/lib/auction_fun_core/operations/auction_context/processor/pause_operation.rb +36 -1
  57. data/lib/auction_fun_core/operations/auction_context/processor/start_operation.rb +23 -11
  58. data/lib/auction_fun_core/operations/auction_context/processor/unpause_operation.rb +36 -1
  59. data/lib/auction_fun_core/operations/bid_context/create_bid_penny_operation.rb +57 -7
  60. data/lib/auction_fun_core/operations/staff_context/registration_operation.rb +10 -0
  61. data/lib/auction_fun_core/relations/auctions.rb +252 -30
  62. data/lib/auction_fun_core/relations/bids.rb +18 -0
  63. data/lib/auction_fun_core/relations/staffs.rb +1 -1
  64. data/lib/auction_fun_core/repos/auction_context/auction_repository.rb +40 -11
  65. data/lib/auction_fun_core/repos/bid_context/bid_repository.rb +27 -5
  66. data/lib/auction_fun_core/repos/staff_context/staff_repository.rb +63 -21
  67. data/lib/auction_fun_core/repos/user_context/user_repository.rb +69 -25
  68. data/lib/auction_fun_core/services/mail/auction_context/post_auction/participant_mailer.rb +37 -0
  69. data/lib/auction_fun_core/services/mail/auction_context/post_auction/winner_mailer.rb +37 -0
  70. data/lib/auction_fun_core/services/mail/auction_context/pre_auction/auction_start_reminder_mailer.rb +35 -0
  71. data/lib/auction_fun_core/services/mail/templates/auction_context/post_auction/participant.html.erb +173 -0
  72. data/lib/auction_fun_core/services/mail/templates/auction_context/post_auction/winner.html.erb +174 -0
  73. data/lib/auction_fun_core/services/mail/templates/auction_context/pre_auction/auction_start_reminder.html.erb +192 -0
  74. data/lib/auction_fun_core/services/mail/templates/user_context/registration.html.erb +2 -2
  75. data/lib/auction_fun_core/services/mail/user_context/registration_mailer.rb +6 -0
  76. data/lib/auction_fun_core/version.rb +1 -1
  77. data/lib/auction_fun_core/workers/application_job.rb +12 -0
  78. data/lib/auction_fun_core/workers/operations/auction_context/post_auction/participant_operation_job.rb +38 -0
  79. data/lib/auction_fun_core/workers/operations/auction_context/post_auction/winner_operation_job.rb +37 -0
  80. data/lib/auction_fun_core/workers/operations/auction_context/pre_auction/auction_start_reminder_operation_job.rb +44 -0
  81. data/lib/auction_fun_core/workers/operations/auction_context/processor/finish/closed_operation_job.rb +37 -0
  82. data/lib/auction_fun_core/workers/operations/auction_context/processor/finish/penny_operation_job.rb +40 -0
  83. data/lib/auction_fun_core/workers/operations/auction_context/processor/finish/standard_operation_job.rb +38 -0
  84. data/lib/auction_fun_core/workers/operations/auction_context/processor/start_operation_job.rb +6 -3
  85. data/lib/auction_fun_core/workers/services/mail/auction_context/post_auction/participant_mailer_job.rb +56 -0
  86. data/lib/auction_fun_core/workers/services/mail/auction_context/post_auction/winner_mailer_job.rb +57 -0
  87. data/lib/auction_fun_core/workers/services/mail/auction_context/pre_auction/auction_start_reminder_mailer_job.rb +48 -0
  88. data/lib/auction_fun_core/workers/services/mail/user_context/registration_mailer_job.rb +8 -6
  89. data/system/providers/background_job.rb +25 -0
  90. metadata +51 -5
  91. data/lib/auction_fun_core/contracts/auction_context/processor/finish_contract.rb +0 -27
  92. data/lib/auction_fun_core/operations/auction_context/processor/finish_operation.rb +0 -61
  93. data/lib/auction_fun_core/workers/operations/auction_context/processor/finish_operation_job.rb +0 -32
@@ -0,0 +1,11 @@
1
+ pt-BR:
2
+ mail:
3
+ auction_context:
4
+ pre_auction:
5
+ auction_start_reminder_mailer:
6
+ subject: "O leilão %{title} começará em breve!"
7
+ body:
8
+ description: "Estamos entusiasmados em informar que o leilão em que você está participando começará em breve!"
9
+ auction_date: "Hora de Início"
10
+ link_to_auction: "Clique no link abaixo para ver os detalhes do leilão"
11
+ thanks: "Agradecemos pela sua participação e desejamos boa sorte!"
@@ -1,9 +1,5 @@
1
1
  pt-BR:
2
2
  mail:
3
- general:
4
- hello: "Olá %{name}"
5
- app_name: "AuctionFun"
6
- team: "Time AuctionFun"
7
3
  user_context:
8
4
  registration:
9
5
  subject: "Bem vindo a AuctionFun"
@@ -2,16 +2,47 @@
2
2
 
3
3
  module AuctionFunCore
4
4
  module Business
5
+ # The Configuration module contains constants that configure various aspects of auctions,
6
+ # user settings, and other business rules within the AuctionFun application.
5
7
  module Configuration
8
+ # An array of available auction kinds derived from auction relations.
9
+ # @return [Array<String>] a list of all possible auction kinds
6
10
  AUCTION_KINDS = Relations::Auctions::KINDS.values
11
+
12
+ # An array of valid auction statuses derived from auction relations.
13
+ # @return [Array<String>] a list of all valid auction statuses
7
14
  AUCTION_STATUSES = Relations::Auctions::STATUSES.values
15
+
16
+ # The minimum length for an auction title.
17
+ # @return [Integer] the minimum number of characters allowed in an auction title
8
18
  AUCTION_MIN_TITLE_LENGTH = 6
19
+
20
+ # The maximum length for an auction title.
21
+ # @return [Integer] the maximum number of characters allowed in an auction title
9
22
  AUCTION_MAX_TITLE_LENGTH = 255
23
+
24
+ # The minimum value for the auction stopwatch timer.
25
+ # @return [Integer] the minimum seconds count for the auction stopwatch timer
10
26
  AUCTION_STOPWATCH_MIN_VALUE = 15
27
+
28
+ # The maximum value for the auction stopwatch timer.
29
+ # @return [Integer] the maximum seconds count for the auction stopwatch timer
11
30
  AUCTION_STOPWATCH_MAX_VALUE = 60
31
+
32
+ # The minimum length for a user's name.
33
+ # @return [Integer] the minimum number of characters allowed in a person's name
12
34
  MIN_NAME_LENGTH = 6
35
+
36
+ # The maximum length for a user's name.
37
+ # @return [Integer] the maximum number of characters allowed in a person's name
13
38
  MAX_NAME_LENGTH = 128
39
+
40
+ # The minimum length for a user's password.
41
+ # @return [Integer] the minimum number of characters required in a person's password
14
42
  MIN_PASSWORD_LENGTH = 6
43
+
44
+ # The maximum length for a user's password.
45
+ # @return [Integer] the maximum number of characters allowed in a person's password
15
46
  MAX_PASSWORD_LENGTH = 128
16
47
  end
17
48
  end
@@ -2,13 +2,31 @@
2
2
 
3
3
  module AuctionFunCore
4
4
  module Business
5
- # Responsible for generating interaction tokens with system users for general operations.
5
+ # The TokenGenerator module is responsible for generating interaction tokens
6
+ # used in various parts of the AuctionFun application for authentication and
7
+ # verification purposes, particularly in interactions with system users.
6
8
  module TokenGenerator
9
+ # Generates a secure, URL-safe base64 token primarily used for email verification.
10
+ # This method modifies certain characters to avoid common readability issues.
11
+ #
12
+ # @param length [Integer] the desired length of the generated token before modification
13
+ # @return [String] a URL-safe, base64 encoded string suitable for email verification links
14
+ # @example Generating an email token
15
+ # email_token = AuctionFunCore::Business::TokenGenerator.generate_email_token(20)
16
+ # puts email_token # Output example: "V4Ph2wkJG_bRzs_zuGyJ"
7
17
  def self.generate_email_token(length = 20)
8
18
  rlength = (length * 3) / 4
9
19
  SecureRandom.urlsafe_base64(rlength).tr("lIO0", "sxyz")
10
20
  end
11
21
 
22
+ # Generates a numerical token used for phone verification processes.
23
+ # The token is zero-padded to ensure it meets the required length.
24
+ #
25
+ # @param length [Integer] the desired length of the numerical token
26
+ # @return [String] a string of digits, zero-padded to the specified length
27
+ # @example Generating a phone token
28
+ # phone_token = AuctionFunCore::Business::TokenGenerator.generate_phone_token(6)
29
+ # puts phone_token # Output example: "045673"
12
30
  def self.generate_phone_token(length = 6)
13
31
  rand(0o00000..999_999).to_s.rjust(length, "0")
14
32
  end
@@ -4,7 +4,10 @@ require "phonelib"
4
4
 
5
5
  module AuctionFunCore
6
6
  module Contracts
7
- # Abstract base class for contracts.
7
+ ##
8
+ # The class includes several macros for common validation tasks, such as validating email format,
9
+ # login format, name format, phone number format, and password format. These macros utilize
10
+ # regular expressions and predefined length ranges to ensure the input data meets specific criteria.
8
11
  # @abstract
9
12
  class ApplicationContract < Dry::Validation::Contract
10
13
  include AuctionFunCore::Business::Configuration
@@ -17,18 +20,21 @@ module AuctionFunCore
17
20
  config.messages.top_namespace = "contracts"
18
21
  config.messages.load_paths << Application.root.join("i18n/#{I18n.default_locale}/contracts/contracts.#{I18n.default_locale}.yml").to_s
19
22
 
23
+ # Validates whether the provided value matches the standard email format.
20
24
  register_macro(:email_format) do
21
25
  next if EMAIL_REGEX.match?(value)
22
26
 
23
27
  key.failure(I18n.t(:email_format, scope: I18N_MACRO_SCOPE))
24
28
  end
25
29
 
30
+ # Validates whether the provided value matches either the email format or a valid phone number format using Phonelib.
26
31
  register_macro(:login_format) do
27
32
  next if EMAIL_REGEX.match?(value) || Phonelib.parse(value).valid?
28
33
 
29
34
  key.failure(I18n.t(:login_format, scope: I18N_MACRO_SCOPE))
30
35
  end
31
36
 
37
+ # Validates whether the length of the provided name falls within the specified range.
32
38
  register_macro(:name_format) do
33
39
  next if value.length.between?(MIN_NAME_LENGTH, MAX_NAME_LENGTH)
34
40
 
@@ -37,12 +43,14 @@ module AuctionFunCore
37
43
  )
38
44
  end
39
45
 
46
+ # Validates whether the provided value matches a valid phone number format using Phonelib.
40
47
  register_macro(:phone_format) do
41
48
  next if ::Phonelib.parse(value).valid?
42
49
 
43
50
  key.failure(I18n.t(:phone_format, scope: I18N_MACRO_SCOPE))
44
51
  end
45
52
 
53
+ # Validates whether the length of the provided password falls within the specified range.
46
54
  register_macro(:password_format) do
47
55
  next if value.length.between?(MIN_PASSWORD_LENGTH, MAX_PASSWORD_LENGTH)
48
56
 
@@ -3,15 +3,36 @@
3
3
  module AuctionFunCore
4
4
  module Contracts
5
5
  module AuctionContext
6
- # Contract class to create new auctions.
6
+ # This class is designed to validate the creation of new auctions.
7
+ # It includes various validations such as staff existence, auction kind, timing, and initial bids.
8
+ #
9
+ # @example Creating a new auction
10
+ # contract = AuctionFunCore::Contracts::AuctionContext::CreateContract.new
11
+ # attributes = {
12
+ # staff_id: 1,
13
+ # title: "Rare Antique Vase",
14
+ # kind: "standard",
15
+ # started_at: Time.current + 5.days,
16
+ # finished_at: Time.current + 10.days,
17
+ # initial_bid_cents: 5000
18
+ # }
19
+ # result = contract.call(attributes)
20
+ # if result.success?
21
+ # puts "Auction created successfully."
22
+ # else
23
+ # puts "Failed to create auction: #{result.errors.to_h}"
24
+ # end
25
+ #
7
26
  class CreateContract < Contracts::ApplicationContract
8
27
  include AuctionFunCore::Business::Configuration
9
28
 
29
+ # Additional validations specific for non-penny auctions.
10
30
  REQUIRED_FINISHED_AT = AUCTION_KINDS - ["penny"]
11
31
 
32
+ # Repository initialized to retrieve staff data for validation.
12
33
  option :staff_repo, default: proc { Repos::StaffContext::StaffRepository.new }
13
34
 
14
- # @param [Hash] opts Sets an allowed list of parameters, as well as some initial validations.
35
+ # Defines necessary parameters and initializes some default values.
15
36
  params do
16
37
  required(:staff_id).filled(:integer)
17
38
  required(:title).filled(:string, size?: (AUCTION_MIN_TITLE_LENGTH..AUCTION_MAX_TITLE_LENGTH))
@@ -22,7 +43,7 @@ module AuctionFunCore
22
43
  optional(:initial_bid_cents).filled(:integer)
23
44
  optional(:stopwatch).filled(:integer)
24
45
 
25
- # Keys with a blank value are discarded.
46
+ # Parameters specifying the required input types and fields.
26
47
  before(:value_coercer) do |result|
27
48
  result.to_h.compact
28
49
  end
@@ -33,59 +54,53 @@ module AuctionFunCore
33
54
  end
34
55
  end
35
56
 
36
- # Validation for staff.
37
- # Validate whether the given staff is valid at the database level.
57
+ # Validates the existence of the staff.
38
58
  rule(:staff_id) do |context:|
39
59
  context[:staff] ||= staff_repo.by_id(value)
40
60
  key.failure(I18n.t("contracts.errors.custom.default.not_found")) unless context[:staff]
41
61
  end
42
62
 
43
- # Validation for started_at.
44
- # Validates if the entered date is greater than or equal to the current time.
63
+ # Validates that the starting time of the auction is in the future.
45
64
  rule(:started_at) do
46
65
  key.failure(I18n.t("contracts.errors.custom.default.future")) if key? && value <= Time.current
47
66
  end
48
67
 
49
- # Specific validation when the auction type is informed and it is not penny,
50
- # it must be mandatory to inform the auction closing date/time
68
+ # Validates that the finished_at time is specified for auctions types that require it,
69
+ # and is not specified for "penny" auctions.
51
70
  rule(:finished_at, :kind) do
52
71
  if key?(:kind) && !key?(:finished_at) && REQUIRED_FINISHED_AT.include?(values[:kind])
53
72
  key.failure(I18n.t("contracts.errors.filled?"))
54
73
  end
55
74
  end
56
75
 
57
- # Basic specific validation to check if the auction end time
58
- # is less than or equal to the start time.
76
+ # Validates that the auction end time is later than the start time.
59
77
  rule(:finished_at, :started_at) do
60
78
  if key?(:finished_at) && (values[:finished_at] <= values[:started_at])
61
79
  key.failure(I18n.t("contracts.errors.custom.auction_context.create.finished_at"))
62
80
  end
63
81
  end
64
82
 
65
- # Validation for initial bid amount.
66
- #
83
+ # Validates the initial bid amount based on auction type.
67
84
  rule(:initial_bid_cents) do
68
- # Must be filled if auction kind is not type penny.
85
+ # Must be specified and positive for non-penny auction types.
69
86
  key.failure(I18n.t("contracts.errors.filled?")) if !key? && REQUIRED_FINISHED_AT.include?(values[:kind])
70
87
 
71
- # Must be greater than zero if action kind is not type penny.
72
88
  if key? && REQUIRED_FINISHED_AT.include?(values[:kind]) && values[:initial_bid_cents] <= 0
73
89
  key.failure(I18n.t("contracts.errors.gt?", num: 0))
74
90
  end
75
91
 
76
- # Must be equal to zero if auction kind is type penny.
92
+ # Must be zero for penny auctions to ensure fairness.
77
93
  if key? && values[:kind] == "penny" && !values[:initial_bid_cents].zero?
78
94
  key.failure(I18n.t("contracts.errors.eql?", left: 0))
79
95
  end
80
96
  end
81
97
 
82
- # Validation for stopwatch.
83
- #
98
+ # Validates stopwatch settings for penny auctions.
84
99
  rule(:stopwatch) do
85
- # Must be filled if auction kind is type penny.
100
+ # Stopwatch must be specified for penny auctions.
86
101
  key.failure(I18n.t("contracts.errors.filled?")) if !key? && values[:kind] == "penny"
87
102
 
88
- # Must be an integer between 15 and 60.
103
+ # Stopwatch value must fall within the specified range.
89
104
  if key? && values[:kind] == "penny" && !value.between?(AUCTION_STOPWATCH_MIN_VALUE, AUCTION_STOPWATCH_MAX_VALUE)
90
105
  key.failure(
91
106
  I18n.t(
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Contracts
5
+ module AuctionContext
6
+ module PostAuction
7
+ ##
8
+ # This class is used to validate the participation of users in an auction.
9
+ # This contract ensures that both the auction and the participant exist and that the participant
10
+ # has already at least placed a bid. It utilizes repositories to fetch data about auctions, users, and bids.
11
+ #
12
+ # @example Using this class to validate a participant
13
+ # contract = AuctionFunCore::Contracts::AuctionContext::PostAuction::ParticipantContract.new
14
+ # attributes = { auction_id: 1, participant_id: 102 }
15
+ # validation_result = contract.call(attributes)
16
+ # if validation_result.success?
17
+ # puts "Participant validation passed"
18
+ # else
19
+ # puts "Participant validation failed: #{validation_result.errors.to_h}"
20
+ # end
21
+ #
22
+ class ParticipantContract < Contracts::ApplicationContract
23
+ # Scope for internationalization (i18n) entries specific to errors in this contract.
24
+ I18N_SCOPE = "contracts.errors.custom.auction_context.post_auction.participation"
25
+
26
+ # Repository options initialized by default. These repositories handle data retrieval.
27
+ option :auction_repository, default: proc { Repos::AuctionContext::AuctionRepository.new }
28
+ option :user_repository, default: proc { Repos::UserContext::UserRepository.new }
29
+ option :bid_repository, default: proc { Repos::BidContext::BidRepository.new }
30
+
31
+ # Defines the data types and required fields for the contract.
32
+ params do
33
+ required(:auction_id).filled(:integer)
34
+ required(:participant_id).filled(:integer)
35
+ end
36
+
37
+ # Validation rule for auction_id to ensure the auction exists.
38
+ # @param context [Hash] Optional context hash to store data and state during validation.
39
+ rule(:auction_id) do |context:|
40
+ context[:auction] ||= auction_repository.by_id(value)
41
+ key.failure(I18n.t("contracts.errors.custom.not_found")) unless context[:auction]
42
+ end
43
+
44
+ # Validation rule for participant_id to ensure the participant exists.
45
+ # @param context [Hash] Optional context hash to store data and state during validation.
46
+ rule(:participant_id) do |context:|
47
+ context[:participant] ||= user_repository.by_id(value)
48
+ key.failure(I18n.t("contracts.errors.custom.not_found")) unless context[:participant]
49
+ end
50
+
51
+ # Combined validation rule for auction_id and participant_id to check that the participant
52
+ # has not already placed a bid in the auction.
53
+ # @param context [Hash] Context containing the auction and participant objects.
54
+ rule(:auction_id, :participant_id) do |context:|
55
+ next if (rule_error?(:auction_id) || schema_error?(:auction_id)) || (rule_error?(:winner_id) || schema_error?(:winner_id))
56
+ next if bid_repository.exists?(auction_id: values[:auction_id], user_id: values[:participant_id])
57
+
58
+ key(:participant_id).failure(I18n.t("none", scope: I18N_SCOPE))
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Contracts
5
+ module AuctionContext
6
+ module PostAuction
7
+ ##
8
+ # This class validates the identification of the winning bidder
9
+ # in an auction context. It ensures that both the auction and the winner exist
10
+ # and are correctly associated within the system.
11
+ #
12
+ # @example Using this class to validate a winner
13
+ # contract = AuctionFunCore::Contracts::AuctionContext::PostAuction::WinnerContract.new
14
+ # attributes = { auction_id: 1, winner_id: 102 }
15
+ # validation_result = contract.call(attributes)
16
+ # if validation_result.success?
17
+ # puts "Winner validation passed"
18
+ # else
19
+ # puts "Winner validation failed: #{validation_result.errors.to_h}"
20
+ # end
21
+ #
22
+ class WinnerContract < Contracts::ApplicationContract
23
+ # Internationalization (i18n) scope for error messages related to winner validation.
24
+ I18N_SCOPE = "contracts.errors.custom.auction_context.post_auction.winner"
25
+
26
+ # Repositories initialized by default to handle data retrieval for validation.
27
+ option :auction_repository, default: proc { Repos::AuctionContext::AuctionRepository.new }
28
+ option :user_repository, default: proc { Repos::UserContext::UserRepository.new }
29
+
30
+ # Parameters defining the expected input types and required fields.
31
+ params do
32
+ required(:auction_id).filled(:integer)
33
+ required(:winner_id).filled(:integer)
34
+ end
35
+
36
+ # Validation rule to ensure the auction exists.
37
+ # @param context [Hash] Optional context hash to store data and state during validation.
38
+ rule(:auction_id) do |context:|
39
+ context[:auction] ||= auction_repository.by_id(value)
40
+ key.failure(I18n.t("contracts.errors.custom.not_found")) unless context[:auction]
41
+ end
42
+
43
+ # Validation rule to ensure the winner exists.
44
+ # @param context [Hash] Optional context hash to store data and state during validation.
45
+ rule(:winner_id) do |context:|
46
+ context[:winner] ||= user_repository.by_id(value)
47
+ key.failure(I18n.t("contracts.errors.custom.not_found")) unless context[:winner]
48
+ end
49
+
50
+ # Combined rule to ensure that the declared winner is the actual winner recorded in the auction.
51
+ # @param context [Hash] Context containing the auction and winner objects.
52
+ rule(:auction_id, :winner_id) do |context:|
53
+ next if (rule_error?(:auction_id) || schema_error?(:auction_id)) || (rule_error?(:winner_id) || schema_error?(:winner_id))
54
+ next if context[:auction].winner_id == values[:winner_id]
55
+
56
+ key(:winner_id).failure(I18n.t("wrong", scope: I18N_SCOPE))
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Contracts
5
+ module AuctionContext
6
+ module PreAuction
7
+ # The AuctionStartReminderContract class validates the scheduled reminder for the auction start.
8
+ # It checks if the auction associated with the provided auction ID has not started yet and validates accordingly.
9
+ #
10
+ # @example Validating auction reminder
11
+ # contract = AuctionFunCore::Contracts::AuctionContext::PreAuction::AuctionStartReminderContract.new
12
+ # attributes = { auction_id: 123 }
13
+ # result = contract.call(attributes)
14
+ # if result.success?
15
+ # puts "Reminder setup is valid."
16
+ # else
17
+ # puts "Failed to validate reminder: #{result.errors.to_h}"
18
+ # end
19
+ #
20
+ class AuctionStartReminderContract < Contracts::ApplicationContract
21
+ # Scope for internationalization (i18n) entries specific to errors in this contract.
22
+ I18N_SCOPE = "contracts.errors.custom.auction_context.pre_auction.auction_start_reminder"
23
+
24
+ # Default repository initialization to retrieve auction data.
25
+ option :auction_repository, default: proc { Repos::AuctionContext::AuctionRepository.new }
26
+
27
+ # Defines the necessary parameters and their types.
28
+ params do
29
+ required(:auction_id).filled(:integer)
30
+ end
31
+
32
+ # Validation rule to ensure the referenced auction exists.
33
+ rule(:auction_id) do |context:|
34
+ context[:auction] ||= auction_repository.by_id(value)
35
+ key.failure(I18n.t("contracts.errors.custom.not_found")) unless context[:auction]
36
+ end
37
+
38
+ # Additional validation to confirm the auction has not started yet.
39
+ rule do |context:|
40
+ next if context[:auction].present? && context[:auction].not_started?
41
+
42
+ key(:base).failure(I18n.t("auction_already_started", scope: I18N_SCOPE))
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Contracts
5
+ module AuctionContext
6
+ module Processor
7
+ module Finish
8
+ ##
9
+ # This class is designed for validate the finishing closed auctions. It ensures that
10
+ # the auction to be closed exists, is of the correct kind ('closed'), and is in the correct
11
+ # status ('running') to be finalized.
12
+ #
13
+ # @example Validating a closed auction
14
+ # contract = AuctionFunCore::Contracts::AuctionContext::Processor::Finish::ClosedContract.new
15
+ # attributes = { auction_id: 123 }
16
+ # result = contract.call(attributes)
17
+ # if result.success?
18
+ # puts "Auction can be finished."
19
+ # else
20
+ # puts "Failed to finish auction: #{result.errors.to_h}"
21
+ # end
22
+ #
23
+ class ClosedContract < Contracts::ApplicationContract
24
+ # Internationalization (i18n) scope for error messages.
25
+ I18N_SCOPE = "contracts.errors.custom.auction_context.processor.finish"
26
+
27
+ # Repository initialized to retrieve auction data for validation.
28
+ option :auction_repository, default: proc { Repos::AuctionContext::AuctionRepository.new }
29
+
30
+ # Parameters specifying the required input types and fields.
31
+ params do
32
+ required(:auction_id).filled(:integer)
33
+ end
34
+
35
+ # Validates the existence of the auction.
36
+ rule(:auction_id) do |context:|
37
+ context[:auction] ||= auction_repository.by_id(value)
38
+ key.failure(I18n.t("contracts.errors.custom.not_found")) unless context[:auction]
39
+ end
40
+
41
+ # Validates the kind of the auction to ensure it is 'closed'.
42
+ rule do |context:|
43
+ next if context[:auction].present? && context[:auction].kind == "closed"
44
+
45
+ key(:base).failure(I18n.t("invalid_kind", scope: I18N_SCOPE))
46
+ end
47
+
48
+ # Validates the status of the auction to ensure it is 'running'.
49
+ rule do |context:|
50
+ next if context[:auction].present? && context[:auction].status == "running"
51
+
52
+ key(:base).failure(I18n.t("invalid_status", scope: I18N_SCOPE))
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Contracts
5
+ module AuctionContext
6
+ module Processor
7
+ module Finish
8
+ ##
9
+ # This class is designed for validate the finishing penny auctions. It ensures that
10
+ # the auction to be penny exists, is of the correct kind ('penny'), and is in the correct
11
+ # status ('running') to be finalized.
12
+ #
13
+ # @example Validating a penny auction
14
+ # contract = AuctionFunCore::Contracts::AuctionContext::Processor::Finish::PennyContract.new
15
+ # attributes = { auction_id: 123 }
16
+ # result = contract.call(attributes)
17
+ # if result.success?
18
+ # puts "Auction can be finished."
19
+ # else
20
+ # puts "Failed to finish auction: #{result.errors.to_h}"
21
+ # end
22
+ #
23
+ class PennyContract < Contracts::ApplicationContract
24
+ # Internationalization (i18n) scope for error messages.
25
+ I18N_SCOPE = "contracts.errors.custom.auction_context.processor.finish"
26
+
27
+ # Repository initialized to retrieve auction data for validation.
28
+ option :auction_repository, default: proc { Repos::AuctionContext::AuctionRepository.new }
29
+
30
+ # Parameters specifying the required input types and fields.
31
+ params do
32
+ required(:auction_id).filled(:integer)
33
+ end
34
+
35
+ # Validates the existence of the auction.
36
+ rule(:auction_id) do |context:|
37
+ context[:auction] ||= auction_repository.by_id(value)
38
+
39
+ key.failure(I18n.t("contracts.errors.custom.not_found")) unless context[:auction]
40
+ end
41
+
42
+ # Validates the kind of the auction to ensure it is 'penny'.
43
+ rule do |context:|
44
+ next if context[:auction].present? && context[:auction].kind == "penny"
45
+
46
+ key(:base).failure(I18n.t("invalid_kind", scope: I18N_SCOPE))
47
+ end
48
+
49
+ # Validates the status of the auction to ensure it is 'running'.
50
+ rule do |context:|
51
+ next if context[:auction].present? && context[:auction].status == "running"
52
+
53
+ key(:base).failure(I18n.t("invalid_status", scope: I18N_SCOPE))
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AuctionFunCore
4
+ module Contracts
5
+ module AuctionContext
6
+ module Processor
7
+ module Finish
8
+ ##
9
+ # This class is designed for validate the finishing standard auctions. It ensures that
10
+ # the auction to be standard exists, is of the correct kind ('standard'), and is in the correct
11
+ # status ('running') to be finalized.
12
+ #
13
+ # @example Validating a standard auction
14
+ # contract = AuctionFunCore::Contracts::AuctionContext::Processor::Finish::StandardContract.new
15
+ # attributes = { auction_id: 123 }
16
+ # result = contract.call(attributes)
17
+ # if result.success?
18
+ # puts "Auction can be finished."
19
+ # else
20
+ # puts "Failed to finish auction: #{result.errors.to_h}"
21
+ # end
22
+ #
23
+ class StandardContract < Contracts::ApplicationContract
24
+ # Internationalization (i18n) scope for error messages.
25
+ I18N_SCOPE = "contracts.errors.custom.auction_context.processor.finish"
26
+
27
+ # Repository initialized to retrieve auction data for validation.
28
+ option :auction_repository, default: proc { Repos::AuctionContext::AuctionRepository.new }
29
+
30
+ # Parameters specifying the required input types and fields.
31
+ params do
32
+ required(:auction_id).filled(:integer)
33
+ end
34
+
35
+ # Validates the existence of the auction.
36
+ rule(:auction_id) do |context:|
37
+ context[:auction] ||= auction_repository.by_id(value)
38
+
39
+ key.failure(I18n.t("contracts.errors.custom.not_found")) unless context[:auction]
40
+ end
41
+
42
+ # Validates the kind of the auction to ensure it is 'standard'.
43
+ rule do |context:|
44
+ next if context[:auction].present? && context[:auction].kind == "standard"
45
+
46
+ key(:base).failure(I18n.t("invalid_kind", scope: I18N_SCOPE))
47
+ end
48
+
49
+ # Validates the status of the auction to ensure it is 'running'.
50
+ rule do |context:|
51
+ next if context[:auction].present? && context[:auction].status == "running"
52
+
53
+ key(:base).failure(I18n.t("invalid_status", scope: I18N_SCOPE))
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -5,18 +5,30 @@ module AuctionFunCore
5
5
  module AuctionContext
6
6
  module Processor
7
7
  ##
8
- # Contract class for pause auction.
8
+ # This class is designed to validate pausing an auction. It ensures
9
+ # that the auction exists in the database and checks that only auctions with
10
+ # a "running" status can be paused.
11
+ #
12
+ # @example Pausing an auction
13
+ # contract = AuctionFunCore::Contracts::AuctionContext::Processor::PauseContract.new
14
+ # attributes = { auction_id: 123 }
15
+ # result = contract.call(attributes)
16
+ # if result.success?
17
+ # puts "Auction paused successfully."
18
+ # else
19
+ # puts "Failed to pause auction: #{result.errors.to_h}"
20
+ # end
9
21
  #
10
22
  class PauseContract < Contracts::ApplicationContract
23
+ # Repository initialized to retrieve auction data for validation.
11
24
  option :auction_repository, default: proc { Repos::AuctionContext::AuctionRepository.new }
12
25
 
26
+ # Parameters specifying the required input types and fields.
13
27
  params do
14
28
  required(:auction_id).filled(:integer)
15
29
  end
16
30
 
17
- # Validation for auction.
18
- # Validates if the auction exists in the database and check if only auctions
19
- # with a "running" status can be paused.
31
+ # Validates the existence of the auction and its status.
20
32
  rule(:auction_id) do |context:|
21
33
  context[:auction] ||= auction_repository.by_id(value)
22
34
  key.failure(I18n.t("contracts.errors.custom.not_found")) unless context[:auction]