From 0a83fb369ecd5c9dc85ac2c8c31ea40b1a0dee7c Mon Sep 17 00:00:00 2001 From: Zhiyuan Liang Date: Thu, 13 Aug 2026 19:42:07 +0800 Subject: [PATCH 1/2] fix correlation context header missing issue --- package-lock.json | 45 +++++++++++++------- package.json | 3 +- src/requestTracing/utils.ts | 6 ++- test/requestTracing.test.ts | 82 ++++++++++++++----------------------- 4 files changed, 68 insertions(+), 68 deletions(-) diff --git a/package-lock.json b/package-lock.json index 84bfb47..51063b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "2.6.0", "license": "MIT", "dependencies": { - "@azure/app-configuration": "^1.11.0", + "@azure-rest/core-client": "^2.6.0", + "@azure/app-configuration": "^1.12.0", "@azure/core-rest-pipeline": "^1.6.0", "@azure/identity": "^4.2.1", "@azure/keyvault-secrets": "^4.7.0", @@ -46,15 +47,15 @@ } }, "node_modules/@azure-rest/core-client": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@azure-rest/core-client/-/core-client-2.5.0.tgz", - "integrity": "sha512-KMVIPxG6ygcQ1M2hKHahF7eddKejYsWTjoLIfTWiqnaj42dBkYzj4+S8rK9xxmlOaEHKZHcMrRbm0NfN4kgwHw==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@azure-rest/core-client/-/core-client-2.6.1.tgz", + "integrity": "sha512-KzI10qnkWTsVS2yRBUdc8NLUJ1rOm+292mYs7Pe9wqAj/jv4bRskVm1l8XkKeVTN0OCQtrU5RG0Yhjbz1Wmg7g==", "license": "MIT", "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-auth": "^1.9.0", - "@azure/core-rest-pipeline": "^1.5.0", - "@azure/core-tracing": "^1.0.1", + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-rest-pipeline": "^1.22.0", + "@azure/core-tracing": "^1.3.0", "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" }, @@ -75,16 +76,17 @@ } }, "node_modules/@azure/app-configuration": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@azure/app-configuration/-/app-configuration-1.11.0.tgz", - "integrity": "sha512-ehfTNvVyr4lFKz1Nfynubqg/kEWMFMjCQs8lADDa+U1HG96QIoelMYFbfWIixw4KhOqoCAIOWEp4bAXIZ0/V/w==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@azure/app-configuration/-/app-configuration-1.12.0.tgz", + "integrity": "sha512-qTJb9bG9j4eQosmyFmeDv9/83rnTlGsCixTM4U7wvUc9gQ2cxI7rHQrur7pLBsfun1Nm1wZ/rhCUJyuKBT70Ow==", "license": "MIT", "dependencies": { - "@azure/abort-controller": "^2.0.0", + "@azure-rest/core-client": "^2.6.0", + "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.3.0", - "@azure/core-client": "^1.5.0", + "@azure/core-client": "^1.9.2", "@azure/core-http-compat": "^2.0.0", - "@azure/core-lro": "^2.5.1", + "@azure/core-lro": "^3.1.0", "@azure/core-paging": "^1.4.0", "@azure/core-rest-pipeline": "^1.6.0", "@azure/core-tracing": "^1.0.0", @@ -96,6 +98,21 @@ "node": ">=20.0.0" } }, + "node_modules/@azure/app-configuration/node_modules/@azure/core-lro": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-3.3.1.tgz", + "integrity": "sha512-bulm3klLqIAhzI3iQMYQ42i+V9EnevScsHdI9amFfjaw6OJqPBK1038cq5qachoKV3yt/iQQEDittHmZW2aSuA==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@azure/core-auth": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.0.tgz", diff --git a/package.json b/package.json index 7e906a2..1b4f996 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,8 @@ "vitest": "^4.1.8" }, "dependencies": { - "@azure/app-configuration": "^1.11.0", + "@azure/app-configuration": "^1.12.0", + "@azure-rest/core-client": "^2.6.0", "@azure/core-rest-pipeline": "^1.6.0", "@azure/identity": "^4.2.1", "@azure/keyvault-secrets": "^4.7.0", diff --git a/src/requestTracing/utils.ts b/src/requestTracing/utils.ts index 3b1337e..db02234 100644 --- a/src/requestTracing/utils.ts +++ b/src/requestTracing/utils.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { OperationOptions } from "@azure/core-client"; +import { OperationOptions } from "@azure-rest/core-client"; import { AppConfigurationClient, ConfigurationSettingId, @@ -101,7 +101,9 @@ function applyRequestTracing(requestTracingOptions: const actualOptions = { ...operationOptions }; if (requestTracingOptions.enabled) { actualOptions.requestOptions = { - customHeaders: { + ...actualOptions.requestOptions, + headers: { + ...actualOptions.requestOptions?.headers, [CORRELATION_CONTEXT_HEADER_NAME]: createCorrelationContextHeader(requestTracingOptions) } }; diff --git a/test/requestTracing.test.ts b/test/requestTracing.test.ts index 6a69698..651f126 100644 --- a/test/requestTracing.test.ts +++ b/test/requestTracing.test.ts @@ -34,7 +34,7 @@ describe("request tracing", function () { after(() => { }); - it("should have correct user agent prefix", async () => { + it("should include the provider identifier in the user agent", async () => { try { await load(createMockedConnectionString(fakeEndpoint), { clientOptions, @@ -44,7 +44,7 @@ describe("request tracing", function () { }); } catch { /* empty */ } expect(headerPolicy.headers).not.undefined; - expect(headerPolicy.headers.get("User-Agent")).satisfy((ua: string) => ua.startsWith("javascript-appconfiguration-provider")); + expect(headerPolicy.headers.get("User-Agent")).satisfy((ua: string) => ua.includes("javascript-appconfiguration-provider")); }); it("should have request type in correlation-context header", async () => { @@ -204,18 +204,14 @@ describe("request tracing", function () { }); it("should have filter type in correlation-context header if feature flags use feature filters", async () => { - let correlationContext: string = ""; - const listKvCallback = (listOptions) => { - correlationContext = listOptions?.requestOptions?.customHeaders[CORRELATION_CONTEXT_HEADER_NAME] ?? ""; - }; - mockAppConfigurationClientListConfigurationSettings([[ createMockedFeatureFlag("Alpha_1", { conditions: { client_filters: [ { name: "Microsoft.TimeWindow" } ] } }), createMockedFeatureFlag("Alpha_2", { conditions: { client_filters: [ { name: "Microsoft.Targeting" } ] } }), createMockedFeatureFlag("Alpha_3", { conditions: { client_filters: [ { name: "CustomFilter" } ] } }) - ]], listKvCallback); + ]]); const settings = await load(createMockedConnectionString(fakeEndpoint), { + clientOptions, featureFlagOptions: { enabled: true, selectors: [ {keyFilter: "*"} ], @@ -226,34 +222,30 @@ describe("request tracing", function () { } }); - expect(correlationContext).not.undefined; - expect(correlationContext?.includes("RequestType=Startup")).eq(true); + restoreMocks(); await sleepInMs(1_000 + 1_000); try { await settings.refresh(); } catch { /* empty */ } expect(headerPolicy.headers).not.undefined; + const correlationContext = headerPolicy.headers.get(CORRELATION_CONTEXT_HEADER_NAME); expect(correlationContext).not.undefined; - expect(correlationContext?.includes("RequestType=Watch")).eq(true); - expect(correlationContext?.includes("Filter=CSTM+TIME+TRGT")).eq(true); + expect(correlationContext.includes("RequestType=Watch")).eq(true); + expect(correlationContext.includes("Filter=CSTM+TIME+TRGT")).eq(true); restoreMocks(); }); it("should have max variants in correlation-context header if feature flags use variants", async () => { - let correlationContext: string = ""; - const listKvCallback = (listOptions) => { - correlationContext = listOptions?.requestOptions?.customHeaders[CORRELATION_CONTEXT_HEADER_NAME] ?? ""; - }; - mockAppConfigurationClientListConfigurationSettings([[ createMockedFeatureFlag("Alpha_1", { variants: [ {name: "a"}, {name: "b"}] }), createMockedFeatureFlag("Alpha_2", { variants: [ {name: "a"}, {name: "b"}, {name: "c"}] }), createMockedFeatureFlag("Alpha_3", { variants: [] }) - ]], listKvCallback); + ]]); const settings = await load(createMockedConnectionString(fakeEndpoint), { + clientOptions, featureFlagOptions: { enabled: true, selectors: [ {keyFilter: "*"} ], @@ -264,32 +256,28 @@ describe("request tracing", function () { } }); - expect(correlationContext).not.undefined; - expect(correlationContext?.includes("RequestType=Startup")).eq(true); + restoreMocks(); await sleepInMs(1_000 + 1_000); try { await settings.refresh(); } catch { /* empty */ } expect(headerPolicy.headers).not.undefined; + const correlationContext = headerPolicy.headers.get(CORRELATION_CONTEXT_HEADER_NAME); expect(correlationContext).not.undefined; - expect(correlationContext?.includes("RequestType=Watch")).eq(true); - expect(correlationContext?.includes("MaxVariants=3")).eq(true); + expect(correlationContext.includes("RequestType=Watch")).eq(true); + expect(correlationContext.includes("MaxVariants=3")).eq(true); restoreMocks(); }); it("should have telemety tag in correlation-context header if feature flags enable telemetry", async () => { - let correlationContext: string = ""; - const listKvCallback = (listOptions) => { - correlationContext = listOptions?.requestOptions?.customHeaders[CORRELATION_CONTEXT_HEADER_NAME] ?? ""; - }; - mockAppConfigurationClientListConfigurationSettings([[ createMockedFeatureFlag("Alpha_1", { telemetry: {enabled: true} }) - ]], listKvCallback); + ]]); const settings = await load(createMockedConnectionString(fakeEndpoint), { + clientOptions, featureFlagOptions: { enabled: true, selectors: [ {keyFilter: "*"} ], @@ -300,33 +288,29 @@ describe("request tracing", function () { } }); - expect(correlationContext).not.undefined; - expect(correlationContext?.includes("RequestType=Startup")).eq(true); + restoreMocks(); await sleepInMs(1_000 + 1_000); try { await settings.refresh(); } catch { /* empty */ } expect(headerPolicy.headers).not.undefined; + const correlationContext = headerPolicy.headers.get(CORRELATION_CONTEXT_HEADER_NAME); expect(correlationContext).not.undefined; - expect(correlationContext?.includes("RequestType=Watch")).eq(true); - expect(correlationContext?.includes("FFFeatures=Telemetry")).eq(true); + expect(correlationContext.includes("RequestType=Watch")).eq(true); + expect(correlationContext.includes("FFFeatures=Telemetry")).eq(true); restoreMocks(); }); it("should have seed tag in correlation-context header if feature flags use allocation seed", async () => { - let correlationContext: string = ""; - const listKvCallback = (listOptions) => { - correlationContext = listOptions?.requestOptions?.customHeaders[CORRELATION_CONTEXT_HEADER_NAME] ?? ""; - }; - mockAppConfigurationClientListConfigurationSettings([[ createMockedFeatureFlag("Alpha_1", { telemetry: {enabled: true} }), createMockedFeatureFlag("Alpha_2", { allocation: {seed: "123"} }) - ]], listKvCallback); + ]]); const settings = await load(createMockedConnectionString(fakeEndpoint), { + clientOptions, featureFlagOptions: { enabled: true, selectors: [ {keyFilter: "*"} ], @@ -337,48 +321,44 @@ describe("request tracing", function () { } }); - expect(correlationContext).not.undefined; - expect(correlationContext?.includes("RequestType=Startup")).eq(true); + restoreMocks(); await sleepInMs(1_000 + 1_000); try { await settings.refresh(); } catch { /* empty */ } expect(headerPolicy.headers).not.undefined; + const correlationContext = headerPolicy.headers.get(CORRELATION_CONTEXT_HEADER_NAME); expect(correlationContext).not.undefined; - expect(correlationContext?.includes("RequestType=Watch")).eq(true); - expect(correlationContext?.includes("FFFeatures=Seed+Telemetry")).eq(true); + expect(correlationContext.includes("RequestType=Watch")).eq(true); + expect(correlationContext.includes("FFFeatures=Seed+Telemetry")).eq(true); restoreMocks(); }); it("should have AI tag in correlation-context header if key values use AI configuration", async () => { - let correlationContext: string = ""; - const listKvCallback = (listOptions) => { - correlationContext = listOptions?.requestOptions?.customHeaders[CORRELATION_CONTEXT_HEADER_NAME] ?? ""; - }; - mockAppConfigurationClientListConfigurationSettings([[ createMockedKeyValue({ contentType: "application/json; profile=\"https://azconfig.io/mime-profiles/ai/chat-completion\"" }) - ]], listKvCallback); + ]]); const settings = await load(createMockedConnectionString(fakeEndpoint), { + clientOptions, refreshOptions: { enabled: true, refreshIntervalInMs: 1000 } }); - expect(correlationContext).not.undefined; - expect(correlationContext?.includes("RequestType=Startup")).eq(true); + restoreMocks(); await sleepInMs(1000 + 1); try { await settings.refresh(); } catch { /* empty */ } expect(headerPolicy.headers).not.undefined; + const correlationContext = headerPolicy.headers.get(CORRELATION_CONTEXT_HEADER_NAME); expect(correlationContext).not.undefined; - expect(correlationContext?.includes("Features=AI+AICC")).eq(true); + expect(correlationContext.includes("Features=AI+AICC")).eq(true); restoreMocks(); }); From 41c127e8d4c12be69f041a6b6c6832e0aa7e671b Mon Sep 17 00:00:00 2001 From: Zhiyuan Liang Date: Thu, 13 Aug 2026 20:16:43 +0800 Subject: [PATCH 2/2] update to 1.12.1 --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 51063b5..fd82949 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "dependencies": { "@azure-rest/core-client": "^2.6.0", - "@azure/app-configuration": "^1.12.0", + "@azure/app-configuration": "^1.12.1", "@azure/core-rest-pipeline": "^1.6.0", "@azure/identity": "^4.2.1", "@azure/keyvault-secrets": "^4.7.0", @@ -76,9 +76,9 @@ } }, "node_modules/@azure/app-configuration": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/@azure/app-configuration/-/app-configuration-1.12.0.tgz", - "integrity": "sha512-qTJb9bG9j4eQosmyFmeDv9/83rnTlGsCixTM4U7wvUc9gQ2cxI7rHQrur7pLBsfun1Nm1wZ/rhCUJyuKBT70Ow==", + "version": "1.12.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@azure/app-configuration/-/app-configuration-1.12.1.tgz", + "integrity": "sha1-2LSU04Fa5i/vOva1DhzFHh3KOcA=", "license": "MIT", "dependencies": { "@azure-rest/core-client": "^2.6.0", @@ -1868,9 +1868,9 @@ } }, "node_modules/@typespec/ts-http-runtime": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.0.tgz", - "integrity": "sha512-sOx1PKSuFwnIl7z4RN0Ls7N9AQawmR9r66eI5rFCzLDIs8HTIYrIpH9QjYWoX0lkgGrkLxXhi4QnK7MizPRrIg==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.6.tgz", + "integrity": "sha512-jIXhD0eWQ1JA6ln/5Dltyx22UxWNrw0hZmhy2rlv6m6KgF7kplHx3g0fzi09lNmTJQRR91OlemYp3xFnvDK9og==", "license": "MIT", "dependencies": { "http-proxy-agent": "^7.0.0", diff --git a/package.json b/package.json index 1b4f996..80074f6 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "vitest": "^4.1.8" }, "dependencies": { - "@azure/app-configuration": "^1.12.0", + "@azure/app-configuration": "^1.12.1", "@azure-rest/core-client": "^2.6.0", "@azure/core-rest-pipeline": "^1.6.0", "@azure/identity": "^4.2.1",