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.
- checksums.yaml +4 -4
- data/.standard.yml +2 -0
- data/CHANGELOG.md +48 -0
- data/Procfile +1 -0
- data/README.md +34 -32
- data/Rakefile +1 -1
- data/auction_fun_core.gemspec +1 -0
- data/db/migrate/20240229143000_create_auctions.rb +1 -0
- data/db/seeds.rb +87 -36
- data/i18n/en-US/contracts/contracts.en-US.yml +12 -0
- data/i18n/en-US/mail/application.en-US.yml +66 -0
- data/i18n/en-US/mail/auction_context/post_auction/participant.en-US.yml +13 -0
- data/i18n/en-US/mail/auction_context/post_auction/winner.en-US.yml +13 -0
- data/i18n/en-US/mail/auction_context/pre_auction/auction_start_reminder.en-US.yml +11 -0
- data/i18n/pt-BR/contracts/contracts.pt-BR.yml +12 -0
- data/i18n/pt-BR/mail/application.pt-BR.yml +66 -0
- data/i18n/pt-BR/mail/auction_context/post_auction/participant.pt-BR.yml +13 -0
- data/i18n/pt-BR/mail/auction_context/post_auction/winner.pt-BR.yml +13 -0
- data/i18n/pt-BR/mail/auction_context/pre_auction/auction_start_reminder.pt-BR.yml +11 -0
- data/i18n/pt-BR/mail/user_context/registration.pt-BR.yml +0 -4
- data/lib/auction_fun_core/business/configuration.rb +31 -0
- data/lib/auction_fun_core/business/token_generator.rb +19 -1
- data/lib/auction_fun_core/contracts/application_contract.rb +9 -1
- data/lib/auction_fun_core/contracts/auction_context/create_contract.rb +35 -20
- data/lib/auction_fun_core/contracts/auction_context/post_auction/participant_contract.rb +64 -0
- data/lib/auction_fun_core/contracts/auction_context/post_auction/winner_contract.rb +62 -0
- data/lib/auction_fun_core/contracts/auction_context/pre_auction/auction_start_reminder_contract.rb +48 -0
- data/lib/auction_fun_core/contracts/auction_context/processor/finish/closed_contract.rb +59 -0
- data/lib/auction_fun_core/contracts/auction_context/processor/finish/penny_contract.rb +60 -0
- data/lib/auction_fun_core/contracts/auction_context/processor/finish/standard_contract.rb +60 -0
- data/lib/auction_fun_core/contracts/auction_context/processor/pause_contract.rb +16 -4
- data/lib/auction_fun_core/contracts/auction_context/processor/start_contract.rb +17 -5
- data/lib/auction_fun_core/contracts/auction_context/processor/unpause_contract.rb +16 -4
- data/lib/auction_fun_core/contracts/bid_context/create_bid_closed_contract.rb +20 -11
- data/lib/auction_fun_core/contracts/bid_context/create_bid_penny_contract.rb +18 -9
- data/lib/auction_fun_core/contracts/bid_context/create_bid_standard_contract.rb +19 -10
- data/lib/auction_fun_core/contracts/staff_context/authentication_contract.rb +18 -4
- data/lib/auction_fun_core/contracts/staff_context/registration_contract.rb +20 -8
- data/lib/auction_fun_core/contracts/user_context/authentication_contract.rb +18 -4
- data/lib/auction_fun_core/contracts/user_context/email_confirmation_contract.rb +17 -2
- data/lib/auction_fun_core/contracts/user_context/phone_confirmation_contract.rb +17 -2
- data/lib/auction_fun_core/contracts/user_context/registration_contract.rb +26 -8
- data/lib/auction_fun_core/entities/auction.rb +52 -2
- data/lib/auction_fun_core/entities/bid.rb +3 -2
- data/lib/auction_fun_core/entities/staff.rb +15 -2
- data/lib/auction_fun_core/entities/user.rb +31 -2
- data/lib/auction_fun_core/events/app.rb +8 -2
- data/lib/auction_fun_core/events/listener.rb +19 -16
- data/lib/auction_fun_core/operations/auction_context/create_operation.rb +37 -11
- data/lib/auction_fun_core/operations/auction_context/post_auction/participant_operation.rb +85 -0
- data/lib/auction_fun_core/operations/auction_context/post_auction/winner_operation.rb +85 -0
- data/lib/auction_fun_core/operations/auction_context/pre_auction/auction_start_reminder_operation.rb +96 -0
- data/lib/auction_fun_core/operations/auction_context/processor/finish/closed_operation.rb +172 -0
- data/lib/auction_fun_core/operations/auction_context/processor/finish/penny_operation.rb +171 -0
- data/lib/auction_fun_core/operations/auction_context/processor/finish/standard_operation.rb +171 -0
- data/lib/auction_fun_core/operations/auction_context/processor/pause_operation.rb +36 -1
- data/lib/auction_fun_core/operations/auction_context/processor/start_operation.rb +23 -11
- data/lib/auction_fun_core/operations/auction_context/processor/unpause_operation.rb +36 -1
- data/lib/auction_fun_core/operations/bid_context/create_bid_penny_operation.rb +57 -7
- data/lib/auction_fun_core/operations/staff_context/registration_operation.rb +10 -0
- data/lib/auction_fun_core/relations/auctions.rb +252 -30
- data/lib/auction_fun_core/relations/bids.rb +18 -0
- data/lib/auction_fun_core/relations/staffs.rb +1 -1
- data/lib/auction_fun_core/repos/auction_context/auction_repository.rb +40 -11
- data/lib/auction_fun_core/repos/bid_context/bid_repository.rb +27 -5
- data/lib/auction_fun_core/repos/staff_context/staff_repository.rb +63 -21
- data/lib/auction_fun_core/repos/user_context/user_repository.rb +69 -25
- data/lib/auction_fun_core/services/mail/auction_context/post_auction/participant_mailer.rb +37 -0
- data/lib/auction_fun_core/services/mail/auction_context/post_auction/winner_mailer.rb +37 -0
- data/lib/auction_fun_core/services/mail/auction_context/pre_auction/auction_start_reminder_mailer.rb +35 -0
- data/lib/auction_fun_core/services/mail/templates/auction_context/post_auction/participant.html.erb +173 -0
- data/lib/auction_fun_core/services/mail/templates/auction_context/post_auction/winner.html.erb +174 -0
- data/lib/auction_fun_core/services/mail/templates/auction_context/pre_auction/auction_start_reminder.html.erb +192 -0
- data/lib/auction_fun_core/services/mail/templates/user_context/registration.html.erb +2 -2
- data/lib/auction_fun_core/services/mail/user_context/registration_mailer.rb +6 -0
- data/lib/auction_fun_core/version.rb +1 -1
- data/lib/auction_fun_core/workers/application_job.rb +12 -0
- data/lib/auction_fun_core/workers/operations/auction_context/post_auction/participant_operation_job.rb +38 -0
- data/lib/auction_fun_core/workers/operations/auction_context/post_auction/winner_operation_job.rb +37 -0
- data/lib/auction_fun_core/workers/operations/auction_context/pre_auction/auction_start_reminder_operation_job.rb +44 -0
- data/lib/auction_fun_core/workers/operations/auction_context/processor/finish/closed_operation_job.rb +37 -0
- data/lib/auction_fun_core/workers/operations/auction_context/processor/finish/penny_operation_job.rb +40 -0
- data/lib/auction_fun_core/workers/operations/auction_context/processor/finish/standard_operation_job.rb +38 -0
- data/lib/auction_fun_core/workers/operations/auction_context/processor/start_operation_job.rb +6 -3
- data/lib/auction_fun_core/workers/services/mail/auction_context/post_auction/participant_mailer_job.rb +56 -0
- data/lib/auction_fun_core/workers/services/mail/auction_context/post_auction/winner_mailer_job.rb +57 -0
- data/lib/auction_fun_core/workers/services/mail/auction_context/pre_auction/auction_start_reminder_mailer_job.rb +48 -0
- data/lib/auction_fun_core/workers/services/mail/user_context/registration_mailer_job.rb +8 -6
- data/system/providers/background_job.rb +25 -0
- metadata +51 -5
- data/lib/auction_fun_core/contracts/auction_context/processor/finish_contract.rb +0 -27
- data/lib/auction_fun_core/operations/auction_context/processor/finish_operation.rb +0 -61
- 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!"
|
@@ -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
|
-
#
|
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
|
-
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
50
|
-
#
|
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
|
-
#
|
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
|
-
#
|
66
|
-
#
|
83
|
+
# Validates the initial bid amount based on auction type.
|
67
84
|
rule(:initial_bid_cents) do
|
68
|
-
# Must be
|
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
|
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
|
-
#
|
83
|
-
#
|
98
|
+
# Validates stopwatch settings for penny auctions.
|
84
99
|
rule(:stopwatch) do
|
85
|
-
#
|
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
|
-
#
|
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
|
data/lib/auction_fun_core/contracts/auction_context/pre_auction/auction_start_reminder_contract.rb
ADDED
@@ -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
|
-
#
|
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
|
-
#
|
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]
|