implement driver license date validation
This commit is contained in:
@@ -0,0 +1,598 @@
|
||||
Driver's License Expiration Validation Implementation Plan
|
||||
Document Version: 1.0
|
||||
Date: 2026-06-11
|
||||
Status: Draft
|
||||
Author: Engineering Team
|
||||
|
||||
Table of Contents
|
||||
Overview
|
||||
|
||||
Requirements
|
||||
|
||||
Architecture Design
|
||||
|
||||
Implementation Phases
|
||||
|
||||
Technical Specifications
|
||||
|
||||
Testing Strategy
|
||||
|
||||
Deployment Plan
|
||||
|
||||
Monitoring & Maintenance
|
||||
|
||||
Risk Assessment
|
||||
|
||||
Appendices
|
||||
|
||||
Overview
|
||||
Business Objective
|
||||
Enforce a mandatory validation rule requiring all renters to possess a driver's license with minimum 30-day remaining validity from the date of submission.
|
||||
|
||||
Scope
|
||||
In Scope: Frontend validation, backend API validation, database schema changes, user notifications, error handling
|
||||
|
||||
Out of Scope: Physical license verification, DMV database integration, international license translation services
|
||||
|
||||
Success Criteria
|
||||
100% of license submissions validated against 30-day rule
|
||||
|
||||
<1% false rejection rate
|
||||
|
||||
<2 second validation response time
|
||||
|
||||
99.9% API uptime for validation endpoint
|
||||
|
||||
Requirements
|
||||
Functional Requirements
|
||||
ID Requirement Priority
|
||||
FR-1 System shall validate license expiration date is ≥30 days from current date P0
|
||||
FR-2 System shall reject submissions with <30 days remaining validity P0
|
||||
FR-3 System shall display clear error messages to users P0
|
||||
FR-4 System shall support manual date entry and OCR scanning P1
|
||||
FR-5 System shall log all validation attempts with results P1
|
||||
FR-6 System shall send reminders at 60, 45, and 30 days before expiration P2
|
||||
FR-7 System shall support state-specific license formats P2
|
||||
Non-Functional Requirements
|
||||
ID Requirement Target
|
||||
NFR-1 API response time <500ms p95
|
||||
NFR-2 Validation accuracy >99.9%
|
||||
NFR-3 System availability 99.95%
|
||||
NFR-4 Data encryption at rest AES-256
|
||||
NFR-5 PII data compliance GDPR, CCPA
|
||||
Architecture Design
|
||||
System Diagram
|
||||
text
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ CLIENT LAYER │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
|
||||
│ │ Web App │ │ Mobile App │ │ Admin Dashboard │ │
|
||||
│ │ (React) │ │ (React │ │ (Internal Tool) │ │
|
||||
│ │ │ │ Native) │ │ │ │
|
||||
│ └──────┬───────┘ └──────┬───────┘ └──────────┬───────────┘ │
|
||||
│ │ │ │ │
|
||||
└─────────┼─────────────────┼──────────────────────┼──────────────┘
|
||||
│ │ │
|
||||
┌─────────┼─────────────────┼──────────────────────┼──────────────┐
|
||||
│ │ API GATEWAY LAYER │ │
|
||||
│ ┌──────┴─────────────────┴──────────────────────┴───────────┐ │
|
||||
│ │ Kong / AWS API Gateway │ │
|
||||
│ │ (Rate Limiting, Authentication, Routing) │ │
|
||||
│ └──────────────────────────┬────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌────────────────────┼────────────────────┐ │
|
||||
│ │ │ │ │
|
||||
│ ┌──────┴──────┐ ┌────────┴────────┐ ┌──────┴──────────┐ │
|
||||
│ │ License │ │ Renter │ │ Notification │ │
|
||||
│ │ Validation │ │ Service │ │ Service │ │
|
||||
│ │ Service │ │ │ │ │ │
|
||||
│ └──────┬──────┘ └────────┬────────┘ └──────┬──────────┘ │
|
||||
│ │ │ │ │
|
||||
│ └────────────────────┼────────────────────┘ │
|
||||
│ │ │
|
||||
│ DATA LAYER│ │
|
||||
│ ┌───────────────────────────┴──────────────────────────────┐ │
|
||||
│ │ PostgreSQL (Primary DB) │ │
|
||||
│ └───────────────────────────┬──────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌───────────────────────────┴──────────────────────────────┐ │
|
||||
│ │ Redis (Cache + Rate Limiting) │ │
|
||||
│ └──────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
Data Flow Sequence
|
||||
Implementation Phases
|
||||
Phase 1: Foundation (Week 1-2)
|
||||
Week 1: Setup & Core Logic
|
||||
Day 1-2: Project setup and repository configuration
|
||||
|
||||
Initialize Git repository with branch protection rules
|
||||
|
||||
Set up CI/CD pipeline (GitHub Actions/Jenkins)
|
||||
|
||||
Configure development, staging, and production environments
|
||||
|
||||
Day 3-4: Database schema design
|
||||
|
||||
sql
|
||||
-- Migration: Create license_validations table
|
||||
CREATE TABLE license_validations (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
renter_id UUID NOT NULL REFERENCES renters(id),
|
||||
license_number_encrypted TEXT NOT NULL,
|
||||
expiration_date DATE NOT NULL,
|
||||
issuing_state VARCHAR(2),
|
||||
validation_status VARCHAR(20) NOT NULL,
|
||||
days_remaining INTEGER,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
validated_by VARCHAR(50) -- 'SYSTEM' or 'MANUAL'
|
||||
);
|
||||
|
||||
CREATE INDEX idx_license_expiration ON license_validations(expiration_date);
|
||||
CREATE INDEX idx_license_status ON license_validations(validation_status);
|
||||
Day 5: Core validation service implementation
|
||||
|
||||
Build LicenseValidator class with business logic
|
||||
|
||||
Implement date parsing with multiple format support
|
||||
|
||||
Add unit tests for all validation scenarios
|
||||
|
||||
Week 2: API & Frontend
|
||||
Day 1-3: REST API endpoint development
|
||||
|
||||
yaml
|
||||
# OpenAPI Specification
|
||||
/api/v1/licenses/validate:
|
||||
post:
|
||||
summary: Validate driver's license expiration
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- expiration_date
|
||||
properties:
|
||||
expiration_date:
|
||||
type: string
|
||||
format: date
|
||||
example: "2027-06-11"
|
||||
license_number:
|
||||
type: string
|
||||
state:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: License is valid
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
valid:
|
||||
type: boolean
|
||||
days_remaining:
|
||||
type: integer
|
||||
message:
|
||||
type: string
|
||||
'422':
|
||||
description: License validation failed
|
||||
Day 4-5: Frontend component development
|
||||
|
||||
Build LicenseUpload component with date picker
|
||||
|
||||
Implement real-time validation feedback
|
||||
|
||||
Add accessibility features (ARIA labels, keyboard navigation)
|
||||
|
||||
Phase 2: Enhancement (Week 3-4)
|
||||
Week 3: OCR Integration & Advanced Validation
|
||||
Day 1-3: OCR service integration
|
||||
|
||||
python
|
||||
class OCRLicenseProcessor:
|
||||
def __init__(self):
|
||||
self.textract_client = boto3.client('textract')
|
||||
|
||||
async def extract_license_data(self, image_bytes: bytes) -> dict:
|
||||
"""Extract license data using AWS Textract"""
|
||||
response = await self.textract_client.analyze_document(
|
||||
Document={'Bytes': image_bytes},
|
||||
FeatureTypes=['FORMS']
|
||||
)
|
||||
return self._parse_license_fields(response)
|
||||
|
||||
def _parse_license_fields(self, textract_response: dict) -> dict:
|
||||
"""Parse extracted fields into structured data"""
|
||||
# Extract expiration date using regex patterns
|
||||
# Map to standard format YYYY-MM-DD
|
||||
pass
|
||||
Day 4: State-specific validation rules
|
||||
|
||||
Implement validation patterns for all 50 US states
|
||||
|
||||
Handle temporary licenses and permits
|
||||
|
||||
Add military ID and passport support
|
||||
|
||||
Day 5: Error handling and edge cases
|
||||
|
||||
Graceful handling of corrupted images
|
||||
|
||||
Invalid date formats
|
||||
|
||||
Network timeouts and retries
|
||||
|
||||
Week 4: Notifications & Admin Tools
|
||||
Day 1-2: Notification system
|
||||
|
||||
python
|
||||
class LicenseReminderService:
|
||||
REMINDER_SCHEDULE = [60, 45, 30] # Days before expiration
|
||||
|
||||
async def schedule_reminders(self, renter_id: str, expiration_date: date):
|
||||
for days_before in self.REMINDER_SCHEDULE:
|
||||
reminder_date = expiration_date - timedelta(days=days_before)
|
||||
await self.notification_queue.enqueue(
|
||||
'send_license_reminder',
|
||||
renter_id=renter_id,
|
||||
scheduled_at=reminder_date,
|
||||
days_remaining=days_before
|
||||
)
|
||||
Day 3-4: Admin dashboard
|
||||
|
||||
Build validation queue for manual review
|
||||
|
||||
Analytics dashboard (rejection rates, common errors)
|
||||
|
||||
Bulk update and override capabilities
|
||||
|
||||
Day 5: Documentation and API docs
|
||||
|
||||
Update API documentation with Swagger/OpenAPI
|
||||
|
||||
Create internal wiki for support team
|
||||
|
||||
Write runbook for common issues
|
||||
|
||||
Phase 3: Testing & Hardening (Week 5-6)
|
||||
Week 5: Comprehensive Testing
|
||||
Day 1-3: Integration testing
|
||||
|
||||
javascript
|
||||
// Integration test example
|
||||
describe('License Validation API', () => {
|
||||
test('Should reject license expiring in 15 days', async () => {
|
||||
const response = await request(app)
|
||||
.post('/api/v1/licenses/validate')
|
||||
.send({
|
||||
expiration_date: '2026-06-26',
|
||||
license_number: 'DL123456'
|
||||
});
|
||||
|
||||
expect(response.status).toBe(422);
|
||||
expect(response.body.valid).toBe(false);
|
||||
expect(response.body.message).toContain('30 days');
|
||||
});
|
||||
|
||||
test('Should accept license expiring in 60 days', async () => {
|
||||
const response = await request(app)
|
||||
.post('/api/v1/licenses/validate')
|
||||
.send({
|
||||
expiration_date: '2026-08-10',
|
||||
license_number: 'DL789012'
|
||||
});
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.valid).toBe(true);
|
||||
expect(response.body.days_remaining).toBeGreaterThan(30);
|
||||
});
|
||||
});
|
||||
Day 4: Performance testing
|
||||
|
||||
Load test with 1000 concurrent requests
|
||||
|
||||
Stress test OCR processing pipeline
|
||||
|
||||
Database query optimization
|
||||
|
||||
Day 5: Security audit
|
||||
|
||||
PII encryption verification
|
||||
|
||||
SQL injection and XSS testing
|
||||
|
||||
Authentication/authorization testing
|
||||
|
||||
Week 6: UAT & Deployment Preparation
|
||||
Day 1-2: User acceptance testing
|
||||
|
||||
Test with real license images (anonymized)
|
||||
|
||||
Validate all error messages and flows
|
||||
|
||||
Cross-browser compatibility testing
|
||||
|
||||
Day 3: Staging deployment
|
||||
|
||||
Deploy to staging environment
|
||||
|
||||
Run smoke tests
|
||||
|
||||
Monitor for 24 hours
|
||||
|
||||
Day 4-5: Production deployment
|
||||
|
||||
Database migration execution
|
||||
|
||||
Blue-green deployment strategy
|
||||
|
||||
Gradual traffic shifting (10% → 50% → 100%)
|
||||
|
||||
Technical Specifications
|
||||
Validation Rules Matrix
|
||||
Scenario Condition Action Response Code
|
||||
Valid License ≥30 days remaining Accept 200
|
||||
Warning Zone 30-60 days remaining Accept + Schedule Reminder 200 + Warning
|
||||
Expiring Soon 1-29 days remaining Reject 422
|
||||
Expired ≤0 days remaining Reject 422
|
||||
Invalid Format Date parse error Reject 400
|
||||
Future Date >20 years from now Reject 400
|
||||
Missing Date No date provided Reject 400
|
||||
API Response Examples
|
||||
Success Response
|
||||
json
|
||||
{
|
||||
"status": "success",
|
||||
"data": {
|
||||
"valid": true,
|
||||
"days_remaining": 365,
|
||||
"expiration_date": "2027-06-11",
|
||||
"message": "License verified successfully",
|
||||
"next_reminder": "2027-04-12"
|
||||
}
|
||||
}
|
||||
Error Response
|
||||
json
|
||||
{
|
||||
"status": "error",
|
||||
"error": {
|
||||
"code": "LICENSE_EXPIRING_SOON",
|
||||
"message": "License must have at least 30 days remaining validity. Your license expires in 15 days on 2026-06-26.",
|
||||
"details": {
|
||||
"expiration_date": "2026-06-26",
|
||||
"days_remaining": 15,
|
||||
"minimum_required": 30,
|
||||
"suggested_action": "Please upload a valid license with more than 30 days remaining."
|
||||
}
|
||||
}
|
||||
}
|
||||
Environment Variables Configuration
|
||||
bash
|
||||
# .env.example
|
||||
LICENSE_MIN_VALIDITY_DAYS=30
|
||||
LICENSE_WARNING_THRESHOLD_DAYS=60
|
||||
OCR_ENABLED=true
|
||||
OCR_PROVIDER=aws_textract
|
||||
REDIS_URL=redis://localhost:6379
|
||||
DATABASE_URL=postgresql://user:pass@localhost:5432/rental_db
|
||||
ENCRYPTION_KEY=your-256-bit-key
|
||||
LOG_LEVEL=info
|
||||
Testing Strategy
|
||||
Test Pyramid
|
||||
text
|
||||
┌──────┐
|
||||
│ E2E │ 10% - Critical user journeys
|
||||
│ │
|
||||
┌┴──────┴┐
|
||||
│Integrat│ 30% - API contracts, DB interactions
|
||||
│ ion │
|
||||
│ │
|
||||
┌┴────────┴┐
|
||||
│ Unit │ 60% - Business logic, validation rules
|
||||
│ │
|
||||
└──────────┘
|
||||
Test Cases
|
||||
Unit Tests (60% coverage)
|
||||
Test ID Description Input Expected Output
|
||||
UT-01 Date exactly 30 days from now today + 30 days valid: true
|
||||
UT-02 Date 29 days from now today + 29 days valid: false
|
||||
UT-03 Date in past 2020-01-01 valid: false
|
||||
UT-04 Invalid date string "invalid" Error handled
|
||||
UT-05 Leap year date 2028-02-29 Correctly calculated
|
||||
UT-06 Timezone edge case Various timezones Consistent results
|
||||
Integration Tests
|
||||
API returns correct status codes for all scenarios
|
||||
|
||||
Database correctly stores encrypted license data
|
||||
|
||||
Redis cache properly invalidates
|
||||
|
||||
Notification queue receives correct messages
|
||||
|
||||
OCR pipeline processes images end-to-end
|
||||
|
||||
End-to-End Tests
|
||||
User uploads valid license → Success flow
|
||||
|
||||
User uploads expired license → Error flow
|
||||
|
||||
User receives reminder notification
|
||||
|
||||
Admin can manually override validation
|
||||
|
||||
Deployment Plan
|
||||
Pre-Deployment Checklist
|
||||
All tests passing (unit, integration, E2E)
|
||||
|
||||
Security scan completed (no critical/high vulnerabilities)
|
||||
|
||||
Database migration scripts reviewed and tested
|
||||
|
||||
Rollback plan documented and tested
|
||||
|
||||
Monitoring dashboards configured
|
||||
|
||||
Alert thresholds set up
|
||||
|
||||
Support team trained on new features
|
||||
|
||||
API documentation updated
|
||||
|
||||
Performance benchmarks met
|
||||
|
||||
Data backup completed
|
||||
|
||||
Deployment Steps
|
||||
Phase 1: Database Migration (30 min downtime)
|
||||
bash
|
||||
# 1. Take database snapshot
|
||||
pg_dump -h production-db -U admin rental_db > backup_20260611.sql
|
||||
|
||||
# 2. Run migrations
|
||||
npm run migrate:production
|
||||
|
||||
# 3. Verify migration
|
||||
SELECT * FROM schema_migrations ORDER BY version DESC LIMIT 5;
|
||||
Phase 2: Backend Deployment (Blue-Green)
|
||||
bash
|
||||
# 1. Deploy to green environment
|
||||
kubectl apply -f deployment-green.yaml
|
||||
|
||||
# 2. Run health checks
|
||||
curl https://green-api.example.com/health
|
||||
|
||||
# 3. Switch traffic (10% → 50% → 100%)
|
||||
# Update load balancer configuration
|
||||
|
||||
# 4. Monitor for 15 minutes
|
||||
# Watch logs: kubectl logs -f deployment/license-validation-green
|
||||
Phase 3: Frontend Deployment
|
||||
bash
|
||||
# 1. Build and deploy to CDN
|
||||
npm run build:production
|
||||
aws s3 sync dist/ s3://rental-app-prod/ --cache-control "max-age=3600"
|
||||
|
||||
# 2. Invalidate CloudFront cache
|
||||
aws cloudfront create-invalidation --distribution-id E1234567890 --paths "/*"
|
||||
Rollback Plan
|
||||
bash
|
||||
# Immediate rollback if error rate >5% or p95 latency >2s
|
||||
# 1. Revert load balancer to blue environment
|
||||
# 2. Scale down green deployment
|
||||
kubectl scale deployment license-validation-green --replicas=0
|
||||
|
||||
# 3. Rollback database (if schema changes)
|
||||
npm run migrate:rollback:production
|
||||
|
||||
# 4. Notify on-call engineer
|
||||
# 5. Create incident postmortem
|
||||
Monitoring & Maintenance
|
||||
Key Metrics Dashboard
|
||||
yaml
|
||||
Dashboard: License Validation Service
|
||||
Metrics:
|
||||
- name: license_validation_total
|
||||
type: Counter
|
||||
description: Total validation attempts
|
||||
labels: [status, source]
|
||||
|
||||
- name: license_validation_duration_seconds
|
||||
type: Histogram
|
||||
description: Validation processing time
|
||||
buckets: [0.1, 0.5, 1, 2, 5]
|
||||
|
||||
- name: license_rejection_rate
|
||||
type: Gauge
|
||||
description: Percentage of rejected licenses
|
||||
alert: ">20% for 5 minutes"
|
||||
|
||||
- name: ocr_processing_errors
|
||||
type: Counter
|
||||
description: OCR processing failures
|
||||
alert: ">10 errors in 5 minutes"
|
||||
Alert Configuration
|
||||
yaml
|
||||
alerts:
|
||||
- name: HighRejectionRate
|
||||
condition: license_rejection_rate > 0.20
|
||||
duration: 5m
|
||||
severity: warning
|
||||
channel: "#engineering-alerts"
|
||||
|
||||
- name: ValidationServiceDown
|
||||
condition: up{job="license-validation"} == 0
|
||||
duration: 1m
|
||||
severity: critical
|
||||
channel: "#on-call"
|
||||
|
||||
- name: SlowValidationResponses
|
||||
condition: histogram_quantile(0.95, license_validation_duration) > 2
|
||||
duration: 5m
|
||||
severity: warning
|
||||
channel: "#engineering-alerts"
|
||||
Maintenance Tasks
|
||||
Task Frequency Owner Duration
|
||||
Database index optimization Monthly DBA 1 hour
|
||||
SSL certificate renewal Annually DevOps 30 min
|
||||
Dependency updates Bi-weekly Engineering 2 hours
|
||||
Security patches As needed Security team Varies
|
||||
Log rotation Automated System N/A
|
||||
Risk Assessment
|
||||
Risk Matrix
|
||||
Risk Impact Probability Mitigation
|
||||
OCR misreads date High Medium Manual review fallback, confidence scoring
|
||||
Timezone mismatch Medium Low Store all dates in UTC, convert at presentation layer
|
||||
Database connection failure High Low Connection pooling, retry logic, circuit breaker
|
||||
Third-party OCR service outage Medium Low Implement fallback to manual entry, cache provider
|
||||
PII data breach Critical Low Encryption at rest/transit, access controls, audit logging
|
||||
Performance degradation under load Medium Medium Auto-scaling, caching, load testing
|
||||
Contingency Plans
|
||||
Scenario 1: OCR Service Unavailable
|
||||
|
||||
Automatically switch to manual entry mode
|
||||
|
||||
Display clear instructions to user
|
||||
|
||||
Queue OCR processing for retry when service recovers
|
||||
|
||||
Scenario 2: High False Rejection Rate
|
||||
|
||||
Temporarily adjust threshold to 15 days
|
||||
|
||||
Notify support team to expect inquiries
|
||||
|
||||
Investigate root cause (date parsing, timezone, etc.)
|
||||
|
||||
Deploy hotfix if needed
|
||||
|
||||
Appendices
|
||||
Appendix A: Supported License Formats
|
||||
Country/State Format Example Notes
|
||||
US Standard MM/DD/YYYY 06/11/2027 Varies by state
|
||||
EU Standard DD/MM/YYYY 11/06/2027
|
||||
ISO 8601 YYYY-MM-DD 2027-06-11 Preferred internal format
|
||||
Military ID DD-MMM-YYYY 11-JUN-2027
|
||||
Appendix B: Error Code Reference
|
||||
Code HTTP Status Description
|
||||
LICENSE_EXPIRED 422 License has expired
|
||||
LICENSE_EXPIRING_SOON 422 Less than 30 days remaining
|
||||
LICENSE_INVALID_FORMAT 400 Date format not recognized
|
||||
LICENSE_FUTURE_DATE 400 Date too far in future
|
||||
LICENSE_MISSING_DATE 400 No date provided
|
||||
OCR_PROCESSING_FAILED 500 Could not extract from image
|
||||
LICENSE_ALREADY_EXISTS 409 Duplicate submission
|
||||
Appendix C: Team Contacts
|
||||
Role Name Contact
|
||||
Product Owner [Name] [Email/Phone]
|
||||
Tech Lead [Name] [Email/Phone]
|
||||
Backend Developer [Name] [Email/Phone]
|
||||
Frontend Developer [Name] [Email/Phone]
|
||||
DevOps Engineer [Name] [Email/Phone]
|
||||
Security Engineer [Name] [Email/Phone]
|
||||
On-Call Rotation Schedule [PagerDuty Link]
|
||||
Document Control
|
||||
Version Date Author Changes
|
||||
1.0 2026-06-11 Engineering Team Initial creation
|
||||
Reference in New Issue
Block a user