740 lines
22 KiB
Markdown
740 lines
22 KiB
Markdown
# Backend Platform Service Update - Agent 3
|
|
|
|
## Task: Update VehicleDataService to use string-based signatures
|
|
|
|
**Status**: Ready for Implementation
|
|
**Dependencies**: Agent 2 (Platform Repository) must be complete
|
|
**Estimated Time**: 1 hour
|
|
**Assigned To**: Agent 3 (Platform Service)
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
Update the service layer to match the new repository signatures. Change from ID-based parameters to string-based parameters, and update return types from objects to string arrays.
|
|
|
|
---
|
|
|
|
## Prerequisites
|
|
|
|
### Verify Agent 2 Completed
|
|
```bash
|
|
# Verify repository compiles
|
|
cd backend && npm run build
|
|
|
|
# Should see no errors in vehicle-data.repository.ts
|
|
```
|
|
|
|
### Files to Modify
|
|
```
|
|
backend/src/features/platform/domain/vehicle-data.service.ts
|
|
backend/src/features/platform/domain/platform-cache.service.ts (cache keys)
|
|
backend/src/features/platform/models/responses.ts (if not already updated)
|
|
```
|
|
|
|
---
|
|
|
|
## Current Implementation Analysis
|
|
|
|
**File**: `backend/src/features/platform/domain/vehicle-data.service.ts`
|
|
|
|
**Current Methods**:
|
|
```typescript
|
|
getYears(pool: Pool): Promise<number[]>
|
|
getMakes(pool: Pool, year: number): Promise<MakeItem[]>
|
|
getModels(pool: Pool, year: number, makeId: number): Promise<ModelItem[]>
|
|
getTrims(pool: Pool, year: number, modelId: number): Promise<TrimItem[]>
|
|
getEngines(pool: Pool, year: number, modelId: number, trimId: number): Promise<EngineItem[]>
|
|
```
|
|
|
|
**Pattern**: Each method has:
|
|
1. Cache lookup
|
|
2. Repository call if cache miss
|
|
3. Cache storage
|
|
4. Logging
|
|
|
|
---
|
|
|
|
## Step 1: Update Imports
|
|
|
|
**Current** (line 8):
|
|
```typescript
|
|
import { MakeItem, ModelItem, TrimItem, EngineItem } from '../models/responses';
|
|
```
|
|
|
|
**New**:
|
|
```typescript
|
|
import { VINDecodeResult } from '../models/responses';
|
|
// MakeItem, ModelItem, TrimItem, EngineItem removed - using string[] now
|
|
```
|
|
|
|
---
|
|
|
|
## Step 2: Update getMakes() Method
|
|
|
|
**Current** (lines 44-60):
|
|
```typescript
|
|
async getMakes(pool: Pool, year: number): Promise<MakeItem[]> {
|
|
try {
|
|
const cached = await this.cache.getMakes(year);
|
|
if (cached) {
|
|
logger.debug('Makes retrieved from cache', { year });
|
|
return cached;
|
|
}
|
|
|
|
const makes = await this.repository.getMakes(pool, year);
|
|
await this.cache.setMakes(year, makes);
|
|
logger.debug('Makes retrieved from database and cached', { year, count: makes.length });
|
|
return makes;
|
|
} catch (error) {
|
|
logger.error('Service error: getMakes', { error, year });
|
|
throw error;
|
|
}
|
|
}
|
|
```
|
|
|
|
**New**:
|
|
```typescript
|
|
async getMakes(pool: Pool, year: number): Promise<string[]> {
|
|
try {
|
|
const cached = await this.cache.getMakes(year);
|
|
if (cached) {
|
|
logger.debug('Makes retrieved from cache', { year });
|
|
return cached;
|
|
}
|
|
|
|
const makes = await this.repository.getMakes(pool, year);
|
|
await this.cache.setMakes(year, makes);
|
|
logger.debug('Makes retrieved from database and cached', { year, count: makes.length });
|
|
return makes;
|
|
} catch (error) {
|
|
logger.error('Service error: getMakes', { error, year });
|
|
throw error;
|
|
}
|
|
}
|
|
```
|
|
|
|
**Changes**:
|
|
- Line 44: Return type `Promise<MakeItem[]>` → `Promise<string[]>`
|
|
- Logic unchanged (cache still works the same way)
|
|
|
|
**Note**: Cache service will need updates (Step 7)
|
|
|
|
---
|
|
|
|
## Step 3: Update getModels() Method
|
|
|
|
**Current** (lines 65-81):
|
|
```typescript
|
|
async getModels(pool: Pool, year: number, makeId: number): Promise<ModelItem[]> {
|
|
try {
|
|
const cached = await this.cache.getModels(year, makeId);
|
|
if (cached) {
|
|
logger.debug('Models retrieved from cache', { year, makeId });
|
|
return cached;
|
|
}
|
|
|
|
const models = await this.repository.getModels(pool, year, makeId);
|
|
await this.cache.setModels(year, makeId, models);
|
|
logger.debug('Models retrieved from database and cached', { year, makeId, count: models.length });
|
|
return models;
|
|
} catch (error) {
|
|
logger.error('Service error: getModels', { error, year, makeId });
|
|
throw error;
|
|
}
|
|
}
|
|
```
|
|
|
|
**New**:
|
|
```typescript
|
|
async getModels(pool: Pool, year: number, make: string): Promise<string[]> {
|
|
try {
|
|
const cached = await this.cache.getModels(year, make);
|
|
if (cached) {
|
|
logger.debug('Models retrieved from cache', { year, make });
|
|
return cached;
|
|
}
|
|
|
|
const models = await this.repository.getModels(pool, year, make);
|
|
await this.cache.setModels(year, make, models);
|
|
logger.debug('Models retrieved from database and cached', { year, make, count: models.length });
|
|
return models;
|
|
} catch (error) {
|
|
logger.error('Service error: getModels', { error, year, make });
|
|
throw error;
|
|
}
|
|
}
|
|
```
|
|
|
|
**Changes**:
|
|
- Line 65: Parameter `makeId: number` → `make: string`
|
|
- Line 65: Return type `Promise<ModelItem[]>` → `Promise<string[]>`
|
|
- Line 67: Cache call uses `make` instead of `makeId`
|
|
- Line 69: Logger uses `make` instead of `makeId`
|
|
- Line 73: Repository call uses `make` instead of `makeId`
|
|
- Line 74: Cache set uses `make` instead of `makeId`
|
|
- Line 75: Logger uses `make` instead of `makeId`
|
|
- Line 78: Logger uses `make` instead of `makeId`
|
|
|
|
---
|
|
|
|
## Step 4: Update getTrims() Method
|
|
|
|
**Current** (lines 86-102):
|
|
```typescript
|
|
async getTrims(pool: Pool, year: number, modelId: number): Promise<TrimItem[]> {
|
|
try {
|
|
const cached = await this.cache.getTrims(year, modelId);
|
|
if (cached) {
|
|
logger.debug('Trims retrieved from cache', { year, modelId });
|
|
return cached;
|
|
}
|
|
|
|
const trims = await this.repository.getTrims(pool, year, modelId);
|
|
await this.cache.setTrims(year, modelId, trims);
|
|
logger.debug('Trims retrieved from database and cached', { year, modelId, count: trims.length });
|
|
return trims;
|
|
} catch (error) {
|
|
logger.error('Service error: getTrims', { error, year, modelId });
|
|
throw error;
|
|
}
|
|
}
|
|
```
|
|
|
|
**New**:
|
|
```typescript
|
|
async getTrims(pool: Pool, year: number, make: string, model: string): Promise<string[]> {
|
|
try {
|
|
const cached = await this.cache.getTrims(year, make, model);
|
|
if (cached) {
|
|
logger.debug('Trims retrieved from cache', { year, make, model });
|
|
return cached;
|
|
}
|
|
|
|
const trims = await this.repository.getTrims(pool, year, make, model);
|
|
await this.cache.setTrims(year, make, model, trims);
|
|
logger.debug('Trims retrieved from database and cached', { year, make, model, count: trims.length });
|
|
return trims;
|
|
} catch (error) {
|
|
logger.error('Service error: getTrims', { error, year, make, model });
|
|
throw error;
|
|
}
|
|
}
|
|
```
|
|
|
|
**Changes**:
|
|
- Line 86: Parameters changed from `modelId: number` to `make: string, model: string`
|
|
- Line 86: Return type `Promise<TrimItem[]>` → `Promise<string[]>`
|
|
- All cache and logger calls updated to use `make, model` instead of `modelId`
|
|
|
|
---
|
|
|
|
## Step 5: Update getEngines() Method
|
|
|
|
**Current** (lines 107-123):
|
|
```typescript
|
|
async getEngines(pool: Pool, year: number, modelId: number, trimId: number): Promise<EngineItem[]> {
|
|
try {
|
|
const cached = await this.cache.getEngines(year, modelId, trimId);
|
|
if (cached) {
|
|
logger.debug('Engines retrieved from cache', { year, modelId, trimId });
|
|
return cached;
|
|
}
|
|
|
|
const engines = await this.repository.getEngines(pool, year, modelId, trimId);
|
|
await this.cache.setEngines(year, modelId, trimId, engines);
|
|
logger.debug('Engines retrieved from database and cached', { year, modelId, trimId, count: engines.length });
|
|
return engines;
|
|
} catch (error) {
|
|
logger.error('Service error: getEngines', { error, year, modelId, trimId });
|
|
throw error;
|
|
}
|
|
}
|
|
```
|
|
|
|
**New**:
|
|
```typescript
|
|
async getEngines(pool: Pool, year: number, make: string, model: string, trim: string): Promise<string[]> {
|
|
try {
|
|
const cached = await this.cache.getEngines(year, make, model, trim);
|
|
if (cached) {
|
|
logger.debug('Engines retrieved from cache', { year, make, model, trim });
|
|
return cached;
|
|
}
|
|
|
|
const engines = await this.repository.getEngines(pool, year, make, model, trim);
|
|
await this.cache.setEngines(year, make, model, trim, engines);
|
|
logger.debug('Engines retrieved from database and cached', { year, make, model, trim, count: engines.length });
|
|
return engines;
|
|
} catch (error) {
|
|
logger.error('Service error: getEngines', { error, year, make, model, trim });
|
|
throw error;
|
|
}
|
|
}
|
|
```
|
|
|
|
**Changes**:
|
|
- Line 107: Parameters changed from `modelId: number, trimId: number` to `make: string, model: string, trim: string`
|
|
- Line 107: Return type `Promise<EngineItem[]>` → `Promise<string[]>`
|
|
- All cache and logger calls updated to use `make, model, trim` instead of `modelId, trimId`
|
|
|
|
---
|
|
|
|
## Step 6: Add getTransmissions() Method (NEW)
|
|
|
|
Add this new method after `getEngines()`:
|
|
|
|
```typescript
|
|
/**
|
|
* Get transmissions for a year, make, and model with caching
|
|
*/
|
|
async getTransmissions(pool: Pool, year: number, make: string, model: string): Promise<string[]> {
|
|
try {
|
|
const cached = await this.cache.getTransmissions(year, make, model);
|
|
if (cached) {
|
|
logger.debug('Transmissions retrieved from cache', { year, make, model });
|
|
return cached;
|
|
}
|
|
|
|
const transmissions = await this.repository.getTransmissions(pool, year, make, model);
|
|
await this.cache.setTransmissions(year, make, model, transmissions);
|
|
logger.debug('Transmissions retrieved from database and cached', { year, make, model, count: transmissions.length });
|
|
return transmissions;
|
|
} catch (error) {
|
|
logger.error('Service error: getTransmissions', { error, year, make, model });
|
|
throw error;
|
|
}
|
|
}
|
|
```
|
|
|
|
**Why This is New**:
|
|
- Repository added `getTransmissions()` method (real data, not hardcoded)
|
|
- Service needs to expose this with caching
|
|
|
|
---
|
|
|
|
## Step 7: Update Cache Service
|
|
|
|
**File**: `backend/src/features/platform/domain/platform-cache.service.ts`
|
|
|
|
The cache service needs updates to handle string-based cache keys instead of ID-based keys.
|
|
|
|
### Cache Key Changes
|
|
|
|
**Current cache keys**:
|
|
```typescript
|
|
`years`
|
|
`makes:${year}`
|
|
`models:${year}:${makeId}`
|
|
`trims:${year}:${modelId}`
|
|
`engines:${year}:${modelId}:${trimId}`
|
|
```
|
|
|
|
**New cache keys**:
|
|
```typescript
|
|
`years`
|
|
`makes:${year}`
|
|
`models:${year}:${make}`
|
|
`trims:${year}:${make}:${model}`
|
|
`engines:${year}:${make}:${model}:${trim}`
|
|
`transmissions:${year}:${make}:${model}` // NEW
|
|
```
|
|
|
|
### Update Cache Method Signatures
|
|
|
|
**Find and update these methods**:
|
|
|
|
#### getMakes / setMakes
|
|
```typescript
|
|
// Signature unchanged (still takes year: number)
|
|
async getMakes(year: number): Promise<string[] | null>
|
|
async setMakes(year: number, makes: string[]): Promise<void>
|
|
```
|
|
|
|
#### getModels / setModels
|
|
```typescript
|
|
// OLD
|
|
async getModels(year: number, makeId: number): Promise<ModelItem[] | null>
|
|
async setModels(year: number, makeId: number, models: ModelItem[]): Promise<void>
|
|
|
|
// NEW
|
|
async getModels(year: number, make: string): Promise<string[] | null>
|
|
async setModels(year: number, make: string, models: string[]): Promise<void>
|
|
|
|
// Implementation change:
|
|
const key = `models:${year}:${make}`; // was makeId
|
|
```
|
|
|
|
#### getTrims / setTrims
|
|
```typescript
|
|
// OLD
|
|
async getTrims(year: number, modelId: number): Promise<TrimItem[] | null>
|
|
async setTrims(year: number, modelId: number, trims: TrimItem[]): Promise<void>
|
|
|
|
// NEW
|
|
async getTrims(year: number, make: string, model: string): Promise<string[] | null>
|
|
async setTrims(year: number, make: string, model: string, trims: string[]): Promise<void>
|
|
|
|
// Implementation change:
|
|
const key = `trims:${year}:${make}:${model}`; // was modelId only
|
|
```
|
|
|
|
#### getEngines / setEngines
|
|
```typescript
|
|
// OLD
|
|
async getEngines(year: number, modelId: number, trimId: number): Promise<EngineItem[] | null>
|
|
async setEngines(year: number, modelId: number, trimId: number, engines: EngineItem[]): Promise<void>
|
|
|
|
// NEW
|
|
async getEngines(year: number, make: string, model: string, trim: string): Promise<string[] | null>
|
|
async setEngines(year: number, make: string, model: string, trim: string, engines: string[]): Promise<void>
|
|
|
|
// Implementation change:
|
|
const key = `engines:${year}:${make}:${model}:${trim}`; // was modelId:trimId
|
|
```
|
|
|
|
#### getTransmissions / setTransmissions (NEW)
|
|
```typescript
|
|
async getTransmissions(year: number, make: string, model: string): Promise<string[] | null> {
|
|
const key = `transmissions:${year}:${make}:${model}`;
|
|
const cached = await this.redisClient.get(key);
|
|
|
|
if (cached) {
|
|
return JSON.parse(cached);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
async setTransmissions(year: number, make: string, model: string, transmissions: string[]): Promise<void> {
|
|
const key = `transmissions:${year}:${make}:${model}`;
|
|
await this.redisClient.setex(key, this.TTL, JSON.stringify(transmissions));
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Step 8: Update Documentation Comments
|
|
|
|
Update the file header:
|
|
|
|
**Old** (lines 1-4):
|
|
```typescript
|
|
/**
|
|
* @ai-summary Vehicle data service with caching
|
|
* @ai-context Business logic for hierarchical vehicle data queries
|
|
*/
|
|
```
|
|
|
|
**New**:
|
|
```typescript
|
|
/**
|
|
* @ai-summary Vehicle data service with caching for dropdown queries
|
|
* @ai-context String-based cascade queries with Redis caching
|
|
* @ai-migration Updated to use string parameters (not IDs)
|
|
*/
|
|
```
|
|
|
|
---
|
|
|
|
## Complete Updated Service Structure
|
|
|
|
After all changes:
|
|
|
|
```typescript
|
|
/**
|
|
* @ai-summary Vehicle data service with caching for dropdown queries
|
|
* @ai-context String-based cascade queries with Redis caching
|
|
* @ai-migration Updated to use string parameters (not IDs)
|
|
*/
|
|
import { Pool } from 'pg';
|
|
import { VehicleDataRepository } from '../data/vehicle-data.repository';
|
|
import { PlatformCacheService } from './platform-cache.service';
|
|
import { VINDecodeResult } from '../models/responses';
|
|
import { logger } from '../../../core/logging/logger';
|
|
|
|
export class VehicleDataService {
|
|
private repository: VehicleDataRepository;
|
|
private cache: PlatformCacheService;
|
|
|
|
constructor(cache: PlatformCacheService, repository?: VehicleDataRepository) {
|
|
this.cache = cache;
|
|
this.repository = repository || new VehicleDataRepository();
|
|
}
|
|
|
|
async getYears(pool: Pool): Promise<number[]> { ... } // Unchanged
|
|
|
|
async getMakes(pool: Pool, year: number): Promise<string[]> { ... }
|
|
|
|
async getModels(pool: Pool, year: number, make: string): Promise<string[]> { ... }
|
|
|
|
async getTrims(pool: Pool, year: number, make: string, model: string): Promise<string[]> { ... }
|
|
|
|
async getEngines(pool: Pool, year: number, make: string, model: string, trim: string): Promise<string[]> { ... }
|
|
|
|
async getTransmissions(pool: Pool, year: number, make: string, model: string): Promise<string[]> { ... } // NEW
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Testing & Verification
|
|
|
|
### Unit Tests
|
|
|
|
Update existing tests:
|
|
|
|
**File**: `backend/src/features/platform/tests/unit/vehicle-data.service.test.ts`
|
|
|
|
```typescript
|
|
import { VehicleDataService } from '../../domain/vehicle-data.service';
|
|
import { VehicleDataRepository } from '../../data/vehicle-data.repository';
|
|
import { PlatformCacheService } from '../../domain/platform-cache.service';
|
|
|
|
describe('VehicleDataService', () => {
|
|
let service: VehicleDataService;
|
|
let mockRepository: jest.Mocked<VehicleDataRepository>;
|
|
let mockCache: jest.Mocked<PlatformCacheService>;
|
|
let mockPool: any;
|
|
|
|
beforeEach(() => {
|
|
mockRepository = {
|
|
getMakes: jest.fn(),
|
|
getModels: jest.fn(),
|
|
getTrims: jest.fn(),
|
|
getEngines: jest.fn(),
|
|
getTransmissions: jest.fn(),
|
|
} as any;
|
|
|
|
mockCache = {
|
|
getMakes: jest.fn(),
|
|
setMakes: jest.fn(),
|
|
getModels: jest.fn(),
|
|
setModels: jest.fn(),
|
|
// ... etc
|
|
} as any;
|
|
|
|
service = new VehicleDataService(mockCache, mockRepository);
|
|
mockPool = {} as any;
|
|
});
|
|
|
|
describe('getMakes', () => {
|
|
it('should return string array from cache', async () => {
|
|
mockCache.getMakes.mockResolvedValue(['Ford', 'Honda']);
|
|
|
|
const result = await service.getMakes(mockPool, 2024);
|
|
|
|
expect(result).toEqual(['Ford', 'Honda']);
|
|
expect(mockRepository.getMakes).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should fetch from repository on cache miss', async () => {
|
|
mockCache.getMakes.mockResolvedValue(null);
|
|
mockRepository.getMakes.mockResolvedValue(['Ford', 'Honda']);
|
|
|
|
const result = await service.getMakes(mockPool, 2024);
|
|
|
|
expect(result).toEqual(['Ford', 'Honda']);
|
|
expect(mockRepository.getMakes).toHaveBeenCalledWith(mockPool, 2024);
|
|
expect(mockCache.setMakes).toHaveBeenCalledWith(2024, ['Ford', 'Honda']);
|
|
});
|
|
});
|
|
|
|
describe('getModels', () => {
|
|
it('should accept make string parameter', async () => {
|
|
mockCache.getModels.mockResolvedValue(null);
|
|
mockRepository.getModels.mockResolvedValue(['F-150', 'Mustang']);
|
|
|
|
const result = await service.getModels(mockPool, 2024, 'Ford');
|
|
|
|
expect(result).toEqual(['F-150', 'Mustang']);
|
|
expect(mockRepository.getModels).toHaveBeenCalledWith(mockPool, 2024, 'Ford');
|
|
});
|
|
});
|
|
|
|
describe('getTransmissions', () => {
|
|
it('should return transmission data', async () => {
|
|
mockCache.getTransmissions.mockResolvedValue(null);
|
|
mockRepository.getTransmissions.mockResolvedValue(['10-Speed Automatic']);
|
|
|
|
const result = await service.getTransmissions(mockPool, 2024, 'Ford', 'F-150');
|
|
|
|
expect(result).toEqual(['10-Speed Automatic']);
|
|
});
|
|
});
|
|
});
|
|
```
|
|
|
|
Run tests:
|
|
```bash
|
|
cd backend
|
|
npm test -- vehicle-data.service.test.ts
|
|
```
|
|
|
|
### Integration Test
|
|
|
|
Test the full flow with real cache:
|
|
|
|
```bash
|
|
# Start backend container
|
|
make start
|
|
|
|
# Access backend container
|
|
docker exec -it mvp-backend node
|
|
|
|
# In Node REPL:
|
|
const { Pool } = require('pg');
|
|
const { VehicleDataService } = require('./src/features/platform/domain/vehicle-data.service');
|
|
const { VehicleDataRepository } = require('./src/features/platform/data/vehicle-data.repository');
|
|
const { PlatformCacheService } = require('./src/features/platform/domain/platform-cache.service');
|
|
const Redis = require('ioredis');
|
|
|
|
const pool = new Pool({
|
|
host: 'postgres',
|
|
database: 'motovaultpro',
|
|
user: 'postgres',
|
|
password: process.env.POSTGRES_PASSWORD
|
|
});
|
|
|
|
const redis = new Redis({ host: 'redis' });
|
|
const cache = new PlatformCacheService(redis);
|
|
const repo = new VehicleDataRepository();
|
|
const service = new VehicleDataService(cache, repo);
|
|
|
|
// Test getMakes
|
|
await service.getMakes(pool, 2024);
|
|
// First call: fetches from DB, caches result
|
|
// Second call: returns from cache
|
|
await service.getMakes(pool, 2024);
|
|
|
|
// Test getModels with string parameter
|
|
await service.getModels(pool, 2024, 'Ford');
|
|
|
|
// Test getTrims
|
|
await service.getTrims(pool, 2024, 'Ford', 'F-150');
|
|
|
|
// Test getEngines
|
|
await service.getEngines(pool, 2024, 'Ford', 'F-150', 'XLT');
|
|
|
|
// Test getTransmissions (NEW)
|
|
await service.getTransmissions(pool, 2024, 'Ford', 'F-150');
|
|
```
|
|
|
|
---
|
|
|
|
## Completion Checklist
|
|
|
|
Before signaling completion:
|
|
|
|
- [ ] All method signatures updated (string parameters, not IDs)
|
|
- [ ] Return types changed to string[] (removed object types)
|
|
- [ ] getMakes() updated
|
|
- [ ] getModels() updated with make parameter
|
|
- [ ] getTrims() updated with make and model parameters
|
|
- [ ] getEngines() updated with make, model, trim parameters
|
|
- [ ] getTransmissions() method added (new)
|
|
- [ ] Cache service signatures updated
|
|
- [ ] Cache keys use strings (not IDs)
|
|
- [ ] Unit tests pass
|
|
- [ ] Integration tests verify caching works
|
|
- [ ] TypeScript compiles with no errors
|
|
- [ ] File documentation updated
|
|
|
|
---
|
|
|
|
## Common Issues
|
|
|
|
### Issue: TypeScript error "Type 'string[]' is not assignable to type 'MakeItem[]'"
|
|
**Cause**: Cache service still using old types
|
|
|
|
**Solution**:
|
|
- Update cache service method signatures (Step 7)
|
|
- Ensure cache methods return `string[] | null` not `MakeItem[] | null`
|
|
|
|
### Issue: Cache keys collision
|
|
**Cause**: String-based keys might collide with old ID-based keys if Redis not cleared
|
|
|
|
**Solution**:
|
|
```bash
|
|
# Clear Redis cache
|
|
docker exec mvp-redis redis-cli FLUSHDB
|
|
|
|
# Or clear specific pattern
|
|
docker exec mvp-redis redis-cli KEYS "makes:*" | xargs docker exec mvp-redis redis-cli DEL
|
|
```
|
|
|
|
### Issue: Service tests fail with parameter mismatch
|
|
**Cause**: Tests still passing old ID parameters
|
|
|
|
**Solution**:
|
|
- Update test calls to use strings: `service.getModels(pool, 2024, 'Ford')` not `service.getModels(pool, 2024, 1)`
|
|
|
|
---
|
|
|
|
## Handoff to Agent 4
|
|
|
|
Once complete, provide this information:
|
|
|
|
### Updated Service Contract
|
|
|
|
**Methods**:
|
|
```typescript
|
|
getYears(pool: Pool): Promise<number[]>
|
|
getMakes(pool: Pool, year: number): Promise<string[]>
|
|
getModels(pool: Pool, year: number, make: string): Promise<string[]>
|
|
getTrims(pool: Pool, year: number, make: string, model: string): Promise<string[]>
|
|
getEngines(pool: Pool, year: number, make: string, model: string, trim: string): Promise<string[]>
|
|
getTransmissions(pool: Pool, year: number, make: string, model: string): Promise<string[]>
|
|
```
|
|
|
|
**Key Changes for Agent 4**:
|
|
- All dropdown methods return `string[]`
|
|
- Parameters use strings (make, model, trim) not IDs
|
|
- `getTransmissions()` is a new method
|
|
- Caching layer handles string-based keys
|
|
|
|
### Verification Command
|
|
```bash
|
|
# Agent 4 can verify service is ready:
|
|
cd backend && npm run build
|
|
# Should compile with no errors in vehicle-data.service.ts
|
|
```
|
|
|
|
---
|
|
|
|
## Completion Message Template
|
|
|
|
```
|
|
Agent 3 (Platform Service): COMPLETE
|
|
|
|
Files Modified:
|
|
- backend/src/features/platform/domain/vehicle-data.service.ts
|
|
- backend/src/features/platform/domain/platform-cache.service.ts
|
|
|
|
Changes Made:
|
|
- Updated all method signatures to accept strings (not IDs)
|
|
- Changed return types to string[] (removed object types)
|
|
- Updated cache keys to use string-based parameters
|
|
- Added getTransmissions() method with caching
|
|
- All methods still use Redis caching (TTL unchanged)
|
|
|
|
Verification:
|
|
✓ TypeScript compiles successfully
|
|
✓ Unit tests pass
|
|
✓ Integration tests confirm caching works
|
|
✓ Cache keys use string parameters
|
|
|
|
Agent 4 (Vehicles API) can now update controllers to use new service signatures.
|
|
|
|
Breaking Changes for Agent 4:
|
|
- Service methods accept strings: make, model, trim (not makeId, modelId, trimId)
|
|
- Service methods return string[] (not {id, name}[])
|
|
- New method: getTransmissions()
|
|
```
|
|
|
|
---
|
|
|
|
**Document Version**: 1.0
|
|
**Last Updated**: 2025-11-10
|
|
**Status**: Ready for Implementation
|