Files
edr-platform/apps/edr-passenger-api/src/modules/configurable-fare/configurable-fare.controller.ts

235 lines
9.2 KiB
TypeScript

import { Body, Controller, Get, Post, Put, Delete, Param, UseGuards, Request, Query } from '@nestjs/common';
import { ApiTags, ApiBearerAuth, ApiOperation, ApiResponse, ApiParam, ApiQuery } from '@nestjs/swagger';
import { ConfigurableFareService } from './configurable-fare.service';
import {
CreateFareConfigurationDto,
UpdateFareConfigurationDto,
FareTestScenarioDto,
FareCalculationResultDto,
MigrateLegacyDto,
CreateNewFormulaDto,
ToggleFeatureDto
} from './configurable-fare.dto';
import { IamGuard } from '../../common/iam-adapter';
import { Roles } from '../../common/roles.decorator';
@ApiTags('Configurable Fares')
@Controller('admin/fare-configurations')
@ApiBearerAuth('IAM-auth')
@UseGuards(IamGuard)
@Roles('ADMIN')
export class ConfigurableFareController {
constructor(private service: ConfigurableFareService) {}
@Get()
@ApiOperation({ summary: 'List all fare configurations' })
@ApiResponse({ status: 200, description: 'List of all configurations with summary counts' })
async getAllConfigurations() {
return this.service.getAllConfigurations();
}
@Post()
@ApiOperation({ summary: 'Create new fare configuration' })
@ApiResponse({ status: 201, description: 'Configuration created successfully' })
@ApiResponse({ status: 400, description: 'Invalid configuration data' })
async createConfiguration(
@Body() dto: CreateFareConfigurationDto,
@Request() req: any
) {
const createdBy = req.user?.id || req.user?.sub;
return this.service.createConfiguration(dto, createdBy);
}
@Get(':id')
@ApiOperation({ summary: 'Get configuration details' })
@ApiParam({ name: 'id', description: 'Configuration ID' })
@ApiResponse({ status: 200, description: 'Configuration details with all rules' })
@ApiResponse({ status: 404, description: 'Configuration not found' })
async getConfigurationById(@Param('id') id: string) {
return this.service.getConfigurationById(id);
}
@Put(':id')
@ApiOperation({ summary: 'Update configuration' })
@ApiParam({ name: 'id', description: 'Configuration ID' })
@ApiResponse({ status: 200, description: 'Configuration updated successfully' })
@ApiResponse({ status: 404, description: 'Configuration not found' })
async updateConfiguration(
@Param('id') id: string,
@Body() dto: UpdateFareConfigurationDto,
@Request() req: any
) {
const updatedBy = req.user?.id || req.user?.sub;
return this.service.updateConfiguration(id, dto, updatedBy);
}
@Post(':id/activate')
@ApiOperation({ summary: 'Activate configuration' })
@ApiParam({ name: 'id', description: 'Configuration ID' })
@ApiResponse({ status: 200, description: 'Configuration activated successfully' })
@ApiResponse({ status: 404, description: 'Configuration not found' })
async activateConfiguration(@Param('id') id: string, @Request() req: any) {
const activatedBy = req.user?.id || req.user?.sub;
return this.service.activateConfiguration(id, activatedBy);
}
@Delete(':id')
@ApiOperation({ summary: 'Delete configuration' })
@ApiParam({ name: 'id', description: 'Configuration ID' })
@ApiResponse({ status: 200, description: 'Configuration deleted successfully' })
@ApiResponse({ status: 404, description: 'Configuration not found' })
@ApiResponse({ status: 409, description: 'Cannot delete active configuration' })
async deleteConfiguration(@Param('id') id: string, @Request() req: any) {
const deletedBy = req.user?.id || req.user?.sub;
return this.service.deleteConfiguration(id, deletedBy);
}
@Post(':id/test')
@ApiOperation({ summary: 'Test fare calculation with configuration' })
@ApiParam({ name: 'id', description: 'Configuration ID' })
@ApiResponse({ status: 200, type: FareCalculationResultDto, description: 'Fare calculation result' })
@ApiResponse({ status: 400, description: 'Invalid test scenario or missing rate rules' })
async testConfiguration(
@Param('id') id: string,
@Body() scenario: FareTestScenarioDto
): Promise<FareCalculationResultDto> {
return this.service.testConfiguration(id, scenario);
}
@Get(':id/audit')
@ApiOperation({ summary: 'Get configuration audit trail' })
@ApiParam({ name: 'id', description: 'Configuration ID' })
@ApiResponse({ status: 200, description: 'Audit trail entries' })
async getAuditTrail(@Param('id') id: string) {
return this.service.getAuditTrail(id);
}
}
@ApiTags('Configurable Fares')
@Controller('admin/fare-migration')
@ApiBearerAuth('IAM-auth')
@UseGuards(IamGuard)
@Roles('ADMIN')
export class FareMigrationController {
constructor(private service: ConfigurableFareService) {}
@Post('migrate-legacy')
@ApiOperation({ summary: 'Migrate existing fare rules to configurable system' })
@ApiResponse({ status: 200, description: 'Migration completed successfully' })
@ApiResponse({ status: 400, description: 'Migration failed' })
async migrateLegacySystem(@Body() dto: MigrateLegacyDto) {
return this.service.migrateLegacySystem(dto);
}
@Post('create-new-formula')
@ApiOperation({ summary: 'Create new formula configuration with defaults' })
@ApiResponse({ status: 201, description: 'New formula configuration created' })
async createNewFormulaConfiguration(@Body() dto: CreateNewFormulaDto) {
return this.service.createNewFormulaConfiguration(dto);
}
@Post('complete-setup')
@ApiOperation({
summary: 'Complete system setup (migrate + create + activate)',
description: 'Performs full system migration and setup in one operation'
})
@ApiResponse({ status: 200, description: 'System setup completed successfully' })
async completeSetup(@Body() body: { activateNewFormula?: boolean; enableFeature?: boolean }) {
// Step 1: Migrate legacy system
const migrationResult = await this.service.migrateLegacySystem({ dryRun: false });
// Step 2: Create new formula configuration
const newConfig = await this.service.createNewFormulaConfiguration({
name: 'Default System Configuration',
description: 'System-generated configuration with optimal defaults',
activateImmediately: body.activateNewFormula !== false
});
// Step 3: Enable feature flag if requested
if (body.enableFeature) {
await this.service.toggleFeature({
featureName: 'USE_CONFIGURABLE_FARES',
enabled: true,
config: { rollout_percentage: 100 }
});
}
return {
migration: migrationResult,
newConfiguration: newConfig,
featureEnabled: body.enableFeature || false,
message: 'System setup completed successfully'
};
}
@Get('status')
@ApiOperation({ summary: 'Get migration and setup status' })
@ApiResponse({ status: 200, description: 'Current system status' })
async getStatus() {
const featureStatus = await this.service.getFeatureStatus('USE_CONFIGURABLE_FARES');
const configurations = await this.service.getAllConfigurations();
const activeConfig = (configurations as any[]).find(config => config.is_active);
return {
configurableFaresEnabled: featureStatus.enabled,
rolloutPercentage: featureStatus.config?.rollout_percentage || 0,
totalConfigurations: (configurations as any[]).length,
activeConfiguration: activeConfig?.id || null,
activeConfigurationName: activeConfig?.name || null,
systemReady: featureStatus.enabled && !!activeConfig
};
}
}
@ApiTags('Configurable Fares')
@Controller('admin/fare-configurations/system')
@ApiBearerAuth('IAM-auth')
@UseGuards(IamGuard)
@Roles('ADMIN')
export class FareSystemController {
constructor(private service: ConfigurableFareService) {}
@Get('feature-status')
@ApiOperation({ summary: 'Check configurable fares feature status' })
@ApiQuery({ name: 'feature', required: false, description: 'Feature name (defaults to USE_CONFIGURABLE_FARES)' })
@ApiResponse({ status: 200, description: 'Feature status retrieved' })
async getFeatureStatus(@Query('feature') featureName = 'USE_CONFIGURABLE_FARES') {
return this.service.getFeatureStatus(featureName);
}
@Post('toggle-feature')
@ApiOperation({ summary: 'Enable or disable configurable fares system' })
@ApiResponse({ status: 200, description: 'Feature toggled successfully' })
async toggleFeature(@Body() dto: ToggleFeatureDto) {
return this.service.toggleFeature(dto);
}
@Post('enable-configurable-fares')
@ApiOperation({
summary: 'Enable configurable fares with rollout percentage',
description: 'Quick endpoint to enable the configurable fares feature'
})
@ApiResponse({ status: 200, description: 'Configurable fares enabled successfully' })
async enableConfigurableFares(@Body() body: { rolloutPercentage?: number }) {
return this.service.toggleFeature({
featureName: 'USE_CONFIGURABLE_FARES',
enabled: true,
config: { rollout_percentage: body.rolloutPercentage || 100 }
});
}
@Post('disable-configurable-fares')
@ApiOperation({
summary: 'Disable configurable fares (fallback to legacy system)',
description: 'Disables the configurable fares feature and falls back to legacy fare calculation'
})
@ApiResponse({ status: 200, description: 'Configurable fares disabled successfully' })
async disableConfigurableFares() {
return this.service.toggleFeature({
featureName: 'USE_CONFIGURABLE_FARES',
enabled: false,
config: { rollout_percentage: 0 }
});
}
}