electionbuddy-ruby 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +7 -1
- data/.yardopts +10 -0
- data/CHANGELOG.md +30 -3
- data/README.md +126 -7
- data/lib/election_buddy/client.rb +14 -2
- data/lib/election_buddy/configuration.rb +9 -0
- data/lib/election_buddy/entities/importation.rb +43 -0
- data/lib/election_buddy/entities/validation/line_error.rb +35 -0
- data/lib/election_buddy/entities/validation/line_errors.rb +61 -0
- data/lib/election_buddy/entities/validation/list_error.rb +37 -0
- data/lib/election_buddy/entities/validation/list_errors.rb +47 -0
- data/lib/election_buddy/entities/validation/result.rb +112 -0
- data/lib/election_buddy/entities/validation.rb +17 -0
- data/lib/election_buddy/error.rb +7 -0
- data/lib/election_buddy/error_formatter.rb +10 -0
- data/lib/election_buddy/resource.rb +23 -1
- data/lib/election_buddy/resources/voter_list_resource.rb +36 -0
- data/lib/election_buddy/version.rb +7 -1
- data/lib/election_buddy.rb +27 -0
- metadata +11 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 64c0f48e8da4cd5811d86506d945b853e4e284afd8a8041878d680bf46117d4d
|
4
|
+
data.tar.gz: b635df1e9e3f7f013c769818e2722f6224de5cea279bd5ad36364a80c6d9d15b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b0dc2ca445cb784a1d72b27c6bcd4a5f0221ea50ca87e577dd301e0603189a11623eb85f9d794005350486efd82ce62bdb40d1492afe5ef536cde58a39fdb060
|
7
|
+
data.tar.gz: 4438b38fefa9ebaf8d22d7b57bc71272042bf89eda5dfe459fe29d647fd1c7d0d6141e31c5befe25e7267c5010cf5fad67b7f8ab729ef067b155b68ceed07fea
|
data/.rubocop.yml
CHANGED
data/.yardopts
ADDED
data/CHANGELOG.md
CHANGED
@@ -7,9 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
9
|
|
10
|
-
## [0.
|
10
|
+
## [0.4.0] - 2024-11-20
|
11
11
|
|
12
|
-
|
12
|
+
### Added
|
13
|
+
|
14
|
+
- Ability to import voters to a vote
|
15
|
+
- Automated gem release using GitHub Actions
|
16
|
+
|
17
|
+
## [0.3.0] - 2024-11-18
|
18
|
+
|
19
|
+
### Added
|
20
|
+
|
21
|
+
- Added Global configuration for setting the API key using `ElectionBuddy.configure` method
|
22
|
+
- Ability to retrieve voter list's validation results
|
23
|
+
- Automated gem documentation using YARD and GitHub Pages
|
24
|
+
|
25
|
+
### Changed
|
26
|
+
|
27
|
+
- Update README.md with get validtion results usage instructions
|
28
|
+
- Update .rubocop.yml with new rules
|
29
|
+
- Enhance code documentation with detailed YARD comments
|
30
|
+
|
31
|
+
### Fixed
|
32
|
+
|
33
|
+
- Fix changelog version ordering
|
13
34
|
|
14
35
|
## [0.2.0] - 2024-11-07
|
15
36
|
|
@@ -29,7 +50,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
29
50
|
|
30
51
|
- Fix console startup issue caused by incorrect code reference
|
31
52
|
|
32
|
-
[
|
53
|
+
## [0.1.0] - 2024-10-29
|
54
|
+
|
55
|
+
- Initial release
|
56
|
+
|
57
|
+
[unreleased]: https://github.com/electionbuddy/electionbuddy-ruby/compare/v0.4.0...HEAD
|
58
|
+
[0.4.0]: https://github.com/electionbuddy/electionbuddy-ruby/compare/v0.3.0...v0.4.0
|
59
|
+
[0.3.0]: https://github.com/electionbuddy/electionbuddy-ruby/compare/v0.2.0...v0.3.0
|
33
60
|
[0.2.0]: https://github.com/electionbuddy/electionbuddy-ruby/compare/v0.1.0...v0.2.0
|
34
61
|
[0.1.0]: https://github.com/electionbuddy/electionbuddy-ruby/releases/tag/v0.1.0
|
35
62
|
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# ElectionBuddy Ruby
|
2
2
|
|
3
|
-
A Ruby client for interacting with the ElectionBuddy API.
|
4
|
-
|
3
|
+
A Ruby client for interacting with the [ElectionBuddy API](https://api.electionbuddy.com).
|
4
|
+
Explore the complete documentation at [https://electionbuddy.github.io/electionbuddy-ruby](https://electionbuddy.github.io/electionbuddy-ruby).
|
5
5
|
|
6
6
|
## Installation
|
7
7
|
|
@@ -21,12 +21,55 @@ gem 'electionbuddy-ruby'
|
|
21
21
|
|
22
22
|
## Usage
|
23
23
|
|
24
|
-
Ensure you have an API key from Electionbuddy
|
24
|
+
Ensure you have an API key from Electionbuddy. You can configure the API key globally or initialize the client directly with the API key.
|
25
|
+
|
26
|
+
### Global Configuration
|
27
|
+
|
28
|
+
#### Example in a Rails application
|
29
|
+
|
30
|
+
Create an initializer file in `config/initializers/electionbuddy.rb` and add the following code:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
ElectionBuddy.configure do |config|
|
34
|
+
config.api_key = Rails.application.credentials.electionbuddy[:api_key]
|
35
|
+
end
|
36
|
+
```
|
37
|
+
|
38
|
+
#### Client Initialization
|
39
|
+
|
40
|
+
You can now initialize the client without passing the API key if it has been configured globally:
|
25
41
|
|
26
42
|
```ruby
|
27
|
-
client = ElectionBuddy::Client.new
|
43
|
+
client = ElectionBuddy::Client.new
|
28
44
|
```
|
29
45
|
|
46
|
+
Alternatively, you can still pass the API key directly during initialization. If the API key is passed during initialization, it will take precedence over the global configuration.
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
client = ElectionBuddy::Client.new(api_key: 'your-api-key')
|
50
|
+
```
|
51
|
+
|
52
|
+
### Voter List Importation
|
53
|
+
|
54
|
+
To import a voter list for an election, use the `voter_list.import(vote_id, voters, append_mode: false)` method:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
voters = [
|
58
|
+
{ email: 'voter1@example.com', label: 'Voter One' },
|
59
|
+
{ email: 'voter2@example.com', label: 'Voter Two' }
|
60
|
+
]
|
61
|
+
|
62
|
+
import = client.voter_list.import(1, voters, append_mode: false)
|
63
|
+
|
64
|
+
if import.done?
|
65
|
+
puts "Import completed successfully! Identifier: #{import.identifier}"
|
66
|
+
else
|
67
|
+
puts "Import failed: #{import.error}"
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
71
|
+
The `append_mode` parameter determines whether the voters should be appended to the existing list (`true`) or replace the existing list (`false`). The default value is `false`.
|
72
|
+
|
30
73
|
### Voter List Validation
|
31
74
|
|
32
75
|
To validate a voter list, use the `voter_list.validate(vote_id)` method:
|
@@ -36,14 +79,79 @@ validation = client.voter_list.validate(1)
|
|
36
79
|
|
37
80
|
if validation.done?
|
38
81
|
puts "Validation completed successfully! Identifier: #{validation.identifier}"
|
39
|
-
# "Validation completed successfully! Identifier: ae0a1724-9791-4bb2-8331-6d4e55a9b7c8"
|
40
82
|
else
|
41
83
|
puts "Validation failed: #{validation.error}"
|
42
|
-
# "Validation failed: Vote: not found"
|
43
84
|
end
|
44
85
|
```
|
45
86
|
|
46
|
-
###
|
87
|
+
### Get the Validation Result
|
88
|
+
|
89
|
+
Once you have a validation identifier, you can check the validation results.
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
begin
|
93
|
+
validation_result = client.voter_list.validation_result('ae0a1724-9791-4bb2-8331-6d4e55a9b7c8')
|
94
|
+
if validation_result.valid?
|
95
|
+
puts "The voter list is valid!"
|
96
|
+
else
|
97
|
+
puts "The voter list is invalid!"
|
98
|
+
puts "Total errors count: #{validation_result.total_errors_count}"
|
99
|
+
end
|
100
|
+
rescue ElectionBuddy::Error => e
|
101
|
+
puts "Something went wrong: #{e.message}"
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
105
|
+
The voter list validation can have two types of errors:
|
106
|
+
|
107
|
+
1. **List-level errors**: Affect the entire voter list (e.g., missing required columns)
|
108
|
+
2. **Line-level errors**: Affect specific lines in the voter list (e.g., invalid email format)
|
109
|
+
|
110
|
+
#### List-Level Errors
|
111
|
+
|
112
|
+
List-level errors can be retrieved using the `list_errors` method.
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
if validation_result.list_errors.any?
|
116
|
+
puts "List-level errors:"
|
117
|
+
validation_result.list_errors.each do |list_error|
|
118
|
+
puts "Error message: #{list_error.error_message}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
```
|
122
|
+
|
123
|
+
#### Line-Level Errors
|
124
|
+
|
125
|
+
Line-level errors are paginated. You can specify the page number and the number of errors per page.
|
126
|
+
If you don't specify the page number and the number of errors per page, the default values are 1 and 10, respectively.
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
validation_result = client.voter_list.validation_result('ae0a1724-9791-4bb2-8331-6d4e55a9b7c8', page: 2, per_page: 10)
|
130
|
+
line_errors = validation_result.line_errors
|
131
|
+
puts "There is a total of #{line_errors.total} line-level errors."
|
132
|
+
puts "The current page is #{line_errors.page}."
|
133
|
+
puts "There are #{line_errors.per_page} errors per page."
|
134
|
+
puts "There are #{line_errors.total_pages} pages in total."
|
135
|
+
```
|
136
|
+
|
137
|
+
PS: The `list_errors` object is always available, regardless of the line-level errors pagination.
|
138
|
+
|
139
|
+
To iterate over the line-level errors, you can use the following code:
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
if line_errors.any?
|
143
|
+
puts "Line-level errors for page #{line_errors.page}:"
|
144
|
+
line_errors.each do |line_error|
|
145
|
+
puts "Line identifier: #{line_error.voter_information_line_id}"
|
146
|
+
puts "Error messages:"
|
147
|
+
line_error.each do |line_error|
|
148
|
+
puts "Error messages #{line_error.error_messages}"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
```
|
153
|
+
|
154
|
+
### Possible API Errors
|
47
155
|
|
48
156
|
The following errors may be raised by the API:
|
49
157
|
|
@@ -56,6 +164,17 @@ The following errors may be raised by the API:
|
|
56
164
|
|
57
165
|
Each error will raise an `Error` exception with a message detailing the status code and the error message returned by the API.
|
58
166
|
|
167
|
+
## Documentation
|
168
|
+
|
169
|
+
The complete documentation for this gem is available at: https://electionbuddy.github.io/electionbuddy-ruby
|
170
|
+
|
171
|
+
You can also generate the documentation locally using:
|
172
|
+
|
173
|
+
```bash
|
174
|
+
yard doc
|
175
|
+
yard server
|
176
|
+
```
|
177
|
+
|
59
178
|
## Development
|
60
179
|
|
61
180
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -1,15 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ElectionBuddy
|
4
|
+
# HTTP client for the ElectionBuddy API
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# client = Client.new(api_key: "your-api-key")
|
8
|
+
# client.voter_list.validate(123)
|
9
|
+
#
|
4
10
|
class Client
|
5
11
|
BASE_URL = "https://secure.electionbuddy.com/api/v2"
|
6
12
|
|
7
|
-
|
8
|
-
|
13
|
+
# @param api_key [String, nil] The API key for authentication (optional if configured globally)
|
14
|
+
# @param adapter [Object, nil] The HTTP client adapter to use (optional, defaults to Faraday's default adapter)
|
15
|
+
# @param stubs [Object, nil] Test stubs for the adapter (optional)
|
16
|
+
def initialize(api_key: nil, adapter: Faraday.default_adapter, stubs: nil)
|
17
|
+
@api_key = api_key || ElectionBuddy.configuration.api_key
|
9
18
|
@adapter = adapter
|
10
19
|
@stubs = stubs
|
11
20
|
end
|
12
21
|
|
22
|
+
# Returns the voter list resource for handling voter-related operations
|
23
|
+
#
|
24
|
+
# @return [VoterListResource] The voter list resource
|
13
25
|
def voter_list
|
14
26
|
VoterListResource.new(connection)
|
15
27
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElectionBuddy
|
4
|
+
# Represents an importation operation for voter lists
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# importation = Importation.new(response)
|
8
|
+
# if importation.done?
|
9
|
+
# puts "Import successful: #{importation.identifier}"
|
10
|
+
# else
|
11
|
+
# puts "Import failed: #{importation.error}"
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
class Importation
|
15
|
+
# @param response [Hash] The API response containing importation details
|
16
|
+
def initialize(response)
|
17
|
+
@response = response
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns the identifier for this importation
|
21
|
+
#
|
22
|
+
# @return [String] The importation identifier or "Not Available"
|
23
|
+
def identifier
|
24
|
+
@response["importation_identifier"] || "Not Available"
|
25
|
+
end
|
26
|
+
|
27
|
+
# Checks if the importation completed successfully
|
28
|
+
#
|
29
|
+
# @return [Boolean] true if importation was successful, false otherwise
|
30
|
+
def done?
|
31
|
+
@response["error"].nil?
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the error message if importation failed
|
35
|
+
#
|
36
|
+
# @return [String, nil] The formatted error message or nil if importation was successful
|
37
|
+
def error
|
38
|
+
return nil if done?
|
39
|
+
|
40
|
+
ErrorFormatter.format(@response["error"])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElectionBuddy
|
4
|
+
class Validation
|
5
|
+
# Represents a single line validation error
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# line_error = LineError.new({ "voter_information_line_id" => 123, "errors" => { "email" => ["is invalid"] } })
|
9
|
+
# line_error.voter_information_line_id #=> 123
|
10
|
+
# line_error.error_messages #=> ["email: is invalid"]
|
11
|
+
#
|
12
|
+
class LineError
|
13
|
+
# @return [Integer] The ID of the voter information line
|
14
|
+
attr_reader :voter_information_line_id
|
15
|
+
|
16
|
+
# @return [Hash] The errors hash containing categories and messages
|
17
|
+
attr_reader :errors
|
18
|
+
|
19
|
+
# @param line_error [Hash] The error hash containing line ID and errors
|
20
|
+
def initialize(line_error)
|
21
|
+
@voter_information_line_id = line_error["voter_information_line_id"]
|
22
|
+
@errors = line_error["errors"]
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns formatted error messages for each category
|
26
|
+
#
|
27
|
+
# @return [Array<String>] Array of formatted error messages
|
28
|
+
def error_messages
|
29
|
+
@errors.map do |(category, messages)|
|
30
|
+
"#{category}: #{messages.join(", ")}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElectionBuddy
|
4
|
+
class Validation
|
5
|
+
# Represents a collection of line-by-line validation errors
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# line_errors = LineErrors.new(response)
|
9
|
+
# line_errors.total #=> 5
|
10
|
+
# line_errors.total_pages #=> 1
|
11
|
+
# line_errors.empty? #=> false
|
12
|
+
#
|
13
|
+
class LineErrors
|
14
|
+
include Enumerable
|
15
|
+
|
16
|
+
# @return [Integer] Total number of validation errors
|
17
|
+
attr_reader :total
|
18
|
+
|
19
|
+
# @return [Integer] Current page number
|
20
|
+
attr_reader :page
|
21
|
+
|
22
|
+
# @return [Integer] Number of items per page
|
23
|
+
attr_reader :per_page
|
24
|
+
|
25
|
+
# @param response [Hash] API response containing validation errors
|
26
|
+
def initialize(response)
|
27
|
+
@line_errors = response["voter_line_errors"]
|
28
|
+
@total = response["meta"]["total"]
|
29
|
+
@page = response["meta"]["page"]
|
30
|
+
@per_page = response["meta"]["per_page"]
|
31
|
+
end
|
32
|
+
|
33
|
+
# Calculates the total number of pages
|
34
|
+
#
|
35
|
+
# @return [Integer] Total number of pages
|
36
|
+
def total_pages
|
37
|
+
(total.to_f / per_page).ceil
|
38
|
+
end
|
39
|
+
|
40
|
+
# Iterates through each line error
|
41
|
+
#
|
42
|
+
# @return [Enumerator] Collection of line errors
|
43
|
+
def each(&block)
|
44
|
+
collection.each(&block)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Checks if there are any line errors
|
48
|
+
#
|
49
|
+
# @return [Boolean] true if no errors exist, false otherwise
|
50
|
+
def empty?
|
51
|
+
collection.empty?
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def collection
|
57
|
+
@collection ||= @line_errors.map { |line_error| LineError.new(line_error) }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElectionBuddy
|
4
|
+
class Validation
|
5
|
+
# Represents a single voter list validation error
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# error = ListError.new({ "email" => ["is invalid", "is required"] })
|
9
|
+
# error.category #=> "email"
|
10
|
+
# error.messages #=> ["is invalid", "is required"]
|
11
|
+
# error.error_message #=> "email: is invalid, is required"
|
12
|
+
#
|
13
|
+
class ListError
|
14
|
+
# @return [Hash] The raw error data
|
15
|
+
attr_reader :error
|
16
|
+
|
17
|
+
# @return [String] The category of the error
|
18
|
+
attr_reader :category
|
19
|
+
|
20
|
+
# @return [Array<String>] The error messages
|
21
|
+
attr_reader :messages
|
22
|
+
|
23
|
+
# @param error [Hash] The error hash containing category and messages
|
24
|
+
def initialize(error)
|
25
|
+
@error = error
|
26
|
+
@category, @messages = error.first
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns a formatted error message
|
30
|
+
#
|
31
|
+
# @return [String] The formatted error message
|
32
|
+
def error_message
|
33
|
+
"#{category}: #{messages.join(", ")}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElectionBuddy
|
4
|
+
class Validation
|
5
|
+
# Represents a collection of voter list validation errors
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# list_errors = ListErrors.new(response)
|
9
|
+
# list_errors.total #=> 2
|
10
|
+
# list_errors.empty? #=> false
|
11
|
+
#
|
12
|
+
class ListErrors
|
13
|
+
include Enumerable
|
14
|
+
|
15
|
+
# @return [Integer] Total number of validation errors
|
16
|
+
attr_reader :total
|
17
|
+
|
18
|
+
# @param response [Hash] API response containing validation errors
|
19
|
+
def initialize(response)
|
20
|
+
@list_errors = response["voter_list_errors"]
|
21
|
+
@total = response["meta"]["total"]
|
22
|
+
end
|
23
|
+
|
24
|
+
# Iterates through each list error
|
25
|
+
#
|
26
|
+
# @return [Enumerator] Collection of list errors
|
27
|
+
def each(&block)
|
28
|
+
collection.each(&block)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Checks if there are any list errors
|
32
|
+
#
|
33
|
+
# @return [Boolean] true if no errors exist, false otherwise
|
34
|
+
def empty?
|
35
|
+
collection.empty?
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def collection
|
41
|
+
@collection ||= @list_errors
|
42
|
+
.map { |category, messages| { category => messages } }
|
43
|
+
.map { |list_error| ListError.new(list_error) }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElectionBuddy
|
4
|
+
class Validation
|
5
|
+
class UnavailableTotalErrorsCount < ElectionBuddy::Error; end
|
6
|
+
class UnavailableValidStatus < ElectionBuddy::Error; end
|
7
|
+
|
8
|
+
Success = ->(data) { { success?: true, data: data } }
|
9
|
+
Failure = ->(error) { { success?: false, error: error } }
|
10
|
+
private_constant :Success, :Failure
|
11
|
+
|
12
|
+
# Represents the result of a validation operation
|
13
|
+
#
|
14
|
+
# @example Check if validation passed
|
15
|
+
# result = Result.new(response)
|
16
|
+
# result.valid? #=> true
|
17
|
+
# result.total_errors_count #=> 0
|
18
|
+
#
|
19
|
+
# @example Handle validation errors
|
20
|
+
# result = Result.new(response)
|
21
|
+
# result.valid? #=> false
|
22
|
+
# result.total_errors_count #=> 3
|
23
|
+
# result.list_errors #=> #<ListErrors @total=1>
|
24
|
+
# result.line_errors #=> #<LineErrors @total=2>
|
25
|
+
#
|
26
|
+
# @example Handle API failures
|
27
|
+
# result = Result.new(response)
|
28
|
+
# result.valid? #=> raises UnavailableValidStatus
|
29
|
+
# result.total_errors_count #=> raises UnavailableTotalErrorsCount
|
30
|
+
# result.failure_message #=> "API call has failed - Error: Validation: not found"
|
31
|
+
class Result
|
32
|
+
# Initializes a new validation result
|
33
|
+
#
|
34
|
+
# @param response [Hash] The raw response from the validation API
|
35
|
+
def initialize(response)
|
36
|
+
@result = process_response(response)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns the list of validation errors related to the voter list
|
40
|
+
# @return [ListErrors, Array] List validation errors or empty array if validation failed
|
41
|
+
def list_errors
|
42
|
+
return [] unless success?
|
43
|
+
|
44
|
+
@list_errors ||= ListErrors.new(@result[:data].dig("results", "voter_list_validations"))
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns the line-by-line validation errors
|
48
|
+
# @return [LineErrors, Array] Line validation errors or empty array if validation failed
|
49
|
+
def line_errors
|
50
|
+
return [] unless success?
|
51
|
+
|
52
|
+
@line_errors ||= LineErrors.new(@result[:data].dig("results", "voter_lines_validation"))
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns the total count of all validation errors
|
56
|
+
#
|
57
|
+
# @return [Integer] Total number of validation errors
|
58
|
+
# @raise [UnavailableTotalErrorsCount] if the API call failed
|
59
|
+
def total_errors_count
|
60
|
+
raise UnavailableTotalErrorsCount, failure_message if failure?
|
61
|
+
|
62
|
+
list_errors.total + line_errors.total
|
63
|
+
end
|
64
|
+
|
65
|
+
# Indicates if the validation passed without errors
|
66
|
+
#
|
67
|
+
# @return [Boolean] true if validation passed, false otherwise
|
68
|
+
# @raise [UnavailableValidStatus] if the API call failed
|
69
|
+
def valid?
|
70
|
+
raise UnavailableValidStatus, failure_message if failure?
|
71
|
+
|
72
|
+
total_errors_count.zero?
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns the error message if validation failed
|
76
|
+
#
|
77
|
+
# @return [String, nil] The formatted error message or nil if successful
|
78
|
+
def failure_error
|
79
|
+
return if success?
|
80
|
+
|
81
|
+
::ElectionBuddy::ErrorFormatter.format(@result[:error])
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns a user-friendly failure message
|
85
|
+
#
|
86
|
+
# @return [String, nil] The failure message or nil if successful
|
87
|
+
def failure_message
|
88
|
+
return if success?
|
89
|
+
|
90
|
+
"API call has failed - Error: #{failure_error}"
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def success?
|
96
|
+
@result[:success?]
|
97
|
+
end
|
98
|
+
|
99
|
+
def failure?
|
100
|
+
!success?
|
101
|
+
end
|
102
|
+
|
103
|
+
def process_response(response)
|
104
|
+
if response["error"]
|
105
|
+
Failure.call(response["error"])
|
106
|
+
else
|
107
|
+
Success.call(response)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -1,19 +1,36 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ElectionBuddy
|
4
|
+
# Represents a validation operation response from the ElectionBuddy API
|
5
|
+
# This class handles both successful and failed validation responses
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# validation = Validation.new(response_hash)
|
9
|
+
# validation.done? #=> true/false
|
10
|
+
#
|
4
11
|
class Validation
|
12
|
+
# @param response [Hash] The raw API response containing validation details
|
5
13
|
def initialize(response)
|
6
14
|
@response = response
|
7
15
|
end
|
8
16
|
|
17
|
+
# Returns the unique identifier for this validation
|
18
|
+
#
|
19
|
+
# @return [String] The validation identifier or "Not available" if not present
|
9
20
|
def identifier
|
10
21
|
@response["validation_identifier"] || "Not available"
|
11
22
|
end
|
12
23
|
|
24
|
+
# Checks if the validation completed successfully without errors
|
25
|
+
#
|
26
|
+
# @return [Boolean] true if validation completed without errors, false otherwise
|
13
27
|
def done?
|
14
28
|
@response["error"].nil?
|
15
29
|
end
|
16
30
|
|
31
|
+
# Returns formatted error message if validation failed
|
32
|
+
#
|
33
|
+
# @return [String, nil] Formatted error message or nil if validation was successful
|
17
34
|
def error
|
18
35
|
return nil if done?
|
19
36
|
|
data/lib/election_buddy/error.rb
CHANGED
@@ -1,6 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ElectionBuddy
|
4
|
+
# @api public
|
5
|
+
# Base error class for ElectionBuddy exceptions
|
6
|
+
#
|
7
|
+
# @example Raising a custom error
|
8
|
+
# raise ElectionBuddy::Error, "Something went wrong"
|
9
|
+
#
|
10
|
+
# @since 0.1.0
|
4
11
|
class Error < StandardError
|
5
12
|
end
|
6
13
|
end
|
@@ -1,7 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ElectionBuddy
|
4
|
+
# Formats error messages from hash responses
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# ErrorFormatter.format({ "email" => ["is invalid", "is required"] })
|
8
|
+
# #=> "Email: is invalid, is required"
|
9
|
+
#
|
4
10
|
class ErrorFormatter
|
11
|
+
# Formats error messages from a hash into a human-readable string
|
12
|
+
#
|
13
|
+
# @param error_hash [Hash, nil] Hash containing error keys and messages
|
14
|
+
# @return [String, nil] Formatted error message or nil if no errors
|
5
15
|
def self.format(error_hash)
|
6
16
|
return if error_hash.nil? || error_hash.empty?
|
7
17
|
|
@@ -1,15 +1,37 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ElectionBuddy
|
4
|
+
# Base class for API resources
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# class VoterListResource < Resource
|
8
|
+
# def validate(vote_id)
|
9
|
+
# post_request("/api/v2/votes/voters/validations", vote_id: vote_id)
|
10
|
+
# end
|
11
|
+
# end
|
12
|
+
#
|
4
13
|
class Resource
|
14
|
+
# @param connection [Object] The HTTP client connection
|
5
15
|
def initialize(connection)
|
6
16
|
@connection = connection
|
7
17
|
end
|
8
18
|
|
19
|
+
# Makes a GET request to the API
|
20
|
+
#
|
21
|
+
# @param url [String] The API endpoint URL
|
22
|
+
# @param params [Hash] Query parameters
|
23
|
+
# @param headers [Hash] Request headers
|
24
|
+
# @return [Hash] The API response
|
9
25
|
def get_request(url, params = {}, headers = {}, &block)
|
10
26
|
handle_response(@connection.get(url, params, headers, &block))
|
11
27
|
end
|
12
28
|
|
29
|
+
# Makes a POST request to the API
|
30
|
+
#
|
31
|
+
# @param url [String] The API endpoint URL
|
32
|
+
# @param body [Hash] Request body parameters
|
33
|
+
# @param headers [Hash] Request headers
|
34
|
+
# @return [Hash] The API response
|
13
35
|
def post_request(url, body = {}, headers = {}, &block)
|
14
36
|
handle_response(@connection.post(url, body, headers, &block))
|
15
37
|
end
|
@@ -17,7 +39,7 @@ module ElectionBuddy
|
|
17
39
|
private
|
18
40
|
|
19
41
|
def handle_response(response)
|
20
|
-
return response.body if response.success? || response.status
|
42
|
+
return response.body if response.success? || [422, 423].include?(response.status)
|
21
43
|
|
22
44
|
raise_error(response.status, ErrorFormatter.format(response.body))
|
23
45
|
end
|
@@ -1,11 +1,47 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ElectionBuddy
|
4
|
+
# Handles voter list validation operations
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# resource = VoterListResource.new(client)
|
8
|
+
# validation = resource.validate(123)
|
9
|
+
# result = resource.get_validation_result(validation.identifier)
|
10
|
+
#
|
4
11
|
class VoterListResource < Resource
|
12
|
+
# Imports voters for a given vote
|
13
|
+
#
|
14
|
+
# @param vote_id [Integer] The ID of the vote to import voters for
|
15
|
+
# @param voters [Array<Hash>] Array of voter data to import
|
16
|
+
# @param append_mode [Boolean] Whether to append voters to existing list (default: false)
|
17
|
+
# @return [Importation] The importation response
|
18
|
+
def import(vote_id, voters, append_mode: false)
|
19
|
+
response = post_request("/api/v2/votes/voters/importations", vote_id: vote_id.to_i, voters: voters, append_mode: append_mode)
|
20
|
+
|
21
|
+
Importation.new(response)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Initiates a validation for a given vote
|
25
|
+
#
|
26
|
+
# @param vote_id [Integer] The ID of the vote to validate
|
27
|
+
# @return [Validation] The validation response
|
5
28
|
def validate(vote_id)
|
6
29
|
response = post_request("/api/v2/votes/voters/validations", vote_id: vote_id.to_i)
|
7
30
|
|
8
31
|
Validation.new(response)
|
9
32
|
end
|
33
|
+
|
34
|
+
# Retrieves the validation results
|
35
|
+
#
|
36
|
+
# @param identifier [String] The validation identifier
|
37
|
+
# @param page [Integer, nil] The page number for paginated results (optional, default: 1)
|
38
|
+
# @param per_page [Integer, nil] The number of items per page (optional, default: 10)
|
39
|
+
# @return [Validation::Result] The validation results
|
40
|
+
def get_validation_result(identifier, page: 1, per_page: 10)
|
41
|
+
params = { "identifier" => identifier, "page" => page, "per_page" => per_page }
|
42
|
+
response = get_request("/api/v2/votes/voters/validations", params)
|
43
|
+
|
44
|
+
Validation::Result.new(response)
|
45
|
+
end
|
10
46
|
end
|
11
47
|
end
|
@@ -1,5 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ElectionBuddy
|
4
|
-
|
4
|
+
# Current version of ElectionBuddy gem
|
5
|
+
#
|
6
|
+
# @api public
|
7
|
+
# @return [String] the current version of the gem
|
8
|
+
# @example Get current version
|
9
|
+
# ElectionBuddy::VERSION #=> "0.4.0"
|
10
|
+
VERSION = "0.4.0"
|
5
11
|
end
|
data/lib/election_buddy.rb
CHANGED
@@ -10,7 +10,34 @@ require "election_buddy/error"
|
|
10
10
|
require "election_buddy/resource"
|
11
11
|
require "election_buddy/resources/voter_list_resource"
|
12
12
|
require "election_buddy/entities/validation"
|
13
|
+
require "election_buddy/entities/importation"
|
13
14
|
require "election_buddy/error_formatter"
|
15
|
+
require "election_buddy/configuration"
|
16
|
+
require "election_buddy/entities/validation/result"
|
17
|
+
require "election_buddy/entities/validation/line_error"
|
18
|
+
require "election_buddy/entities/validation/line_errors"
|
19
|
+
require "election_buddy/entities/validation/list_error"
|
20
|
+
require "election_buddy/entities/validation/list_errors"
|
14
21
|
|
22
|
+
# ElectionBuddy API client library
|
23
|
+
#
|
24
|
+
# @api public
|
25
|
+
# @example Configure the client
|
26
|
+
# ElectionBuddy.configure do |config|
|
27
|
+
# config.api_key = 'your-api-key'
|
28
|
+
# end
|
15
29
|
module ElectionBuddy
|
30
|
+
class << self
|
31
|
+
# @return [Configuration] Current configuration
|
32
|
+
attr_accessor :configuration
|
33
|
+
|
34
|
+
# Configures the ElectionBuddy client
|
35
|
+
#
|
36
|
+
# @yield [config] Configuration instance to be modified
|
37
|
+
# @yieldparam [Configuration] config The configuration instance
|
38
|
+
def configure
|
39
|
+
self.configuration ||= Configuration.new
|
40
|
+
yield(configuration)
|
41
|
+
end
|
42
|
+
end
|
16
43
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: electionbuddy-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alberto Rocha
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2024-11-
|
12
|
+
date: 2024-11-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: faraday
|
@@ -35,13 +35,21 @@ extra_rdoc_files: []
|
|
35
35
|
files:
|
36
36
|
- ".devcontainer/devcontainer.json"
|
37
37
|
- ".rubocop.yml"
|
38
|
+
- ".yardopts"
|
38
39
|
- CHANGELOG.md
|
39
40
|
- LICENSE.txt
|
40
41
|
- README.md
|
41
42
|
- Rakefile
|
42
43
|
- lib/election_buddy.rb
|
43
44
|
- lib/election_buddy/client.rb
|
45
|
+
- lib/election_buddy/configuration.rb
|
46
|
+
- lib/election_buddy/entities/importation.rb
|
44
47
|
- lib/election_buddy/entities/validation.rb
|
48
|
+
- lib/election_buddy/entities/validation/line_error.rb
|
49
|
+
- lib/election_buddy/entities/validation/line_errors.rb
|
50
|
+
- lib/election_buddy/entities/validation/list_error.rb
|
51
|
+
- lib/election_buddy/entities/validation/list_errors.rb
|
52
|
+
- lib/election_buddy/entities/validation/result.rb
|
45
53
|
- lib/election_buddy/error.rb
|
46
54
|
- lib/election_buddy/error_formatter.rb
|
47
55
|
- lib/election_buddy/resource.rb
|
@@ -72,7 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
80
|
- !ruby/object:Gem::Version
|
73
81
|
version: '0'
|
74
82
|
requirements: []
|
75
|
-
rubygems_version: 3.
|
83
|
+
rubygems_version: 3.2.33
|
76
84
|
signing_key:
|
77
85
|
specification_version: 4
|
78
86
|
summary: Ruby SDK for the ElectionBuddy API.
|