forked from Talenttrust/Talenttrust-Backend
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcorrelationId.ts
More file actions
158 lines (147 loc) · 5.01 KB
/
Copy pathcorrelationId.ts
File metadata and controls
158 lines (147 loc) · 5.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/**
* @module utils/correlationId
* @description Utility functions for accessing and propagating correlation IDs
* across the request lifecycle, event processing, and webhook deliveries.
*
* Correlation IDs enable distributed tracing by providing a unique identifier
* that can be used to correlate all related operations across service boundaries.
*
* @security
* - Correlation IDs are validated before use (alphanumeric + hyphen/underscore, max 128 chars)
* - IDs are never logged or propagated without validation
* - HTTP headers are only set after validation to prevent injection attacks
*/
import { Response } from 'express';
const SAFE_CORRELATION_ID_PATTERN = /^[a-zA-Z0-9\-_]{1,128}$/;
/**
* Returns true when a value is a safe correlation ID for logs and HTTP headers.
*
* Correlation IDs are limited to alphanumeric characters, hyphen, and
* underscore with a maximum length of 128 characters. This rejects CR/LF and
* other control characters so caller-supplied IDs cannot inject headers.
*/
export function isValidCorrelationId(value: unknown): value is string {
return typeof value === 'string' && SAFE_CORRELATION_ID_PATTERN.test(value);
}
/**
* Returns a safe correlation ID or undefined when the caller-supplied value is
* missing or invalid.
*/
export function sanitizeCorrelationId(value: unknown): string | undefined {
return isValidCorrelationId(value) ? value : undefined;
}
/**
* Extract correlation ID from Express response locals.
*
* The correlation ID is set by the requestIdMiddleware during request processing
* and made available in `res.locals.correlationId`.
*
* @param res - Express Response object
* @returns The correlation ID if present, undefined otherwise
*
* @example
* ```typescript
* const correlationId = getCorrelationId(res);
* if (correlationId) {
* // Propagate to downstream services
* await eventAuditService.processEvent(event, contractType, correlationId);
* }
* ```
*/
export function getCorrelationId(res: Response): string | undefined {
return sanitizeCorrelationId(res.locals['correlationId']);
}
/**
* Extract request ID from Express response locals.
*
* The request ID is set by the requestIdMiddleware during request processing
* and made available in `res.locals.requestId`.
*
* @param res - Express Response object
* @returns The request ID (always present)
*
* @example
* ```typescript
* const requestId = getRequestId(res);
* console.log('Request ID:', requestId);
* ```
*/
export function getRequestId(res: Response): string {
const requestId = res.locals['requestId'] as string | undefined;
if (!requestId) {
throw new Error('Request ID not found in response locals. Ensure requestIdMiddleware is registered before this handler.');
}
return requestId;
}
/**
* Extract the request-scoped logger from Express response locals.
*
* The logger is set by the requestIdMiddleware with both requestId and
* correlationId already bound to its context.
*
* @param res - Express Response object
* @returns The request-scoped logger
*
* @example
* ```typescript
* const log = getRequestLogger(res);
* log.info('Processing event', { eventId: event.id });
* // Output: { ..., requestId: '...', correlationId: '...', message: 'Processing event', eventId: '...' }
* ```
*/
export function getRequestLogger(res: Response) {
const log = res.locals['log'];
if (!log) {
throw new Error('Request logger not found in response locals. Ensure requestIdMiddleware is registered before this handler.');
}
return log;
}
/**
* Extract both request ID and correlation ID from Express response locals.
*
* Convenience function that returns both IDs in a single call.
*
* @param res - Express Response object
* @returns Object containing requestId (always present) and correlationId (optional)
*
* @example
* ```typescript
* const { requestId, correlationId } = getRequestContext(res);
* // Use both IDs for tracing
* ```
*/
export function getRequestContext(
res: Response
): { requestId: string; correlationId?: string } {
const requestId = getRequestId(res);
const correlationId = getCorrelationId(res);
return { requestId, correlationId };
}
/**
* Build webhook headers including correlation ID for distributed tracing.
*
* Creates a headers object suitable for propagating to external webhook deliveries.
* Includes the correlation ID if present, enabling end-to-end tracing.
*
* @param correlationId - Optional correlation ID to propagate
* @returns Headers object with correlation ID (if provided)
*
* @example
* ```typescript
* const headers = buildWebhookHeaders(correlationId);
* // Use headers when making outbound webhook requests
* await axios.post(webhookUrl, payload, { headers });
* ```
*/
export function buildWebhookHeaders(
correlationId?: string
): Record<string, string> {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
};
const safeCorrelationId = sanitizeCorrelationId(correlationId);
if (safeCorrelationId) {
headers['X-Correlation-Id'] = safeCorrelationId;
}
return headers;
}