electionbuddy-ruby 0.1.0 → 0.3.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 +16 -0
- data/.yardopts +10 -0
- data/CHANGELOG.md +49 -0
- data/README.md +145 -11
- data/lib/election_buddy/client.rb +40 -0
- data/lib/election_buddy/configuration.rb +9 -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 +40 -0
- data/lib/election_buddy/error.rb +13 -0
- data/lib/election_buddy/error_formatter.rb +25 -0
- data/lib/election_buddy/resource.rb +70 -0
- data/lib/election_buddy/resources/voter_list_resource.rb +35 -0
- data/lib/election_buddy/version.rb +11 -0
- data/lib/election_buddy.rb +42 -0
- data/lib/electionbuddy-ruby.rb +3 -0
- metadata +33 -5
- data/lib/electionbuddy/ruby/version.rb +0 -7
- data/lib/electionbuddy/ruby.rb +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 05e63dbc220f5455343b5a43e4a2fa3754c377f7cb56aceb133acddf3a09fe84
|
4
|
+
data.tar.gz: 57afed7e7acaaf77e7ba70a7afae1d1efd56e058d403bf5eed9904d7e546d1d2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 39d9ac1b3891a72787f0c2a80603d2e824e769109c04f977f9a7ef810bd78a34c0dd4403356d7658b67db80462297e59f8040df8f9ee10e4f96658c6f65f8b7c
|
7
|
+
data.tar.gz: b6f5c57a45c8075f25c515efeddf23825db14d8c694bf4fa9b6f70b919501d66bd4dffa9639cc940a0a809a8d0739febdd83f06b378e0247591fc9e844ab95fc
|
data/.rubocop.yml
CHANGED
@@ -6,3 +6,19 @@ Style/StringLiterals:
|
|
6
6
|
|
7
7
|
Style/StringLiteralsInInterpolation:
|
8
8
|
EnforcedStyle: double_quotes
|
9
|
+
|
10
|
+
Style/Documentation:
|
11
|
+
Enabled: false
|
12
|
+
|
13
|
+
Naming/FileName:
|
14
|
+
Exclude:
|
15
|
+
- 'lib/electionbuddy-ruby.rb'
|
16
|
+
|
17
|
+
Metrics/MethodLength:
|
18
|
+
Max: 30
|
19
|
+
|
20
|
+
Metrics/ClassLength:
|
21
|
+
Max: 200
|
22
|
+
|
23
|
+
Layout/LineLength:
|
24
|
+
Max: 150
|
data/.yardopts
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,54 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
|
+
|
1
8
|
## [Unreleased]
|
2
9
|
|
10
|
+
## [0.3.0] - 2024-11-18
|
11
|
+
|
12
|
+
### Added
|
13
|
+
|
14
|
+
- Added Global configuration for setting the API key using `ElectionBuddy.configure` method
|
15
|
+
- Ability to retrieve voter list's validation results
|
16
|
+
- Automated gem documentation using YARD and GitHub Pages
|
17
|
+
|
18
|
+
### Changed
|
19
|
+
|
20
|
+
- Update README.md with get validtion results usage instructions
|
21
|
+
- Update .rubocop.yml with new rules
|
22
|
+
- Enhance code documentation with detailed YARD comments
|
23
|
+
|
24
|
+
### Fixed
|
25
|
+
|
26
|
+
- Fix changelog version ordering
|
27
|
+
|
28
|
+
## [0.2.0] - 2024-11-07
|
29
|
+
|
30
|
+
### Added
|
31
|
+
|
32
|
+
- Faraday as a runtime dependency
|
33
|
+
- Debug as a development dependency
|
34
|
+
- API call framework: resource-oriented design to structure and support API calls
|
35
|
+
- Ability to validate the voter list of a given vote
|
36
|
+
|
37
|
+
### Changed
|
38
|
+
|
39
|
+
- Update README.md with usage instructions
|
40
|
+
- Update .rubocop.yml with new rules
|
41
|
+
|
42
|
+
### Fixed
|
43
|
+
|
44
|
+
- Fix console startup issue caused by incorrect code reference
|
45
|
+
|
3
46
|
## [0.1.0] - 2024-10-29
|
4
47
|
|
5
48
|
- Initial release
|
49
|
+
|
50
|
+
[unreleased]: https://github.com/electionbuddy/electionbuddy-ruby/compare/v0.3.0...HEAD
|
51
|
+
[0.3.0]: https://github.com/electionbuddy/electionbuddy-ruby/compare/v0.2.0...v0.3.0
|
52
|
+
[0.2.0]: https://github.com/electionbuddy/electionbuddy-ruby/compare/v0.1.0...v0.2.0
|
53
|
+
[0.1.0]: https://github.com/electionbuddy/electionbuddy-ruby/releases/tag/v0.1.0
|
54
|
+
|
data/README.md
CHANGED
@@ -1,24 +1,158 @@
|
|
1
|
-
#
|
1
|
+
# ElectionBuddy Ruby
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/electionbuddy/ruby`. To experiment with that code, run `bin/console` for an interactive prompt.
|
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).
|
6
5
|
|
7
6
|
## Installation
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
Install the gem and add to the application's Gemfile by executing:
|
8
|
+
Install the gem and add it to the application's Gemfile by executing:
|
12
9
|
|
13
|
-
|
10
|
+
bundle add electionbuddy-ruby
|
14
11
|
|
15
12
|
If bundler is not being used to manage dependencies, install the gem by executing:
|
16
13
|
|
17
|
-
|
14
|
+
gem install electionbuddy-ruby
|
15
|
+
|
16
|
+
Alternatively, add the gem to the Gemfile:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
gem 'electionbuddy-ruby'
|
20
|
+
```
|
18
21
|
|
19
22
|
## Usage
|
20
23
|
|
21
|
-
|
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:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
client = ElectionBuddy::Client.new
|
44
|
+
```
|
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 Validation
|
53
|
+
|
54
|
+
To validate a voter list, use the `voter_list.validate(vote_id)` method:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
validation = client.voter_list.validate(1)
|
58
|
+
|
59
|
+
if validation.done?
|
60
|
+
puts "Validation completed successfully! Identifier: #{validation.identifier}"
|
61
|
+
else
|
62
|
+
puts "Validation failed: #{validation.error}"
|
63
|
+
end
|
64
|
+
```
|
65
|
+
|
66
|
+
### Get the Validation Result
|
67
|
+
|
68
|
+
Once you have a validation identifier, you can check the validation results.
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
begin
|
72
|
+
validation_result = client.voter_list.validation_result('ae0a1724-9791-4bb2-8331-6d4e55a9b7c8')
|
73
|
+
if validation_result.valid?
|
74
|
+
puts "The voter list is valid!"
|
75
|
+
else
|
76
|
+
puts "The voter list is invalid!"
|
77
|
+
puts "Total errors count: #{validation_result.total_errors_count}"
|
78
|
+
end
|
79
|
+
rescue ElectionBuddy::Error => e
|
80
|
+
puts "Something went wrong: #{e.message}"
|
81
|
+
end
|
82
|
+
```
|
83
|
+
|
84
|
+
The voter list validation can have two types of errors:
|
85
|
+
|
86
|
+
1. **List-level errors**: Affect the entire voter list (e.g., missing required columns)
|
87
|
+
2. **Line-level errors**: Affect specific lines in the voter list (e.g., invalid email format)
|
88
|
+
|
89
|
+
#### List-Level Errors
|
90
|
+
|
91
|
+
List-level errors can be retrieved using the `list_errors` method.
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
if validation_result.list_errors.any?
|
95
|
+
puts "List-level errors:"
|
96
|
+
validation_result.list_errors.each do |list_error|
|
97
|
+
puts "Error message: #{list_error.error_message}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
```
|
101
|
+
|
102
|
+
#### Line-Level Errors
|
103
|
+
|
104
|
+
Line-level errors are paginated. You can specify the page number and the number of errors per page.
|
105
|
+
If you don't specify the page number and the number of errors per page, the default values are 1 and 10, respectively.
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
validation_result = client.voter_list.validation_result('ae0a1724-9791-4bb2-8331-6d4e55a9b7c8', page: 2, per_page: 10)
|
109
|
+
line_errors = validation_result.line_errors
|
110
|
+
puts "There is a total of #{line_errors.total} line-level errors."
|
111
|
+
puts "The current page is #{line_errors.page}."
|
112
|
+
puts "There are #{line_errors.per_page} errors per page."
|
113
|
+
puts "There are #{line_errors.total_pages} pages in total."
|
114
|
+
```
|
115
|
+
|
116
|
+
PS: The `list_errors` object is always available, regardless of the line-level errors pagination.
|
117
|
+
|
118
|
+
To iterate over the line-level errors, you can use the following code:
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
if line_errors.any?
|
122
|
+
puts "Line-level errors for page #{line_errors.page}:"
|
123
|
+
line_errors.each do |line_error|
|
124
|
+
puts "Line identifier: #{line_error.voter_information_line_id}"
|
125
|
+
puts "Error messages:"
|
126
|
+
line_error.each do |line_error|
|
127
|
+
puts "Error messages #{line_error.error_messages}"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
```
|
132
|
+
|
133
|
+
### Possible API Errors
|
134
|
+
|
135
|
+
The following errors may be raised by the API:
|
136
|
+
|
137
|
+
- **400**: Malformed request.
|
138
|
+
- **401**: Invalid authentication credentials.
|
139
|
+
- **403**: Unauthorized.
|
140
|
+
- **404**: Resource not found.
|
141
|
+
- **429**: Your request exceeded the API rate limit.
|
142
|
+
- **500**: We were unable to perform the request due to server-side problems.
|
143
|
+
|
144
|
+
Each error will raise an `Error` exception with a message detailing the status code and the error message returned by the API.
|
145
|
+
|
146
|
+
## Documentation
|
147
|
+
|
148
|
+
The complete documentation for this gem is available at: https://electionbuddy.github.io/electionbuddy-ruby
|
149
|
+
|
150
|
+
You can also generate the documentation locally using:
|
151
|
+
|
152
|
+
```bash
|
153
|
+
yard doc
|
154
|
+
yard server
|
155
|
+
```
|
22
156
|
|
23
157
|
## Development
|
24
158
|
|
@@ -28,7 +162,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
28
162
|
|
29
163
|
## Contributing
|
30
164
|
|
31
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
165
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/electionbuddy/electionbuddy-ruby.
|
32
166
|
|
33
167
|
## License
|
34
168
|
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
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
|
+
#
|
10
|
+
class Client
|
11
|
+
BASE_URL = "https://secure.electionbuddy.com/api/v2"
|
12
|
+
|
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
|
18
|
+
@adapter = adapter
|
19
|
+
@stubs = stubs
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the voter list resource for handling voter-related operations
|
23
|
+
#
|
24
|
+
# @return [VoterListResource] The voter list resource
|
25
|
+
def voter_list
|
26
|
+
VoterListResource.new(connection)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def connection
|
32
|
+
@connection ||= Faraday.new(BASE_URL) do |conn|
|
33
|
+
conn.headers["authorization"] = @api_key
|
34
|
+
conn.request :json
|
35
|
+
conn.response :json, content_type: /\bjson$/
|
36
|
+
conn.adapter @adapter, @stubs
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
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
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
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
|
+
#
|
11
|
+
class Validation
|
12
|
+
# @param response [Hash] The raw API response containing validation details
|
13
|
+
def initialize(response)
|
14
|
+
@response = response
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns the unique identifier for this validation
|
18
|
+
#
|
19
|
+
# @return [String] The validation identifier or "Not available" if not present
|
20
|
+
def identifier
|
21
|
+
@response["validation_identifier"] || "Not available"
|
22
|
+
end
|
23
|
+
|
24
|
+
# Checks if the validation completed successfully without errors
|
25
|
+
#
|
26
|
+
# @return [Boolean] true if validation completed without errors, false otherwise
|
27
|
+
def done?
|
28
|
+
@response["error"].nil?
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns formatted error message if validation failed
|
32
|
+
#
|
33
|
+
# @return [String, nil] Formatted error message or nil if validation was successful
|
34
|
+
def error
|
35
|
+
return nil if done?
|
36
|
+
|
37
|
+
ErrorFormatter.format(@response["error"])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
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
|
11
|
+
class Error < StandardError
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
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
|
+
#
|
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
|
15
|
+
def self.format(error_hash)
|
16
|
+
return if error_hash.nil? || error_hash.empty?
|
17
|
+
|
18
|
+
error_hash.map do |key, value|
|
19
|
+
formatted_key = key.gsub("_", " ").split.map(&:capitalize).join(" ")
|
20
|
+
formatted_value = value.is_a?(Array) ? value.join(", ") : value
|
21
|
+
"#{formatted_key}: #{formatted_value}"
|
22
|
+
end.join(", ")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
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
|
+
#
|
13
|
+
class Resource
|
14
|
+
# @param connection [Object] The HTTP client connection
|
15
|
+
def initialize(connection)
|
16
|
+
@connection = connection
|
17
|
+
end
|
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
|
25
|
+
def get_request(url, params = {}, headers = {}, &block)
|
26
|
+
handle_response(@connection.get(url, params, headers, &block))
|
27
|
+
end
|
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
|
35
|
+
def post_request(url, body = {}, headers = {}, &block)
|
36
|
+
handle_response(@connection.post(url, body, headers, &block))
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def handle_response(response)
|
42
|
+
return response.body if response.success? || [422, 423].include?(response.status)
|
43
|
+
|
44
|
+
raise_error(response.status, ErrorFormatter.format(response.body))
|
45
|
+
end
|
46
|
+
|
47
|
+
def raise_error(status, formatted_error)
|
48
|
+
base_message = error_messages(status)
|
49
|
+
message = if formatted_error
|
50
|
+
"Error #{status}: #{base_message} - #{formatted_error}."
|
51
|
+
else
|
52
|
+
"Error #{status}: #{base_message}."
|
53
|
+
end
|
54
|
+
|
55
|
+
raise Error, message
|
56
|
+
end
|
57
|
+
|
58
|
+
def error_messages(status)
|
59
|
+
messages = {
|
60
|
+
400 => "Malformed request",
|
61
|
+
401 => "Invalid authentication credentials",
|
62
|
+
403 => "Unauthorized",
|
63
|
+
404 => "Resource not found",
|
64
|
+
429 => "Your request exceeded the API rate limit",
|
65
|
+
500 => "We were unable to perform the request due to server-side problems"
|
66
|
+
}
|
67
|
+
messages.fetch(status, "Unexpected status code.")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
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
|
+
#
|
11
|
+
class VoterListResource < Resource
|
12
|
+
# Initiates a validation for a given vote
|
13
|
+
#
|
14
|
+
# @param vote_id [Integer] The ID of the vote to validate
|
15
|
+
# @return [Validation] The validation response
|
16
|
+
def validate(vote_id)
|
17
|
+
response = post_request("/api/v2/votes/voters/validations", vote_id: vote_id.to_i)
|
18
|
+
|
19
|
+
Validation.new(response)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Retrieves the validation results
|
23
|
+
#
|
24
|
+
# @param identifier [String] The validation identifier
|
25
|
+
# @param page [Integer, nil] The page number for paginated results (optional, default: 1)
|
26
|
+
# @param per_page [Integer, nil] The number of items per page (optional, default: 10)
|
27
|
+
# @return [Validation::Result] The validation results
|
28
|
+
def get_validation_result(identifier, page: 1, per_page: 10)
|
29
|
+
params = { "identifier" => identifier, "page" => page, "per_page" => per_page }
|
30
|
+
response = get_request("/api/v2/votes/voters/validations", params)
|
31
|
+
|
32
|
+
Validation::Result.new(response)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElectionBuddy
|
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.3.0"
|
10
|
+
VERSION = "0.3.0"
|
11
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "faraday"
|
4
|
+
require "faraday/middleware"
|
5
|
+
require "json"
|
6
|
+
|
7
|
+
require "election_buddy/version"
|
8
|
+
require "election_buddy/client"
|
9
|
+
require "election_buddy/error"
|
10
|
+
require "election_buddy/resource"
|
11
|
+
require "election_buddy/resources/voter_list_resource"
|
12
|
+
require "election_buddy/entities/validation"
|
13
|
+
require "election_buddy/error_formatter"
|
14
|
+
require "election_buddy/configuration"
|
15
|
+
require "election_buddy/entities/validation/result"
|
16
|
+
require "election_buddy/entities/validation/line_error"
|
17
|
+
require "election_buddy/entities/validation/line_errors"
|
18
|
+
require "election_buddy/entities/validation/list_error"
|
19
|
+
require "election_buddy/entities/validation/list_errors"
|
20
|
+
|
21
|
+
# ElectionBuddy API client library
|
22
|
+
#
|
23
|
+
# @api public
|
24
|
+
# @example Configure the client
|
25
|
+
# ElectionBuddy.configure do |config|
|
26
|
+
# config.api_key = 'your-api-key'
|
27
|
+
# end
|
28
|
+
module ElectionBuddy
|
29
|
+
class << self
|
30
|
+
# @return [Configuration] Current configuration
|
31
|
+
attr_accessor :configuration
|
32
|
+
|
33
|
+
# Configures the ElectionBuddy client
|
34
|
+
#
|
35
|
+
# @yield [config] Configuration instance to be modified
|
36
|
+
# @yieldparam [Configuration] config The configuration instance
|
37
|
+
def configure
|
38
|
+
self.configuration ||= Configuration.new
|
39
|
+
yield(configuration)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
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.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alberto Rocha
|
@@ -9,8 +9,22 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2024-
|
13
|
-
dependencies:
|
12
|
+
date: 2024-11-18 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: faraday
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '2.12'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '2.12'
|
14
28
|
description: This gem provides a Ruby SDK for easy interaction with the ElectionBuddy
|
15
29
|
API
|
16
30
|
email:
|
@@ -21,12 +35,26 @@ extra_rdoc_files: []
|
|
21
35
|
files:
|
22
36
|
- ".devcontainer/devcontainer.json"
|
23
37
|
- ".rubocop.yml"
|
38
|
+
- ".yardopts"
|
24
39
|
- CHANGELOG.md
|
25
40
|
- LICENSE.txt
|
26
41
|
- README.md
|
27
42
|
- Rakefile
|
28
|
-
- lib/
|
29
|
-
- lib/
|
43
|
+
- lib/election_buddy.rb
|
44
|
+
- lib/election_buddy/client.rb
|
45
|
+
- lib/election_buddy/configuration.rb
|
46
|
+
- lib/election_buddy/entities/validation.rb
|
47
|
+
- lib/election_buddy/entities/validation/line_error.rb
|
48
|
+
- lib/election_buddy/entities/validation/line_errors.rb
|
49
|
+
- lib/election_buddy/entities/validation/list_error.rb
|
50
|
+
- lib/election_buddy/entities/validation/list_errors.rb
|
51
|
+
- lib/election_buddy/entities/validation/result.rb
|
52
|
+
- lib/election_buddy/error.rb
|
53
|
+
- lib/election_buddy/error_formatter.rb
|
54
|
+
- lib/election_buddy/resource.rb
|
55
|
+
- lib/election_buddy/resources/voter_list_resource.rb
|
56
|
+
- lib/election_buddy/version.rb
|
57
|
+
- lib/electionbuddy-ruby.rb
|
30
58
|
- sig/electionbuddy/ruby.rbs
|
31
59
|
homepage: https://github.com/electionbuddy/electionbuddy-ruby
|
32
60
|
licenses:
|