-
Notifications
You must be signed in to change notification settings - Fork 0
feat(backend): implement controller-service architecture and query service integration #44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Signed-off-by: Asitha de Silva <[email protected]>
- Add ApiErrorResponse interface for standardized error responses - Add ValidationError interface for field-specific validation errors - Add PaginationInfo interface for pagination metadata - Centralize response interfaces for reuse across frontend and backend References: LFXV2-269, LFXV2-270 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <[email protected]>
- Add Logger helper with simplified methods (start, success, error, validation, etag, warning) - Add Responder helper for consistent error handling - Maintain microservice response format compatibility - Add sensitive data sanitization in Logger - Import interfaces from shared package in Responder References: LFXV2-268, LFXV2-269 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <[email protected]>
- Add CommitteeController for HTTP request handling - Add CommitteeService for business logic and query service integration - Add EtagService for concurrency control - Implement proper separation of concerns - Add comprehensive validation and error handling - Integrate Logger and Responder helpers References: LFXV2-270 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <[email protected]>
- Update committees route to use new controller-service architecture - Implement GET operations with query parameter support - Implement CREATE operation with validation - Implement UPDATE operation with ETag concurrency control - Implement DELETE operation with ETag validation - Add comprehensive error handling and logging - Update API client and microservice proxy services References: LFXV2-260, LFXV2-261, LFXV2-262, LFXV2-263 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <[email protected]>
- Update committee form component for new backend endpoints - Update table component for improved functionality - Maintain compatibility with existing UI patterns - Support ETag-based concurrency control in forms References: LFXV2-264, LFXV2-265, LFXV2-266, LFXV2-267 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <[email protected]>
- Update committee interfaces for microservice compatibility - Add support for new data structures from query service - Maintain backwards compatibility with existing frontend References: LFXV2-24 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <[email protected]>
- Add commit convention guidelines with JIRA ticket requirements - Update development memories with architecture patterns - Add branch naming conventions - Document JIRA ticket association requirements References: LFXV2-24 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <[email protected]>
- Document new Controller-Service architecture pattern - Add detailed implementation examples for Logger, Responder, and ETag helpers - Include directory structure and best practices - Add code quality standards and development guidelines - Document microservice integration patterns - Include comprehensive API examples and usage patterns References: LFXV2-270 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <[email protected]>
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughRepository-wide migration of committee/meeting identity and public fields (id → uid; public_enabled → public; public_name → display_name; committee_website → website). Introduces Controller-Service backend pattern (CommitteeController, CommitteeService), ETagService, Logger, Responder, extended microservice proxy and API client, shared API/ETag/error interfaces, and related frontend bindings and docs updates. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Client as Client
participant Router as /committees Router
participant Controller as CommitteeController
participant Service as CommitteeService
participant ETag as ETagService
participant Proxy as MicroserviceProxyService
participant LFX as LFX_V2 Service
Client->>Router: HTTP request (GET/POST/PUT/DELETE)
Router->>Controller: delegate request
Controller->>Service: perform action (list/get/create/update/delete)
alt get/update/delete requires ETag
Service->>ETag: fetchWithETag(path)
ETag->>Proxy: GET with headers
Proxy->>LFX: proxied request
LFX-->>Proxy: response + ETag
Proxy-->>ETag: ApiResponse
ETag-->>Service: { data, etag, headers }
opt update/delete
Service->>ETag: updateWithETag / deleteWithETag(path, etag)
ETag->>Proxy: PUT/DELETE with If-Match/ETag
Proxy->>LFX: proxied request
LFX-->>Proxy: response
Proxy-->>ETag: ApiResponse
end
end
Service-->>Controller: result or error
Controller-->>Client: JSON (200/201) or 204 / Responder.handle on errors
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR refactors the committee management system from a monolithic architecture to a clean Controller-Service pattern while integrating with the LFX Query Service microservice. The implementation includes standardized logging, error handling, ETag-based concurrency control, and comprehensive interface updates across the codebase.
Key changes:
- Implemented Controller-Service architecture with separation of concerns between HTTP handling and business logic
- Integrated committee CRUD operations with LFX Query Service microservice using standardized API patterns
- Added helper classes for consistent logging, response formatting, and ETag concurrency control
- Updated all frontend components to use
uidinstead ofidfor committee identification
Reviewed Changes
Copilot reviewed 29 out of 29 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/shared/src/interfaces/committee.interface.ts | Updated committee interfaces with new fields and validation structures for microservice compatibility |
| packages/shared/src/interfaces/meeting.interface.ts | Changed committee ID field from id to uid for consistency |
| packages/shared/src/interfaces/api.ts | Added ETag support interfaces and standardized API error response structures |
| apps/lfx-pcc/src/server/services/committee.service.ts | New service layer implementing business logic for committee operations with microservice integration |
| apps/lfx-pcc/src/server/controllers/committee.controller.ts | New controller implementing HTTP request handling with standardized logging and error responses |
| apps/lfx-pcc/src/server/helpers/logger.ts | Added standardized logging helper with timing, sanitization, and request correlation |
| apps/lfx-pcc/src/server/helpers/responder.ts | Added consistent error response formatting helper with proper HTTP status codes |
| apps/lfx-pcc/src/server/services/etag.service.ts | New service for ETag-based concurrency control with safe update operations |
| apps/lfx-pcc/src/server/routes/committees.ts | Refactored routes to use new controller pattern while maintaining member route compatibility |
| Multiple frontend components | Updated to use uid instead of id and adapted to new committee interface structure |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
You can also share your feedback on Copilot code review for a chance to win a $100 gift card. Take the survey.
✅ E2E Tests PassedBrowser: chromium All E2E tests passed successfully. Test Configuration
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 12
🔭 Outside diff range comments (6)
packages/shared/src/interfaces/meeting.interface.ts (1)
114-121: Duplicate DeleteMeetingRequest interface causes a compile-time errorThere are two identical DeleteMeetingRequest declarations. This will fail TypeScript compilation due to a duplicate identifier.
Apply this diff to remove the duplicate:
export interface DeleteMeetingRequest { deleteType?: 'single' | 'series'; } -export interface DeleteMeetingRequest { - deleteType?: 'single' | 'series'; -}apps/lfx-pcc/src/app/modules/project/settings/components/user-form/user-form.component.ts (1)
154-167: Potential null/undefined project UID usage in initCommittees()initCommittees() reads this.project()?.uid at construction time. If the project signal has not resolved yet, this passes "undefined" down to getCommitteesByProject, likely resulting in a bad request.
Apply this diff to wait for a valid project UID before querying committees:
- private initCommittees(): Signal<{ label: string; value: string }[]> { - return toSignal( - this.committeeService.getCommitteesByProject(this.project()?.uid as string).pipe( - take(1), - map((committees) => - committees.map((committee) => ({ - label: committee.name, - value: committee.uid, - })) - ) - ), - { - initialValue: [], - } - ); - } + private initCommittees(): Signal<{ label: string; value: string }[]> { + return toSignal( + this.projectService.project$.pipe( + // wait until a project with uid is available + // eslint-disable-next-line rxjs/no-ignored-notifier + map((p) => p?.uid || null), + // filter out null/empty uids + // prefer explicit check to avoid passing "undefined" as string + // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type + switchMap((uid) => + uid ? this.committeeService.getCommitteesByProject(uid) : of([]) + ), + take(1), + map((committees) => + committees.map((committee) => ({ + label: committee.name, + value: committee.uid, + })) + ) + ), + { initialValue: [] } + ); + }CLAUDE.md (1)
126-127: Fix typo: “resuable” → “reusable”Minor typo in the new guideline line.
-- All interfaces, resuable constants, and enums should live in the shared package. +- All interfaces, reusable constants, and enums should live in the shared package.apps/lfx-pcc/src/app/modules/project/committees/components/committee-table/committee-table.component.html (1)
69-101: Fix visibility property in CommitteeTable templateThe
Committeeinterface now usespublic: boolean(packages/shared/src/interfaces/committee.interface.ts:12), so the template’scommittee.public_enabledwill be undefined and break at runtime. Update the visibility checks accordingly:• File:
apps/lfx-pcc/src/app/modules/project/committees/components/committee-table/committee-table.component.html
– Replace all occurrences ofcommittee.public_enabledwithcommittee.publicSuggested diff:
<!-- Visibility column --> <td class="px-4 py-3 text-center"> - @if (committee.public_enabled) { + @if (committee.public) { <div class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full bg-blue-100 text-blue-700 border border-blue-200"> <i class="fa-light fa-eye text-[10px]"></i> <span class="text-[10px] font-medium">Public</span> </div> - } @else { + } @else { <div class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full bg-gray-100 text-gray-700 border border-gray-200"> <i class="fa-light fa-eye-slash text-[10px]"></i> <span class="text-[10px] font-medium">Private</span> </div> } </td>apps/lfx-pcc/src/app/modules/project/committees/committee-dashboard/committee-dashboard.component.ts (1)
191-203: Include ETag (If-Match) in your DELETE callThe current front-end
deleteCommitteeonly takes anidand issueshttp.delete<void>(`/api/committees/${id}`)but the backend now requires an
If-Match: <etag>header.Please update:
apps/lfx-pcc/src/app/shared/services/committee.service.ts
• Changepublic deleteCommittee(id: string): Observable<void> { return this.http.delete<void>(`/api/committees/${id}`).pipe(take(1)); }to something like
public deleteCommittee(id: string, etag: string): Observable<void> { const headers = new HttpHeaders({ 'If-Match': etag }); return this.http .delete<void>(`/api/committees/${id}`, { headers }) .pipe(take(1)); }apps/lfx-pcc/src/app/modules/project/committees/committee-dashboard/committee-dashboard.component.ts
• Update the call at line 191 to pass the ETag:this.committeeService.deleteCommittee(committee.uid, committee.etag).subscribe({ … });apps/lfx-pcc/src/app/modules/project/committees/committee-view/committee-view.component.ts
• Likewise, adjust the call around line 116 to includecommittee.etag.Once you’ve added the
If-Matchheader and updated both the service signature and its usages, the DELETE flow will satisfy the backend’s ETag validation.docs/architecture/backend/README.md (1)
60-62: Fix logging library inconsistency (Winston → Pino).Docs mention Winston, but codebase and earlier sections standardize on Pino.
Apply this diff:
-Explore Winston logging configuration, structured logging, and monitoring strategies. +Explore Pino logging configuration, structured logging, and monitoring strategies.
🧹 Nitpick comments (36)
apps/lfx-pcc/src/app/modules/project/dashboard/project-dashboard/project.component.ts (1)
241-247: Prefer display_name (fallback to name) if available in the Committee interfaceIf Committee.display_name exists (per shared interfaces update), consider using it for table titles to reflect the user-facing label.
Apply this diff to improve the label:
- return committees.map((committee) => ({ - id: committee.uid, - title: committee.name, - url: `/project/${project.slug}/committees/${committee.uid}`, + return committees.map((committee) => ({ + id: committee.uid, + title: committee.display_name || committee.name, + url: `/project/${project.slug}/committees/${committee.uid}`, status: 'Active', date: committee.updated_at || committee.created_at, }));packages/shared/src/interfaces/meeting.interface.ts (1)
70-112: Clarify committees identity in request DTOs (string[] should be UIDs)If committees in CreateMeetingRequest/UpdateMeetingRequest are expected to be committee UIDs post-migration, consider documenting it in a JSDoc comment to prevent accidental use of legacy numeric IDs.
Apply this doc-comment addition to both interfaces:
export interface CreateMeetingRequest { project_uid: string; @@ - committees?: string[]; + /** + * Array of committee UIDs associated with this meeting. + */ + committees?: string[]; } export interface UpdateMeetingRequest { project_uid: string; @@ - committees?: string[]; + /** + * Array of committee UIDs associated with this meeting. + */ + committees?: string[]; }apps/lfx-pcc/src/app/shared/components/table/table.component.ts (1)
13-15: Good call adding ngSkipHydration for PrimeNG tableSkipping hydration here helps avoid SSR/CSR mismatch issues common with dynamic DOM manipulation in PrimeNG’s table.
Consider adding a short comment explaining why hydration is skipped, to prevent future “cleanup” PRs from removing it unintentionally.
packages/shared/src/interfaces/committee.interface.ts (3)
26-29: Clarify duplication of settings-derived fields on Committee vs CommitteeSettingsCommittee includes business_email_required and is_audit_enabled with a note “populated from the settings endpoint,” while CommitteeSettings also defines these as canonical. Keeping them on Committee is fine if they’re denormalized for convenience, but it can drift. Consider documenting the intended source of truth and update semantics to avoid divergence.
Would you like me to propose a small JSDoc block to clarify that these fields on Committee are read-only/denormalized from CommitteeSettings?
48-63: “joinable” in create payload but not present on CommitteeCreateData includes joinable?: boolean, but Committee no longer has joinable. If this field is removed platform-wide, keep CreateData consistent to avoid confusion and dead params across FE/BE.
Proposed cleanup:
export interface CommitteeCreateData { name: string; category: string; description?: string; parent_uid?: string; business_email_required?: boolean; enable_voting?: boolean; is_audit_enabled?: boolean; public?: boolean; display_name?: string; sso_group_enabled?: boolean; sso_group_name?: string; website?: string; project_uid?: string; - joinable?: boolean; }If joinable is still supported at creation (but omitted from the returned entity), consider adding a brief comment indicating it’s a write-only attribute.
78-81: Consider codifying validation error “code” as an enumIf error codes are finite/standardized, an enum (e.g., CommitteeValidationCode) improves discoverability and prevents typos in consumers.
apps/lfx-pcc/src/app/modules/project/committees/components/committee-table/committee-table.component.ts (1)
48-56: Optional: avoid O(N^2) filtering by pre-indexing childrenPrecompute children grouped by parent_uid once to prevent repeated scans per parent. Useful if committees list grows large.
Apply within this block:
- const subcommittees = allCommittees.filter((c) => c.parent_uid === parent.uid); - subcommittees.forEach((sub) => { - result.push({ ...sub, level: 1 }); - }); + const subs = childrenByParent[parent.uid] ?? []; + for (const sub of subs) { + result.push({ ...sub, level: 1 }); + }And add this just above parentCommittees.forEach(...):
// Pre-index children by parent for O(N) assembly const childrenByParent = allCommittees.reduce((acc, c) => { if (c.parent_uid) { (acc[c.parent_uid] ||= []).push(c); } return acc; }, {} as Record<string, Committee[]>);apps/lfx-pcc/src/app/shared/services/committee.service.ts (1)
26-28: RenameprojectIdtoprojectUidin service and confirm backend tag forwardingPlease update the service methods in
apps/lfx-pcc/src/app/shared/services/committee.service.tsas follows:--- a/apps/lfx-pcc/src/app/shared/services/committee.service.ts +++ b/apps/lfx-pcc/src/app/shared/services/committee.service.ts @@ -26,7 +26,7 @@ export class CommitteeService { - public getCommitteesByProject(projectId: string, limit?: number, orderBy?: string): Observable<Committee[]> { - let params = new HttpParams().set('tags', `project_uid:${projectId}`); + public getCommitteesByProject(projectUid: string, limit?: number, orderBy?: string): Observable<Committee[]> { + let params = new HttpParams().set('tags', `project_uid:${projectUid}`); - public getRecentCommitteesByProject(projectId: string, limit: number = 3): Observable<Committee[]> { - return this.getCommitteesByProject(projectId, limit, 'updated_at.desc'); + public getRecentCommitteesByProject(projectUid: string, limit: number = 3): Observable<Committee[]> { + return this.getCommitteesByProject(projectUid, limit, 'updated_at.desc');• Update all internal callers of these methods to pass
projectUidinstead ofprojectId.
• Ensure any unit/integration tests reflect the renamed parameter.Verification required:
• Confirm the backend’s committees endpoint inapps/lfx-pcc/src/server/…accepts atagsquery (formatted asproject_uid:<uid>) and correctly forwards it to the Query Service.
• If no existing handler inspectsreq.query.tags, add or update the controller to passtagsthrough.apps/lfx-pcc/src/server/services/supabase.service.ts (2)
147-175: Align getCommitteeById with UID migration (name/param/query).The project moved to UID as canonical. This method still queries
id=eq.${id}and then computes counts by passing the sameid. If route params now carry a UID, this works but is confusing. Consider aligning naming and query to reflect UID to prevent future breakage.Apply this diff to better reflect the UID-based API (adjust only if the
committeestable has auidcolumn):- public async getCommitteeById(id: string) { + public async getCommitteeById(id: string) { const params = { - id: `eq.${id}`, + uid: `eq.${id}`, limit: '1', }; const queryString = new URLSearchParams(params).toString(); const url = `${this.baseUrl}/committees?${queryString}`; @@ - if (committee) { - // Calculate total members and voting reps - const [totalMembers, totalVotingReps] = await Promise.all([this.getCommitteeMemberCountByCommitteeId(id), this.getCommitteeVotingRepsCount(id)]); + if (committee) { + // Calculate total members and voting reps (treat incoming id as UID) + const [totalMembers, totalVotingReps] = await Promise.all([ + this.getCommitteeMemberCountByCommitteeId(id), + this.getCommitteeVotingRepsCount(id), + ]);If the DB doesn’t expose
uidoncommittees, skip changing the query key and instead rename local variables for clarity (e.g.,committeeUid).
132-134: Confirm database schema for committee_members.committee_id before mergingI didn’t find any
committee_uidcolumn in the codebase—every Supabase REST call (and themember.interface.ts) still filters oncommittee_id. Since we’re now passingcommittee.uid(a string) into:
- apps/lfx-pcc/src/server/services/supabase.service.ts:
• getCommitteeMemberCountByCommitteeId (line 380):committee_id: \eq.${committeeId}`• getCommitteeVotingRepsCount (line 410):committee_id: `eq.${committeeId}``—but the table column remains named
committee_id, it’s almost certain that it still holds the legacy internal numeric ID unless a migration repointed it to store UIDs. If that migration hasn’t run (or if there isn’t acommittee_uidcolumn), these count queries will silently return 0.Please verify that in your DB:
committee_members.committee_idnow stores the committee UID
OR- there is a
committee_uidcolumn you can filter onOtherwise you’ll need to either update the schema/view or revert to using the internal ID until the UID migration is fully in place.
apps/lfx-pcc/src/app/modules/project/committees/components/committee-members/committee-members.component.ts (1)
210-235: Use UID for member delete; consider using finalize() to avoid duplicated state toggles.The switch to
committee.uidis consistent. Minor cleanup: use RxJSfinalizeto ensureisDeletingis reset in both success and error paths.Apply:
- this.committeeService.deleteCommitteeMember(committee.uid, member.id).subscribe({ - next: () => { - this.isDeleting.set(false); + this.committeeService + .deleteCommitteeMember(committee.uid, member.id) + .pipe(finalize(() => this.isDeleting.set(false))) + .subscribe({ + next: () => { @@ - }, - error: (error) => { - this.isDeleting.set(false); + }, + error: (error) => { console.error('Failed to delete member:', error); @@ - }, - }); + }, + });Also confirm the backend expects a committee UID in this endpoint signature (not an internal ID).
apps/lfx-pcc/src/app/modules/project/committees/components/committee-form/committee-form.component.html (2)
117-126: Use new Angular control flow consistently and avoid explicit=== truecheckYou’re mixing new
@ifwith*ngIf. For consistency and readability, consider using@ifhere and drop the explicit=== true.- <div *ngIf="form().get('public')?.value === true"> + @if (form().get('public')?.value) { <label for="public-name" class="block text-sm font-medium text-gray-700 mb-1"> Public Name </label> <lfx-input-text size="small" [form]="form()" control="display_name" id="public-name" placeholder="Enter public display name" styleClass="w-full"></lfx-input-text> - </div> + }
110-115: Add data-testid attributes to key inputs/toggles for E2E stabilityWhile not a new component, adding stable test selectors on changed/critical controls improves E2E resilience and aligns with our testing guidelines.
<lfx-toggle size="small" [form]="form()" control="public" label="Make Committee Public" - id="public-toggle" + id="public-toggle" + data-testid="committees-form-public-toggle" tooltip="When enabled, the committee will be visible to the public on LFX tools like the public meeting calendar" tooltipPosition="right"></lfx-toggle> <lfx-input-text size="small" [form]="form()" control="display_name" - id="public-name" + id="public-name" + data-testid="committees-form-public-name" placeholder="Enter public display name" styleClass="w-full"></lfx-input-text> <lfx-input-text size="small" [form]="form()" control="website" - id="committee-website" + id="committee-website" + data-testid="committees-form-website" type="url" placeholder="https://example.com" styleClass="w-full"></lfx-input-text> <p class="mt-1 text-sm text-red-600" *ngIf="form().get('website')?.errors?.['pattern'] && form().get('website')?.touched">Please enter a valid URL</p>Also applies to: 120-126, 154-165
apps/lfx-pcc/src/app/modules/project/committees/committee-dashboard/committee-dashboard.component.ts (1)
191-203: Simplify isDeleting toggling with finalizeMinor readability improvement: use finalize to centralize the flag reset.
- this.committeeService.deleteCommittee(committee.uid).subscribe({ - next: () => { - this.isDeleting.set(false); - // Refresh the committees list by reloading - this.refreshCommittees(); - }, - error: (error) => { - this.isDeleting.set(false); - console.error('Failed to delete committee:', error); - // TODO: Show error toast/notification - }, - }); + this.committeeService + .deleteCommittee(committee.uid) + .pipe(finalize(() => this.isDeleting.set(false))) + .subscribe({ + next: () => { + // Refresh the committees list by reloading + this.refreshCommittees(); + }, + error: (error) => { + console.error('Failed to delete committee:', error); + // TODO: Show error toast/notification + }, + });Note: add
import { finalize } from 'rxjs/operators';if not already present.apps/lfx-pcc/src/app/modules/project/meetings/components/meeting-committee-modal/meeting-committee-modal.component.ts (1)
124-132: Avoid mutating arrays when comparing sets of IDs
Array.prototype.sort()mutates the array. Clone before sorting to avoid mutating form values or local references.- const selectedIds = this.form.value.committees as string[]; + const selectedIds = this.form.value.committees as string[]; // If no changes, just close - const currentIds = this.meeting.meeting_committees?.map((c) => c.uid) || []; - if (JSON.stringify(selectedIds.sort()) === JSON.stringify(currentIds.sort())) { + const currentIds = this.meeting.meeting_committees?.map((c) => c.uid) || []; + const sortedSelected = [...(selectedIds || [])].sort(); + const sortedCurrent = [...currentIds].sort(); + if (JSON.stringify(sortedSelected) === JSON.stringify(sortedCurrent)) { this.ref.close(); return; }apps/lfx-pcc/src/app/modules/project/committees/committee-view/committee-view.component.html (2)
131-141: Consider updating label text to match field renameNit: “Public Enabled” could be shortened to “Public” or “Public Visibility” to reflect the new
publicfield name.
77-79: Optionally add a data-testid to Next Meeting componentFor E2E targeting, consider adding a test id to the upcoming meeting component.
- <lfx-upcoming-committee-meeting [committeeId]="committee()!.uid"></lfx-upcoming-committee-meeting> + <lfx-upcoming-committee-meeting + data-testid="committee-view-next-meeting" + [committeeId]="committee()!.uid"></lfx-upcoming-committee-meeting>apps/lfx-pcc/src/server/routes/committees.ts (2)
25-58: Unify member routes with Logger/Responder for consistent responses and logsMember endpoints still hand-roll JSON errors and use
req.log. For consistency with the new Logger/Responder approach, consider migrating these to a smallCommitteeMemberControllerthat uses the same helpers, even if they still back Supabase for now. This will:
- Standardize error schema and codes (ETag/validation/conflict formatting)
- Centralize logging and timing
- Ease later migration to the query microservice
If you prefer to keep routes inline, at least wrap errors via Responder and swap
req.logfor the Logger helper.I can provide a drop-in controller skeleton for committee members using Responder/Logger if helpful.
Also applies to: 60-109, 111-161, 162-215, 216-268
17-23: Path param naming: consider:uidfor clarityOptional: since the canonical identity is
uid, consider renaming:idto:uidin these routes for clarity. The controller would simply readreq.params.uid.packages/shared/src/interfaces/api.ts (1)
56-61: Replace union of string literals with a shared enum for error codesTo align with the guideline “Use TypeScript interfaces instead of union types” and centralize shared enums, introduce an enum for ETag error codes (in packages/shared/src/enums) and reference it here. This improves maintainability and avoids scattering string unions.
Apply this diff in the current file:
-export interface ETagError { - code: 'NOT_FOUND' | 'ETAG_MISSING' | 'NETWORK_ERROR' | 'PRECONDITION_FAILED'; +import { ETagErrorCode } from '../enums/etag-error-code.enum'; + +export interface ETagError { + code: ETagErrorCode; message: string; statusCode: number; headers?: Record<string, string>; }Add this new enum file (outside the selected range):
// packages/shared/src/enums/etag-error-code.enum.ts // Copyright The Linux Foundation and each contributor to LFX. // SPDX-License-Identifier: MIT export enum ETagErrorCode { NOT_FOUND = 'NOT_FOUND', ETAG_MISSING = 'ETAG_MISSING', NETWORK_ERROR = 'NETWORK_ERROR', PRECONDITION_FAILED = 'PRECONDITION_FAILED', }apps/lfx-pcc/src/server/controllers/committee.controller.ts (5)
47-68: Surface ETag on GET by id responses to enable optimistic concurrency on the clientGiven the PR’s ETag support, consider returning the ETag header with GET /committees/:id. Either have the service return an ETagResult or retrieve headers via proxyRequestWithResponse and set res.set('ETag', etag) before res.json.
If the service already returns an ETagResult, the controller could look like:
-const committee = await this.committeeService.getCommitteeById(req, id); +const { data: committee, etag } = await this.committeeService.getCommitteeById(req, id); +if (etag) { + res.set('ETag', etag); +}
73-92: Consider adding Location header on creationFor POST /committees, set a Location header pointing to the new resource (e.g., /committees/:uid) to follow REST best practices.
- res.status(201).json(newCommittee); + res.location(`/committees/${newCommittee.uid}`).status(201).json(newCommittee);
25-41: Pagination metadata opportunity for list endpointIf the downstream Query Service supports pagination, consider returning pagination info (e.g., via Responder.createPagination) along with the result to standardize list responses.
18-20: Remove unused constructor property or use itmicroserviceProxy is stored as a private field but only used to instantiate CommitteeService. You can drop the class property and pass the parameter directly, or keep and reuse it if you plan to.
- public constructor(private microserviceProxy: MicroserviceProxyService) { - this.committeeService = new CommitteeService(microserviceProxy); + public constructor(microserviceProxy: MicroserviceProxyService) { + this.committeeService = new CommitteeService(microserviceProxy); }
56-60: Use uid consistently in logs and metadata keysGiven the repo-wide shift to uid, prefer committee_uid over committee_id in log metadata for consistency.
Example:
- committee_id: id, + committee_uid: id,Also applies to: 82-85, 108-111, 133-135
apps/lfx-pcc/src/server/services/microservice-proxy.service.ts (3)
48-59: Log the actual endpoint URLendpoint in the log currently uses
${service}${path}, which can be misleading. Prefer the fully resolved endpoint URL for clarity.- endpoint: `${service}${path}`, + endpoint,Also applies to: 95-106
32-38: Avoid recreating MICROSERVICE_URLS per callMinor: hoist MICROSERVICE_URLS to a module-level constant or inject config. Reduces per-call overhead and centralizes config.
Also applies to: 79-85
136-204: Error transformation is comprehensive; consider ETag-specific conflictsIf you plan to surface PRECONDITION_FAILED (412) conflicts distinctly, add a 412 case here and map to a more specific code (e.g., PRECONDITION_FAILED) for downstream Responder handling.
apps/lfx-pcc/src/app/modules/project/committees/components/committee-form/committee-form.component.ts (3)
25-28: Verify Angular version: styleUrl vs styleUrlsstyleUrl is only supported in newer Angular versions. If your project uses an older version, switch to styleUrls: ['...'].
- styleUrl: './committee-form.component.scss', + styleUrls: ['./committee-form.component.scss'],
248-249: Loosen website URL pattern to allow localhost/dev and intranet URLsThe current pattern requires a dot, which rejects localhost or internal hosts. Consider a simpler scheme-based check or rely on backend validation.
- website: new FormControl(committee?.website || '', [Validators.pattern(/^https?:\/\/.+\..+/)]), + website: new FormControl(committee?.website || '', [Validators.pattern(/^https?:\/\/.+/)]),
144-159: Optional: deep-clean nested fields and arrayscleanFormData only handles top-level strings. If nested optional strings (now or in future) appear, a recursive cleaner improves consistency.
Example recursive cleaner:
private cleanFormDataDeep<T>(value: T): T { if (typeof value === 'string') return (value.trim() === '' ? null : value) as unknown as T; if (Array.isArray(value)) return value.map((v) => this.cleanFormDataDeep(v)) as unknown as T; if (value && typeof value === 'object') { const out: any = {}; for (const [k, v] of Object.entries(value as any)) out[k] = this.cleanFormDataDeep(v); return out as T; } return value; }apps/lfx-pcc/src/server/services/committee.service.ts (3)
23-27: Prefer injecting ETagService for testability and separation of concerns.Constructing ETagService internally couples this class and makes mocking harder. Inject it (optionally defaulting) to improve testability.
One option (minimal change) is to accept an optional ETagService:
- private etagService: ETagService; - - public constructor(private microserviceProxy: MicroserviceProxyService) { - this.etagService = new ETagService(microserviceProxy); - } + public constructor( + private microserviceProxy: MicroserviceProxyService, + private etagService: ETagService = new ETagService(microserviceProxy) + ) {}
46-64: Align naming with UID-based identifiers.The repo is shifting to uid; method parameters still named committeeId. Consider renaming to committeeUid for clarity and consistency.
Example:
- public async getCommitteeById(req: Request, committeeId: string): Promise<Committee> { + public async getCommitteeById(req: Request, committeeUid: string): Promise<Committee> { ... - parent: `committee:${committeeId}`, + parent: `committee:${committeeUid}`,Apply similarly to updateCommittee and deleteCommittee.
Also applies to: 120-175, 180-194
110-115: Returned settings may be misleading if settings update fails.When settings update throws, the response still includes the requested settings values, which may not match persisted state. Either re-fetch settings before merging or omit these fields when the settings update fails.
Possible adjustment: track success and only merge on success, or fetch
/committees/${id}/settingsafter update to return the authoritative state.Also applies to: 170-175
apps/lfx-pcc/src/server/helpers/responder.ts (2)
99-110: Guard against invalid limits in pagination.limit <= 0 yields Infinity/NaN pages. Add a floor to 1 to avoid bad metadata.
Apply this diff:
- public static createPagination(page: number, limit: number, total: number): PaginationInfo { - const pages = Math.ceil(total / limit); + public static createPagination(page: number, limit: number, total: number): PaginationInfo { + const safeLimit = Math.max(1, limit || 0); + const pages = Math.ceil(total / safeLimit);
115-146: Propagate details and handle 409 conflicts in handler.If transformError maps 409 to code 'CONFLICT', let Responder return 409. Also forward error.details where available for troubleshooting.
Apply this diff:
public static handle(res: Response, error: any, operation = 'operation'): void { @@ if (error.code === 'ETAG_MISSING') { this.internalError(res, 'ETag header missing from upstream service'); return; } + if (error.code === 'CONFLICT') { + this.conflict(res, error.message); + return; + } @@ - this.error(res, message, { - statusCode, - code: statusCode >= 500 ? 'INTERNAL_ERROR' : undefined, - }); + this.error(res, message, { + statusCode, + code: statusCode >= 500 ? 'INTERNAL_ERROR' : undefined, + details: error.details, + });
apps/lfx-pcc/src/app/modules/project/committees/committee-view/committee-view.component.html
Show resolved
Hide resolved
...pcc/src/app/modules/project/committees/components/committee-form/committee-form.component.ts
Show resolved
Hide resolved
...pcc/src/app/modules/project/committees/components/committee-form/committee-form.component.ts
Show resolved
Hide resolved
...pcc/src/app/modules/project/committees/components/committee-form/committee-form.component.ts
Show resolved
Hide resolved
- Prevent Authorization header override by placing customHeaders first - Fix error status code handling to prefer statusCode over status - Update documentation examples to match actual service implementations - Correct ETagService to show instance methods instead of static - Fix MicroserviceProxyService example to use proxyRequest method Addresses PR #44 review feedback References: LFXV2-24 Signed-off-by: Asitha de Silva <[email protected]>
- Created comprehensive error interface types with proper type guards - Updated all error handling to use extractErrorDetails utility - Implemented ValidationApiError interface for validation-specific errors - Added proper type guards for isApiError and isValidationApiError - Fixed all type casting issues in services and helpers - Updated API client, microservice proxy, and ETag services with type safety - Replaced 'as any' with proper type assertions for file type validation - Enhanced error logging and response handling with typed error details - Used 'in' operator checks instead of unsafe type casting All backend error handling now uses proper TypeScript types without any type assertions. References: LFXV2-24 Signed-off-by: Asitha de Silva <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🔭 Outside diff range comments (2)
apps/lfx-pcc/src/server/services/microservice-proxy.service.ts (1)
137-204: Critical: createApiError receives ‘statusCode’ but setter only assigns ‘status’ (status lost → always 500 downstream)createApiError sets error.status when options.status is provided. Passing statusCode leaves status undefined, causing extractErrorDetails to fall back to 500. Fix by passing status and add explicit 412 mapping for ETag conflicts.
Apply this diff:
switch (errorDetails.statusCode) { case 400: @@ - case 409: + case 409: userMessage = 'Conflict. The resource you are trying to create already exists.'; errorCode = 'CONFLICT'; break; + case 412: + userMessage = 'Resource has been modified. Please refresh and try again.'; + errorCode = 'PRECONDITION_FAILED'; + break; @@ - return createApiError({ - message: userMessage, - statusCode: errorDetails.statusCode, - code: errorCode, - service, - path, - originalMessage: errorDetails.message, - originalError: error instanceof Error ? error : undefined, - }); + return createApiError({ + message: userMessage, + status: errorDetails.statusCode, + code: errorCode, + service, + path, + originalMessage: errorDetails.message, + originalError: error instanceof Error ? error : undefined, + });docs/architecture/backend/README.md (1)
60-62: Documentation inconsistency: mentions Winston but project uses PinoEarlier sections (Lines 13-15) correctly document Pino. Update this section to avoid confusion.
Apply this diff:
-Explore Winston logging configuration, structured logging, and monitoring strategies. +Explore Pino logging configuration, structured logging, and monitoring strategies.
♻️ Duplicate comments (5)
apps/lfx-pcc/src/server/services/microservice-proxy.service.ts (2)
46-46: Sanitize and allowlist custom headers before forwarding to the HTTP clientForwarding arbitrary custom headers can introduce security/behavioral issues and unintentionally override protected headers. Filter to an allowlist (e.g., If-Match, If-None-Match, X-Request-Id, X-Correlation-Id) before passing to the client.
Apply this diff to use a sanitized header set here:
- const response = await this.executeRequest<T>(method, endpoint, token, data, mergedParams, customHeaders); + const safeHeaders = this.filterCustomHeaders(customHeaders); + const response = await this.executeRequest<T>(method, endpoint, token, data, mergedParams, safeHeaders);Add this helper method in the class (outside the selected range):
// Inside MicroserviceProxyService private filterCustomHeaders(custom?: Record<string, string>): Record<string, string> | undefined { if (!custom) return undefined; const allowlist = new Set(['if-match', 'if-none-match', 'x-request-id', 'x-correlation-id']); const safe: Record<string, string> = {}; for (const [k, v] of Object.entries(custom)) { const key = k.toLowerCase(); if (allowlist.has(key)) safe[key] = v; } return Object.keys(safe).length ? safe : undefined; }
66-74: Mirror header sanitization in proxyRequestWithResponse for consistencyEnsure both proxy paths apply the same header filtering to avoid inconsistent behavior.
Apply this diff:
- const response = await this.executeRequest<T>(method, endpoint, token, data, mergedParams, customHeaders); + const safeHeaders = this.filterCustomHeaders(customHeaders); + const response = await this.executeRequest<T>(method, endpoint, token, data, mergedParams, safeHeaders);Also applies to: 93-95
apps/lfx-pcc/src/server/services/etag.service.ts (2)
107-108: Use the standard If-Match header for conditional updates (not ETag)Most services enforce preconditions via If-Match. Sending ETag won’t trigger a precondition check.
Apply this diff:
- return await this.microserviceProxy.proxyRequest<T>(req, service, path, 'PUT', {}, data, { [HTTP_HEADERS.ETAG]: etag }); + return await this.microserviceProxy.proxyRequest<T>(req, service, path, 'PUT', {}, data, { ['If-Match']: etag });
124-125: Use the standard If-Match header for conditional deletes (not ETag)Apply this diff:
- await this.microserviceProxy.proxyRequest(req, service, path, 'DELETE', {}, undefined, { [HTTP_HEADERS.ETAG]: etag }); + await this.microserviceProxy.proxyRequest(req, service, path, 'DELETE', {}, undefined, { ['If-Match']: etag });apps/lfx-pcc/src/server/services/api-client.service.ts (1)
43-53: Authorization cannot be overridden by custom headers — goodMerging customHeaders first and setting Authorization afterwards ensures the bearer token remains authoritative while allowing ETag/correlation headers through.
🧹 Nitpick comments (15)
packages/shared/src/constants/committees.ts (1)
92-97: Return a read-only array to discourage accidental mutationThe values are used for validation; returning a read-only view communicates intent and avoids accidental mutations by consumers.
Apply this diff:
-export function getValidCommitteeCategories(): string[] { +export function getValidCommitteeCategories(): ReadonlyArray<string> { return COMMITTEE_CATEGORIES.map((category) => category.value); }packages/shared/src/interfaces/api.ts (4)
21-30: Tighten response typing and document status vs statusCode
- Prefer ApiResponse over any to retain type-safety.
- If both status and statusCode are needed for interop, add a brief JSDoc explaining precedence to reduce confusion.
Apply this diff:
export interface ApiError extends Error { status?: number; statusCode?: number; code?: string; service?: string; path?: string; originalMessage?: string; cause?: { code?: string }; - response?: ApiResponse<any>; + response?: ApiResponse<unknown>; } export interface ApiErrorOptions { message: string; status?: number; statusCode?: number; code?: string; service?: string; path?: string; originalMessage?: string; originalError?: Error; - response?: ApiResponse<any>; + response?: ApiResponse<unknown>; }Also applies to: 32-42
54-66: Derive ETagError.code from shared ERROR_CODES to avoid driftLeverage the shared ERROR_CODES to centralize allowable codes and prevent divergence across the codebase.
Apply this diff:
+type ETagErrorCode = + | typeof ERROR_CODES.NOT_FOUND + | typeof ERROR_CODES.ETAG_MISSING + | typeof ERROR_CODES.NETWORK_ERROR + | typeof ERROR_CODES.PRECONDITION_FAILED; + export interface ETagError { - code: 'NOT_FOUND' | 'ETAG_MISSING' | 'NETWORK_ERROR' | 'PRECONDITION_FAILED'; + code: ETagErrorCode; message: string; statusCode: number; headers?: Record<string, string>; }And add this import at the top of the file:
import { ERROR_CODES } from '../constants/server';
104-112: Broaden type guard to detect HTTP client errors with response.statusSome HTTP client errors (e.g., Axios) may expose only response.status without setting status/statusCode on the error object. Including this increases reliability of the guard.
Apply this diff:
export function isApiError(error: unknown): error is ApiError { if (!(error instanceof Error)) return false; return ( ('status' in error && typeof error.status === 'number') || ('statusCode' in error && typeof error.statusCode === 'number') || - ('code' in error && typeof error.code === 'string') + ('code' in error && typeof error.code === 'string') || + ('response' in error && typeof (error as any).response?.status === 'number') ); }
126-141: Include response.status fallback when extracting status codeImproves accuracy when upstream libraries set only response.status.
Apply this diff:
return { message: error.message, - statusCode: error.statusCode || error.status || 500, + statusCode: error.statusCode || error.status || error.response?.status || 500, code: error.code || 'UNKNOWN_ERROR', service: error.service, path: error.path, };apps/lfx-pcc/src/server/routes/meetings.ts (3)
25-25: LGTM: Passing req.query directly is fine given logging/validationAssuming SupabaseService.getMeetings validates/sanitizes its input, this is acceptable. Consider a typed DTO at the service boundary in a future pass for stronger typing.
477-495: Normalize MIME types before validation to handle params and casingIncoming MIME types can include parameters (e.g., "image/jpeg; charset=binary") or casing variants, which would fail exact includes checks.
Apply this diff:
- if (!ALLOWED_FILE_TYPES.includes(mimeType as (typeof ALLOWED_FILE_TYPES)[number])) { + const normalizedMimeType = String(mimeType).split(';')[0].trim().toLowerCase(); + if (!ALLOWED_FILE_TYPES.includes(normalizedMimeType as (typeof ALLOWED_FILE_TYPES)[number])) {
658-665: Normalize MIME types here as well for consistencyMirror the normalization in the general storage upload endpoint.
Apply this diff:
- if (!ALLOWED_FILE_TYPES.includes(mimeType as (typeof ALLOWED_FILE_TYPES)[number])) { + const normalizedMimeType = String(mimeType).split(';')[0].trim().toLowerCase(); + if (!ALLOWED_FILE_TYPES.includes(normalizedMimeType as (typeof ALLOWED_FILE_TYPES)[number])) {packages/shared/src/constants/server.ts (3)
8-23: Augment sensitive fields and note over-redaction risk for generic "key"
- Consider adding common header/property variants often logged in metadata (e.g., set-cookie, x-api-key, session identifiers, CSRF/XSRF tokens).
- Caution: the generic "key" may over-redact benign fields; acceptable if your sanitizer is context-aware, but worth documenting.
Apply this diff:
'access_token', 'refresh_token', + 'set-cookie', + 'x-api-key', + 'session', + 'session_id', + 'csrf', + 'x-csrf-token', + 'xsrf', + 'x-xsrf-token', ] as const;
28-35: Add If-Match and If-None-Match headers to support ETag flowsThese are commonly used in optimistic concurrency and align with the new ETag service.
Apply this diff:
export const HTTP_HEADERS = { ETAG: 'ETag', + IF_MATCH: 'If-Match', + IF_NONE_MATCH: 'If-None-Match', CONTENT_TYPE: 'Content-Type', AUTHORIZATION: 'Authorization', USER_AGENT: 'User-Agent', ACCEPT: 'Accept', CACHE_CONTROL: 'Cache-Control', } as const;
40-51: Export a centralized ErrorCode type for reuse across interfacesThis enables ApiError.code and ETagError.code to reference a single source of truth.
Apply this diff:
export const ERROR_CODES = { NOT_FOUND: 'NOT_FOUND', PRECONDITION_FAILED: 'PRECONDITION_FAILED', ETAG_MISSING: 'ETAG_MISSING', NETWORK_ERROR: 'NETWORK_ERROR', VALIDATION_ERROR: 'VALIDATION_ERROR', BAD_REQUEST: 'BAD_REQUEST', UNAUTHORIZED: 'UNAUTHORIZED', FORBIDDEN: 'FORBIDDEN', CONFLICT: 'CONFLICT', INTERNAL_ERROR: 'INTERNAL_ERROR', } as const; + +export type ErrorCode = typeof ERROR_CODES[keyof typeof ERROR_CODES];apps/lfx-pcc/src/server/services/etag.service.ts (2)
20-27: Optional: avoid logging full ETag valuesETags can reflect resource versions; logging full values isn’t necessary for diagnostics and can be noisy. Log presence only.
Suggested small change (optional):
- etag_value: etag, + etag_present: Boolean(etag),Also applies to: 97-105, 114-122
32-39: Question: treating falsy data as NOT_FOUND might misclassify valid empty payloadsIf upstream returns 200 with {}, [], or null for valid cases, this throws NOT_FOUND. Consider relying on HTTP status (but proxy already throws on non-2xx) or add a stricter predicate based on expected schema.
Example (optional):
- For resource objects: check for required identifiers (e.g., uid).
- For collections: accept empty arrays.
If helpful, we can scan committee service responses to confirm expected shapes and adjust this predicate accordingly.
apps/lfx-pcc/src/server/services/api-client.service.ts (1)
44-61: Optional: avoid sending Content-Type for GET requestsSetting Content-Type on GETs is unnecessary and can confuse some intermediaries. If desired, only set Content-Type when a body is present.
Example minimal change (optional):
// After building headers, delete Content-Type for GET/DELETE without body if ((method === 'GET' || method === 'DELETE') && requestInit.body == null) { delete (requestInit.headers as Record<string, string>)['Content-Type']; }Also applies to: 128-174
docs/architecture/backend/README.md (1)
146-152: Doc snippet for ETagService looks good; consider noting If-Match usage explicitlyTo align with implementation and best practices, add a short note that updates/deletes must send the ETag via the If-Match header.
Example addition (optional):
Note: For conditional PUT/DELETE operations, pass the ETag using the If-Match header (not ETag).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (14)
apps/lfx-pcc/src/app/modules/project/committees/committee-view/committee-view.component.html(5 hunks)apps/lfx-pcc/src/app/modules/project/meetings/components/meeting-form/meeting-form.component.ts(1 hunks)apps/lfx-pcc/src/server/helpers/logger.ts(1 hunks)apps/lfx-pcc/src/server/helpers/responder.ts(1 hunks)apps/lfx-pcc/src/server/routes/meetings.ts(4 hunks)apps/lfx-pcc/src/server/services/api-client.service.ts(5 hunks)apps/lfx-pcc/src/server/services/committee.service.ts(1 hunks)apps/lfx-pcc/src/server/services/etag.service.ts(1 hunks)apps/lfx-pcc/src/server/services/microservice-proxy.service.ts(6 hunks)docs/architecture/backend/README.md(2 hunks)packages/shared/src/constants/committees.ts(1 hunks)packages/shared/src/constants/index.ts(1 hunks)packages/shared/src/constants/server.ts(1 hunks)packages/shared/src/interfaces/api.ts(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- apps/lfx-pcc/src/app/modules/project/committees/committee-view/committee-view.component.html
- apps/lfx-pcc/src/server/services/committee.service.ts
- apps/lfx-pcc/src/server/helpers/logger.ts
🧰 Additional context used
📓 Path-based instructions (6)
{apps,packages}/**/*.{ts,tsx,js,jsx,css,scss}
📄 CodeRabbit Inference Engine (CLAUDE.md)
License headers are required on all source files
Files:
packages/shared/src/constants/index.tspackages/shared/src/constants/committees.tsapps/lfx-pcc/src/app/modules/project/meetings/components/meeting-form/meeting-form.component.tspackages/shared/src/constants/server.tsapps/lfx-pcc/src/server/routes/meetings.tsapps/lfx-pcc/src/server/services/etag.service.tspackages/shared/src/interfaces/api.tsapps/lfx-pcc/src/server/services/microservice-proxy.service.tsapps/lfx-pcc/src/server/helpers/responder.tsapps/lfx-pcc/src/server/services/api-client.service.ts
{apps,packages}/**/index.ts
📄 CodeRabbit Inference Engine (CLAUDE.md)
Always use direct imports for standalone components - no barrel exports
Files:
packages/shared/src/constants/index.ts
packages/shared/src/{interfaces,constants,enums}/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
All shared types, interfaces, and constants are centralized in @lfx-pcc/shared package
Files:
packages/shared/src/constants/index.tspackages/shared/src/constants/committees.tspackages/shared/src/constants/server.tspackages/shared/src/interfaces/api.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
Use TypeScript interfaces instead of union types for better maintainability
Files:
packages/shared/src/constants/index.tspackages/shared/src/constants/committees.tsapps/lfx-pcc/src/app/modules/project/meetings/components/meeting-form/meeting-form.component.tspackages/shared/src/constants/server.tsapps/lfx-pcc/src/server/routes/meetings.tsapps/lfx-pcc/src/server/services/etag.service.tspackages/shared/src/interfaces/api.tsapps/lfx-pcc/src/server/services/microservice-proxy.service.tsapps/lfx-pcc/src/server/helpers/responder.tsapps/lfx-pcc/src/server/services/api-client.service.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
Do not nest ternary expressions
Files:
packages/shared/src/constants/index.tspackages/shared/src/constants/committees.tsapps/lfx-pcc/src/app/modules/project/meetings/components/meeting-form/meeting-form.component.tspackages/shared/src/constants/server.tsapps/lfx-pcc/src/server/routes/meetings.tsapps/lfx-pcc/src/server/services/etag.service.tspackages/shared/src/interfaces/api.tsapps/lfx-pcc/src/server/services/microservice-proxy.service.tsapps/lfx-pcc/src/server/helpers/responder.tsapps/lfx-pcc/src/server/services/api-client.service.ts
packages/shared/src/interfaces/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
Interfaces go into the shared packages
Files:
packages/shared/src/interfaces/api.ts
🧠 Learnings (1)
📚 Learning: 2025-08-07T16:35:57.360Z
Learnt from: CR
PR: linuxfoundation/lfx-v2-pcc-ui#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-07T16:35:57.360Z
Learning: Applies to packages/shared/src/{interfaces,constants,enums}/**/*.{ts,tsx} : All shared types, interfaces, and constants are centralized in lfx-pcc/shared package
Applied to files:
packages/shared/src/constants/index.tsdocs/architecture/backend/README.md
🧬 Code Graph Analysis (7)
apps/lfx-pcc/src/app/modules/project/meetings/components/meeting-form/meeting-form.component.ts (1)
packages/shared/src/constants/file-upload.ts (1)
ALLOWED_FILE_TYPES(4-17)
apps/lfx-pcc/src/server/routes/meetings.ts (1)
packages/shared/src/constants/file-upload.ts (1)
ALLOWED_FILE_TYPES(4-17)
apps/lfx-pcc/src/server/services/etag.service.ts (3)
apps/lfx-pcc/src/server/services/microservice-proxy.service.ts (1)
MicroserviceProxyService(12-205)packages/shared/src/interfaces/api.ts (3)
ETagResult(54-58)ETagError(60-65)extractErrorDetails(126-156)packages/shared/src/constants/server.ts (1)
HTTP_HEADERS(28-35)
packages/shared/src/interfaces/api.ts (2)
apps/lfx-pcc/src/server/helpers/responder.ts (1)
error(14-34)apps/lfx-pcc/src/server/helpers/logger.ts (1)
error(53-71)
apps/lfx-pcc/src/server/services/microservice-proxy.service.ts (4)
packages/shared/src/interfaces/api.ts (4)
MicroserviceUrls(17-19)ApiResponse(10-15)ApiError(21-30)extractErrorDetails(126-156)packages/shared/src/constants/api.ts (1)
DEFAULT_QUERY_PARAMS(8-10)apps/lfx-pcc/src/server/helpers/responder.ts (1)
error(14-34)apps/lfx-pcc/src/server/utils/api-error.ts (1)
createApiError(6-39)
apps/lfx-pcc/src/server/helpers/responder.ts (2)
packages/shared/src/interfaces/api.ts (5)
ValidationError(77-81)ApiErrorResponse(70-75)PaginationInfo(83-90)isValidationApiError(117-121)extractErrorDetails(126-156)apps/lfx-pcc/src/server/helpers/logger.ts (1)
error(53-71)
apps/lfx-pcc/src/server/services/api-client.service.ts (2)
packages/shared/src/interfaces/api.ts (2)
ApiResponse(10-15)extractErrorDetails(126-156)apps/lfx-pcc/src/server/helpers/logger.ts (1)
error(53-71)
🪛 LanguageTool
docs/architecture/backend/README.md
[grammar] ~5-~5: There might be a mistake here.
Context: ...a modern Controller-Service pattern with Express.js server handling SSR, authent...
(QB_NEW_EN)
[grammar] ~7-~7: There might be a mistake here.
Context: ...services. ## 🏗 Architecture Components ### Server Stack - Express.js server wit...
(QB_NEW_EN)
[grammar] ~11-~11: There might be a mistake here.
Context: ...s.js** server with Angular Universal SSR - Auth0 authentication integration with ...
(QB_NEW_EN)
[grammar] ~12-~12: There might be a mistake here.
Context: ... integration with express-openid-connect - Pino structured JSON logging with sens...
(QB_NEW_EN)
[grammar] ~13-~13: There might be a mistake here.
Context: ...ON logging with sensitive data redaction - PM2 process management for production ...
(QB_NEW_EN)
[grammar] ~14-~14: There might be a mistake here.
Context: ...ess management for production deployment - Controller-Service Pattern for clean a...
(QB_NEW_EN)
[grammar] ~31-~31: There might be a mistake here.
Context: ... request logging with timing and context - Responder Helper: Consistent error res...
(QB_NEW_EN)
[grammar] ~32-~32: There might be a mistake here.
Context: ...**: Consistent error response formatting - ETag Service: Concurrency control for ...
(QB_NEW_EN)
[grammar] ~39-~39: There might be a mistake here.
Context: ...**: HTTP request handling and validation - CommitteeService: Business logic and m...
(QB_NEW_EN)
[grammar] ~40-~40: There might be a mistake here.
Context: ...iness logic and microservice integration - CRUD Operations: Create, Read, Update,...
(QB_NEW_EN)
[grammar] ~41-~41: There might be a mistake here.
Context: ...te, Delete with ETag concurrency control - Query Service Integration: Integration...
(QB_NEW_EN)
[grammar] ~46-~46: There might be a mistake here.
Context: ...onnect with session-based authentication - Token Management: Automatic token refr...
(QB_NEW_EN)
[grammar] ~47-~47: There might be a mistake here.
Context: ...: Automatic token refresh and validation - User Context: Request-scoped user info...
(QB_NEW_EN)
[grammar] ~72-~72: There might be a mistake here.
Context: ...between HTTP handling and business logic - Helper Classes: Reusable utilities for...
(QB_NEW_EN)
[grammar] ~73-~73: There might be a mistake here.
Context: ...response formatting, and data validation - Microservice Integration: Seamless int...
(QB_NEW_EN)
[grammar] ~74-~74: There might be a mistake here.
Context: ...FX Query Service and other microservices - ETag Concurrency Control: Safe concurr...
(QB_NEW_EN)
[grammar] ~79-~79: There might be a mistake here.
Context: ...press.js for optimal SEO and performance - Authentication: Auth0 integration with...
(QB_NEW_EN)
[grammar] ~80-~80: There might be a mistake here.
Context: ...th OpenID Connect and session management - Structured Logging: Pino with request ...
(QB_NEW_EN)
[grammar] ~81-~81: There might be a mistake here.
Context: ...on, timing, and sensitive data redaction - Process Management: PM2 for production...
(QB_NEW_EN)
[grammar] ~82-~82: There might be a mistake here.
Context: ...uction deployment with health monitoring - Health Monitoring: Built-in health che...
(QB_NEW_EN)
[grammar] ~87-~87: There might be a mistake here.
Context: ...d interfaces across frontend and backend - Validation: Comprehensive input valida...
(QB_NEW_EN)
[grammar] ~88-~88: There might be a mistake here.
Context: ...validation with detailed error responses - Error Handling: Consistent error forma...
(QB_NEW_EN)
[grammar] ~89-~89: There might be a mistake here.
Context: ...ormatting and microservice compatibility - Testing: E2E testing with Playwright a...
(QB_NEW_EN)
[grammar] ~92-~92: There might be a mistake here.
Context: ...t coverage ## 🔧 Implementation Details ### Helper Classes Architecture #### Logger ...
(QB_NEW_EN)
[grammar] ~137-~137: There might be a mistake here.
Context: ...ror response format across all endpoints - HTTP status code standardization - Valid...
(QB_NEW_EN)
[grammar] ~138-~138: There might be a mistake here.
Context: ...oints - HTTP status code standardization - Validation error aggregation - ETag conf...
(QB_NEW_EN)
[grammar] ~139-~139: There might be a mistake here.
Context: ...rdization - Validation error aggregation - ETag conflict handling - Microservice re...
(QB_NEW_EN)
[grammar] ~140-~140: There might be a mistake here.
Context: ...ror aggregation - ETag conflict handling - Microservice response compatibility ###...
(QB_NEW_EN)
[grammar] ~156-~156: There might be a mistake here.
Context: ...res:** - Optimistic concurrency control - ETag extraction and validation - Conflic...
(QB_NEW_EN)
[grammar] ~157-~157: There might be a mistake here.
Context: ...control - ETag extraction and validation - Conflict detection and resolution - Inte...
(QB_NEW_EN)
[grammar] ~158-~158: There might be a mistake here.
Context: ...tion - Conflict detection and resolution - Integration with microservice ETag heade...
(QB_NEW_EN)
[grammar] ~214-~214: There might be a mistake here.
Context: ...se formatting ## 📁 Directory Structure text apps/lfx-pcc/src/server/ ├── controllers/ # HTTP request handling layer │ └── committee.controller.ts ├── services/ # Business logic layer │ ├── committee.service.ts │ ├── etag.service.ts │ ├── api-client.service.ts │ └── microservice-proxy.service.ts ├── helpers/ # Utility classes │ ├── logger.ts # Standardized logging │ ├── responder.ts # Response formatting │ └── url-validation.ts # Input validation ├── middleware/ # Express middleware │ ├── auth-token.middleware.ts │ ├── error-handler.middleware.ts │ └── token-refresh.middleware.ts ├── routes/ # Route definitions │ ├── committees.ts │ ├── meetings.ts │ ├── permissions.ts │ └── projects.ts └── utils/ # Shared utilities └── api-error.ts ## 🎯 Best Practices ### Development Guidel...
(QB_NEW_EN)
[grammar] ~242-~242: There might be a mistake here.
Context: ...─ api-error.ts ``` ## 🎯 Best Practices ### Development Guidelines 1. **Controller R...
(QB_NEW_EN)
[grammar] ~246-~246: There might be a mistake here.
Context: ...nes 1. Controller Responsibilities: - HTTP request/response handling - Inpu...
(QB_NEW_EN)
[grammar] ~247-~247: There might be a mistake here.
Context: ...s**: - HTTP request/response handling - Input validation and sanitization - R...
(QB_NEW_EN)
[grammar] ~248-~248: There might be a mistake here.
Context: ...g - Input validation and sanitization - Request logging and timing - Error re...
(QB_NEW_EN)
[grammar] ~249-~249: There might be a mistake here.
Context: ...tization - Request logging and timing - Error response formatting 2. **Service ...
(QB_NEW_EN)
[grammar] ~252-~252: There might be a mistake here.
Context: ...atting 2. Service Responsibilities: - Business logic implementation - Micro...
(QB_NEW_EN)
[grammar] ~253-~253: There might be a mistake here.
Context: ...es**: - Business logic implementation - Microservice integration - Data trans...
(QB_NEW_EN)
[grammar] ~254-~254: There might be a mistake here.
Context: ...ementation - Microservice integration - Data transformation and validation - ...
(QB_NEW_EN)
[grammar] ~255-~255: There might be a mistake here.
Context: ... - Data transformation and validation - Complex error handling 3. **Helper Usag...
(QB_NEW_EN)
[grammar] ~258-~258: There might be a mistake here.
Context: ...lex error handling 3. Helper Usage: - Use Logger for all operations with timin...
(QB_NEW_EN)
[grammar] ~264-~264: There might be a mistake here.
Context: ...currency control 4. Error Handling: - Always log errors with context - Use ...
(QB_NEW_EN)
[grammar] ~272-~272: There might be a mistake here.
Context: ...peScript**: Strict type checking enabled - Interfaces: All interfaces in shared p...
(QB_NEW_EN)
[grammar] ~273-~273: There might be a mistake here.
Context: ...aces**: All interfaces in shared package - Validation: Comprehensive input valida...
(QB_NEW_EN)
[grammar] ~274-~274: There might be a mistake here.
Context: ...dation**: Comprehensive input validation - Testing: Unit tests for services, E2E ...
(QB_NEW_EN)
[grammar] ~275-~275: There might be a mistake here.
Context: ... tests for services, E2E for controllers - Documentation: JSDoc comments for publ...
(QB_NEW_EN)
[grammar] ~276-~276: There might be a mistake here.
Context: ...ion**: JSDoc comments for public methods - Linting: ESLint with Prettier formatti...
(QB_NEW_EN)
[grammar] ~279-~279: There might be a mistake here.
Context: ...h Prettier formatting ## 🔗 Quick Links - [Server Configuration](../../CLAUDE.md#bac...
(QB_NEW_EN)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Code Quality Checks
🔇 Additional comments (16)
packages/shared/src/constants/index.ts (1)
9-9: LGTM: Central barrel re-exports server constantsThis keeps consumers aligned with the shared package strategy and avoids deep import paths.
apps/lfx-pcc/src/server/routes/meetings.ts (1)
5-5: LGTM: Explicit import of express typesClear separation of shared constants and framework types; no behavioral change.
apps/lfx-pcc/src/server/services/microservice-proxy.service.ts (3)
41-45: Good: default query params take precedence as intendedMerging with defaults last ensures caller cannot override required defaults. Clear comment explains intent.
123-131: LGTM: custom headers are threaded through all HTTP methodsOnce sanitized at the proxy layer, this maintains flexibility for conditional requests (If-Match/If-None-Match) and correlation headers.
49-60: Good: structured error logging with contextual fieldsCaptures path, method, service, and token presence; avoids stack in production. This will help correlate failures in observability tools.
apps/lfx-pcc/src/server/services/etag.service.ts (2)
79-90: Good: clear mapping of unexpected errors to a consistent ETagErrorKeeps ETag flows predictable while rethrowing known ETagError cases.
41-42: Good: case-insensitive retrieval of ETag headerApiClient lower-cases header keys; checking both cases ensures robustness.
apps/lfx-pcc/src/server/services/api-client.service.ts (2)
72-86: Improved retry logging with structured error detailsUsing extractErrorDetails provides consistent fields (message, code, statusCode) in logs for both terminal and non-retryable cases.
Also applies to: 107-121
176-214: Retry policy is reasonable and leverages structured error extractionTimeouts, common network errors, 5xx, and 429 are retried. Looks good.
apps/lfx-pcc/src/server/helpers/responder.ts (3)
115-148: Responder maps PRECONDITION_FAILED and validation errors correctlyThis aligns with the ETag concurrency pathway once the proxy maps 412 to PRECONDITION_FAILED.
39-76: Good: consistent helpers for common error responsesCentralizing response construction improves consistency and reduces duplication across controllers.
Also applies to: 81-94
99-110: Pagination metadata calculation is straightforward and readableSimple, derived fields are correct.
docs/architecture/backend/README.md (1)
193-203: Service example aligns with MicroserviceProxyService APIUsing proxyRequest against '/query/resources' with type filtering reflects the new backend integration.
apps/lfx-pcc/src/app/modules/project/meetings/components/meeting-form/meeting-form.component.ts (3)
308-311: Type-safety improvement for file type check — LGTMUsing the literal-union type from ALLOWED_FILE_TYPES instead of any tightens the includes() contract without changing runtime behavior. Good catch.
186-194: Keep usingmeeting.id—nomeeting.uidexists
TheMeetinginterface (packages/shared/src/interfaces/meeting.interface.ts) definesid: string(nouidfield), and all service/route code—includingcreateAttachmentFromUrl(meetingId, …)—expectsmeeting_id. Changing this tomeeting.uidwould break existing APIs.Likely an incorrect or invalid review comment.
43-45: Component metadata: styleUrl vs styleUrls
Angular 16+ adds a singularstyleUrlfor a single stylesheet, but versions prior to v16 only support the array-basedstyleUrls. Please verify your project’s Angular version and, if it’s below 16, switch to the plural form:• File: apps/lfx-pcc/src/app/modules/project/meetings/components/meeting-form/meeting-form.component.ts
• Lines: 43–45- styleUrl: './meeting-form.component.scss', + styleUrls: ['./meeting-form.component.scss'],
- Added If-Match header constant to HTTP_HEADERS - Updated ETagService to use If-Match for conditional PUT/DELETE operations - Maintained ETag header for reading response headers (per HTTP spec) - Updated documentation to reflect HTTP standard compliance - Improved logging messages to indicate If-Match usage This follows HTTP RFC 7232 where If-Match is the correct header for conditional requests, while ETag is for responses. References: LFXV2-24 Signed-off-by: Asitha de Silva <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🔭 Outside diff range comments (1)
docs/architecture/backend/README.md (1)
60-63: Fix logging docs: Winston mentioned but project uses PinoEarlier sections state Pino is the logging stack, but this section says “Winston.” Update to avoid confusion.
Apply this diff:
-Explore Winston logging configuration, structured logging, and monitoring strategies. +Explore Pino logging configuration, structured logging, and monitoring strategies.
♻️ Duplicate comments (1)
docs/architecture/backend/README.md (1)
196-202: Service example now correctly uses proxyRequest and endpointThis aligns with MicroserviceProxyService API and the query endpoint used in code. Good correction.
🧹 Nitpick comments (3)
docs/architecture/backend/README.md (3)
146-151: Tighten ETagService method signatures to the actual service name typeThe examples elsewhere (and code) use 'LFX_V2_SERVICE'. Using string here weakens type guidance and can mislead readers. Narrow the parameter type.
Apply this diff:
export class ETagService { constructor(private microserviceProxy: MicroserviceProxyService); - async fetchWithETag<T>(req: Request, service: string, path: string, operation: string): Promise<ETagResult<T>>; - async updateWithETag<T>(req: Request, service: string, path: string, etag: string, data: any, operation: string): Promise<T>; - async deleteWithETag(req: Request, service: string, path: string, etag: string, operation: string): Promise<void>; + async fetchWithETag<T>(req: Request, service: 'LFX_V2_SERVICE', path: string, operation: string): Promise<ETagResult<T>>; + async updateWithETag<T>(req: Request, service: 'LFX_V2_SERVICE', path: string, etag: string, data: any, operation: string): Promise<T>; + async deleteWithETag(req: Request, service: 'LFX_V2_SERVICE', path: string, etag: string, operation: string): Promise<void>; }
168-188: Clarify controller dependency injection in exampleThe snippet uses this.committeeService but doesn’t show how it’s provided. Add a minimal constructor to reduce ambiguity for readers.
Apply this diff:
export class CommitteeController { + constructor(private readonly committeeService: CommitteeService) {} public async getCommittees(req: Request, res: Response): Promise<void> { const startTime = Logger.start(req, 'get_committees', { query_params: Logger.sanitize(req.query as Record<string, any>), });
193-204: Clarify service dependency injection in exampleThe snippet calls this.microserviceProxy but doesn’t show how it’s injected. Add a constructor so the example compiles conceptually.
Apply this diff:
export class CommitteeService { + constructor(private readonly microserviceProxy: MicroserviceProxyService) {} public async getCommittees(req: Request, queryParams: any): Promise<Committee[]> { // Business logic and microservice integration const { resources } = await this.microserviceProxy.proxyRequest<QueryServiceResponse<Committee>>(req, 'LFX_V2_SERVICE', '/query/resources', 'GET', { ...queryParams, type: 'committee', });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
apps/lfx-pcc/src/server/services/etag.service.ts(1 hunks)docs/architecture/backend/README.md(2 hunks)packages/shared/src/constants/server.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/shared/src/constants/server.ts
- apps/lfx-pcc/src/server/services/etag.service.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-07T16:35:57.360Z
Learnt from: CR
PR: linuxfoundation/lfx-v2-pcc-ui#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-07T16:35:57.360Z
Learning: Applies to packages/shared/src/{interfaces,constants,enums}/**/*.{ts,tsx} : All shared types, interfaces, and constants are centralized in lfx-pcc/shared package
Applied to files:
docs/architecture/backend/README.md
🪛 LanguageTool
docs/architecture/backend/README.md
[grammar] ~5-~5: There might be a mistake here.
Context: ...a modern Controller-Service pattern with Express.js server handling SSR, authent...
(QB_NEW_EN)
[grammar] ~7-~7: There might be a mistake here.
Context: ...services. ## 🏗 Architecture Components ### Server Stack - Express.js server wit...
(QB_NEW_EN)
[grammar] ~11-~11: There might be a mistake here.
Context: ...s.js** server with Angular Universal SSR - Auth0 authentication integration with ...
(QB_NEW_EN)
[grammar] ~12-~12: There might be a mistake here.
Context: ... integration with express-openid-connect - Pino structured JSON logging with sens...
(QB_NEW_EN)
[grammar] ~13-~13: There might be a mistake here.
Context: ...ON logging with sensitive data redaction - PM2 process management for production ...
(QB_NEW_EN)
[grammar] ~14-~14: There might be a mistake here.
Context: ...ess management for production deployment - Controller-Service Pattern for clean a...
(QB_NEW_EN)
[grammar] ~31-~31: There might be a mistake here.
Context: ... request logging with timing and context - Responder Helper: Consistent error res...
(QB_NEW_EN)
[grammar] ~32-~32: There might be a mistake here.
Context: ...**: Consistent error response formatting - ETag Service: Concurrency control for ...
(QB_NEW_EN)
[grammar] ~39-~39: There might be a mistake here.
Context: ...**: HTTP request handling and validation - CommitteeService: Business logic and m...
(QB_NEW_EN)
[grammar] ~40-~40: There might be a mistake here.
Context: ...iness logic and microservice integration - CRUD Operations: Create, Read, Update,...
(QB_NEW_EN)
[grammar] ~41-~41: There might be a mistake here.
Context: ...te, Delete with ETag concurrency control - Query Service Integration: Integration...
(QB_NEW_EN)
[grammar] ~46-~46: There might be a mistake here.
Context: ...onnect with session-based authentication - Token Management: Automatic token refr...
(QB_NEW_EN)
[grammar] ~47-~47: There might be a mistake here.
Context: ...: Automatic token refresh and validation - User Context: Request-scoped user info...
(QB_NEW_EN)
[grammar] ~72-~72: There might be a mistake here.
Context: ...between HTTP handling and business logic - Helper Classes: Reusable utilities for...
(QB_NEW_EN)
[grammar] ~73-~73: There might be a mistake here.
Context: ...response formatting, and data validation - Microservice Integration: Seamless int...
(QB_NEW_EN)
[grammar] ~74-~74: There might be a mistake here.
Context: ...FX Query Service and other microservices - ETag Concurrency Control: Safe concurr...
(QB_NEW_EN)
[grammar] ~79-~79: There might be a mistake here.
Context: ...press.js for optimal SEO and performance - Authentication: Auth0 integration with...
(QB_NEW_EN)
[grammar] ~80-~80: There might be a mistake here.
Context: ...th OpenID Connect and session management - Structured Logging: Pino with request ...
(QB_NEW_EN)
[grammar] ~81-~81: There might be a mistake here.
Context: ...on, timing, and sensitive data redaction - Process Management: PM2 for production...
(QB_NEW_EN)
[grammar] ~82-~82: There might be a mistake here.
Context: ...uction deployment with health monitoring - Health Monitoring: Built-in health che...
(QB_NEW_EN)
[grammar] ~87-~87: There might be a mistake here.
Context: ...d interfaces across frontend and backend - Validation: Comprehensive input valida...
(QB_NEW_EN)
[grammar] ~88-~88: There might be a mistake here.
Context: ...validation with detailed error responses - Error Handling: Consistent error forma...
(QB_NEW_EN)
[grammar] ~89-~89: There might be a mistake here.
Context: ...ormatting and microservice compatibility - Testing: E2E testing with Playwright a...
(QB_NEW_EN)
[grammar] ~92-~92: There might be a mistake here.
Context: ...t coverage ## 🔧 Implementation Details ### Helper Classes Architecture #### Logger ...
(QB_NEW_EN)
[grammar] ~137-~137: There might be a mistake here.
Context: ...ror response format across all endpoints - HTTP status code standardization - Valid...
(QB_NEW_EN)
[grammar] ~138-~138: There might be a mistake here.
Context: ...oints - HTTP status code standardization - Validation error aggregation - ETag conf...
(QB_NEW_EN)
[grammar] ~139-~139: There might be a mistake here.
Context: ...rdization - Validation error aggregation - ETag conflict handling - Microservice re...
(QB_NEW_EN)
[grammar] ~140-~140: There might be a mistake here.
Context: ...ror aggregation - ETag conflict handling - Microservice response compatibility ###...
(QB_NEW_EN)
[grammar] ~156-~156: There might be a mistake here.
Context: ...ency control using HTTP If-Match headers - ETag extraction and validation from resp...
(QB_NEW_EN)
[grammar] ~157-~157: There might be a mistake here.
Context: ...ion and validation from response headers - Conflict detection and resolution (412 P...
(QB_NEW_EN)
[grammar] ~158-~158: There might be a mistake here.
Context: ...and resolution (412 Precondition Failed) - Integration with microservice ETag heade...
(QB_NEW_EN)
[grammar] ~214-~214: There might be a mistake here.
Context: ...se formatting ## 📁 Directory Structure text apps/lfx-pcc/src/server/ ├── controllers/ # HTTP request handling layer │ └── committee.controller.ts ├── services/ # Business logic layer │ ├── committee.service.ts │ ├── etag.service.ts │ ├── api-client.service.ts │ └── microservice-proxy.service.ts ├── helpers/ # Utility classes │ ├── logger.ts # Standardized logging │ ├── responder.ts # Response formatting │ └── url-validation.ts # Input validation ├── middleware/ # Express middleware │ ├── auth-token.middleware.ts │ ├── error-handler.middleware.ts │ └── token-refresh.middleware.ts ├── routes/ # Route definitions │ ├── committees.ts │ ├── meetings.ts │ ├── permissions.ts │ └── projects.ts └── utils/ # Shared utilities └── api-error.ts ## 🎯 Best Practices ### Development Guidel...
(QB_NEW_EN)
[grammar] ~242-~242: There might be a mistake here.
Context: ...─ api-error.ts ``` ## 🎯 Best Practices ### Development Guidelines 1. **Controller R...
(QB_NEW_EN)
[grammar] ~246-~246: There might be a mistake here.
Context: ...nes 1. Controller Responsibilities: - HTTP request/response handling - Inpu...
(QB_NEW_EN)
[grammar] ~247-~247: There might be a mistake here.
Context: ...s**: - HTTP request/response handling - Input validation and sanitization - R...
(QB_NEW_EN)
[grammar] ~248-~248: There might be a mistake here.
Context: ...g - Input validation and sanitization - Request logging and timing - Error re...
(QB_NEW_EN)
[grammar] ~249-~249: There might be a mistake here.
Context: ...tization - Request logging and timing - Error response formatting 2. **Service ...
(QB_NEW_EN)
[grammar] ~252-~252: There might be a mistake here.
Context: ...atting 2. Service Responsibilities: - Business logic implementation - Micro...
(QB_NEW_EN)
[grammar] ~253-~253: There might be a mistake here.
Context: ...es**: - Business logic implementation - Microservice integration - Data trans...
(QB_NEW_EN)
[grammar] ~254-~254: There might be a mistake here.
Context: ...ementation - Microservice integration - Data transformation and validation - ...
(QB_NEW_EN)
[grammar] ~255-~255: There might be a mistake here.
Context: ... - Data transformation and validation - Complex error handling 3. **Helper Usag...
(QB_NEW_EN)
[grammar] ~258-~258: There might be a mistake here.
Context: ...lex error handling 3. Helper Usage: - Use Logger for all operations with timin...
(QB_NEW_EN)
[grammar] ~264-~264: There might be a mistake here.
Context: ...currency control 4. Error Handling: - Always log errors with context - Use ...
(QB_NEW_EN)
[grammar] ~272-~272: There might be a mistake here.
Context: ...peScript**: Strict type checking enabled - Interfaces: All interfaces in shared p...
(QB_NEW_EN)
[grammar] ~273-~273: There might be a mistake here.
Context: ...aces**: All interfaces in shared package - Validation: Comprehensive input valida...
(QB_NEW_EN)
[grammar] ~274-~274: There might be a mistake here.
Context: ...dation**: Comprehensive input validation - Testing: Unit tests for services, E2E ...
(QB_NEW_EN)
[grammar] ~275-~275: There might be a mistake here.
Context: ... tests for services, E2E for controllers - Documentation: JSDoc comments for publ...
(QB_NEW_EN)
[grammar] ~276-~276: There might be a mistake here.
Context: ...ion**: JSDoc comments for public methods - Linting: ESLint with Prettier formatti...
(QB_NEW_EN)
[grammar] ~279-~279: There might be a mistake here.
Context: ...h Prettier formatting ## 🔗 Quick Links - [Server Configuration](../../CLAUDE.md#bac...
(QB_NEW_EN)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Code Quality Checks
🔇 Additional comments (1)
docs/architecture/backend/README.md (1)
217-240: All referenced files and docs verified
I’ve confirmed every path in docs/architecture/backend/README.md (lines 217–240 and 281–286) exists—no broken links detected.
Summary
Changes Made
Backend Architecture Refactoring
Frontend Updates
Shared Package Updates
Documentation
Related JIRA Tickets
Main Ticket
Subtasks Completed
Test Plan
🤖 Generated with Claude Code