diff --git a/CHANGELOG.md b/CHANGELOG.md index 334eadf..b6f9cba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to the Agent365 TypeScript SDK will be documented in this fi The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added +- Support for `A365_OBSERVABILITY_SCOPE_OVERRIDE` (singular) environment variable for cross-SDK compatibility with .NET SDK (PR #133 parity). + - The singular form takes precedence when both singular and plural forms are set + - Maintains backward compatibility with existing `A365_OBSERVABILITY_SCOPES_OVERRIDE` (plural) that supports multiple whitespace-separated scopes + - Updated documentation in `.env.example` files + +### Notes +- This change maintains full backward compatibility. Existing code using `A365_OBSERVABILITY_SCOPES_OVERRIDE` continues to work without any changes. + ## [1.1.0] - 2025-12-09 ### Changed diff --git a/packages/agents-a365-runtime/src/environment-utils.ts b/packages/agents-a365-runtime/src/environment-utils.ts index 25e4a6f..e4fd7bf 100644 --- a/packages/agents-a365-runtime/src/environment-utils.ts +++ b/packages/agents-a365-runtime/src/environment-utils.ts @@ -18,19 +18,31 @@ export const DEVELOPMENT_ENVIRONMENT_NAME = 'Development'; * Returns the scope for authenticating to the observability service * * The default is the production observability scope, but this can be overridden - * for internal development and testing scenarios using the - * `A365_OBSERVABILITY_SCOPES_OVERRIDE` environment variable. + * for internal development and testing scenarios using either the + * `A365_OBSERVABILITY_SCOPE_OVERRIDE` (singular, for a single scope) or + * `A365_OBSERVABILITY_SCOPES_OVERRIDE` (plural, supports multiple whitespace-separated scopes) + * environment variable. * - * When the override is set to a non-empty string, it is split on whitespace - * into individual scopes. + * The singular form takes precedence and returns a single scope without splitting. + * The plural form splits on whitespace to support multiple scopes. + * This dual support provides cross-SDK compatibility with the .NET SDK while maintaining + * enhanced functionality. * * @returns The authentication scopes for the current environment. */ export function getObservabilityAuthenticationScope(): string[] { - const override = process.env.A365_OBSERVABILITY_SCOPES_OVERRIDE; + // Check singular form first for .NET SDK compatibility (PR #133) + // Returns a single scope without splitting on whitespace + const singularOverride = process.env.A365_OBSERVABILITY_SCOPE_OVERRIDE; + if (singularOverride && singularOverride.trim().length > 0) { + return [singularOverride.trim()]; + } - if (override && override.trim().length > 0) { - return override.trim().split(/\s+/); + // Check plural form (original TypeScript implementation) + // Supports multiple whitespace-separated scopes + const pluralOverride = process.env.A365_OBSERVABILITY_SCOPES_OVERRIDE; + if (pluralOverride && pluralOverride.trim().length > 0) { + return pluralOverride.trim().split(/\s+/); } return [PROD_OBSERVABILITY_SCOPE]; diff --git a/tests-agent/basic-agent-sdk-sample/.env.example b/tests-agent/basic-agent-sdk-sample/.env.example index ca45fbd..4c9fce9 100644 --- a/tests-agent/basic-agent-sdk-sample/.env.example +++ b/tests-agent/basic-agent-sdk-sample/.env.example @@ -16,4 +16,5 @@ CLUSTER_CATEGORY=prod # optional - defaults to 'prod' if not set A365_OBSERVABILITY_LOG_LEVEL= # optional - set to enable observability logs, value can be 'info', 'warn', or 'error', default to 'none' if not set Use_Custom_Resolver= # optional - set to 'true' to use custom token resolver, defaults to 'false' if not set A365_OBSERVABILITY_DOMAIN_OVERRIDE= # optional - set to override the default observability domain -A365_OBSERVABILITY_SCOPES_OVERRIDE= # optional - set to override the default observability scopes +A365_OBSERVABILITY_SCOPE_OVERRIDE= # optional - set to override the default observability scope (singular, for single scope, cross-SDK compatible) +A365_OBSERVABILITY_SCOPES_OVERRIDE= # optional - set to override the default observability scopes (plural, supports multiple whitespace-separated scopes) diff --git a/tests/runtime/environment-utils.test.ts b/tests/runtime/environment-utils.test.ts index 512b22c..82c8b04 100644 --- a/tests/runtime/environment-utils.test.ts +++ b/tests/runtime/environment-utils.test.ts @@ -25,6 +25,7 @@ describe('environment-utils', () => { describe('getObservabilityAuthenticationScope', () => { it('should return production observability scope when override is not set', () => { delete process.env.A365_OBSERVABILITY_SCOPES_OVERRIDE; + delete process.env.A365_OBSERVABILITY_SCOPE_OVERRIDE; const scopes = getObservabilityAuthenticationScope(); @@ -33,6 +34,7 @@ describe('environment-utils', () => { }); it('should return overridden observability scope when A365_OBSERVABILITY_SCOPES_OVERRIDE is set', () => { + delete process.env.A365_OBSERVABILITY_SCOPE_OVERRIDE; process.env.A365_OBSERVABILITY_SCOPES_OVERRIDE = 'https://override.example.com/.default'; const scopes = getObservabilityAuthenticationScope(); @@ -41,6 +43,7 @@ describe('environment-utils', () => { }); it('should support multiple scopes separated by whitespace', () => { + delete process.env.A365_OBSERVABILITY_SCOPE_OVERRIDE; process.env.A365_OBSERVABILITY_SCOPES_OVERRIDE = 'scope-one/.default scope-two/.default'; const scopes = getObservabilityAuthenticationScope(); @@ -49,12 +52,40 @@ describe('environment-utils', () => { }); it('should fall back to production scope when override is empty or whitespace', () => { + delete process.env.A365_OBSERVABILITY_SCOPE_OVERRIDE; process.env.A365_OBSERVABILITY_SCOPES_OVERRIDE = ' '; const scopes = getObservabilityAuthenticationScope(); expect(scopes).toEqual([PROD_OBSERVABILITY_SCOPE]); }); + + it('should return overridden observability scope when A365_OBSERVABILITY_SCOPE_OVERRIDE is set (singular form for .NET SDK compatibility)', () => { + delete process.env.A365_OBSERVABILITY_SCOPES_OVERRIDE; + process.env.A365_OBSERVABILITY_SCOPE_OVERRIDE = 'https://override.singular.com/.default'; + + const scopes = getObservabilityAuthenticationScope(); + + expect(scopes).toEqual(['https://override.singular.com/.default']); + }); + + it('should prioritize A365_OBSERVABILITY_SCOPE_OVERRIDE (singular) over A365_OBSERVABILITY_SCOPES_OVERRIDE (plural) when both are set', () => { + process.env.A365_OBSERVABILITY_SCOPE_OVERRIDE = 'https://singular.example.com/.default'; + process.env.A365_OBSERVABILITY_SCOPES_OVERRIDE = 'https://plural.example.com/.default'; + + const scopes = getObservabilityAuthenticationScope(); + + expect(scopes).toEqual(['https://singular.example.com/.default']); + }); + + it('should fall back to production scope when singular override is empty or whitespace', () => { + delete process.env.A365_OBSERVABILITY_SCOPES_OVERRIDE; + process.env.A365_OBSERVABILITY_SCOPE_OVERRIDE = ' '; + + const scopes = getObservabilityAuthenticationScope(); + + expect(scopes).toEqual([PROD_OBSERVABILITY_SCOPE]); + }); }); describe('getClusterCategory', () => {