feat: add automatic region enrichment to all analytics events
All checks were successful
armco-org/analytics/pipeline/head This commit looks good
All checks were successful
armco-org/analytics/pipeline/head This commit looks good
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@armco/analytics",
|
||||
"version": "0.3.1",
|
||||
"version": "0.3.2",
|
||||
"description": "Universal Analytics Library for Browser and Node.js",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
getTimestamp,
|
||||
deepMerge,
|
||||
} from "../utils/helpers";
|
||||
import { ensureRegion } from "../utils/region";
|
||||
import { resolveEndpoint, detectEnvironment } from "../utils/config-loader";
|
||||
import type { EndpointConfig } from "./types";
|
||||
|
||||
@@ -316,12 +317,12 @@ export class Analytics implements IAnalytics {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create base event
|
||||
// Create base event with region enrichment
|
||||
const event: TrackingEvent<T> = {
|
||||
eventType,
|
||||
timestamp: getTimestamp(),
|
||||
eventId: uuidv4(),
|
||||
data: sanitizeEventData(data || {}) as T,
|
||||
data: ensureRegion(sanitizeEventData(data || {})) as T,
|
||||
};
|
||||
|
||||
// Process through plugins
|
||||
|
||||
@@ -95,6 +95,8 @@ export {
|
||||
resolveEndpoint
|
||||
} from "./utils/config-loader";
|
||||
|
||||
export { getRegionFallback, ensureRegion } from "./utils/region";
|
||||
|
||||
// Create helper function
|
||||
import { AnalyticsBuilder as Builder } from "./core/analytics";
|
||||
|
||||
|
||||
60
src/utils/region.ts
Normal file
60
src/utils/region.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Region detection and enrichment utilities
|
||||
*
|
||||
* Ensures every analytics event has a `region` property.
|
||||
* Priority:
|
||||
* 1. Explicit region in event data (preserved)
|
||||
* 2. Environment variables: ANALYTICS_REGION, REGION, AWS_REGION, AWS_DEFAULT_REGION
|
||||
* 3. Fallback: 'local' in development/local env, 'unknown' otherwise
|
||||
*/
|
||||
|
||||
import { getEnvironmentType } from "./helpers";
|
||||
import { detectEnvironment } from "./config-loader";
|
||||
|
||||
/**
|
||||
* Get fallback region from environment
|
||||
*/
|
||||
export function getRegionFallback(): string {
|
||||
const envType = getEnvironmentType();
|
||||
|
||||
if (envType === "node") {
|
||||
const explicit =
|
||||
process.env.ANALYTICS_REGION ||
|
||||
process.env.REGION ||
|
||||
process.env.AWS_REGION ||
|
||||
process.env.AWS_DEFAULT_REGION;
|
||||
|
||||
if (explicit && explicit.trim().length > 0) {
|
||||
return explicit.trim();
|
||||
}
|
||||
|
||||
const currentEnv = detectEnvironment();
|
||||
return currentEnv === "development" || currentEnv === "local"
|
||||
? "local"
|
||||
: "unknown";
|
||||
}
|
||||
|
||||
// Browser: attempt to read from window config or default
|
||||
if (envType === "browser") {
|
||||
const win = window as any;
|
||||
if (win.__ANALYTICS_REGION__ && typeof win.__ANALYTICS_REGION__ === "string") {
|
||||
return win.__ANALYTICS_REGION__;
|
||||
}
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure event data always has a region property
|
||||
*/
|
||||
export function ensureRegion<T extends Record<string, unknown>>(data: T): T {
|
||||
if (data.region && typeof data.region === "string" && data.region.trim().length > 0) {
|
||||
return data;
|
||||
}
|
||||
|
||||
return {
|
||||
...data,
|
||||
region: getRegionFallback(),
|
||||
};
|
||||
}
|
||||
83
tests/unit/utils/region.test.ts
Normal file
83
tests/unit/utils/region.test.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { getRegionFallback, ensureRegion } from "../../../src/utils/region";
|
||||
|
||||
describe("Region Utils", () => {
|
||||
const originalEnv = process.env;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
process.env = { ...originalEnv };
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
process.env = originalEnv;
|
||||
});
|
||||
|
||||
describe("getRegionFallback", () => {
|
||||
it("should return ANALYTICS_REGION if set", () => {
|
||||
process.env.ANALYTICS_REGION = "US";
|
||||
expect(getRegionFallback()).toBe("US");
|
||||
});
|
||||
|
||||
it("should return REGION if ANALYTICS_REGION not set", () => {
|
||||
delete process.env.ANALYTICS_REGION;
|
||||
process.env.REGION = "EU";
|
||||
expect(getRegionFallback()).toBe("EU");
|
||||
});
|
||||
|
||||
it("should return AWS_REGION as fallback", () => {
|
||||
delete process.env.ANALYTICS_REGION;
|
||||
delete process.env.REGION;
|
||||
process.env.AWS_REGION = "ap-south-1";
|
||||
expect(getRegionFallback()).toBe("ap-south-1");
|
||||
});
|
||||
|
||||
it("should return 'local' in development environment", () => {
|
||||
delete process.env.ANALYTICS_REGION;
|
||||
delete process.env.REGION;
|
||||
delete process.env.AWS_REGION;
|
||||
delete process.env.AWS_DEFAULT_REGION;
|
||||
process.env.NODE_ENV = "development";
|
||||
expect(getRegionFallback()).toBe("local");
|
||||
});
|
||||
|
||||
it("should return 'unknown' in production environment with no env vars", () => {
|
||||
delete process.env.ANALYTICS_REGION;
|
||||
delete process.env.REGION;
|
||||
delete process.env.AWS_REGION;
|
||||
delete process.env.AWS_DEFAULT_REGION;
|
||||
process.env.NODE_ENV = "production";
|
||||
expect(getRegionFallback()).toBe("unknown");
|
||||
});
|
||||
});
|
||||
|
||||
describe("ensureRegion", () => {
|
||||
it("should preserve existing region", () => {
|
||||
const data = { region: "IN", foo: "bar" };
|
||||
const result = ensureRegion(data);
|
||||
expect(result.region).toBe("IN");
|
||||
expect(result.foo).toBe("bar");
|
||||
});
|
||||
|
||||
it("should add region when missing", () => {
|
||||
process.env.ANALYTICS_REGION = "US";
|
||||
const data = { foo: "bar" };
|
||||
const result = ensureRegion(data as any);
|
||||
expect(result.region).toBe("US");
|
||||
expect(result.foo).toBe("bar");
|
||||
});
|
||||
|
||||
it("should add region when empty string", () => {
|
||||
process.env.ANALYTICS_REGION = "EU";
|
||||
const data = { region: "", foo: "bar" };
|
||||
const result = ensureRegion(data);
|
||||
expect(result.region).toBe("EU");
|
||||
});
|
||||
|
||||
it("should add region when whitespace only", () => {
|
||||
process.env.ANALYTICS_REGION = "APAC";
|
||||
const data = { region: " ", foo: "bar" };
|
||||
const result = ensureRegion(data);
|
||||
expect(result.region).toBe("APAC");
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user