| Field | Value |
|---|---|
| File | C:\Documents\Notes\AI Answer\PR-Review-Gitea-AK.Web.Gen2-20-20260224.md |
| PR | https://git.andalsoftware.com/Andal.Kharisma/AK.Web.Gen2/pulls/20 |
| Decision | CONDITIONAL APPROVAL |
| Files | 37 changed |
| Risk | MEDIUM-HIGH |
Use this path for next step: C:\Documents\Notes\AI Answer\PR-Review-Gitea-AK.Web.Gen2-20-20260224.md
This PR implements critical authentication fixes for the PMS micro-frontend, introducing a dynamic AuthLink Apollo middleware to resolve stale JWT token issues. It also adds identity bootstrapping for fresh logins and implements a Strategy pattern for KPI data loading. While the core changes are architecturally sound and address real security concerns, there are test hygiene issues and commented-out code that need resolution before merge.
Overall Assessment: The AuthLink implementation is a critical security fix that addresses a fundamental flaw where static context was evaluated once at startup before token exchange completed. The Strategy pattern implementation for KPI data loading demonstrates good separation of concerns.
| Micro-Frontend | Files Changed | Impact Level |
|---|---|---|
| PMS (Performance Management System) | 35 files | HIGH |
| shared-lib | Indirect (via auth patterns) | LOW |
- Framework: Angular v20 with standalone components
- State Management: RxJS BehaviorSubjects
- GraphQL: Apollo Client with custom Link chain
- UI Components: DevExtreme Angular
- Testing: Karma/Jasmine
File: projects/pms/src/app/core/interceptors/auth-link.ts (NEW)
The Problem Being Fixed:
The previous implementation used defaultOptions.context which was evaluated once at config time before the token exchange completed. This resulted in stale/empty Authorization headers on GraphQL requests after fresh login.
The Solution:
// AuthLink reads token dynamically from localStorage on EACH request
export class AuthLink extends ApolloLink {
private static request(operation: Operation, forward?: NextLink) {
const accessToken = AuthLink.getAccessToken(); // Fresh read every time
if (accessToken) {
operation.setContext({
headers: {
...operation.getContext()['headers'],
Authorization: `Bearer ${accessToken}`,
},
});
}
return forward ? forward(operation) : null;
}
}Applied To All Apollo Clients:
- Default client (PMS GraphQL)
- Named clients:
pms,hrms,dpt,orchestrator
Security Considerations:
- Properly guards against SSR (
typeof window === 'undefined') - Handles private browsing mode gracefully (try/catch around localStorage)
- Uses
ACCESS_TOKEN_KEYconstant for consistent key naming
Recommendation: APPROVED - This is a critical security fix that resolves authentication race conditions.
File: projects/pms/src/app/core/services/identity.service.ts
Changes:
- Added
identityReady$BehaviorSubject to signal when identity context is initialized - Implements
bootstrapIdentityForFreshLogin()method - Dashboard component now waits for
identityReady$before loading data
Code Pattern:
// DRR-2026-02-20: Bootstrap identity on fresh login
if (!this.targetIdentityContext$.value) {
this.bootstrapIdentityForFreshLogin();
} else {
this.identityReadySubject$.next(true);
}Impact: Prevents "not authorized" errors on fresh login by ensuring identity context is established before API calls.
Recommendation: APPROVED - Properly handles the identity initialization race condition.
New Files:
projects/pms/src/app/features/setting-kpi/strategies/kpi-data-loading.strategy.tsprojects/pms/src/app/features/setting-kpi/strategies/settings-kpi.strategy.tsprojects/pms/src/app/features/setting-kpi/strategies/review-kpi.strategy.tsprojects/pms/src/app/features/setting-kpi/strategies/monitoring-kpi.strategy.ts
Implementation: Abstract strategy class with three concrete implementations:
| Strategy | Behavior |
|---|---|
SettingsKPIStrategy |
Returns empty array if no job position, else fetches by hierarchy |
ReviewKPIStrategy |
Same as Settings - returns empty array if no job position |
MonitoringKPIStrategy |
Always fetches ALL KPIs (admin/HR only) using new kpiDetailsForMonitoringAll query |
New GraphQL Query:
query KpiDetailsForMonitoringAll {
kpiDetailsForMonitoringAll { ... }
}Recommendation: APPROVED - Clean separation of concerns, properly documented with DRR reference.
File: projects/pms/src/app/core/services/auth-logout-integration.service.spec.ts
afterEach(() => {
// Restore window.location
// window.location = originalWindowLocation; // <-- COMMENTED OUT
});File: projects/pms/src/app/features/competency/components/assessment/self-assessment/self-assessment.component.spec.ts
it('should hide dialog', () => {
component.showConfirmDialog = true;
// component.cancelSubmission(); // <-- COMMENTED OUT
expect(component.showConfirmDialog).toBeFalse();
});Recommendation: Either restore the code or delete the test. Commented-out code should not be committed.
Deleted Files:
projects/pms/src/app/features/kpi-approval/tests/index.tsprojects/pms/src/app/features/kpi-approval/tests/integration-test-utils.tsprojects/pms/src/app/features/kpi-approval/tests/kpi-approval.integration.spec.tsprojects/pms/src/app/features/kpi-approval/tests/test-config.ts
Impact: Integration test coverage removed. Verify this is intentional and that E2E tests (Playwright) provide adequate coverage.
Deleted Spec Files:
kpi-approval.service.spec.tskpi-monitoring.component.spec.tskpi-review-history.component.spec.tskpi-settings.component.spec.tskpi-submission-history.component.spec.tskpi.service.spec.ts
Recommendation: Verify these deletions are intentional. If tests were failing and removed rather than fixed, this reduces test coverage.
File: projects/pms/src/app/features/kpi-approval/services/kpi-approval.service.ts
Fix: Added null check for approverId to avoid GraphQL validation errors:
// Return empty array if no current employee (avoids GraphQL validation error)
if (!approverId) {
this.pendingApprovalsSubject$.next([]);
return new BehaviorSubject<ApprovalStatus[]>([]).asObservable();
}Also added error propagation:
catchError(error => {
const errorMessage = this.extractError(error);
this.errorSubject$.next(errorMessage); // <-- Now propagates to error$
return throwError(() => errorMessage);
})Recommendation: APPROVED - Proper defensive programming.
File: projects/pms/src/app/features/settings/components/workflow-configuration/status-color-mapping.service.spec.ts
Issue: Multiple duplicate lines removed:
// Before (duplicate):
const op = controller.expectOne(UPSERT_GENERAL_SETTINGS);
const op = controller.expectOne(UPSERT_GENERAL_SETTINGS); // Duplicate removed
// Before (duplicate test definition):
it('should return value string on successful save', done => { ... });
it('should return value string on successful save', done => { ... }); // Duplicate removedRecommendation: APPROVED - Code cleanup.
- DRR Comments: Design Rationale Records included (DRR-2026-02-20)
- JSDoc Documentation: Comprehensive comments on AuthLink and strategies
- Error Handling: Proper try/catch around localStorage access
- RxJS Patterns: Proper use of BehaviorSubject, takeUntilDestroyed
- Dependency Injection: Modern
inject()function used instead of constructor injection
- Test Coverage: Significant test file deletions need justification
- Commented Code: Two instances of commented-out test code should be resolved
- GraphQL Context: AuthLink removes need for
authService.getAuthContext()in defaultOptions but it's still used ingetAllKPIsForMonitoring()- verify consistency
| Principle | Status | Notes |
|---|---|---|
| Feature-Based Architecture | PASS | Changes confined to features/ directory |
| Standalone Components | PASS | No NgModule changes detected |
| Strategy Pattern | PASS | Proper abstraction for KPI loading |
| Apollo Link Chain | PASS | AuthLink -> CustomerIdLink -> TargetIdentityLink -> HttpLink |
| Barrel Exports | PASS | strategies/index.ts properly exports all strategies |
| Check | Status | Notes |
|---|---|---|
| Token Storage | PASS | Uses localStorage with proper guards |
| SSR Safety | PASS | Checks typeof window before localStorage access |
| Private Mode | PASS | try/catch handles localStorage unavailability |
| Authorization Header | PASS | Proper Bearer ${token} format |
| Token Refresh | N/A | Not in scope - handled by token exchange initializer |
- AuthLink Overhead: Minimal - localStorage read is synchronous and fast
- Identity Bootstrap: One-time cost on fresh login, prevents subsequent auth errors
- Strategy Pattern: No runtime overhead - DI resolves strategy at component creation
- Monitoring Query: New
kpiDetailsForMonitoringAllfetches all KPIs - ensure pagination or filtering on large datasets
| Metric | Before | After | Change |
|---|---|---|---|
| Spec Files | 37+ | ~28 | -9 files |
| Integration Tests | 1 suite | 0 | -1 suite |
Recommendation: Verify that E2E tests (Playwright) provide adequate coverage for the removed integration tests.
-
Resolve commented-out test code in:
auth-logout-integration.service.spec.ts(line 235)self-assessment.component.spec.ts(line 314)
-
Document test deletions - Add PR description explaining why integration tests and spec files were removed
- Consider adding unit tests for
AuthLinkclass - Verify
getAllKPIsForMonitoring()still needs manualauthService.getAuthContext()or if AuthLink handles it - Monitor error rates for "not authorized" errors post-deployment
| Category | Score | Weight | Weighted |
|---|---|---|---|
| Code Quality | 85/100 | 25% | 21.25 |
| Security | 95/100 | 25% | 23.75 |
| Architecture | 90/100 | 20% | 18.00 |
| Test Coverage | 60/100 | 20% | 12.00 |
| Documentation | 90/100 | 10% | 9.00 |
| Overall | 84.0/100 |
Score Interpretation: 84/100 = Good with minor issues
- Remove or restore commented-out test code
- Add PR description documenting test file deletions
- Verify E2E test coverage compensates for removed integration tests
- Confirm
getAllKPIsForMonitoringauth context handling is intentional - Run full test suite:
npm test(PMS project) - Run lint check:
npm run quality:pms
| Stack | Impact | Notes |
|---|---|---|
| PMS | HIGH | Core auth and identity changes |
| Recruitment | NONE | No files changed |
| Andalkharisma | NONE | No files changed |
| shared-lib | INDIRECT | Auth patterns may need replication |
Breaking Changes: None detected - all changes are internal to PMS.
CONDITIONAL APPROVAL
This PR addresses critical authentication issues with the AuthLink implementation and improves architecture with the Strategy pattern. However, the commented-out test code must be resolved before merge. The test file deletions should be documented in the PR description.
Merge Criteria:
- Remove commented-out code OR restore with explanation
- Document rationale for test file deletions
- Verify CI passes with
npm run quality:pms
Review generated by Claude Code - Multi-Stack PR Orchestrator v5.0 Date: 2026-02-24 Files Analyzed: 37 changed files across PMS micro-frontend