dilisense_pep_client 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.
data/README.md ADDED
@@ -0,0 +1,500 @@
1
+ # dilisense_PEP_client
2
+
3
+ A Ruby gem providing a client for [Dilisense's](https://dilisense.com) PEP (Politically Exposed Persons) and sanctions screening API. This gem is designed for Estonian financial institutions and FinTech companies requiring AML/KYC compliance through automated screening of individuals and entities against global PEP databases and sanctions lists.
4
+
5
+ [![Ruby Version](https://img.shields.io/badge/ruby-%3E%3D%202.7-red)](https://www.ruby-lang.org/en/)
6
+ [![Gem Version](https://img.shields.io/gem/v/dilisense_pep_client)](https://rubygems.org/gems/dilisense_pep_client)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
8
+
9
+ ## Overview
10
+
11
+ The Dilisense PEP Client provides a simple, industrial-grade Ruby interface for:
12
+
13
+ - **Individual Screening**: Screen persons against PEP and sanctions databases
14
+ - **Entity Screening**: Screen companies and organizations against sanctions lists
15
+ - **Fuzzy Matching**: Find potential matches even with name variations or typos
16
+ - **Compliance Logging**: Enterprise-grade audit trails for regulatory compliance
17
+ - **Error Handling**: Comprehensive error management with detailed context
18
+ - **Security**: Built-in data sanitization and PII protection
19
+
20
+ ## Features
21
+
22
+ ### Core Functionality
23
+ - Screen individuals with name, date of birth, and gender parameters
24
+ - Screen entities (companies/organizations) with flexible search options
25
+ - Support for fuzzy search to catch name variations and typos
26
+ - Real-time screening against Dilisense's comprehensive databases
27
+
28
+ ### Enterprise Features
29
+ - **Industrial-grade error handling** with detailed error contexts
30
+ - **Comprehensive logging** with PII anonymization for compliance
31
+ - **Circuit breaker pattern** for API resilience and fault tolerance
32
+ - **Input validation and sanitization** to prevent injection attacks
33
+ - **Metrics collection** for monitoring and performance analysis
34
+ - **Audit logging** for AML/KYC/GDPR compliance requirements
35
+
36
+ ### Security & Compliance
37
+ - Automatic PII anonymization in logs and error messages
38
+ - Security event logging and monitoring
39
+ - Built-in data sanitization and validation
40
+ - Support for multiple compliance frameworks (AML, KYC, GDPR, MiFID)
41
+
42
+ ## Installation
43
+
44
+ Add this line to your application's Gemfile:
45
+
46
+ ```ruby
47
+ gem 'dilisense_pep_client'
48
+ ```
49
+
50
+ And then execute:
51
+
52
+ ```bash
53
+ $ bundle install
54
+ ```
55
+
56
+ Or install it yourself as:
57
+
58
+ ```bash
59
+ $ gem install dilisense_pep_client
60
+ ```
61
+
62
+ ## Configuration
63
+
64
+ ### Basic Setup
65
+
66
+ The gem requires a Dilisense API key to function. You can configure it in several ways:
67
+
68
+ #### Option 1: Environment Variable (Recommended)
69
+
70
+ Set your API key as an environment variable:
71
+
72
+ ```bash
73
+ export DILISENSE_API_KEY="your_api_key_here"
74
+ ```
75
+
76
+ Or add it to your `.env` file:
77
+
78
+ ```env
79
+ DILISENSE_API_KEY=your_api_key_here
80
+ ```
81
+
82
+ #### Option 2: Configuration Block
83
+
84
+ ```ruby
85
+ require 'dilisense_pep_client'
86
+
87
+ DilisensePepClient.configure do |config|
88
+ config.api_key = "your_api_key_here"
89
+ config.timeout = 30 # Request timeout in seconds (default: 30)
90
+ config.base_url = "https://api.dilisense.com" # API base URL (default)
91
+ end
92
+ ```
93
+
94
+ ### Advanced Configuration
95
+
96
+ For production environments, you may want to customize additional settings:
97
+
98
+ ```ruby
99
+ DilisensePepClient.configure do |config|
100
+ config.api_key = ENV['DILISENSE_API_KEY']
101
+ config.timeout = 45 # Increase timeout for slower networks
102
+ end
103
+ ```
104
+
105
+ ## Quick Start
106
+
107
+ ### Individual Screening
108
+
109
+ Screen a person against PEP and sanctions databases:
110
+
111
+ ```ruby
112
+ require 'dilisense_pep_client'
113
+
114
+ # Basic individual screening
115
+ results = DilisensePepClient.check_individual(
116
+ names: "Vladimir Putin"
117
+ )
118
+
119
+ # Enhanced screening with additional parameters
120
+ results = DilisensePepClient.check_individual(
121
+ names: "Vladimir Putin",
122
+ dob: "07/10/1952",
123
+ gender: "male",
124
+ fuzzy_search: 1 # Enable fuzzy matching
125
+ )
126
+
127
+ # Process results
128
+ results.each do |person|
129
+ puts "Name: #{person[:name]}"
130
+ puts "Source Type: #{person[:source_type]}"
131
+ puts "PEP Type: #{person[:pep_type]}" if person[:pep_type]
132
+ puts "Total Records: #{person[:total_records]}"
133
+ puts "---"
134
+ end
135
+ ```
136
+
137
+ ### Entity Screening
138
+
139
+ Screen companies and organizations:
140
+
141
+ ```ruby
142
+ # Screen a company
143
+ results = DilisensePepClient.check_entity(
144
+ names: "Bank Rossiya"
145
+ )
146
+
147
+ # Enhanced entity screening
148
+ results = DilisensePepClient.check_entity(
149
+ names: "Gazprom",
150
+ fuzzy_search: 2 # More aggressive fuzzy matching
151
+ )
152
+
153
+ # Process entity results
154
+ results.each do |entity|
155
+ puts "Entity: #{entity[:name]}"
156
+ puts "Source Type: #{entity[:source_type]}"
157
+ puts "Sanctions: #{entity[:sanction_details]}" if entity[:sanction_details]
158
+ puts "Sources: #{entity[:sources].join(', ')}"
159
+ puts "---"
160
+ end
161
+ ```
162
+
163
+ ### Using the Client Directly
164
+
165
+ For more control, you can instantiate the client directly:
166
+
167
+ ```ruby
168
+ client = DilisensePepClient::Client.new
169
+
170
+ # Individual screening
171
+ individual_results = client.check_individual(
172
+ names: "Xi Jinping",
173
+ dob: "15/06/1953",
174
+ gender: "male"
175
+ )
176
+
177
+ # Entity screening
178
+ entity_results = client.check_entity(
179
+ names: "Huawei Technologies"
180
+ )
181
+ ```
182
+
183
+ ## Usage Examples
184
+
185
+ ### Estonian eID Integration
186
+
187
+ For Estonian financial institutions integrating with eID systems:
188
+
189
+ ```ruby
190
+ # Screen Estonian resident
191
+ results = DilisensePepClient.check_individual(
192
+ names: "#{first_name} #{last_name}",
193
+ dob: estonian_personal_code_to_dob(personal_code),
194
+ gender: extract_gender_from_personal_code(personal_code)
195
+ )
196
+
197
+ # Check if person is a PEP or sanctioned
198
+ is_pep = results.any? { |person| person[:source_type] == 'PEP' }
199
+ is_sanctioned = results.any? { |person| person[:source_type] == 'SANCTION' }
200
+
201
+ if is_pep || is_sanctioned
202
+ # Handle enhanced due diligence requirements
203
+ trigger_enhanced_due_diligence(results)
204
+ end
205
+ ```
206
+
207
+ ### Batch Processing
208
+
209
+ ```ruby
210
+ # Screen multiple individuals
211
+ people_to_screen = [
212
+ { names: "Person One", dob: "01/01/1980" },
213
+ { names: "Person Two", dob: "02/02/1975" }
214
+ ]
215
+
216
+ people_to_screen.each do |person|
217
+ begin
218
+ results = DilisensePepClient.check_individual(**person)
219
+
220
+ # Process results
221
+ if results.any?
222
+ puts "Potential matches found for #{person[:names]}"
223
+ # Handle matches according to your compliance procedures
224
+ end
225
+
226
+ # Rate limiting - wait between requests
227
+ sleep(2)
228
+
229
+ rescue DilisensePepClient::Error => e
230
+ puts "Error screening #{person[:names]}: #{e.message}"
231
+ # Log error for audit trail
232
+ end
233
+ end
234
+ ```
235
+
236
+ ### Error Handling
237
+
238
+ The gem provides comprehensive error handling:
239
+
240
+ ```ruby
241
+ begin
242
+ results = DilisensePepClient.check_individual(
243
+ names: "Test Person",
244
+ dob: "invalid-date" # This will cause a validation error
245
+ )
246
+ rescue DilisensePepClient::ValidationError => e
247
+ puts "Validation error: #{e.message}"
248
+ puts "Validation errors: #{e.validation_errors}"
249
+ rescue DilisensePepClient::AuthenticationError => e
250
+ puts "Authentication failed: #{e.message}"
251
+ # Check your API key configuration
252
+ rescue DilisensePepClient::APIError => e
253
+ puts "API error: #{e.message}"
254
+ puts "Status code: #{e.status}"
255
+ puts "Retryable? #{e.retryable?}"
256
+ rescue DilisensePepClient::NetworkError => e
257
+ puts "Network error: #{e.message}"
258
+ # Implement retry logic for network issues
259
+ rescue DilisensePepClient::Error => e
260
+ puts "General error: #{e.message}"
261
+ puts "Error context: #{e.context}"
262
+ end
263
+ ```
264
+
265
+ ### Fuzzy Search Examples
266
+
267
+ ```ruby
268
+ # Search with typos and variations
269
+ results = DilisensePepClient.check_individual(
270
+ names: "Vladmir Putin", # Typo in "Vladimir"
271
+ fuzzy_search: 1
272
+ )
273
+
274
+ # More aggressive fuzzy matching
275
+ results = DilisensePepClient.check_individual(
276
+ names: "V Putin", # Abbreviated first name
277
+ fuzzy_search: 2
278
+ )
279
+
280
+ # Entity fuzzy search
281
+ results = DilisensePepClient.check_entity(
282
+ names: "Bank Russia", # Variation of "Bank Rossiya"
283
+ fuzzy_search: 1
284
+ )
285
+ ```
286
+
287
+ ## Development and Testing
288
+
289
+ ### Setting Up Development Environment
290
+
291
+ 1. Clone the repository:
292
+ ```bash
293
+ git clone https://github.com/your-org/dilisense_pep_client.git
294
+ cd dilisense_pep_client
295
+ ```
296
+
297
+ 2. Install dependencies:
298
+ ```bash
299
+ make install
300
+ # or
301
+ bundle install
302
+ ```
303
+
304
+ 3. Set up your API key for testing:
305
+ ```bash
306
+ cp .env.example .env
307
+ # Edit .env and add your DILISENSE_API_KEY
308
+ ```
309
+
310
+ ### Running Tests
311
+
312
+ The gem includes comprehensive test suites:
313
+
314
+ ```bash
315
+ # Show all available commands
316
+ make help
317
+
318
+ # Run all tests (unit + integration)
319
+ make test
320
+
321
+ # Run only unit tests (no API calls)
322
+ make unit_test
323
+
324
+ # Run integration tests with real API calls
325
+ make individual_test
326
+
327
+ # Run fuzzy search tests
328
+ make test_fuzzy
329
+
330
+ # Run entity screening tests
331
+ make entity_test
332
+
333
+ # Run linter
334
+ make lint
335
+
336
+ # Run CI suite (tests + linting)
337
+ make ci
338
+ ```
339
+
340
+ ### Test Categories
341
+
342
+ - **Unit Tests**: Test internal logic without external API calls
343
+ - **Integration Tests**: Test against live Dilisense API (requires API key)
344
+ - **Fuzzy Search Tests**: Test fuzzy matching capabilities
345
+ - **Entity Tests**: Test company/organization screening
346
+
347
+ **Note**: Integration tests include automatic 2-second delays between requests to respect API rate limits.
348
+
349
+ ## Architecture
350
+
351
+ The gem is designed with enterprise-grade architecture:
352
+
353
+ ```
354
+ ┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
355
+ │ Application │ │ Configuration │ │ Validation │
356
+ │ Client │───▶│ Manager │───▶│ & Sanitization│
357
+ └─────────────────┘ └──────────────────┘ └─────────────────┘
358
+ │ │
359
+ ▼ ▼
360
+ ┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
361
+ │ HTTP Client │───▶│ Circuit Breaker │───▶│ API Gateway │
362
+ │ (Faraday) │ │ & Resilience │ │ (Dilisense) │
363
+ └─────────────────┘ └──────────────────┘ └─────────────────┘
364
+ │ │
365
+ ▼ ▼
366
+ ┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
367
+ │ Response │ │ Logging │───▶│ Monitoring │
368
+ │ Processing │ │ & Auditing │ │ & Metrics │
369
+ └─────────────────┘ └──────────────────┘ └─────────────────┘
370
+ ```
371
+
372
+ ### Core Components
373
+
374
+ - **Client**: Main interface for API communication
375
+ - **Configuration**: Simple, flexible configuration management
376
+ - **Validator**: Input validation and sanitization with security focus
377
+ - **Logger**: Structured logging with PII anonymization
378
+ - **Metrics**: Performance and business metrics collection
379
+ - **AuditLogger**: Compliance-focused audit trail generation
380
+ - **CircuitBreaker**: API resilience and fault tolerance
381
+ - **Errors**: Comprehensive error hierarchy with context
382
+
383
+ ## API Reference
384
+
385
+ For detailed API documentation, see the inline documentation in the source code. Key classes:
386
+
387
+ - `DilisensePepClient::Client` - Main API client
388
+ - `DilisensePepClient::Configuration` - Configuration management
389
+ - `DilisensePepClient::Validator` - Input validation
390
+ - `DilisensePepClient::Logger` - Structured logging
391
+ - `DilisensePepClient::Error` - Error handling
392
+
393
+ ## Performance Considerations
394
+
395
+ ### Rate Limiting
396
+
397
+ The Dilisense API has rate limits. For production use:
398
+
399
+ 1. Implement request queuing for batch operations
400
+ 2. Add delays between requests (2+ seconds recommended)
401
+ 3. Use the circuit breaker for resilience
402
+ 4. Monitor API usage through the metrics system
403
+
404
+ ### Caching
405
+
406
+ For high-volume applications, consider implementing caching:
407
+
408
+ ```ruby
409
+ # Example with Redis caching (not included in gem)
410
+ class CachedScreeningService
411
+ def self.screen_individual(params)
412
+ cache_key = "screening:individual:#{Digest::SHA256.hexdigest(params.to_s)}"
413
+
414
+ cached_result = Redis.current.get(cache_key)
415
+ return JSON.parse(cached_result) if cached_result
416
+
417
+ result = DilisensePepClient.check_individual(**params)
418
+ Redis.current.setex(cache_key, 3600, result.to_json) # 1 hour cache
419
+
420
+ result
421
+ end
422
+ end
423
+ ```
424
+
425
+ ## Compliance and Security
426
+
427
+ ### Data Protection
428
+
429
+ - All PII is automatically anonymized in logs
430
+ - API keys are redacted from error messages and logs
431
+ - Response data is sanitized to prevent credential exposure
432
+ - Input validation prevents injection attacks
433
+
434
+ ### Audit Trail
435
+
436
+ The gem provides comprehensive audit logging for compliance:
437
+
438
+ - All screening requests are logged with anonymized search terms
439
+ - API errors and security events are tracked
440
+ - User actions and system events are recorded
441
+ - Configurable retention periods for different compliance frameworks
442
+
443
+ ### Regulatory Compliance
444
+
445
+ Supports multiple compliance frameworks:
446
+
447
+ - **AML (Anti-Money Laundering)**: Customer due diligence logging
448
+ - **KYC (Know Your Customer)**: Identity verification audit trails
449
+ - **GDPR**: Privacy-compliant logging with data subject rights
450
+ - **MiFID**: Financial services compliance requirements
451
+
452
+ ## Contributing
453
+
454
+ We welcome contributions! Please:
455
+
456
+ 1. Fork the repository
457
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
458
+ 3. Make your changes
459
+ 4. Add tests for new functionality
460
+ 5. Ensure all tests pass (`make ci`)
461
+ 6. Commit your changes (`git commit -m 'Add amazing feature'`)
462
+ 7. Push to the branch (`git push origin feature/amazing-feature`)
463
+ 8. Open a Pull Request
464
+
465
+ ### Development Guidelines
466
+
467
+ - Follow Ruby style guidelines (enforced by RuboCop)
468
+ - Add comprehensive tests for new features
469
+ - Update documentation for API changes
470
+ - Ensure security best practices
471
+ - Maintain backward compatibility when possible
472
+
473
+ ## License
474
+
475
+ The gem is available as open source under the terms of the [MIT License](LICENSE).
476
+
477
+ ## Support
478
+
479
+ ### Documentation
480
+
481
+ - [Dilisense API Documentation](https://dilisense.com/api)
482
+ - [Gem Documentation](https://rubydoc.info/gems/dilisense_pep_client)
483
+
484
+ ### Contact
485
+
486
+ - **Email**: angelos@sorbet.ee
487
+ - **Company**: Sorbeet Payments OU
488
+ - **Issues**: [GitHub Issues](https://github.com/your-org/dilisense_pep_client/issues)
489
+
490
+ ### Professional Services
491
+
492
+ For enterprise support, custom integrations, or compliance consulting, please contact us directly.
493
+
494
+ ## Changelog
495
+
496
+ See [CHANGELOG.md](CHANGELOG.md) for a detailed history of changes.
497
+
498
+ ---
499
+
500
+ **Note**: This gem is designed specifically for Estonian financial institutions and FinTech companies. It provides enterprise-grade features for PEP and sanctions screening as required by Estonian financial regulations and AML/KYC compliance standards.
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ # Load dotenv for tests
7
+ begin
8
+ require "dotenv/load"
9
+ rescue LoadError
10
+ # dotenv not available, skip loading
11
+ end
12
+
13
+ Rake::TestTask.new(:test) do |t|
14
+ t.libs << "test"
15
+ t.libs << "lib"
16
+ t.test_files = FileList["test/**/test_*.rb"]
17
+ end
18
+
19
+ task default: :test
20
+
21
+ desc "Run RuboCop"
22
+ task :rubocop do
23
+ sh "rubocop"
24
+ end
25
+
26
+ desc "Run tests and linting"
27
+ task ci: [:test, :rubocop]
28
+
29
+ desc "Generate documentation"
30
+ task :doc do
31
+ sh "yard doc"
32
+ end
33
+
34
+ desc "Open console with gem loaded"
35
+ task :console do
36
+ sh "bundle console"
37
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/dilisense_pep_client/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "dilisense_pep_client"
7
+ spec.version = DilisensePepClient::VERSION
8
+ spec.authors = ["Sorbeet Payments OU"]
9
+ spec.email = ["angelos@sorbet.ee"]
10
+
11
+ spec.summary = "Enterprise Ruby client for Dilisense PEP and sanctions screening API"
12
+ spec.description = "Industrial-grade Ruby client for Dilisense's PEP (Politically Exposed Persons) and sanctions screening API. Designed for Estonian financial institutions and FinTech companies requiring AML/KYC compliance with comprehensive error handling, audit logging, and security features."
13
+ spec.homepage = "https://github.com/sorbet-ee/dilisense_PEP_client"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 2.7.0"
16
+
17
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
18
+
19
+ spec.metadata["homepage_uri"] = spec.homepage
20
+ spec.metadata["source_code_uri"] = "https://github.com/sorbet-ee/dilisense_PEP_client"
21
+ spec.metadata["changelog_uri"] = "https://github.com/sorbet-ee/dilisense_PEP_client/blob/master/CHANGELOG.md"
22
+
23
+ # Specify which files should be added to the gem when it is released.
24
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
+ spec.files = Dir.chdir(__dir__) do
26
+ `git ls-files -z`.split("\x0").reject do |f|
27
+ (File.expand_path(f) == __FILE__) ||
28
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile])
29
+ end
30
+ end
31
+ spec.bindir = "exe"
32
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
33
+ spec.require_paths = ["lib"]
34
+
35
+ # Runtime dependencies
36
+ spec.add_dependency "faraday", "~> 2.0"
37
+ spec.add_dependency "dotenv", "~> 2.0"
38
+ spec.add_dependency "semantic_logger", "~> 4.0"
39
+ spec.add_dependency "dry-validation", "~> 1.10"
40
+ spec.add_dependency "concurrent-ruby", "~> 1.2"
41
+
42
+ # Development dependencies
43
+ spec.add_development_dependency "bundler", "~> 2.0"
44
+ spec.add_development_dependency "rake", "~> 13.0"
45
+ spec.add_development_dependency "minitest", "~> 5.0"
46
+ spec.add_development_dependency "rubocop", "~> 1.21"
47
+ spec.add_development_dependency "rubocop-minitest", "~> 0.31"
48
+ spec.add_development_dependency "pry", "~> 0.14"
49
+ spec.add_development_dependency "yard", "~> 0.9"
50
+ spec.add_development_dependency "simplecov", "~> 0.21"
51
+ end