dni_peru 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7847ab9a63151aa066c6e6132fb8c153607a992011a09029718bb7de1f4aca2e
4
+ data.tar.gz: b68a9e93403b81cb5add28774122308fcbeb046de481378a5fd6793139fa1034
5
+ SHA512:
6
+ metadata.gz: ab9be7bab4c723630871fc454df03c13ba5e17b8c2ba9233b1ec6ce6b6628fc3c4e7a6a150f4b334deb20cd3d44cacb41d0419f6875e7f088e670ea999131dca
7
+ data.tar.gz: 0ec1fefcc683bcd0af7a048d2c4203e9e5b3464b78ebd97c2739520233cd8870c1320bd458740bb37904493113246f88d278e8a02f3071ca8dc5d871a8d23e0c
data/.env.example ADDED
@@ -0,0 +1,7 @@
1
+ # Decolecta API Configuration
2
+ # Get your API key from: https://decolecta.com/profile/
3
+ DECOLECTA_API_KEY=your_decolecta_api_key_here
4
+
5
+ # ApiPeruDev API Configuration (optional alternative provider)
6
+ # Get your API key from: https://apiperu.dev/
7
+ API_PERU_DEV_KEY=your_apiperu_dev_key_here
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --require spec_helper
2
+ --color
3
+ --format documentation
data/.rubocop.yml ADDED
@@ -0,0 +1,42 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.7
3
+ NewCops: enable
4
+ Exclude:
5
+ - 'vendor/**/*'
6
+ - 'bin/**/*'
7
+
8
+ Style/StringLiterals:
9
+ Enabled: true
10
+ EnforcedStyle: double_quotes
11
+
12
+ Style/StringLiteralsInInterpolation:
13
+ Enabled: true
14
+ EnforcedStyle: double_quotes
15
+
16
+ Style/Documentation:
17
+ Enabled: false
18
+
19
+ Metrics/BlockLength:
20
+ Exclude:
21
+ - 'spec/**/*'
22
+ - '*.gemspec'
23
+
24
+ Metrics/MethodLength:
25
+ Exclude:
26
+ - 'examples/**/*'
27
+
28
+ Metrics/AbcSize:
29
+ Exclude:
30
+ - 'examples/**/*'
31
+
32
+ Metrics/CyclomaticComplexity:
33
+ Max: 10
34
+
35
+ Metrics/PerceivedComplexity:
36
+ Max: 10
37
+
38
+ Layout/LineLength:
39
+ Max: 120
40
+
41
+ Style/FrozenStringLiteralComment:
42
+ Enabled: false
data/CHANGELOG.md ADDED
@@ -0,0 +1,23 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2025-11-28
4
+
5
+ ### Added
6
+ - Initial release
7
+ - Support for multiple API providers
8
+ - Built-in providers: Decolecta (decolecta.com) and ApiPeruDev (apiperu.dev)
9
+ - Decolecta as default provider (official RENIEC data source)
10
+ - Configuration system with API key management
11
+ - Comprehensive error handling
12
+ - Response normalization across providers
13
+ - DNI validation (8-digit format)
14
+ - Configurable timeouts and automatic retries
15
+ - Full documentation and usage examples
16
+ - RSpec test suite with WebMock
17
+ - Rails integration guide
18
+
19
+ ### Security
20
+ - HTTPS-only connections
21
+ - Bearer token authentication
22
+ - API key validation
23
+ - Backend-only access (CORS disabled)
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in dni_peru.gemspec
4
+ gemspec
5
+
6
+ gem "rake", "~> 13.0"
7
+ gem "rspec", "~> 3.0"
8
+ gem "rubocop", "~> 1.21"
9
+ gem "simplecov", "~> 0.21"
10
+ gem "vcr", "~> 6.0"
11
+ gem "webmock", "~> 3.0"
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Ruben Paz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,322 @@
1
+ # DniPeru
2
+
3
+ A Ruby gem that provides a unified interface to query DNI (Documento Nacional de Identidad) information from multiple Peruvian API providers.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'dni_peru'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle install
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install dni_peru
20
+
21
+ ## Getting Started
22
+
23
+ ### 1. Get Your API Key
24
+
25
+ To use this gem, you need to register and get an API key from Decolecta:
26
+
27
+ 1. Visit https://decolecta.com/profile/
28
+ 2. Sign up for a free account
29
+ 3. Generate your API token
30
+ 4. Copy the token for configuration
31
+
32
+ ### 2. Store Your API Key Securely
33
+
34
+ **For Rails applications**, add to your credentials:
35
+
36
+ ```bash
37
+ EDITOR="code --wait" rails credentials:edit
38
+ ```
39
+
40
+ Add:
41
+ ```yaml
42
+ decolecta:
43
+ api_key: your_api_key_here
44
+ ```
45
+
46
+ **For other applications**, use environment variables:
47
+
48
+ ```bash
49
+ export DECOLECTA_API_KEY="your_api_key_here"
50
+ ```
51
+
52
+ Or use a `.env` file with the `dotenv` gem.
53
+
54
+ ## Configuration
55
+
56
+ Configure the gem with your preferred settings and API keys:
57
+
58
+ ```ruby
59
+ DniPeru.configure do |config|
60
+ # Set the default provider (optional, defaults to :decolecta)
61
+ config.default_provider = :decolecta
62
+
63
+ # Set timeout in seconds (optional, defaults to 30)
64
+ config.timeout = 30
65
+
66
+ # Set API keys for different providers
67
+ config.set_api_key(:decolecta, ENV['DECOLECTA_API_KEY'])
68
+ config.set_api_key(:api_peru_dev, ENV['API_PERU_DEV_KEY'])
69
+ end
70
+ ```
71
+
72
+ ### Quick Start
73
+
74
+ ```ruby
75
+ require 'dni_peru'
76
+
77
+ # Configure with your API key
78
+ DniPeru.configure do |config|
79
+ config.set_api_key(:decolecta, 'your-api-key')
80
+ end
81
+
82
+ # Query a DNI
83
+ response = DniPeru.query('12345678')
84
+
85
+ if response.success?
86
+ puts "Nombre: #{response.nombre_completo}"
87
+ # => "Nombre: DOE SMITH, JOHN"
88
+ end
89
+ ```
90
+
91
+ ## Usage
92
+
93
+ ### Basic Usage
94
+
95
+ Query a DNI using the default provider:
96
+
97
+ ```ruby
98
+ response = DniPeru.query('12345678')
99
+
100
+ if response.success?
101
+ puts response.nombre_completo
102
+ # => "DOE SMITH, JOHN"
103
+
104
+ puts response.nombres
105
+ # => "JOHN"
106
+
107
+ puts response.apellido_paterno
108
+ # => "DOE"
109
+
110
+ puts response.apellido_materno
111
+ # => "SMITH"
112
+
113
+ puts response.dni
114
+ # => "12345678"
115
+ end
116
+ ```
117
+
118
+ ### Using a Specific Provider
119
+
120
+ You can specify which provider to use for a query:
121
+
122
+ ```ruby
123
+ # Using decolecta.com (recommended)
124
+ response = DniPeru.query('12345678', provider: :decolecta)
125
+
126
+ # Using apiperu.dev
127
+ response = DniPeru.query('12345678', provider: :api_peru_dev)
128
+ ```
129
+
130
+ ### Using the Client Directly
131
+
132
+ For more control, you can create a client instance:
133
+
134
+ ```ruby
135
+ client = DniPeru::Client.new(provider: :decolecta)
136
+ response = client.query('12345678')
137
+
138
+ puts response.to_h
139
+ # => {
140
+ # dni: "12345678",
141
+ # nombres: "JOHN",
142
+ # apellido_paterno: "DOE",
143
+ # apellido_materno: "SMITH",
144
+ # nombre_completo: "DOE SMITH, JOHN",
145
+ # provider: "Decolecta"
146
+ # }
147
+ ```
148
+
149
+ ### Error Handling
150
+
151
+ The gem raises specific errors for different scenarios:
152
+
153
+ ```ruby
154
+ begin
155
+ response = DniPeru.query('12345678')
156
+ rescue DniPeru::InvalidDniError => e
157
+ puts "Invalid DNI format: #{e.message}"
158
+ rescue DniPeru::NotFoundError => e
159
+ puts "DNI not found: #{e.message}"
160
+ rescue DniPeru::UnauthorizedError => e
161
+ puts "Invalid API key: #{e.message}"
162
+ rescue DniPeru::RateLimitError => e
163
+ puts "Rate limit exceeded: #{e.message}"
164
+ rescue DniPeru::ConnectionError => e
165
+ puts "Connection failed: #{e.message}"
166
+ rescue DniPeru::ApiError => e
167
+ puts "API error: #{e.message}"
168
+ end
169
+ ```
170
+
171
+ ## Supported Providers
172
+
173
+ ### Decolecta (Recommended)
174
+
175
+ Provider identifier: `:decolecta`
176
+
177
+ - Website: https://decolecta.com/
178
+ - Documentation: https://decolecta.gitbook.io/docs
179
+ - Registration: https://decolecta.com/profile/
180
+ - **Requires API key** (register for free)
181
+ - Data source: RENIEC (official government registry)
182
+ - Security: HTTPS only, Bearer token authentication
183
+ - Features: DNI, RUC, Exchange rates
184
+ - Status monitor: https://status.apis.net.pe/
185
+ - Configuration: `config.set_api_key(:decolecta, 'your-api-key')`
186
+
187
+ **API Endpoint:**
188
+ ```bash
189
+ curl -H 'Accept: application/json' -H "Authorization: Bearer YOUR_TOKEN" \
190
+ 'https://api.decolecta.com/v1/reniec/dni?numero=12345678'
191
+ ```
192
+
193
+ ### apiperu.dev
194
+
195
+ Provider identifier: `:api_peru_dev`
196
+
197
+ - Website: https://apiperu.dev/
198
+ - Requires API key
199
+ - Free tier: 100 queries/month
200
+ - Data source: SUNAT reduced registry and public sources
201
+ - Configuration: `config.set_api_key(:api_peru_dev, 'your-key')`
202
+
203
+ ## Rails Integration
204
+
205
+ ### Initializer
206
+
207
+ Create an initializer file `config/initializers/dni_peru.rb`:
208
+
209
+ ```ruby
210
+ DniPeru.configure do |config|
211
+ config.default_provider = :decolecta
212
+ config.timeout = 30
213
+ config.set_api_key(:decolecta, Rails.application.credentials.dig(:decolecta, :api_key))
214
+ config.set_api_key(:api_peru_dev, Rails.application.credentials.dig(:api_peru_dev, :api_key))
215
+ end
216
+ ```
217
+
218
+ ### Usage in Models
219
+
220
+ ```ruby
221
+ class User < ApplicationRecord
222
+ validates :dni, presence: true, length: { is: 8 }
223
+
224
+ def fetch_dni_info
225
+ begin
226
+ response = DniPeru.query(self.dni)
227
+ update(
228
+ first_name: response.nombres,
229
+ paternal_surname: response.apellido_paterno,
230
+ maternal_surname: response.apellido_materno
231
+ )
232
+ rescue DniPeru::Error => e
233
+ errors.add(:dni, "Could not verify DNI: #{e.message}")
234
+ false
235
+ end
236
+ end
237
+ end
238
+ ```
239
+
240
+ ### Usage in Controllers
241
+
242
+ ```ruby
243
+ class UsersController < ApplicationController
244
+ def verify_dni
245
+ @response = DniPeru.query(params[:dni])
246
+ render json: @response.to_h
247
+ rescue DniPeru::Error => e
248
+ render json: { error: e.message }, status: :unprocessable_entity
249
+ end
250
+ end
251
+ ```
252
+
253
+ ## Response Object
254
+
255
+ The `Response` object provides the following attributes:
256
+
257
+ - `dni` - The DNI number
258
+ - `nombres` - First/given names
259
+ - `apellido_paterno` - Paternal surname
260
+ - `apellido_materno` - Maternal surname
261
+ - `nombre_completo` - Full name in format "PATERNAL MATERNAL, NAMES"
262
+ - `provider` - The provider used for the query
263
+ - `raw_data` - Raw data from the API response
264
+
265
+ Methods:
266
+ - `success?` - Returns true if the query was successful
267
+ - `to_h` - Returns a hash representation of the response
268
+
269
+ ## Development
270
+
271
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
272
+
273
+ To install this gem onto your local machine, run `bundle exec rake install`.
274
+
275
+ ### Testing Your Configuration
276
+
277
+ Test your API configuration quickly:
278
+
279
+ ```bash
280
+ export DECOLECTA_API_KEY='your-api-key'
281
+ ruby examples/test_config.rb 12345678
282
+ ```
283
+
284
+ ### Examples
285
+
286
+ The `examples/` directory contains practical examples:
287
+
288
+ - `examples/basic_usage.rb` - Basic gem usage examples
289
+ - `examples/rails_integration.rb` - Rails integration patterns (services, controllers, models, jobs)
290
+ - `examples/test_config.rb` - Configuration testing script
291
+
292
+ ## Testing
293
+
294
+ Run the test suite:
295
+
296
+ ```bash
297
+ bundle exec rspec
298
+ ```
299
+
300
+ Run with coverage:
301
+
302
+ ```bash
303
+ COVERAGE=true bundle exec rspec
304
+ ```
305
+
306
+ ## Contributing
307
+
308
+ Bug reports and pull requests are welcome on GitHub at https://github.com/rubenpazch/dni-peru.
309
+
310
+ 1. Fork it
311
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
312
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
313
+ 4. Push to the branch (`git push origin my-new-feature`)
314
+ 5. Create a new Pull Request
315
+
316
+ ## License
317
+
318
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
319
+
320
+ ## Code of Conduct
321
+
322
+ Everyone interacting in the DniPeru project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/rubenpazch/dni-peru/blob/main/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ require "rubocop/rake_task"
7
+
8
+ RuboCop::RakeTask.new
9
+
10
+ task default: %i[spec rubocop]
data/TEST_COVERAGE.md ADDED
@@ -0,0 +1,122 @@
1
+ # Test Coverage Report
2
+
3
+ ## Test Suite Structure
4
+
5
+ ```
6
+ spec/
7
+ ├── client_spec.rb # Client initialization and query logic
8
+ ├── configuration_spec.rb # Configuration and API key management
9
+ ├── dni_peru_spec.rb # Main module interface
10
+ ├── errors_spec.rb # Error classes and hierarchy
11
+ ├── response_spec.rb # Response parsing and formatting
12
+ └── providers/
13
+ ├── base_spec.rb # Base provider functionality
14
+ ├── decolecta_spec.rb # Decolecta provider integration
15
+ └── api_peru_dev_spec.rb # ApiPeruDev provider integration
16
+ ```
17
+
18
+ ## Coverage Areas
19
+
20
+ ### ✅ Core Functionality
21
+ - [x] **DniPeru Module** - Main interface and delegation
22
+ - [x] **Configuration** - Default settings, API key storage
23
+ - [x] **Client** - Provider selection, DNI validation, error wrapping
24
+ - [x] **Response** - Data parsing, normalization, full name generation
25
+ - [x] **Errors** - All error types and hierarchy
26
+
27
+ ### ✅ Providers
28
+ - [x] **Base Provider** - Connection setup, response parsing, error handling
29
+ - [x] **Decolecta** - API integration, authentication, data normalization
30
+ - [x] **ApiPeruDev** - API integration, nested data handling
31
+
32
+ ### ✅ Test Scenarios
33
+
34
+ #### Configuration Tests
35
+ - Default provider and timeout settings
36
+ - API key storage and retrieval
37
+ - Multiple provider support
38
+ - Key overwriting
39
+
40
+ #### Client Tests
41
+ - Provider initialization
42
+ - DNI format validation (empty, short, invalid characters)
43
+ - Integer to string conversion
44
+ - Provider delegation
45
+ - Faraday error wrapping
46
+ - Default configuration fallback
47
+
48
+ #### Provider Tests
49
+ - API authentication (with/without keys)
50
+ - Successful responses (200)
51
+ - Error responses (401, 404, 429, 500)
52
+ - Data normalization
53
+ - Connection configuration
54
+ - Response building
55
+
56
+ #### Response Tests
57
+ - Symbol and string key parsing
58
+ - Different key formats (camelCase, snake_case)
59
+ - Full name generation
60
+ - Success validation
61
+ - Hash conversion
62
+ - Raw data preservation
63
+
64
+ #### Error Tests
65
+ - Error inheritance hierarchy
66
+ - Custom error messages
67
+ - Error catching patterns
68
+ - API vs non-API error distinction
69
+
70
+ ## Running Tests
71
+
72
+ ### Run all tests
73
+ ```bash
74
+ bundle exec rspec
75
+ ```
76
+
77
+ ### Run with coverage
78
+ ```bash
79
+ COVERAGE=true bundle exec rspec
80
+ ```
81
+
82
+ ### Run specific test file
83
+ ```bash
84
+ bundle exec rspec spec/client_spec.rb
85
+ ```
86
+
87
+ ### Run specific test
88
+ ```bash
89
+ bundle exec rspec spec/client_spec.rb:15
90
+ ```
91
+
92
+ ### Run with documentation format
93
+ ```bash
94
+ bundle exec rspec --format documentation
95
+ ```
96
+
97
+ ## Expected Coverage
98
+
99
+ With the comprehensive test suite, we should achieve:
100
+
101
+ - **Overall Coverage**: ~95-100%
102
+ - **Core Classes**: 100%
103
+ - **Providers**: ~95% (some edge cases may be hard to simulate)
104
+ - **Error Handling**: 100%
105
+
106
+ ## Key Test Dependencies
107
+
108
+ - **RSpec** - Testing framework
109
+ - **WebMock** - HTTP request stubbing
110
+ - **SimpleCov** - Coverage reporting
111
+
112
+ ## Coverage Report Location
113
+
114
+ After running tests with `COVERAGE=true`, view the report at:
115
+ ```
116
+ coverage/index.html
117
+ ```
118
+
119
+ Open in browser:
120
+ ```bash
121
+ open coverage/index.html
122
+ ```
data/dni_peru.gemspec ADDED
@@ -0,0 +1,34 @@
1
+ require_relative "lib/dni_peru/version"
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "dni_peru"
5
+ spec.version = DniPeru::VERSION
6
+ spec.authors = ["Ruben Paz"]
7
+ spec.email = ["rubenpazch@gmail.com"]
8
+
9
+ spec.summary = "Ruby gem to query DNI information from Peruvian API providers"
10
+ spec.description = "A flexible Ruby gem that provides a unified interface to query DNI " \
11
+ "(Documento Nacional de Identidad) information from multiple Peruvian API providers."
12
+ spec.homepage = "https://github.com/rubenpazch/dni-peru"
13
+ spec.license = "MIT"
14
+ spec.required_ruby_version = ">= 2.7.0"
15
+
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+ spec.metadata["source_code_uri"] = "https://github.com/rubenpazch/dni-peru"
18
+ spec.metadata["changelog_uri"] = "https://github.com/rubenpazch/dni-peru/blob/main/CHANGELOG.md"
19
+ spec.metadata["rubygems_mfa_required"] = "true"
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
23
+ `git ls-files -z`.split("\x0").reject do |f|
24
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
25
+ end
26
+ end
27
+ spec.bindir = "exe"
28
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
29
+ spec.require_paths = ["lib"]
30
+
31
+ # Runtime dependencies
32
+ spec.add_dependency "faraday", "~> 2.0"
33
+ spec.add_dependency "faraday-retry", "~> 2.0"
34
+ end
@@ -0,0 +1,103 @@
1
+ # Example usage of dni_peru gem
2
+
3
+ require "bundler/setup"
4
+ require "dni_peru"
5
+
6
+ # Configure the gem with your Decolecta API key
7
+ # Get your API key from: https://decolecta.com/profile/
8
+ DniPeru.configure do |config|
9
+ config.default_provider = :decolecta
10
+ config.timeout = 30
11
+
12
+ # Option 1: Use environment variable (recommended)
13
+ config.set_api_key(:decolecta, ENV.fetch("DECOLECTA_API_KEY", nil))
14
+
15
+ # Option 2: Hard-code (not recommended for production)
16
+ # config.set_api_key(:decolecta, "your-api-key-here")
17
+ end
18
+
19
+ # Example 1: Simple query
20
+ puts "Example 1: Simple DNI Query"
21
+ puts "-" * 50
22
+
23
+ begin
24
+ response = DniPeru.query("12345678")
25
+
26
+ if response.success?
27
+ puts "DNI encontrado!"
28
+ puts "DNI: #{response.dni}"
29
+ puts "Nombres: #{response.nombres}"
30
+ puts "Apellido Paterno: #{response.apellido_paterno}"
31
+ puts "Apellido Materno: #{response.apellido_materno}"
32
+ puts "Nombre Completo: #{response.nombre_completo}"
33
+ puts "Proveedor: #{response.provider}"
34
+ else
35
+ puts "No se encontró información"
36
+ end
37
+ rescue DniPeru::InvalidDniError => e
38
+ puts "Error: DNI inválido - #{e.message}"
39
+ rescue DniPeru::UnauthorizedError => e
40
+ puts "Error: API key inválida - #{e.message}"
41
+ rescue DniPeru::NotFoundError => e
42
+ puts "Error: DNI no encontrado - #{e.message}"
43
+ rescue DniPeru::Error => e
44
+ puts "Error: #{e.message}"
45
+ end
46
+
47
+ puts "\n"
48
+
49
+ # Example 2: Using specific provider
50
+ puts "Example 2: Using Specific Provider"
51
+ puts "-" * 50
52
+
53
+ begin
54
+ client = DniPeru::Client.new(provider: :decolecta)
55
+ response = client.query("87654321")
56
+
57
+ puts response.to_h.inspect
58
+ rescue DniPeru::Error => e
59
+ puts "Error: #{e.message}"
60
+ end
61
+
62
+ puts "\n"
63
+
64
+ # Example 3: Validate DNI before querying
65
+ puts "Example 3: DNI Validation"
66
+ puts "-" * 50
67
+
68
+ invalid_dnis = ["", "123", "1234567a", "123456789"]
69
+
70
+ invalid_dnis.each do |dni|
71
+ DniPeru.query(dni)
72
+ rescue DniPeru::InvalidDniError => e
73
+ puts "DNI '#{dni}': #{e.message}"
74
+ end
75
+
76
+ puts "\n"
77
+
78
+ # Example 4: Error handling
79
+ puts "Example 4: Comprehensive Error Handling"
80
+ puts "-" * 50
81
+
82
+ def query_dni_safely(dni)
83
+ response = DniPeru.query(dni)
84
+ {
85
+ success: true,
86
+ data: response.to_h
87
+ }
88
+ rescue DniPeru::InvalidDniError => e
89
+ { success: false, error: "Formato inválido", message: e.message }
90
+ rescue DniPeru::NotFoundError => e
91
+ { success: false, error: "No encontrado", message: e.message }
92
+ rescue DniPeru::UnauthorizedError => e
93
+ { success: false, error: "No autorizado", message: e.message }
94
+ rescue DniPeru::RateLimitError => e
95
+ { success: false, error: "Límite excedido", message: e.message }
96
+ rescue DniPeru::ConnectionError => e
97
+ { success: false, error: "Error de conexión", message: e.message }
98
+ rescue DniPeru::ApiError => e
99
+ { success: false, error: "Error de API", message: e.message }
100
+ end
101
+
102
+ result = query_dni_safely("12345678")
103
+ puts result.inspect
@@ -0,0 +1,133 @@
1
+ # Rails Example: DNI Verification Service
2
+ # Place this in app/services/dni_verification_service.rb
3
+
4
+ class DniVerificationService
5
+ def initialize(dni)
6
+ @dni = dni
7
+ end
8
+
9
+ def call
10
+ response = DniPeru.query(@dni)
11
+
12
+ if response.success?
13
+ {
14
+ success: true,
15
+ data: {
16
+ dni: response.dni,
17
+ nombres: response.nombres,
18
+ apellido_paterno: response.apellido_paterno,
19
+ apellido_materno: response.apellido_materno,
20
+ nombre_completo: response.nombre_completo
21
+ }
22
+ }
23
+ else
24
+ { success: false, error: "DNI no encontrado" }
25
+ end
26
+ rescue DniPeru::InvalidDniError => e
27
+ { success: false, error: "Formato de DNI inválido: #{e.message}" }
28
+ rescue DniPeru::NotFoundError
29
+ { success: false, error: "DNI no encontrado en RENIEC" }
30
+ rescue DniPeru::UnauthorizedError => e
31
+ Rails.logger.error "DniPeru API key error: #{e.message}"
32
+ { success: false, error: "Error de configuración del servicio" }
33
+ rescue DniPeru::RateLimitError => e
34
+ Rails.logger.warn "DniPeru rate limit exceeded: #{e.message}"
35
+ { success: false, error: "Servicio temporalmente no disponible. Intente más tarde." }
36
+ rescue DniPeru::ConnectionError => e
37
+ Rails.logger.error "DniPeru connection error: #{e.message}"
38
+ { success: false, error: "Error de conexión con el servicio" }
39
+ rescue DniPeru::ApiError => e
40
+ Rails.logger.error "DniPeru API error: #{e.message}"
41
+ { success: false, error: "Error en el servicio de verificación" }
42
+ end
43
+ end
44
+
45
+ # Usage in controller:
46
+ # app/controllers/api/v1/dni_controller.rb
47
+
48
+ module Api
49
+ module V1
50
+ class DniController < ApplicationController
51
+ def verify
52
+ result = DniVerificationService.new(params[:dni]).call
53
+
54
+ if result[:success]
55
+ render json: result[:data], status: :ok
56
+ else
57
+ render json: { error: result[:error] }, status: :unprocessable_entity
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ # Usage in model:
65
+ # app/models/user.rb
66
+
67
+ class User < ApplicationRecord
68
+ validates :dni, presence: true, length: { is: 8 }
69
+
70
+ # Callback to auto-fill names from DNI
71
+ before_validation :fetch_dni_info, if: :will_save_change_to_dni?
72
+
73
+ def verify_dni
74
+ result = DniVerificationService.new(dni).call
75
+
76
+ if result[:success]
77
+ data = result[:data]
78
+ update(
79
+ nombres: data[:nombres],
80
+ apellido_paterno: data[:apellido_paterno],
81
+ apellido_materno: data[:apellido_materno],
82
+ nombre_completo: data[:nombre_completo],
83
+ dni_verified: true,
84
+ dni_verified_at: Time.current
85
+ )
86
+ else
87
+ errors.add(:dni, result[:error])
88
+ false
89
+ end
90
+ end
91
+
92
+ private
93
+
94
+ def fetch_dni_info
95
+ return unless dni.present? && dni.length == 8
96
+
97
+ result = DniVerificationService.new(dni).call
98
+
99
+ if result[:success]
100
+ data = result[:data]
101
+ self.nombres = data[:nombres]
102
+ self.apellido_paterno = data[:apellido_paterno]
103
+ self.apellido_materno = data[:apellido_materno]
104
+ self.nombre_completo = data[:nombre_completo]
105
+ end
106
+ rescue StandardError => e
107
+ Rails.logger.error "Failed to fetch DNI info: #{e.message}"
108
+ # Don't block user creation if DNI service fails
109
+ end
110
+ end
111
+
112
+ # Usage in background job:
113
+ # app/jobs/verify_dni_job.rb
114
+
115
+ class VerifyDniJob < ApplicationJob
116
+ queue_as :default
117
+
118
+ def perform(user_id)
119
+ user = User.find(user_id)
120
+ result = DniVerificationService.new(user.dni).call
121
+
122
+ if result[:success]
123
+ user.update(
124
+ dni_verified: true,
125
+ dni_verified_at: Time.current,
126
+ **result[:data]
127
+ )
128
+ else
129
+ user.update(dni_verified: false)
130
+ Rails.logger.warn "DNI verification failed for user #{user.id}: #{result[:error]}"
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env ruby
2
+ # Test your Decolecta API configuration
3
+ # Usage: ruby examples/test_config.rb
4
+
5
+ require "bundler/setup"
6
+ require "dni_peru"
7
+
8
+ puts "=" * 60
9
+ puts "DNI Peru Gem - Configuration Test"
10
+ puts "=" * 60
11
+ puts ""
12
+
13
+ # Check if API key is set
14
+ api_key = ENV.fetch("DECOLECTA_API_KEY", nil)
15
+
16
+ if api_key.nil? || api_key.empty?
17
+ puts "❌ ERROR: DECOLECTA_API_KEY environment variable not set"
18
+ puts ""
19
+ puts "To fix this:"
20
+ puts " 1. Get your API key from: https://decolecta.com/profile/"
21
+ puts " 2. Set it as environment variable:"
22
+ puts " export DECOLECTA_API_KEY='your-api-key-here'"
23
+ puts ""
24
+ exit 1
25
+ end
26
+
27
+ puts "✓ API key found in environment"
28
+ puts ""
29
+
30
+ # Configure the gem
31
+ DniPeru.configure do |config|
32
+ config.set_api_key(:decolecta, api_key)
33
+ config.timeout = 30
34
+ end
35
+
36
+ puts "✓ Gem configured successfully"
37
+ puts ""
38
+
39
+ # Test with a sample DNI (you can change this)
40
+ test_dni = ARGV[0] || "12345678"
41
+
42
+ puts "Testing with DNI: #{test_dni}"
43
+ puts "-" * 60
44
+
45
+ begin
46
+ response = DniPeru.query(test_dni)
47
+
48
+ if response.success?
49
+ puts "✓ API connection successful!"
50
+ puts ""
51
+ puts "Response details:"
52
+ puts " DNI: #{response.dni}"
53
+ puts " Nombres: #{response.nombres}"
54
+ puts " Apellido Paterno: #{response.apellido_paterno}"
55
+ puts " Apellido Materno: #{response.apellido_materno}"
56
+ puts " Nombre Completo: #{response.nombre_completo}"
57
+ puts " Provider: #{response.provider}"
58
+ puts ""
59
+ puts "✓ Everything is working correctly!"
60
+ else
61
+ puts "⚠ API responded but no data found"
62
+ end
63
+ rescue DniPeru::UnauthorizedError => e
64
+ puts "❌ Authentication Error"
65
+ puts " #{e.message}"
66
+ puts ""
67
+ puts "Please check:"
68
+ puts " 1. Your API key is correct"
69
+ puts " 2. Your API key is active at https://decolecta.com/profile/"
70
+ rescue DniPeru::NotFoundError
71
+ puts "⚠ DNI not found in database"
72
+ puts " This is normal if the DNI doesn't exist"
73
+ puts " Try with a different DNI number"
74
+ rescue DniPeru::InvalidDniError => e
75
+ puts "❌ Invalid DNI format"
76
+ puts " #{e.message}"
77
+ puts " DNI must be exactly 8 digits"
78
+ rescue DniPeru::RateLimitError => e
79
+ puts "⚠ Rate limit exceeded"
80
+ puts " #{e.message}"
81
+ puts " Please wait a moment before trying again"
82
+ rescue DniPeru::ConnectionError => e
83
+ puts "❌ Connection Error"
84
+ puts " #{e.message}"
85
+ puts ""
86
+ puts "Please check:"
87
+ puts " 1. Your internet connection"
88
+ puts " 2. Decolecta service status"
89
+ rescue DniPeru::ApiError => e
90
+ puts "❌ API Error"
91
+ puts " #{e.message}"
92
+ rescue StandardError => e
93
+ puts "❌ Unexpected Error"
94
+ puts " #{e.class}: #{e.message}"
95
+ puts ""
96
+ puts " Please report this issue at:"
97
+ puts " https://github.com/rubenpazch/dni-peru/issues"
98
+ end
99
+
100
+ puts ""
101
+ puts "=" * 60
102
+ puts "Test complete"
103
+ puts "=" * 60
@@ -0,0 +1,46 @@
1
+ require "faraday"
2
+ require "faraday/retry"
3
+
4
+ module DniPeru
5
+ class Client
6
+ attr_reader :provider
7
+
8
+ PROVIDERS = {
9
+ decolecta: Providers::Decolecta,
10
+ api_peru_dev: Providers::ApiPeruDev
11
+ }.freeze
12
+
13
+ def initialize(provider: nil)
14
+ @provider_name = provider || config.default_provider
15
+ @provider = load_provider(@provider_name)
16
+ end
17
+
18
+ def query(dni)
19
+ dni_string = validate_dni!(dni)
20
+ provider.query(dni_string)
21
+ rescue Faraday::Error => e
22
+ raise ConnectionError, "Failed to connect to provider: #{e.message}"
23
+ end
24
+
25
+ private
26
+
27
+ def load_provider(provider_name)
28
+ provider_class = PROVIDERS[provider_name.to_sym]
29
+ raise InvalidProviderError, "Unknown provider: #{provider_name}" unless provider_class
30
+
31
+ provider_class.new(config)
32
+ end
33
+
34
+ def validate_dni!(dni)
35
+ dni_string = dni.to_s
36
+ raise InvalidDniError, "DNI cannot be empty" if dni_string.empty?
37
+ raise InvalidDniError, "DNI must be 8 digits" unless dni_string.match?(/^\d{8}$/)
38
+
39
+ dni_string
40
+ end
41
+
42
+ def config
43
+ DniPeru.configuration || Configuration.new
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,19 @@
1
+ module DniPeru
2
+ class Configuration
3
+ attr_accessor :default_provider, :timeout, :api_keys
4
+
5
+ def initialize
6
+ @default_provider = :decolecta
7
+ @timeout = 30
8
+ @api_keys = {}
9
+ end
10
+
11
+ def set_api_key(provider, key)
12
+ @api_keys[provider.to_sym] = key
13
+ end
14
+
15
+ def get_api_key(provider)
16
+ @api_keys[provider.to_sym]
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,10 @@
1
+ module DniPeru
2
+ class Error < StandardError; end
3
+ class InvalidDniError < Error; end
4
+ class InvalidProviderError < Error; end
5
+ class ConnectionError < Error; end
6
+ class ApiError < Error; end
7
+ class NotFoundError < ApiError; end
8
+ class UnauthorizedError < ApiError; end
9
+ class RateLimitError < ApiError; end
10
+ end
@@ -0,0 +1,33 @@
1
+ module DniPeru
2
+ module Providers
3
+ class ApiPeruDev < Base
4
+ BASE_URL = "https://apiperu.dev/api/dni".freeze
5
+
6
+ def query(dni)
7
+ api_key = config.get_api_key(:api_peru_dev)
8
+
9
+ response = connection.get("#{BASE_URL}/#{dni}") do |req|
10
+ req.headers["Authorization"] = "Bearer #{api_key}" if api_key
11
+ req.headers["Content-Type"] = "application/json"
12
+ end
13
+
14
+ data = parse_response(response)
15
+ build_response(normalize_data(data))
16
+ end
17
+
18
+ private
19
+
20
+ def normalize_data(data)
21
+ # Extract from nested structure if present
22
+ person_data = data[:data] || data
23
+
24
+ {
25
+ dni: person_data[:numero] || person_data[:dni],
26
+ nombres: person_data[:nombres],
27
+ apellido_paterno: person_data[:apellido_paterno],
28
+ apellido_materno: person_data[:apellido_materno]
29
+ }
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,32 @@
1
+ module DniPeru
2
+ module Providers
3
+ class Decolecta < Base
4
+ BASE_URL = "https://api.decolecta.com/v1/reniec/dni".freeze
5
+
6
+ def query(dni)
7
+ api_key = config.get_api_key(:decolecta)
8
+ raise UnauthorizedError, "API key is required for Decolecta provider" unless api_key
9
+
10
+ response = connection.get(BASE_URL) do |req|
11
+ req.params["numero"] = dni
12
+ req.headers["Authorization"] = "Bearer #{api_key}"
13
+ req.headers["Accept"] = "application/json"
14
+ end
15
+
16
+ data = parse_response(response)
17
+ build_response(normalize_data(data))
18
+ end
19
+
20
+ private
21
+
22
+ def normalize_data(data)
23
+ {
24
+ dni: data[:numeroDocumento] || data[:numero_documento],
25
+ nombres: data[:nombres],
26
+ apellido_paterno: data[:apellidoPaterno] || data[:apellido_paterno],
27
+ apellido_materno: data[:apellidoMaterno] || data[:apellido_materno]
28
+ }
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,52 @@
1
+ require "faraday"
2
+ require "json"
3
+
4
+ module DniPeru
5
+ module Providers
6
+ class Base
7
+ attr_reader :config
8
+
9
+ def initialize(config)
10
+ @config = config
11
+ end
12
+
13
+ def query(dni)
14
+ raise NotImplementedError, "Subclasses must implement the query method"
15
+ end
16
+
17
+ private
18
+
19
+ def connection
20
+ @connection ||= Faraday.new do |f|
21
+ f.request :retry, max: 2, interval: 0.5, backoff_factor: 2
22
+ f.adapter Faraday.default_adapter
23
+ f.options.timeout = config.timeout
24
+ f.options.open_timeout = 10
25
+ end
26
+ end
27
+
28
+ def parse_response(response)
29
+ return JSON.parse(response.body, symbolize_names: true) if response.status == 200
30
+
31
+ handle_error_response(response)
32
+ end
33
+
34
+ def handle_error_response(response)
35
+ case response.status
36
+ when 404
37
+ raise NotFoundError, "DNI not found"
38
+ when 401
39
+ raise UnauthorizedError, "Invalid API key or unauthorized access"
40
+ when 429
41
+ raise RateLimitError, "Rate limit exceeded"
42
+ else
43
+ raise ApiError, "API returned status #{response.status}: #{response.body}"
44
+ end
45
+ end
46
+
47
+ def build_response(data)
48
+ Response.new(data, provider: self.class.name.split("::").last)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,32 @@
1
+ module DniPeru
2
+ module Providers
3
+ class Decolecta < Base
4
+ BASE_URL = "https://api.decolecta.com/v1/reniec/dni".freeze
5
+
6
+ def query(dni)
7
+ api_key = config.get_api_key(:decolecta)
8
+ raise UnauthorizedError, "API key is required for Decolecta provider" unless api_key
9
+
10
+ response = connection.get(BASE_URL) do |req|
11
+ req.params["numero"] = dni
12
+ req.headers["Authorization"] = "Bearer #{api_key}"
13
+ req.headers["Accept"] = "application/json"
14
+ end
15
+
16
+ data = parse_response(response)
17
+ build_response(normalize_data(data))
18
+ end
19
+
20
+ private
21
+
22
+ def normalize_data(data)
23
+ {
24
+ dni: data[:numeroDocumento] || data[:numero_documento],
25
+ nombres: data[:nombres],
26
+ apellido_paterno: data[:apellidoPaterno] || data[:apellido_paterno],
27
+ apellido_materno: data[:apellidoMaterno] || data[:apellido_materno]
28
+ }
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,51 @@
1
+ module DniPeru
2
+ class Response
3
+ attr_reader :dni, :nombres, :apellido_paterno, :apellido_materno,
4
+ :nombre_completo, :raw_data, :provider
5
+
6
+ def initialize(data, provider:)
7
+ @provider = provider
8
+ @raw_data = data
9
+ parse_data(data)
10
+ end
11
+
12
+ def success?
13
+ !@dni.nil?
14
+ end
15
+
16
+ def to_h
17
+ {
18
+ dni: @dni,
19
+ nombres: @nombres,
20
+ apellido_paterno: @apellido_paterno,
21
+ apellido_materno: @apellido_materno,
22
+ nombre_completo: @nombre_completo,
23
+ provider: @provider
24
+ }
25
+ end
26
+
27
+ private
28
+
29
+ def parse_data(data)
30
+ @dni = extract_field(data, :dni, "dni")
31
+ @nombres = extract_field(data, :nombres, "nombres")
32
+ @apellido_paterno = extract_field(data, :apellido_paterno, "apellidoPaterno", "apellido_paterno")
33
+ @apellido_materno = extract_field(data, :apellido_materno, "apellidoMaterno", "apellido_materno")
34
+ @nombre_completo = extract_field(data, :nombre_completo, "nombreCompleto") || build_full_name
35
+ end
36
+
37
+ def extract_field(data, *keys)
38
+ keys.each do |key|
39
+ value = data[key]
40
+ return value if value
41
+ end
42
+ nil
43
+ end
44
+
45
+ def build_full_name
46
+ return nil unless @nombres && @apellido_paterno && @apellido_materno
47
+
48
+ "#{@apellido_paterno} #{@apellido_materno}, #{@nombres}"
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,3 @@
1
+ module DniPeru
2
+ VERSION = "0.1.0".freeze
3
+ end
data/lib/dni_peru.rb ADDED
@@ -0,0 +1,24 @@
1
+ require_relative "dni_peru/version"
2
+ require_relative "dni_peru/configuration"
3
+ require_relative "dni_peru/errors"
4
+ require_relative "dni_peru/response"
5
+ require_relative "dni_peru/providers/base"
6
+ require_relative "dni_peru/providers/decolecta"
7
+ require_relative "dni_peru/providers/api_peru_dev"
8
+ require_relative "dni_peru/client"
9
+
10
+ module DniPeru
11
+ class << self
12
+ attr_accessor :configuration
13
+ end
14
+
15
+ def self.configure
16
+ self.configuration ||= Configuration.new
17
+ yield(configuration)
18
+ end
19
+
20
+ def self.query(dni, provider: nil)
21
+ client = Client.new(provider: provider)
22
+ client.query(dni)
23
+ end
24
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dni_peru
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ruben Paz
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2025-11-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday-retry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ description: A flexible Ruby gem that provides a unified interface to query DNI (Documento
42
+ Nacional de Identidad) information from multiple Peruvian API providers.
43
+ email:
44
+ - rubenpazch@gmail.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".env.example"
50
+ - ".rspec"
51
+ - ".rubocop.yml"
52
+ - CHANGELOG.md
53
+ - Gemfile
54
+ - LICENSE.txt
55
+ - README.md
56
+ - Rakefile
57
+ - TEST_COVERAGE.md
58
+ - dni_peru.gemspec
59
+ - examples/basic_usage.rb
60
+ - examples/rails_integration.rb
61
+ - examples/test_config.rb
62
+ - lib/dni_peru.rb
63
+ - lib/dni_peru/client.rb
64
+ - lib/dni_peru/configuration.rb
65
+ - lib/dni_peru/errors.rb
66
+ - lib/dni_peru/providers/api_peru_dev.rb
67
+ - lib/dni_peru/providers/apis_net_pe.rb
68
+ - lib/dni_peru/providers/base.rb
69
+ - lib/dni_peru/providers/decolecta.rb
70
+ - lib/dni_peru/response.rb
71
+ - lib/dni_peru/version.rb
72
+ homepage: https://github.com/rubenpazch/dni-peru
73
+ licenses:
74
+ - MIT
75
+ metadata:
76
+ homepage_uri: https://github.com/rubenpazch/dni-peru
77
+ source_code_uri: https://github.com/rubenpazch/dni-peru
78
+ changelog_uri: https://github.com/rubenpazch/dni-peru/blob/main/CHANGELOG.md
79
+ rubygems_mfa_required: 'true'
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: 2.7.0
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ requirements: []
95
+ rubygems_version: 3.5.22
96
+ signing_key:
97
+ specification_version: 4
98
+ summary: Ruby gem to query DNI information from Peruvian API providers
99
+ test_files: []