OpenAPI query param discovery and header cleanup completion
Backend: ExtractParameters() now discovers query params from [AsParameters] records and [FromQuery] attributes via handler method reflection. Gateway OpenApiDocumentGenerator emits parameters arrays in the aggregated spec. QueryParameterInfo added to EndpointSchemaInfo for HELLO payload transport. Frontend: Remaining spec files and straggler services updated to canonical X-Stella-Ops-* header names. Sprint 026 archived (tasks 01-06 DONE, 07-09 TODO for backend service rename pass). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -53,10 +53,10 @@ describe('AdvisoryApiHttpClient', () => {
|
||||
|
||||
const req = httpMock.expectOne((r) => r.url === '/api/advisories' && r.params.get('search') === 'demo');
|
||||
expect(req.request.method).toBe('GET');
|
||||
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe('tenant-x');
|
||||
expect(req.request.headers.get('X-Stella-Project')).toBe('proj-1');
|
||||
expect(req.request.headers.get('X-Stella-Trace-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('X-Stella-Request-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('tenant-x');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Project')).toBe('proj-1');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Trace-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Request-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('If-None-Match')).toBe('"etag-1"');
|
||||
req.flush({ items: [], count: 0, continuationToken: null, etag: '"etag-2"', traceId: 'trace-1' });
|
||||
});
|
||||
|
||||
@@ -150,7 +150,7 @@ describe('AdvisoryAiApiHttpClient', () => {
|
||||
|
||||
const callArgs = httpClientSpy.get.calls.mostRecent()!.args;
|
||||
const headers = callArgs[1]!.headers as HttpHeaders;
|
||||
expect(headers.get('X-Stella-Trace-Id')).toBe('custom-trace-123');
|
||||
expect(headers.get('X-Stella-Ops-Trace-Id')).toBe('custom-trace-123');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -347,7 +347,7 @@ describe('AdvisoryAiApiHttpClient', () => {
|
||||
|
||||
const callArgs = httpClientSpy.get.calls.mostRecent()!.args;
|
||||
const headers = callArgs[1]!.headers as HttpHeaders;
|
||||
expect(headers.get('X-StellaOps-Tenant')).toBe('tenant-xyz');
|
||||
expect(headers.get('X-Stella-Ops-Tenant')).toBe('tenant-xyz');
|
||||
});
|
||||
|
||||
it('should include trace ID header', () => {
|
||||
@@ -357,7 +357,7 @@ describe('AdvisoryAiApiHttpClient', () => {
|
||||
|
||||
const callArgs = httpClientSpy.get.calls.mostRecent()!.args;
|
||||
const headers = callArgs[1]!.headers as HttpHeaders;
|
||||
expect(headers.get('X-Stella-Trace-Id')).toBeTruthy();
|
||||
expect(headers.get('X-Stella-Ops-Trace-Id')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should include request ID header', () => {
|
||||
@@ -367,7 +367,7 @@ describe('AdvisoryAiApiHttpClient', () => {
|
||||
|
||||
const callArgs = httpClientSpy.get.calls.mostRecent()!.args;
|
||||
const headers = callArgs[1]!.headers as HttpHeaders;
|
||||
expect(headers.get('X-Stella-Request-Id')).toBeTruthy();
|
||||
expect(headers.get('X-Stella-Ops-Request-Id')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should include Accept header', () => {
|
||||
@@ -388,7 +388,7 @@ describe('AdvisoryAiApiHttpClient', () => {
|
||||
|
||||
const callArgs = httpClientSpy.get.calls.mostRecent()!.args;
|
||||
const headers = callArgs[1]!.headers as HttpHeaders;
|
||||
expect(headers.get('X-StellaOps-Tenant')).toBe('');
|
||||
expect(headers.get('X-Stella-Ops-Tenant')).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -44,8 +44,8 @@ describe('AdvisoryAiApiHttpClient', () => {
|
||||
|
||||
const req = httpMock.expectOne('/api/v1/advisory-ai/explain');
|
||||
expect(req.request.method).toBe('POST');
|
||||
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe('tenant-default');
|
||||
expect(req.request.headers.get('X-Stella-Trace-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('tenant-default');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Trace-Id')).toBe('trace-1');
|
||||
expect(req.request.body).toEqual(request);
|
||||
|
||||
req.flush({
|
||||
@@ -67,8 +67,8 @@ describe('AdvisoryAiApiHttpClient', () => {
|
||||
|
||||
const req = httpMock.expectOne('/api/v1/advisory-ai/remediate');
|
||||
expect(req.request.method).toBe('POST');
|
||||
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe('tenant-default');
|
||||
expect(req.request.headers.get('X-Stella-Trace-Id')).toBe('trace-2');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('tenant-default');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Trace-Id')).toBe('trace-2');
|
||||
expect(req.request.body).toEqual(request);
|
||||
|
||||
req.flush({
|
||||
@@ -93,8 +93,8 @@ describe('AdvisoryAiApiHttpClient', () => {
|
||||
|
||||
const req = httpMock.expectOne('/api/v1/advisory-ai/justify');
|
||||
expect(req.request.method).toBe('POST');
|
||||
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe('tenant-default');
|
||||
expect(req.request.headers.get('X-Stella-Trace-Id')).toBe('trace-3');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('tenant-default');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Trace-Id')).toBe('trace-3');
|
||||
|
||||
req.flush({
|
||||
justificationId: 'justify-1',
|
||||
@@ -114,7 +114,7 @@ describe('AdvisoryAiApiHttpClient', () => {
|
||||
|
||||
const req = httpMock.expectOne('/api/v1/advisory-ai/rate-limits');
|
||||
expect(req.request.method).toBe('GET');
|
||||
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe('tenant-default');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('tenant-default');
|
||||
|
||||
req.flush([
|
||||
{ feature: 'explain', limit: 10, remaining: 8, resetsAt: '2025-01-15T00:00:00Z' },
|
||||
|
||||
@@ -41,9 +41,9 @@ describe('AnalyticsHttpClient', () => {
|
||||
r.params.get('environment') === 'prod'
|
||||
);
|
||||
|
||||
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe('tenant-analytics');
|
||||
expect(req.request.headers.get('X-Stella-Trace-Id')).toBe('trace-123');
|
||||
expect(req.request.headers.get('X-Stella-Request-Id')).toBe('trace-123');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('tenant-analytics');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Trace-Id')).toBe('trace-123');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Request-Id')).toBe('trace-123');
|
||||
|
||||
const response: PlatformListResponse<unknown> = {
|
||||
tenantId: 'tenant-analytics',
|
||||
|
||||
@@ -105,7 +105,7 @@ describe('AuditBundlesHttpClient', () => {
|
||||
);
|
||||
expect(request.request.headers.get('Authorization')).toBe('DPoP access-token');
|
||||
expect(request.request.headers.get('DPoP')).toBe('proof-token');
|
||||
expect(request.request.headers.get('X-Stella-Tenant')).toBe('demo-prod');
|
||||
expect(request.request.headers.get('X-Stella-Ops-Tenant')).toBe('demo-prod');
|
||||
request.flush({
|
||||
bundleId: 'bndl-0001',
|
||||
status: 'queued',
|
||||
|
||||
@@ -54,8 +54,8 @@ describe('ConsoleExportClient', () => {
|
||||
|
||||
const req = httpMock.expectOne('/console/exports');
|
||||
expect(req.request.method).toBe('POST');
|
||||
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe('tenant-default');
|
||||
expect(req.request.headers.get('X-Stella-Trace-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('tenant-default');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Trace-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('Idempotency-Key')).toBe('abc');
|
||||
req.flush({ exportId: 'exp-1', status: 'queued' });
|
||||
});
|
||||
@@ -65,8 +65,8 @@ describe('ConsoleExportClient', () => {
|
||||
|
||||
const req = httpMock.expectOne('/console/exports/exp-1');
|
||||
expect(req.request.method).toBe('GET');
|
||||
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe('tenant-xyz');
|
||||
expect(req.request.headers.get('X-Stella-Trace-Id')).toBe('trace-2');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('tenant-xyz');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Trace-Id')).toBe('trace-2');
|
||||
req.flush({ exportId: 'exp-1', status: 'running' });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -47,9 +47,9 @@ describe('ConsoleSearchHttpClient', () => {
|
||||
|
||||
const req = httpMock.expectOne((r) => r.url === '/api/search' && r.params.get('query') === 'jwt');
|
||||
expect(req.request.method).toBe('GET');
|
||||
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe('tenant-x');
|
||||
expect(req.request.headers.get('X-Stella-Trace-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('X-Stella-Request-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('tenant-x');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Trace-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Request-Id')).toBe('trace-1');
|
||||
req.flush({
|
||||
items: [],
|
||||
ranking: { sortKeys: [], payloadHash: 'sha256:test' },
|
||||
@@ -65,8 +65,8 @@ describe('ConsoleSearchHttpClient', () => {
|
||||
client.search({ traceId: 'trace-2' }).subscribe();
|
||||
|
||||
const req = httpMock.expectOne('/api/search');
|
||||
expect(req.request.headers.has('X-StellaOps-Tenant')).toBeFalse();
|
||||
expect(req.request.headers.get('X-Stella-Trace-Id')).toBe('trace-2');
|
||||
expect(req.request.headers.has('X-Stella-Ops-Tenant')).toBeFalse();
|
||||
expect(req.request.headers.get('X-Stella-Ops-Trace-Id')).toBe('trace-2');
|
||||
req.flush({
|
||||
items: [],
|
||||
ranking: { sortKeys: [], payloadHash: 'sha256:test2' },
|
||||
|
||||
@@ -83,9 +83,9 @@ describe('ConsoleStatusClient', () => {
|
||||
|
||||
const req = httpMock.expectOne('/api/console/status');
|
||||
expect(req.request.method).toBe('GET');
|
||||
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe('tenant-dev');
|
||||
expect(req.request.headers.get('X-Stella-Trace-Id')).toBeTruthy();
|
||||
expect(req.request.headers.get('X-Stella-Request-Id')).toBeTruthy();
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('tenant-dev');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Trace-Id')).toBeTruthy();
|
||||
expect(req.request.headers.get('X-Stella-Ops-Request-Id')).toBeTruthy();
|
||||
req.flush(sample);
|
||||
});
|
||||
|
||||
|
||||
@@ -77,8 +77,8 @@ describe('CvssClient', () => {
|
||||
|
||||
const req = httpMock.expectOne('/api/cvss/receipts/rcpt-1');
|
||||
expect(req.request.method).toBe('GET');
|
||||
expect(req.request.headers.get('X-Stella-Tenant')).toBe('tenant-123');
|
||||
expect(req.request.headers.has('X-Stella-Trace-Id')).toBeTrue();
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('tenant-123');
|
||||
expect(req.request.headers.has('X-Stella-Ops-Trace-Id')).toBeTrue();
|
||||
req.flush(dto);
|
||||
|
||||
expect(receipt?.score.overall).toBe(9.1);
|
||||
|
||||
@@ -42,8 +42,8 @@ describe('EvidencePackHttpClient', () => {
|
||||
const request = httpMock.expectOne((pending) => pending.url === '/v1/evidence-packs');
|
||||
expect(request.request.method).toBe('GET');
|
||||
expect(request.request.params.get('runId')).toBe('run-42');
|
||||
expect(request.request.headers.get('X-StellaOps-Tenant')).toBe('demo-prod');
|
||||
expect(request.request.headers.get('X-Stella-Trace-Id')).toBe('trace-ep-42');
|
||||
expect(request.request.headers.get('X-Stella-Ops-Tenant')).toBe('demo-prod');
|
||||
expect(request.request.headers.get('X-Stella-Ops-Trace-Id')).toBe('trace-ep-42');
|
||||
request.flush({ count: 0, packs: [] });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -45,10 +45,10 @@ describe('ExceptionApiHttpClient', () => {
|
||||
|
||||
const req = httpMock.expectOne((r) => r.url === '/api/exceptions' && r.params.get('status') === 'approved');
|
||||
expect(req.request.method).toBe('GET');
|
||||
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe('tenant-x');
|
||||
expect(req.request.headers.get('X-Stella-Project')).toBe('proj-1');
|
||||
expect(req.request.headers.get('X-Stella-Trace-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('X-Stella-Request-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('tenant-x');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Project')).toBe('proj-1');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Trace-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Request-Id')).toBe('trace-1');
|
||||
req.flush({ items: [], count: 0, continuationToken: null });
|
||||
});
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ describe('FirstSignalHttpClient', () => {
|
||||
|
||||
const req = httpMock.expectOne('/api/console/runs/run%3A%3Atenant-dev%3A%3A20260309/first-signal');
|
||||
expect(req.request.method).toBe('GET');
|
||||
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe('tenant-dev');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('tenant-dev');
|
||||
expect(req.request.headers.get('If-None-Match')).toBe('"compat-prev"');
|
||||
req.flush(
|
||||
{
|
||||
|
||||
@@ -64,10 +64,10 @@ describe('JobEngineControlHttpClient', () => {
|
||||
expect(req.request.params.get('paused')).toBe('false');
|
||||
expect(req.request.params.get('limit')).toBe('25');
|
||||
expect(req.request.params.get('continuationToken')).toBe('cursor-1');
|
||||
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe('tenant-x');
|
||||
expect(req.request.headers.get('X-Stella-Project')).toBe('proj-1');
|
||||
expect(req.request.headers.get('X-Stella-Trace-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('X-Stella-Request-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('tenant-x');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Project')).toBe('proj-1');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Trace-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Request-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('If-None-Match')).toBe('"etag-1"');
|
||||
req.flush({ items: [], count: 0, continuationToken: null, etag: '"etag-2"', traceId: 'trace-1' });
|
||||
});
|
||||
@@ -82,8 +82,8 @@ describe('JobEngineControlHttpClient', () => {
|
||||
|
||||
const req = httpMock.expectOne('/api/jobengine/quotas');
|
||||
expect(req.request.method).toBe('POST');
|
||||
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe('tenant-x');
|
||||
expect(req.request.headers.get('X-Stella-Require-Operator')).toBe('1');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('tenant-x');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Require-Operator')).toBe('1');
|
||||
req.flush({
|
||||
quotaId: 'q-1',
|
||||
tenantId: 'tenant-x',
|
||||
@@ -109,8 +109,8 @@ describe('JobEngineControlHttpClient', () => {
|
||||
|
||||
const req = httpMock.expectOne('/api/jobengine/deadletter/entry-1/replay');
|
||||
expect(req.request.method).toBe('POST');
|
||||
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe('tenant-x');
|
||||
expect(req.request.headers.get('X-Stella-Require-Operator')).toBe('1');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('tenant-x');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Require-Operator')).toBe('1');
|
||||
req.flush({ success: true, newJobId: 'job-1', errorMessage: null, updatedEntry: null, traceId: 'trace-3' });
|
||||
});
|
||||
|
||||
@@ -144,8 +144,8 @@ describe('JobEngineControlHttpClient', () => {
|
||||
client.listQuotas({ traceId: 'trace-6' }).subscribe();
|
||||
|
||||
const req = httpMock.expectOne('/api/jobengine/quotas');
|
||||
expect(req.request.headers.has('X-StellaOps-Tenant')).toBeFalse();
|
||||
expect(req.request.headers.get('X-Stella-Trace-Id')).toBe('trace-6');
|
||||
expect(req.request.headers.has('X-Stella-Ops-Tenant')).toBeFalse();
|
||||
expect(req.request.headers.get('X-Stella-Ops-Trace-Id')).toBe('trace-6');
|
||||
req.flush({ items: [], count: 0, continuationToken: null, etag: '"etag-3"', traceId: 'trace-6' });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -54,10 +54,10 @@ describe('OrchestratorHttpClient', () => {
|
||||
const req = httpMock.expectOne((r) => r.url === '/api/jobengine/sources' && r.params.get('sourceType') === 'concelier');
|
||||
expect(req.request.method).toBe('GET');
|
||||
expect(req.request.params.get('enabled')).toBe('true');
|
||||
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe('tenant-x');
|
||||
expect(req.request.headers.get('X-Stella-Project')).toBe('proj-1');
|
||||
expect(req.request.headers.get('X-Stella-Trace-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('X-Stella-Request-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('tenant-x');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Project')).toBe('proj-1');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Trace-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Request-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('If-None-Match')).toBe('"etag-1"');
|
||||
req.flush({ items: [], count: 0, continuationToken: null, etag: '"etag-2"', traceId: 'trace-1' });
|
||||
});
|
||||
|
||||
@@ -52,7 +52,7 @@ describe('NotifyApiHttpClient', () => {
|
||||
client.listChannels().subscribe();
|
||||
|
||||
const request = httpMock.expectOne('/api/v1/notify/channels');
|
||||
expect(request.request.headers.get('X-StellaOps-Tenant')).toBe('demo-prod');
|
||||
expect(request.request.headers.get('X-Stella-Ops-Tenant')).toBe('demo-prod');
|
||||
request.flush([]);
|
||||
});
|
||||
|
||||
@@ -62,7 +62,7 @@ describe('NotifyApiHttpClient', () => {
|
||||
client.listRules().subscribe();
|
||||
|
||||
const request = httpMock.expectOne('/api/v1/notify/rules');
|
||||
expect(request.request.headers.get('X-StellaOps-Tenant')).toBe('tenant-explicit');
|
||||
expect(request.request.headers.get('X-Stella-Ops-Tenant')).toBe('tenant-explicit');
|
||||
request.flush([]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -48,10 +48,10 @@ describe('PolicyExceptionsHttpClient', () => {
|
||||
|
||||
const req = httpMock.expectOne('/api/policy/effective');
|
||||
expect(req.request.method).toBe('POST');
|
||||
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe('tenant-x');
|
||||
expect(req.request.headers.get('X-Stella-Project')).toBe('proj-1');
|
||||
expect(req.request.headers.get('X-Stella-Trace-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('X-Stella-Request-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('tenant-x');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Project')).toBe('proj-1');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Trace-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Request-Id')).toBe('trace-1');
|
||||
req.flush({ policyVersion: 'sha256:test', items: [], continuationToken: null, traceId: 'trace-1' });
|
||||
});
|
||||
|
||||
|
||||
@@ -43,8 +43,8 @@ describe('HttpPolicyGovernanceApi', () => {
|
||||
|
||||
const req = httpMock.expectOne((request) => request.url === '/api/v1/governance/trust-weights');
|
||||
expect(req.request.params.get('tenantId')).toBe('demo-prod');
|
||||
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe('demo-prod');
|
||||
expect(req.request.headers.get('X-Stella-Tenant')).toBe('demo-prod');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('demo-prod');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('demo-prod');
|
||||
req.flush({ tenantId: 'demo-prod', projectId: null, weights: [], defaultWeight: 1, modifiedAt: '2026-03-09T00:00:00Z' });
|
||||
});
|
||||
|
||||
@@ -54,7 +54,7 @@ describe('HttpPolicyGovernanceApi', () => {
|
||||
const req = httpMock.expectOne((request) => request.url === '/api/v1/governance/staleness/config');
|
||||
expect(req.request.params.get('tenantId')).toBe('tenant-blue');
|
||||
expect(req.request.params.get('projectId')).toBe('proj-a');
|
||||
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe('tenant-blue');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('tenant-blue');
|
||||
req.flush({ tenantId: 'tenant-blue', projectId: 'proj-a', configs: [], modifiedAt: '2026-03-09T00:00:00Z', etag: '"staleness"' });
|
||||
});
|
||||
|
||||
@@ -63,7 +63,7 @@ describe('HttpPolicyGovernanceApi', () => {
|
||||
|
||||
const req = httpMock.expectOne((request) => request.url === '/api/v1/governance/audit/events');
|
||||
expect(req.request.params.get('tenantId')).toBe('demo-prod');
|
||||
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe('demo-prod');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('demo-prod');
|
||||
req.flush({ events: [], total: 0, page: 1, pageSize: 20, hasMore: false });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -70,7 +70,7 @@ describe('PolicySimulationHttpClient', () => {
|
||||
const promise = firstValueFrom(httpClient.getShadowModeConfig());
|
||||
const req = httpMock.expectOne(`${baseUrl}/policy/shadow/config`);
|
||||
expect(req.request.method).toBe('GET');
|
||||
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe('tenant-001');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('tenant-001');
|
||||
req.flush(mockConfig);
|
||||
|
||||
const result = await promise;
|
||||
@@ -732,7 +732,7 @@ describe('PolicySimulationHttpClient', () => {
|
||||
|
||||
const promise = firstValueFrom(httpClient.getShadowModeConfig({ tenantId: customTenant }));
|
||||
const req = httpMock.expectOne(`${baseUrl}/policy/shadow/config`);
|
||||
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe(customTenant);
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe(customTenant);
|
||||
req.flush({ enabled: false, status: 'disabled' });
|
||||
|
||||
await promise;
|
||||
@@ -749,7 +749,7 @@ describe('PolicySimulationHttpClient', () => {
|
||||
}),
|
||||
);
|
||||
const req = httpMock.expectOne((request) => request.url === `${baseUrl}/policy/simulations/history`);
|
||||
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe('demo-prod');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('demo-prod');
|
||||
req.flush({ items: [], total: 0, hasMore: false });
|
||||
|
||||
await promise;
|
||||
|
||||
@@ -53,10 +53,10 @@ describe('VexEvidenceHttpClient', () => {
|
||||
|
||||
const req = httpMock.expectOne((r) => r.url === '/api/vex/statements' && r.params.get('vulnId') === 'CVE-2024-12345');
|
||||
expect(req.request.method).toBe('GET');
|
||||
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe('tenant-x');
|
||||
expect(req.request.headers.get('X-Stella-Project')).toBe('proj-1');
|
||||
expect(req.request.headers.get('X-Stella-Trace-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('X-Stella-Request-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('tenant-x');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Project')).toBe('proj-1');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Trace-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Request-Id')).toBe('trace-1');
|
||||
expect(req.request.headers.get('If-None-Match')).toBe('"etag-1"');
|
||||
req.flush({ items: [], count: 0, continuationToken: null, etag: '"etag-2"', traceId: 'trace-1' });
|
||||
});
|
||||
|
||||
@@ -193,7 +193,7 @@ describe('VexHubApiHttpClient', () => {
|
||||
|
||||
const callArgs = httpClientSpy.get.calls.mostRecent().args;
|
||||
const headers = callArgs[1]!.headers as HttpHeaders;
|
||||
expect(headers.get('X-Stella-Trace-Id')).toBe('custom-trace-123');
|
||||
expect(headers.get('X-Stella-Ops-Trace-Id')).toBe('custom-trace-123');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -474,7 +474,7 @@ describe('VexHubApiHttpClient', () => {
|
||||
|
||||
const callArgs = httpClientSpy.get.calls.mostRecent().args;
|
||||
const headers = callArgs[1]!.headers as HttpHeaders;
|
||||
expect(headers.get('X-StellaOps-Tenant')).toBe('tenant-abc');
|
||||
expect(headers.get('X-Stella-Ops-Tenant')).toBe('tenant-abc');
|
||||
});
|
||||
|
||||
it('should include trace ID header', () => {
|
||||
@@ -484,7 +484,7 @@ describe('VexHubApiHttpClient', () => {
|
||||
|
||||
const callArgs = httpClientSpy.get.calls.mostRecent().args;
|
||||
const headers = callArgs[1]!.headers as HttpHeaders;
|
||||
expect(headers.get('X-Stella-Trace-Id')).toBeTruthy();
|
||||
expect(headers.get('X-Stella-Ops-Trace-Id')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should include Accept header', () => {
|
||||
@@ -505,7 +505,7 @@ describe('VexHubApiHttpClient', () => {
|
||||
|
||||
const callArgs = httpClientSpy.get.calls.mostRecent().args;
|
||||
const headers = callArgs[1]!.headers as HttpHeaders;
|
||||
expect(headers.get('X-StellaOps-Tenant')).toBe('');
|
||||
expect(headers.get('X-Stella-Ops-Tenant')).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -52,8 +52,8 @@ describe('VulnerabilityHttpClient', () => {
|
||||
});
|
||||
|
||||
const req = httpMock.expectOne('https://api.example.local/vuln?page=1&pageSize=5');
|
||||
expect(req.request.headers.get('X-Stella-Tenant')).toBe('tenant-dev');
|
||||
expect(req.request.headers.has('X-Stella-Trace-Id')).toBeTrue();
|
||||
expect(req.request.headers.get('X-Stella-Ops-Tenant')).toBe('tenant-dev');
|
||||
expect(req.request.headers.has('X-Stella-Ops-Trace-Id')).toBeTrue();
|
||||
req.flush(stub);
|
||||
});
|
||||
|
||||
@@ -61,7 +61,7 @@ describe('VulnerabilityHttpClient', () => {
|
||||
client.listVulnerabilities({ page: 1, projectId: 'proj-ops' }).subscribe();
|
||||
|
||||
const req = httpMock.expectOne('https://api.example.local/vuln?page=1');
|
||||
expect(req.request.headers.get('X-Stella-Project')).toBe('proj-ops');
|
||||
expect(req.request.headers.get('X-Stella-Ops-Project')).toBe('proj-ops');
|
||||
req.flush({ items: [], total: 0, page: 1, pageSize: 20 });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -269,22 +269,22 @@ describe('parsePolicyError', () => {
|
||||
});
|
||||
|
||||
describe('trace ID extraction', () => {
|
||||
it('should extract X-Stella-Trace-Id header', () => {
|
||||
it('should extract X-Stella-Ops-Trace-Id header', () => {
|
||||
const response = createErrorResponse(
|
||||
500,
|
||||
{},
|
||||
{ 'X-Stella-Trace-Id': 'stella-trace-123' }
|
||||
{ 'X-Stella-Ops-Trace-Id': 'stella-trace-123' }
|
||||
);
|
||||
const error = parsePolicyError(response);
|
||||
|
||||
expect(error.traceId).toBe('stella-trace-123');
|
||||
});
|
||||
|
||||
it('should fall back to X-Request-Id header', () => {
|
||||
it('should fall back to X-Stella-Ops-Request-Id header', () => {
|
||||
const response = createErrorResponse(
|
||||
500,
|
||||
{},
|
||||
{ 'X-Request-Id': 'request-456' }
|
||||
{ 'X-Stella-Ops-Request-Id': 'request-456' }
|
||||
);
|
||||
const error = parsePolicyError(response);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { StellaOpsHeaders } from '../http/stella-ops-headers';
|
||||
import {
|
||||
PolicyError,
|
||||
PolicyErrorCode,
|
||||
@@ -177,8 +178,8 @@ export function parsePolicyError(response: HttpErrorResponse): PolicyApiError {
|
||||
|
||||
// Extract trace ID from headers
|
||||
const traceId =
|
||||
response.headers?.get('X-Stella-Trace-Id') ??
|
||||
response.headers?.get('X-Request-Id') ??
|
||||
response.headers?.get(StellaOpsHeaders.TraceId) ??
|
||||
response.headers?.get(StellaOpsHeaders.RequestId) ??
|
||||
(body?.traceId as string | undefined);
|
||||
|
||||
// Get error code
|
||||
|
||||
@@ -4,6 +4,7 @@ import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
import { Observable, BehaviorSubject, timer, of, catchError, map, tap } from 'rxjs';
|
||||
|
||||
import { AppConfigService } from '../config/app-config.service';
|
||||
import { StellaOpsHeaders } from '../http/stella-ops-headers';
|
||||
import { ConsoleSessionStore } from '../console/console-session.store';
|
||||
import { QuotaInfo, RateLimitInfo } from '../api/policy-engine.models';
|
||||
|
||||
@@ -188,7 +189,7 @@ export class PolicyQuotaService {
|
||||
* Load quota info from server.
|
||||
*/
|
||||
refreshQuotaInfo(): void {
|
||||
const headers = new HttpHeaders().set('X-Tenant-Id', this.tenantId);
|
||||
const headers = new HttpHeaders().set(StellaOpsHeaders.Tenant, this.tenantId);
|
||||
|
||||
this.http
|
||||
.get<QuotaInfo>(`${this.baseUrl}/api/policy/quota`, { headers })
|
||||
|
||||
Reference in New Issue
Block a user