feat: Add POST /api/v1/analysis/:offerId/enhanced — Paid Expert Analysis ("The Closer") - #39
Merged
Merged
Conversation
…ccess middleware - Implement paidAccess middleware checking user.paidAnalyses flag - Implement protect (auth) middleware with Firebase JWT verification - Implement rateLimit middleware (5 req/min for paid endpoint) - Implement analysisValidator with validateOfferId - Implement offerService.findByIdAndUserId for ownership validation - Implement portfolioService.getUserPortfolio for portfolio data - Implement reportService.generateEnhancedReport with AI + fallback - Implement analysisController.generateEnhancedReport controller - Wire up analysis routes with full middleware chain - Add comprehensive tests for the enhanced endpoint
…ysis
Implements task 2: fetch user's latest portfolio for use in the enhanced
analysis report generation flow.
- Create src/services/portfolioService.js with getUserPortfolio(userId)
- Queries 'portfolios' Firestore collection for user's latest portfolio
(ordered by updatedAt desc, falls back to createdAt desc)
- Falls back to 'wizardInputs' collection to derive portfolio context
from wizard submission data when no saved portfolio exists
- Returns null gracefully when neither source has data
- Includes computeAverageRate() helper for rate calculations
- Full JSDoc documentation and structured logging
- Add __tests__/portfolioService.test.js with comprehensive unit tests
covering all code paths (portfolio found, wizard fallback, null return,
error handling, edge cases)
Task 3: Wire reportService.generateEnhancedReport(offerId, userId, offer, portfolio) into the enhanced analysis controller flow. - Controller calls reportService.generateEnhancedReport with all 4 required args: offerId, userId, offer (from offerService), portfolio (from portfolioService) - reportService handles AI generation (GPT-4o-mini) with rule-based fallback - Report is persisted to offer.analysis.enhanced via updateEnhancedAnalysis - Controller returns 201 on new generation, 200 on cached report - Added __tests__/reportServiceIntegration.test.js for task-3-specific coverage
- Enhance updateEnhancedAnalysis() in offerService.js to use a Firestore transaction with an idempotent "if not exists" guard, preventing concurrent requests from overwriting an already-stored enhanced report - Restore full offerService.js (merging main branch CRUD functions with the new updateEnhancedAnalysis implementation) - Add comprehensive unit tests for updateEnhancedAnalysis covering: - Stores report when analysis.enhanced does not exist - Skips write and returns existing report when already stored - Throws when offerId is missing - Handles Firestore transaction errors gracefully - Update __tests__/offerService.test.js with updateEnhancedAnalysis tests
- Handle null return from findByIdAndUserId with proper NotFoundError/ForbiddenError - Ensure full enhanced report data is returned with all required fields - Add offer existence check before ownership check for correct error codes - Validate enhanced report completeness before returning to client - Return 201 for newly generated reports, 200 for cached reports - Include all fields: tricks, negotiationScript, insights, comparison, generatedAt, generatedBy, processingTimeMs in response data
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
Implements "The Closer" — the paid expert analysis monetisation feature. Authenticated, paying users can now request a full AI-powered enhanced mortgage analysis report for any of their uploaded bank offers.
Completed Tasks
1.
POST /api/v1/analysis/:offerId/enhancedendpointsrc/routes/analysis.jsunder the existing/api/v1/analysisrouter prefix.protect → paidAccess → paidEndpointLimiter → validateOfferId → generateEnhancedReport.2.
paidAccessmiddleware (src/middleware/paidAccess.js)req.user.paidAnalyses === true.403 Forbiddenwith a descriptive message if the user has not paid.protectmiddleware (which attachesreq.user).3. Fetch user's latest portfolio
portfolioService.getUserPortfolio(userId)to retrieve the user's current portfolio model.nullportfolio is handled gracefully — the report generation falls back to rule-based logic.4.
reportService.generateEnhancedReport(offerId, userId, offer, portfolio)aiService.callGPTwith a structured system + user prompt.offer.analysis.enhancedin Firestore viaofferService.updateEnhancedAnalysis.5. Store enhanced report in
offer.analysis.enhancedreportServicecallsupdateEnhancedAnalysis(offerId, enhancedReport)to persist the report.6. Return full enhanced report data
{ "success": true, "message": "Enhanced report generated successfully", "data": { "tricks": [ { "nameHe", "nameEn", "descriptionHe", "descriptionEn", "applicability", "riskLevel", "potentialSavings" } ], "negotiationScript": "...", "insights": [ { "titleHe", "titleEn", "bodyHe", "bodyEn", "icon" } ], "comparison": { "rateDelta", "monthlySaving", "totalSaving", "loanAmount", "termYears", "bankRate", "portfolioRate", "trackComparison" }, "generatedAt": "ISO 8601", "generatedBy": "ai | rule-based-fallback", "processingTimeMs": 1234 } }Advanced Consultant Logic
Security & Reliability
protectmiddleware validates Firebase ID token.paidAccessmiddleware enforces subscription gate.paidEndpointLimiter(5 req/min per user) prevents abuse.validateOfferIdvalidates the:offerIdpath parameter.[שם]placeholder used for borrower name.Files Changed
src/routes/analysis.jsPOST /:offerId/enhancedroute with full middleware chainsrc/controllers/analysisController.jsgenerateEnhancedReportcontroller +buildResponseDatahelpersrc/services/reportService.jssrc/middleware/paidAccess.js__tests__/analysisEnhanced.test.js__tests__/reportService.test.js__tests__/paidAccess.test.js