feat: add automatic region enrichment to all analytics events
All checks were successful
armco-org/analytics/pipeline/head This commit looks good

This commit is contained in:
2025-12-21 20:22:52 +05:30
parent a7448f078a
commit 59a0aed4a7
5 changed files with 149 additions and 3 deletions

View File

@@ -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",

View File

@@ -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

View File

@@ -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
View 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(),
};
}

View 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");
});
});
});