electionbuddy-ruby 0.2.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|