auction_fun_core 0.8.7 → 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 +23 -0
- data/Procfile +1 -0
- data/README.md +34 -32
- data/Rakefile +1 -1
- data/i18n/en-US/contracts/contracts.en-US.yml +3 -0
- data/i18n/en-US/mail/application.en-US.yml +7 -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 +3 -0
- data/i18n/pt-BR/mail/application.pt-BR.yml +7 -0
- data/i18n/pt-BR/mail/auction_context/pre_auction/auction_start_reminder.pt-BR.yml +11 -0
- 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 +23 -1
- data/lib/auction_fun_core/contracts/auction_context/post_auction/winner_contract.rb +22 -1
- 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 +19 -7
- data/lib/auction_fun_core/contracts/auction_context/processor/finish/penny_contract.rb +19 -7
- data/lib/auction_fun_core/contracts/auction_context/processor/finish/standard_contract.rb +19 -7
- 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 +48 -4
- 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 +25 -9
- data/lib/auction_fun_core/operations/auction_context/post_auction/participant_operation.rb +36 -3
- data/lib/auction_fun_core/operations/auction_context/post_auction/winner_operation.rb +36 -2
- 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 +82 -10
- data/lib/auction_fun_core/operations/auction_context/processor/finish/penny_operation.rb +81 -10
- data/lib/auction_fun_core/operations/auction_context/processor/finish/standard_operation.rb +81 -12
- data/lib/auction_fun_core/operations/auction_context/processor/pause_operation.rb +36 -1
- data/lib/auction_fun_core/operations/auction_context/processor/unpause_operation.rb +36 -1
- data/lib/auction_fun_core/relations/auctions.rb +178 -97
- data/lib/auction_fun_core/relations/bids.rb +18 -0
- 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 +7 -1
- data/lib/auction_fun_core/services/mail/auction_context/post_auction/winner_mailer.rb +7 -1
- 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 +1 -1
- data/lib/auction_fun_core/services/mail/templates/auction_context/post_auction/winner.html.erb +1 -1
- 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/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 +10 -0
- data/lib/auction_fun_core/workers/operations/auction_context/post_auction/participant_operation_job.rb +7 -2
- data/lib/auction_fun_core/workers/operations/auction_context/post_auction/winner_operation_job.rb +6 -2
- 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 +6 -3
- data/lib/auction_fun_core/workers/operations/auction_context/processor/finish/penny_operation_job.rb +6 -3
- data/lib/auction_fun_core/workers/operations/auction_context/processor/finish/standard_operation_job.rb +6 -2
- 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 +12 -7
- data/lib/auction_fun_core/workers/services/mail/auction_context/post_auction/winner_mailer_job.rb +11 -5
- 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 +6 -0
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a93057cdc62225a319b45c2836b5cfd713935664c8a7d9bdf2d39678909dacc7
|
4
|
+
data.tar.gz: 87f51a7eb83f0b47bc13394c36e37f36821a8ef5f4c429ec18e272155d9a1261
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70b11f00396f1195ae8e7060eb8316141abbe3e3f30621d80769d94b63b4d1469695b6ca0a35a9910419e7579f047b2bd9d4f7a3ad469803cf47cb3aae4efc75
|
7
|
+
data.tar.gz: 520de3854f324a0e79bca1c4bfdfe69288077e208de37831b3c25cd84e75acb2664f79cf87372cee2ffb4a4a3800398b0fd72c537cb83a330b925c2d0113cdbe
|
data/.standard.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,28 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.8.9] - 2024-04-29
|
4
|
+
|
5
|
+
### Added
|
6
|
+
|
7
|
+
- Using yadr as a standard documentation tool;
|
8
|
+
|
9
|
+
### Changed
|
10
|
+
|
11
|
+
- Upgrade README;
|
12
|
+
- Updating CI to run lint;
|
13
|
+
- Using the heredoc standard to build SQL queries;
|
14
|
+
|
15
|
+
## [0.8.8] - 2024-04-25
|
16
|
+
|
17
|
+
### Added
|
18
|
+
|
19
|
+
- Auction start reminder sent to participants;
|
20
|
+
- I18n time format;
|
21
|
+
|
22
|
+
### Fixed:
|
23
|
+
|
24
|
+
- I18n messages for winner and auction participant emails;
|
25
|
+
|
3
26
|
## [0.8.7] - 2024-04-23
|
4
27
|
|
5
28
|
### Added
|
data/Procfile
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# AuctionFunCore
|
2
2
|
|
3
|
-
This lib contains all the business logic necessary to run the auctions. A principle and standard must be strictly followed: The [
|
3
|
+
This lib contains all the business logic necessary to run the auctions. A principle and standard must be strictly followed: The [The principle of single responsibility](https://en.wikipedia.org/wiki/Single_responsibility_principle) and [clean architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) concepts.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
@@ -13,13 +13,13 @@ gem 'auction_fun_core'
|
|
13
13
|
And then execute:
|
14
14
|
|
15
15
|
```sh
|
16
|
-
|
16
|
+
bundle install
|
17
17
|
```
|
18
18
|
|
19
19
|
Or install it yourself as:
|
20
20
|
|
21
21
|
```sh
|
22
|
-
|
22
|
+
gem install auction_fun_core
|
23
23
|
```
|
24
24
|
|
25
25
|
## Getting Started
|
@@ -27,10 +27,10 @@ user@host:~$ gem install auction_fun_core
|
|
27
27
|
Initially, download the project remotely and copy the templates from the project's environment variables:
|
28
28
|
|
29
29
|
```sh
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
git clone git@github.com:ricardopacheco/auction-fun-core.git core
|
31
|
+
cd core
|
32
|
+
cp .env.development.template .env.development
|
33
|
+
cp .env.test.template .env.test
|
34
34
|
```
|
35
35
|
|
36
36
|
> As the idea of this project is to be a lib, it was decided to use a specific environment variable called `APP_ENV`, so as not to conflict with others if the lib is used by a framework.
|
@@ -40,7 +40,7 @@ user@host:~$ cp .env.test.template .env.test
|
|
40
40
|
Configure the `.env.development` file with the values according to your machine or network.
|
41
41
|
|
42
42
|
Run the commands below to create the database and its structure, remembering to replace
|
43
|
-
the `
|
43
|
+
the `postgres` by the user of your postgresql service. By default, if `APP_ENV` is not provided
|
44
44
|
is considered the value `development` by default.
|
45
45
|
|
46
46
|
### OS dependencies
|
@@ -52,50 +52,50 @@ is considered the value `development` by default.
|
|
52
52
|
#### Database (PostgreSQL)
|
53
53
|
|
54
54
|
```sh
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
55
|
+
sudo apt install build-essential libssl-dev libreadline-dev zlib1g-dev libcurl4-openssl-dev uuid-dev
|
56
|
+
asdf plugin add postgres
|
57
|
+
asdf install postgres 16.1
|
58
|
+
rm -rf $HOME/.asdf/installs/postgres/16.1/data
|
59
|
+
initdb -D $HOME/.asdf/installs/postgres/16.1/data -U postgres
|
60
60
|
```
|
61
61
|
|
62
62
|
```sh
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
63
|
+
sudo apt install autoconf patch build-essential rustc libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libgmp-dev libncurses5-dev libffi-dev libgdbm6 libgdbm-dev libdb-dev uuid-dev
|
64
|
+
asdf plugin add ruby
|
65
|
+
asdf install ruby 3.3.0
|
66
|
+
gem install pg -v 1.5.5 --verbose -- --with-pg-config=$HOME/.asdf/installs/postgres/16.1/bin/pg_config # Fix pg_config
|
67
|
+
bin/setup
|
68
68
|
```
|
69
69
|
|
70
70
|
#### Overmind (Procfile manager)
|
71
71
|
|
72
72
|
```sh
|
73
|
-
|
74
|
-
|
75
|
-
|
73
|
+
asdf install golang latest
|
74
|
+
go install github.com/DarthSim/overmind/v2
|
75
|
+
asdf reshim
|
76
76
|
```
|
77
77
|
|
78
78
|
#### Create database for development environment
|
79
79
|
|
80
|
-
|
80
|
+
- **postgres** in rake commands is a name of user for postgres. Change if needed
|
81
81
|
|
82
82
|
In current tab:
|
83
83
|
|
84
84
|
```sh
|
85
|
-
|
85
|
+
overmind s -l database
|
86
86
|
```
|
87
87
|
|
88
88
|
Open a new tab and create development database:
|
89
89
|
|
90
90
|
```sh
|
91
|
-
|
92
|
-
|
91
|
+
bundle exec rake 'auction_fun_core:db:create_database[postgres]'
|
92
|
+
bundle exec rake 'auction_fun_core:db:migrate'
|
93
93
|
```
|
94
94
|
|
95
95
|
Now come back to overmind tab, kill the current database process using **Ctrl+c**. After that:
|
96
96
|
|
97
97
|
```sh
|
98
|
-
|
98
|
+
overmind start
|
99
99
|
```
|
100
100
|
|
101
101
|
This will start all required services needed to run core application.
|
@@ -103,7 +103,7 @@ This will start all required services needed to run core application.
|
|
103
103
|
In new tab, you could run seed data for development with
|
104
104
|
|
105
105
|
```sh
|
106
|
-
|
106
|
+
bundle exec rake 'auction_fun_core:db:seed'
|
107
107
|
```
|
108
108
|
|
109
109
|
## Interactive prompt
|
@@ -117,9 +117,9 @@ Configure the `.env.test` file with the values according to your machine or netw
|
|
117
117
|
Run the test suite with the coverage report using the command:
|
118
118
|
|
119
119
|
```sh
|
120
|
-
|
121
|
-
|
122
|
-
|
120
|
+
APP_ENV=test bundle exec rake auction_fun_core:db:create_database[postgres]
|
121
|
+
APP_ENV=test bundle exec rake auction_fun_core:db:migrate
|
122
|
+
CI=true APP_ENV=test bundle exec rspec .
|
123
123
|
```
|
124
124
|
|
125
125
|
## Documentation
|
@@ -127,10 +127,12 @@ user@host:~$ CI=true APP_ENV=test bundle exec rspec .
|
|
127
127
|
This project uses `yadr` as a documentation tool. To generate documentation and view it, run
|
128
128
|
|
129
129
|
```sh
|
130
|
-
|
130
|
+
bundle exec yard server --reload
|
131
131
|
```
|
132
132
|
|
133
|
-
Documentation will be available at `http://localhost:8808
|
133
|
+
Documentation will be available at `http://localhost:8808`.
|
134
|
+
|
135
|
+
> If you already use overmind, the documentation server starts automatically.
|
134
136
|
|
135
137
|
## External resources
|
136
138
|
|
data/Rakefile
CHANGED
@@ -70,6 +70,9 @@ en-US:
|
|
70
70
|
auction_context:
|
71
71
|
create:
|
72
72
|
finished_at: "must be after started time"
|
73
|
+
pre_auction:
|
74
|
+
auction_start_reminder:
|
75
|
+
auction_already_started: "auction already started"
|
73
76
|
post_auction:
|
74
77
|
participant:
|
75
78
|
none: "there was no participation from this user in the auction reported"
|
@@ -0,0 +1,11 @@
|
|
1
|
+
pt-BR:
|
2
|
+
mail:
|
3
|
+
auction_context:
|
4
|
+
pre_auction:
|
5
|
+
auction_start_reminder_mailer:
|
6
|
+
subject: "The %{title} auction will start soon!"
|
7
|
+
body:
|
8
|
+
description: "We are excited to inform you that the auction you are participating in will begin soon!"
|
9
|
+
auction_date: "Start Time"
|
10
|
+
link_to_auction: "Click the link below to view auction details"
|
11
|
+
thanks: "Thank you for your participation and we wish you good luck!"
|
@@ -70,6 +70,9 @@ pt-BR:
|
|
70
70
|
auction_context:
|
71
71
|
create:
|
72
72
|
finished_at: "deve ser depois da hora de início"
|
73
|
+
pre_auction:
|
74
|
+
auction_start_reminder:
|
75
|
+
auction_already_started: "leilão já foi iniciado"
|
73
76
|
post_auction:
|
74
77
|
participant:
|
75
78
|
none: "não houve nenhuma participação deste usuário no leilão informado"
|
@@ -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(
|
@@ -5,30 +5,52 @@ module AuctionFunCore
|
|
5
5
|
module AuctionContext
|
6
6
|
module PostAuction
|
7
7
|
##
|
8
|
-
#
|
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
|
9
21
|
#
|
10
22
|
class ParticipantContract < Contracts::ApplicationContract
|
23
|
+
# Scope for internationalization (i18n) entries specific to errors in this contract.
|
11
24
|
I18N_SCOPE = "contracts.errors.custom.auction_context.post_auction.participation"
|
12
25
|
|
26
|
+
# Repository options initialized by default. These repositories handle data retrieval.
|
13
27
|
option :auction_repository, default: proc { Repos::AuctionContext::AuctionRepository.new }
|
14
28
|
option :user_repository, default: proc { Repos::UserContext::UserRepository.new }
|
15
29
|
option :bid_repository, default: proc { Repos::BidContext::BidRepository.new }
|
16
30
|
|
31
|
+
# Defines the data types and required fields for the contract.
|
17
32
|
params do
|
18
33
|
required(:auction_id).filled(:integer)
|
19
34
|
required(:participant_id).filled(:integer)
|
20
35
|
end
|
21
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.
|
22
39
|
rule(:auction_id) do |context:|
|
23
40
|
context[:auction] ||= auction_repository.by_id(value)
|
24
41
|
key.failure(I18n.t("contracts.errors.custom.not_found")) unless context[:auction]
|
25
42
|
end
|
26
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.
|
27
46
|
rule(:participant_id) do |context:|
|
28
47
|
context[:participant] ||= user_repository.by_id(value)
|
29
48
|
key.failure(I18n.t("contracts.errors.custom.not_found")) unless context[:participant]
|
30
49
|
end
|
31
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.
|
32
54
|
rule(:auction_id, :participant_id) do |context:|
|
33
55
|
next if (rule_error?(:auction_id) || schema_error?(:auction_id)) || (rule_error?(:winner_id) || schema_error?(:winner_id))
|
34
56
|
next if bid_repository.exists?(auction_id: values[:auction_id], user_id: values[:participant_id])
|
@@ -5,29 +5,50 @@ module AuctionFunCore
|
|
5
5
|
module AuctionContext
|
6
6
|
module PostAuction
|
7
7
|
##
|
8
|
-
#
|
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
|
9
21
|
#
|
10
22
|
class WinnerContract < Contracts::ApplicationContract
|
23
|
+
# Internationalization (i18n) scope for error messages related to winner validation.
|
11
24
|
I18N_SCOPE = "contracts.errors.custom.auction_context.post_auction.winner"
|
12
25
|
|
26
|
+
# Repositories initialized by default to handle data retrieval for validation.
|
13
27
|
option :auction_repository, default: proc { Repos::AuctionContext::AuctionRepository.new }
|
14
28
|
option :user_repository, default: proc { Repos::UserContext::UserRepository.new }
|
15
29
|
|
30
|
+
# Parameters defining the expected input types and required fields.
|
16
31
|
params do
|
17
32
|
required(:auction_id).filled(:integer)
|
18
33
|
required(:winner_id).filled(:integer)
|
19
34
|
end
|
20
35
|
|
36
|
+
# Validation rule to ensure the auction exists.
|
37
|
+
# @param context [Hash] Optional context hash to store data and state during validation.
|
21
38
|
rule(:auction_id) do |context:|
|
22
39
|
context[:auction] ||= auction_repository.by_id(value)
|
23
40
|
key.failure(I18n.t("contracts.errors.custom.not_found")) unless context[:auction]
|
24
41
|
end
|
25
42
|
|
43
|
+
# Validation rule to ensure the winner exists.
|
44
|
+
# @param context [Hash] Optional context hash to store data and state during validation.
|
26
45
|
rule(:winner_id) do |context:|
|
27
46
|
context[:winner] ||= user_repository.by_id(value)
|
28
47
|
key.failure(I18n.t("contracts.errors.custom.not_found")) unless context[:winner]
|
29
48
|
end
|
30
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.
|
31
52
|
rule(:auction_id, :winner_id) do |context:|
|
32
53
|
next if (rule_error?(:auction_id) || schema_error?(:auction_id)) || (rule_error?(:winner_id) || schema_error?(:winner_id))
|
33
54
|
next if context[:auction].winner_id == values[:winner_id]
|