From 99383c7ac0636b1e4975bf599dcaafaa6f9748e0 Mon Sep 17 00:00:00 2001 From: codevon Date: Wed, 7 Jan 2026 10:26:18 +0800 Subject: [PATCH 1/3] add test file --- test.ts | 189 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 test.ts diff --git a/test.ts b/test.ts new file mode 100644 index 0000000..9529888 --- /dev/null +++ b/test.ts @@ -0,0 +1,189 @@ +import { Body, Controller, Get, Logger, Param, Post, Query, UseGuards } from '@nestjs/common'; +import { ThirdPartyApiClientService } from '@/common/third-party-api-client.service'; +import { ApifyClientService } from '@/common/apify-client.service'; +import { DevelopmentOnlyGuard } from '@/common/guards/development-only.guard'; +import { APIFY_JOB_STATUSES } from '@/constants/registration.constants'; +import { ApifyPromptPilotDatasetItem } from '@/types/apify.types'; +import { CreateCompanyContactDto, CreateCompanyContactResponse } from '@/modules/registration/dto/create-company-contact.dto'; +import { JobStatusService } from '@/modules/registration/job-status.service'; +import { RegistrationService } from '@/modules/registration/registration.service'; +import { IataRegistrationCheckResponse } from '@/types/registration-job-status.types'; +import { CheckTipaltiCompletionStatusResponse } from '@/types/tipalti-status-check.types'; + +@Controller('test') +export class TestController { + private readonly logger = new Logger(TestController.name); + + constructor( + private readonly thirdPartyApiClient: ThirdPartyApiClientService, + private readonly apifyClientService: ApifyClientService, + private readonly jobStatusService: JobStatusService, + private readonly registrationService: RegistrationService, + ) {} + + /** + * Search Ventrata resellers by IATA code + * Only available in development environment + * @param iataCode - The IATA code to search for + */ + @UseGuards(DevelopmentOnlyGuard) + @Get('ventrata/resellers/search') + async searchVentrataResellers(@Query('iataCode') iataCode: string) { + if (!iataCode) { + return { + success: false, + message: 'iataCode query parameter is required', + }; + } + + try { + this.logger.log(`API call: Searching Ventrata resellers for IATA code: ${iataCode}`); + + const result = await this.thirdPartyApiClient.searchVentrataResellers(iataCode); + + return { + success: result.success, + data: result.data, + exists: result.exists, + message: result.success ? `Found ${result.data.length} resellers for IATA code ${iataCode}` : result.error || 'Search failed', + }; + } catch (error) { + this.logger.error(`API call failed for IATA code ${iataCode}:`, error); + return { + success: false, + message: `Search failed: ${error.message}`, + }; + } + } + + /** + * Trigger Ventrata registration step (dev-only) + */ + @UseGuards(DevelopmentOnlyGuard) + @Post('ventrata/registration/:registrationId?') + async executeVentrataRegistration(@Body() request: CreateCompanyContactDto, @Param('registrationId') registrationId?: string) { + const registrationIdNumber = registrationId ? Number(registrationId) : undefined; + return this.thirdPartyApiClient.executeVentrataRegistration(request, registrationIdNumber); + } + + /** + * Get Apify run status by run ID + * Only available in development environment + * @param runId - The Apify run ID to check status for + */ + @UseGuards(DevelopmentOnlyGuard) + @Get('apify/run-status') + async getApifyRunStatus(@Query('runId') runId: string) { + if (!runId) { + return { + success: false, + message: 'runId query parameter is required', + }; + } + + try { + this.logger.log(`API call: Getting Apify run status for runId: ${runId}`); + + const runResult = await this.apifyClientService.getRunStatus(runId); + const apifyStatus = runResult.status; + + if (apifyStatus === APIFY_JOB_STATUSES.SUCCEEDED) { + if (runResult.defaultDatasetId) { + const datasetItems = await this.apifyClientService.getDatasetItems(runResult.defaultDatasetId, 1); + let result = true; + if (datasetItems.length === 0) { + result = false; + this.logger.warn(`No dataset items found for succeeded run ${runId}, treating as failed`); + } + + // If error exists and error doesn't contain 'panelAgencyResult2Columns', we treat it as failed + if (datasetItems[0] && datasetItems[0].error && !datasetItems[0].error?.includes('panelAgencyResult2Columns')) { + result = false; + this.logger.warn(`Dataset item contains error without 'panelAgencyResult2Columns' for jobId ${runId}, treating as failed`); + } + return { + success: result, + data: datasetItems, + message: `Retrieved run status: ${runResult.status} for runId ${runId}`, + }; + } + } + + return { + success: false, + data: null, + message: `Retrieved run status: ${runResult.status} for runId ${runId}`, + }; + } catch (error) { + this.logger.error(`API call failed for runId ${runId}:`, error); + return { + success: false, + message: `Failed to get run status: ${error.message}`, + }; + } + } + + /** + * Check job statuses for registrations with READY job status and PENDING registration status + * Only available in development environment + * POST /test/registration/job-status/check + */ + @UseGuards(DevelopmentOnlyGuard) + @Post('registration/job-status/check') + async checkJobStatuses(): Promise { + try { + this.logger.log('Checking job statuses via API'); + + const results = await this.jobStatusService.checkJobStatuses(); + + return { + success: true, + data: results, + message: `Checked ${results.length} job statuses`, + }; + } catch (error) { + this.logger.error('Error checking job statuses via API:', error); + return { + success: false, + data: [], + message: `Failed to check job statuses: ${error instanceof Error ? error.message : String(error)}`, + }; + } + } + + /** + * Register agent and agency in HubSpot (second step of registration form) + * Only available in development environment + * POST /test/registration/workflow + */ + @UseGuards(DevelopmentOnlyGuard) + @Post('registration/workflow') + async registerAgentAndAgency(@Body() request: CreateCompanyContactDto): Promise { + try { + this.logger.log(`Registering agent and agency via API: ${request.companyName} - ${request.contactEmail}`); + + return await this.registrationService.registerAgentAndAgency(request); + } catch (error) { + this.logger.error(`Error creating company and contact via API: ${request.companyName}`, error); + throw error; + } + } + + /** + * Check Tipalti completion status for all companies with payees + * Only available in development environment + * POST /test/registration/tipalti-status-check + */ + @UseGuards(DevelopmentOnlyGuard) + @Post('registration/tipalti-status-check') + async checkTipaltiCompletionStatus(): Promise { + try { + this.logger.log('Checking Tipalti completion status via API'); + + return await this.registrationService.checkTipaltiCompletionStatus(); + } catch (error) { + this.logger.error('Error checking Tipalti completion status via API:', error); + throw error; + } + } +} From 70522ebf30ff67002822e81c30c7466d7d3e24df Mon Sep 17 00:00:00 2001 From: codevon Date: Wed, 7 Jan 2026 11:31:47 +0800 Subject: [PATCH 2/3] enable review --- .github/scripts/review.sh | 10 +++++----- .github/workflows/pr-review.yml | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/scripts/review.sh b/.github/scripts/review.sh index 43785fc..4e764f8 100644 --- a/.github/scripts/review.sh +++ b/.github/scripts/review.sh @@ -105,7 +105,7 @@ if [ ! -f "$PROJECT_CONVENTIONS_FILE" ]; then **IMPORTANT**: Write your analysis output directly to the file: ${PROJECT_CONVENTIONS_FILE} " echo $ANALYZE_PROMPT - cursor-agent --model auto -p --force --output-format stream-json --stream-partial-output "$ANALYZE_PROMPT" + claude -p --model "sonnet" --output-format stream-json --permission-mode acceptEdits --verbose "$ANALYZE_PROMPT" echo "" echo "Codebase analysis complete." else @@ -162,10 +162,10 @@ echo "$FULL_REVIEW_PROMPT" > review_prompt.md echo "Running code review..." -#claude -p --model "sonnet" --output-format stream-json --permission-mode acceptEdits --verbose "$FULL_REVIEW_PROMPT" -#mv CODE_REVIEW.md CODE_REVIEW_SONNET.md -#claude -p --model "haiku" --output-format stream-json --permission-mode acceptEdits --verbose "$FULL_REVIEW_PROMPT" -#mv CODE_REVIEW.md CODE_REVIEW_HAIKU.md +claude -p --model "sonnet" --output-format stream-json --permission-mode acceptEdits --verbose "$FULL_REVIEW_PROMPT" +mv CODE_REVIEW.md CODE_REVIEW_SONNET.md +claude -p --model "haiku" --output-format stream-json --permission-mode acceptEdits --verbose "$FULL_REVIEW_PROMPT" +mv CODE_REVIEW.md CODE_REVIEW_HAIKU.md GENERATE_JSON_PROMPT=$(cat "$REVIEW_PROMPT_FILE") cursor-agent --model auto -p --force --output-format stream-json --stream-partial-output "$GENERATE_JSON_PROMPT" diff --git a/.github/workflows/pr-review.yml b/.github/workflows/pr-review.yml index d906309..9d1c1bb 100644 --- a/.github/workflows/pr-review.yml +++ b/.github/workflows/pr-review.yml @@ -126,5 +126,6 @@ jobs: name: ai-code-review-report path: | CODE_REVIEW* - CODE_ANALYSIS.md + docs/conventions/project_conventions.md + retention-days: 30 From 3f92f075287d31a6b4388b619c7fd08988f2b409 Mon Sep 17 00:00:00 2001 From: codevon Date: Wed, 7 Jan 2026 14:37:24 +0800 Subject: [PATCH 3/3] merge master --- test2.ts | 189 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 test2.ts diff --git a/test2.ts b/test2.ts new file mode 100644 index 0000000..9529888 --- /dev/null +++ b/test2.ts @@ -0,0 +1,189 @@ +import { Body, Controller, Get, Logger, Param, Post, Query, UseGuards } from '@nestjs/common'; +import { ThirdPartyApiClientService } from '@/common/third-party-api-client.service'; +import { ApifyClientService } from '@/common/apify-client.service'; +import { DevelopmentOnlyGuard } from '@/common/guards/development-only.guard'; +import { APIFY_JOB_STATUSES } from '@/constants/registration.constants'; +import { ApifyPromptPilotDatasetItem } from '@/types/apify.types'; +import { CreateCompanyContactDto, CreateCompanyContactResponse } from '@/modules/registration/dto/create-company-contact.dto'; +import { JobStatusService } from '@/modules/registration/job-status.service'; +import { RegistrationService } from '@/modules/registration/registration.service'; +import { IataRegistrationCheckResponse } from '@/types/registration-job-status.types'; +import { CheckTipaltiCompletionStatusResponse } from '@/types/tipalti-status-check.types'; + +@Controller('test') +export class TestController { + private readonly logger = new Logger(TestController.name); + + constructor( + private readonly thirdPartyApiClient: ThirdPartyApiClientService, + private readonly apifyClientService: ApifyClientService, + private readonly jobStatusService: JobStatusService, + private readonly registrationService: RegistrationService, + ) {} + + /** + * Search Ventrata resellers by IATA code + * Only available in development environment + * @param iataCode - The IATA code to search for + */ + @UseGuards(DevelopmentOnlyGuard) + @Get('ventrata/resellers/search') + async searchVentrataResellers(@Query('iataCode') iataCode: string) { + if (!iataCode) { + return { + success: false, + message: 'iataCode query parameter is required', + }; + } + + try { + this.logger.log(`API call: Searching Ventrata resellers for IATA code: ${iataCode}`); + + const result = await this.thirdPartyApiClient.searchVentrataResellers(iataCode); + + return { + success: result.success, + data: result.data, + exists: result.exists, + message: result.success ? `Found ${result.data.length} resellers for IATA code ${iataCode}` : result.error || 'Search failed', + }; + } catch (error) { + this.logger.error(`API call failed for IATA code ${iataCode}:`, error); + return { + success: false, + message: `Search failed: ${error.message}`, + }; + } + } + + /** + * Trigger Ventrata registration step (dev-only) + */ + @UseGuards(DevelopmentOnlyGuard) + @Post('ventrata/registration/:registrationId?') + async executeVentrataRegistration(@Body() request: CreateCompanyContactDto, @Param('registrationId') registrationId?: string) { + const registrationIdNumber = registrationId ? Number(registrationId) : undefined; + return this.thirdPartyApiClient.executeVentrataRegistration(request, registrationIdNumber); + } + + /** + * Get Apify run status by run ID + * Only available in development environment + * @param runId - The Apify run ID to check status for + */ + @UseGuards(DevelopmentOnlyGuard) + @Get('apify/run-status') + async getApifyRunStatus(@Query('runId') runId: string) { + if (!runId) { + return { + success: false, + message: 'runId query parameter is required', + }; + } + + try { + this.logger.log(`API call: Getting Apify run status for runId: ${runId}`); + + const runResult = await this.apifyClientService.getRunStatus(runId); + const apifyStatus = runResult.status; + + if (apifyStatus === APIFY_JOB_STATUSES.SUCCEEDED) { + if (runResult.defaultDatasetId) { + const datasetItems = await this.apifyClientService.getDatasetItems(runResult.defaultDatasetId, 1); + let result = true; + if (datasetItems.length === 0) { + result = false; + this.logger.warn(`No dataset items found for succeeded run ${runId}, treating as failed`); + } + + // If error exists and error doesn't contain 'panelAgencyResult2Columns', we treat it as failed + if (datasetItems[0] && datasetItems[0].error && !datasetItems[0].error?.includes('panelAgencyResult2Columns')) { + result = false; + this.logger.warn(`Dataset item contains error without 'panelAgencyResult2Columns' for jobId ${runId}, treating as failed`); + } + return { + success: result, + data: datasetItems, + message: `Retrieved run status: ${runResult.status} for runId ${runId}`, + }; + } + } + + return { + success: false, + data: null, + message: `Retrieved run status: ${runResult.status} for runId ${runId}`, + }; + } catch (error) { + this.logger.error(`API call failed for runId ${runId}:`, error); + return { + success: false, + message: `Failed to get run status: ${error.message}`, + }; + } + } + + /** + * Check job statuses for registrations with READY job status and PENDING registration status + * Only available in development environment + * POST /test/registration/job-status/check + */ + @UseGuards(DevelopmentOnlyGuard) + @Post('registration/job-status/check') + async checkJobStatuses(): Promise { + try { + this.logger.log('Checking job statuses via API'); + + const results = await this.jobStatusService.checkJobStatuses(); + + return { + success: true, + data: results, + message: `Checked ${results.length} job statuses`, + }; + } catch (error) { + this.logger.error('Error checking job statuses via API:', error); + return { + success: false, + data: [], + message: `Failed to check job statuses: ${error instanceof Error ? error.message : String(error)}`, + }; + } + } + + /** + * Register agent and agency in HubSpot (second step of registration form) + * Only available in development environment + * POST /test/registration/workflow + */ + @UseGuards(DevelopmentOnlyGuard) + @Post('registration/workflow') + async registerAgentAndAgency(@Body() request: CreateCompanyContactDto): Promise { + try { + this.logger.log(`Registering agent and agency via API: ${request.companyName} - ${request.contactEmail}`); + + return await this.registrationService.registerAgentAndAgency(request); + } catch (error) { + this.logger.error(`Error creating company and contact via API: ${request.companyName}`, error); + throw error; + } + } + + /** + * Check Tipalti completion status for all companies with payees + * Only available in development environment + * POST /test/registration/tipalti-status-check + */ + @UseGuards(DevelopmentOnlyGuard) + @Post('registration/tipalti-status-check') + async checkTipaltiCompletionStatus(): Promise { + try { + this.logger.log('Checking Tipalti completion status via API'); + + return await this.registrationService.checkTipaltiCompletionStatus(); + } catch (error) { + this.logger.error('Error checking Tipalti completion status via API:', error); + throw error; + } + } +}