mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 21:11:53 +00:00
refactor(ui): rename integration enable flags to past tense (#11917)
Co-authored-by: Pablo F.G <pablo.fernandez@prowler.com>
This commit is contained in:
committed by
GitHub
parent
9d899ae0e2
commit
80c5363649
@@ -146,7 +146,7 @@ DJANGO_SENTRY_DSN=
|
||||
DJANGO_THROTTLE_TOKEN_OBTAIN=50/minute
|
||||
|
||||
# Sentry for the web app (server + browser). The UI_SENTRY_* values load only
|
||||
# when UI_SENTRY_ENABLE="true"; without it they are ignored (default off, zero
|
||||
# when UI_SENTRY_ENABLED="true"; without it they are ignored (default off, zero
|
||||
# egress). The deprecated NEXT_PUBLIC_SENTRY_DSN still activates Sentry without
|
||||
# the flag. SENTRY_RELEASE (unprefixed) feeds the web app's server/edge SDKs.
|
||||
UI_SENTRY_DSN=
|
||||
|
||||
@@ -45,16 +45,16 @@ Prowler App gates each optional third-party integration behind an explicit enabl
|
||||
|
||||
| Integration | Enable flag | Required configuration when enabled |
|
||||
|-------------|-------------|-------------------------------------|
|
||||
| Sentry (error monitoring) | `UI_SENTRY_ENABLE` | `UI_SENTRY_DSN` |
|
||||
| Google Tag Manager | `UI_GOOGLE_TAG_MANAGER_ENABLE` | `UI_GOOGLE_TAG_MANAGER_ID` |
|
||||
| PostHog (product analytics) | `UI_POSTHOG_ENABLE` | `UI_POSTHOG_KEY` and `UI_POSTHOG_HOST` |
|
||||
| Sentry (error monitoring) | `UI_SENTRY_ENABLED` | `UI_SENTRY_DSN` |
|
||||
| Google Tag Manager | `UI_GOOGLE_TAG_MANAGER_ENABLED` | `UI_GOOGLE_TAG_MANAGER_ID` |
|
||||
| PostHog (product analytics) | `UI_POSTHOG_ENABLED` | `UI_POSTHOG_KEY` and `UI_POSTHOG_HOST` |
|
||||
|
||||
When an integration is enabled but its required configuration is missing, Prowler App fails fast at server startup with a clear error, so a misconfigured container never starts silently. A new `UI_*` value set while its enable flag is not `"true"` is ignored, and the server logs a one-time startup warning noting that the integration will not load. Legacy names follow the backward-compatible rule described in [Deprecated Names](#deprecated-names).
|
||||
|
||||
PostHog support is currently limited to configuration validation: Prowler App reads and validates the PostHog variables but does not yet load a PostHog client.
|
||||
|
||||
<Warning>
|
||||
Configuring an integration through the new `UI_*` variables now requires its enable flag. A deployment that adopted `UI_SENTRY_DSN` or `UI_GOOGLE_TAG_MANAGER_ID` must also set `UI_SENTRY_ENABLE=true` or `UI_GOOGLE_TAG_MANAGER_ENABLE=true` to keep the integration active. Deployments still using the legacy names (`NEXT_PUBLIC_*`, or `POSTHOG_KEY` and `POSTHOG_HOST`) keep working without the flag.
|
||||
Configuring an integration through the new `UI_*` variables now requires its enable flag. A deployment that adopted `UI_SENTRY_DSN` or `UI_GOOGLE_TAG_MANAGER_ID` must also set `UI_SENTRY_ENABLED=true` or `UI_GOOGLE_TAG_MANAGER_ENABLED=true` to keep the integration active. Deployments still using the legacy names (`NEXT_PUBLIC_*`, or `POSTHOG_KEY` and `POSTHOG_HOST`) keep working without the flag.
|
||||
</Warning>
|
||||
|
||||
## Upcoming Breaking Change
|
||||
|
||||
+4
-4
@@ -79,12 +79,12 @@ ENV HOSTNAME="0.0.0.0"
|
||||
# Helm/K8s):
|
||||
# - required: UI_API_BASE_URL, AUTH_URL, AUTH_SECRET (missing ⇒ fail fast at boot)
|
||||
# - optional: UI_API_DOCS_URL
|
||||
# - gated integrations (load only when *_ENABLE="true"; the value is then
|
||||
# - gated integrations (load only when *_ENABLED="true"; the value is then
|
||||
# required or boot fails). Legacy names (NEXT_PUBLIC_*, POSTHOG_KEY/HOST)
|
||||
# still activate without the flag:
|
||||
# UI_SENTRY_ENABLE + UI_SENTRY_DSN (+ optional UI_SENTRY_ENVIRONMENT)
|
||||
# UI_GOOGLE_TAG_MANAGER_ENABLE + UI_GOOGLE_TAG_MANAGER_ID
|
||||
# UI_POSTHOG_ENABLE + UI_POSTHOG_KEY + UI_POSTHOG_HOST (no consumer yet)
|
||||
# UI_SENTRY_ENABLED + UI_SENTRY_DSN (+ optional UI_SENTRY_ENVIRONMENT)
|
||||
# UI_GOOGLE_TAG_MANAGER_ENABLED + UI_GOOGLE_TAG_MANAGER_ID
|
||||
# UI_POSTHOG_ENABLED + UI_POSTHOG_KEY + UI_POSTHOG_HOST (no consumer yet)
|
||||
# - reserved: REO_DEV_CLIENT_ID (no consumer yet)
|
||||
# server.js is created by next build from the standalone output
|
||||
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
|
||||
|
||||
@@ -43,7 +43,7 @@ export default async function AuthLayout({
|
||||
await connection();
|
||||
|
||||
const gtmId = readGatedEnv(
|
||||
"UI_GOOGLE_TAG_MANAGER_ENABLE",
|
||||
"UI_GOOGLE_TAG_MANAGER_ENABLED",
|
||||
"UI_GOOGLE_TAG_MANAGER_ID",
|
||||
"NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID",
|
||||
);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
UI integration enable flags renamed to past tense — `UI_SENTRY_ENABLE` → `UI_SENTRY_ENABLED`, `UI_GOOGLE_TAG_MANAGER_ENABLE` → `UI_GOOGLE_TAG_MANAGER_ENABLED`, `UI_POSTHOG_ENABLE` → `UI_POSTHOG_ENABLED`; deployments that set the former names must update them
|
||||
@@ -22,7 +22,7 @@ import * as Sentry from "@sentry/nextjs";
|
||||
import { readGatedEnv } from "@/lib/integrations";
|
||||
|
||||
const sentryDsn = readGatedEnv(
|
||||
"UI_SENTRY_ENABLE",
|
||||
"UI_SENTRY_ENABLED",
|
||||
"UI_SENTRY_DSN",
|
||||
"NEXT_PUBLIC_SENTRY_DSN",
|
||||
);
|
||||
|
||||
+11
-11
@@ -49,15 +49,15 @@ describe("lib/env gated integration validation", () => {
|
||||
// Clear every gated flag/config so ambient shell env cannot affect assertions,
|
||||
// then satisfy the unconditional REQUIRED vars (they are checked first).
|
||||
const GATED_ENV_VARS = [
|
||||
"UI_SENTRY_ENABLE",
|
||||
"UI_SENTRY_ENABLED",
|
||||
"UI_SENTRY_DSN",
|
||||
"NEXT_PUBLIC_SENTRY_DSN",
|
||||
"UI_SENTRY_ENVIRONMENT",
|
||||
"NEXT_PUBLIC_SENTRY_ENVIRONMENT",
|
||||
"UI_GOOGLE_TAG_MANAGER_ENABLE",
|
||||
"UI_GOOGLE_TAG_MANAGER_ENABLED",
|
||||
"UI_GOOGLE_TAG_MANAGER_ID",
|
||||
"NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID",
|
||||
"UI_POSTHOG_ENABLE",
|
||||
"UI_POSTHOG_ENABLED",
|
||||
"UI_POSTHOG_KEY",
|
||||
"POSTHOG_KEY",
|
||||
"UI_POSTHOG_HOST",
|
||||
@@ -80,14 +80,14 @@ describe("lib/env gated integration validation", () => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("throws when UI_SENTRY_ENABLE is true but the DSN is unset", async () => {
|
||||
vi.stubEnv("UI_SENTRY_ENABLE", "true");
|
||||
it("throws when UI_SENTRY_ENABLED is true but the DSN is unset", async () => {
|
||||
vi.stubEnv("UI_SENTRY_ENABLED", "true");
|
||||
|
||||
await expect(import("@/lib/env")).rejects.toThrow("UI_SENTRY_DSN");
|
||||
});
|
||||
|
||||
it("resolves when UI_SENTRY_ENABLE is true and the DSN is present", async () => {
|
||||
vi.stubEnv("UI_SENTRY_ENABLE", "true");
|
||||
it("resolves when UI_SENTRY_ENABLED is true and the DSN is present", async () => {
|
||||
vi.stubEnv("UI_SENTRY_ENABLED", "true");
|
||||
vi.stubEnv("UI_SENTRY_DSN", "https://key@o0.ingest.sentry.io/1");
|
||||
|
||||
await expect(import("@/lib/env")).resolves.toBeDefined();
|
||||
@@ -99,8 +99,8 @@ describe("lib/env gated integration validation", () => {
|
||||
await expect(import("@/lib/env")).resolves.toBeDefined();
|
||||
});
|
||||
|
||||
it("throws when UI_GOOGLE_TAG_MANAGER_ENABLE is true without an id", async () => {
|
||||
vi.stubEnv("UI_GOOGLE_TAG_MANAGER_ENABLE", "true");
|
||||
it("throws when UI_GOOGLE_TAG_MANAGER_ENABLED is true without an id", async () => {
|
||||
vi.stubEnv("UI_GOOGLE_TAG_MANAGER_ENABLED", "true");
|
||||
|
||||
await expect(import("@/lib/env")).rejects.toThrow(
|
||||
"UI_GOOGLE_TAG_MANAGER_ID",
|
||||
@@ -108,14 +108,14 @@ describe("lib/env gated integration validation", () => {
|
||||
});
|
||||
|
||||
it("throws on UI_POSTHOG_HOST when PostHog is enabled with only the key", async () => {
|
||||
vi.stubEnv("UI_POSTHOG_ENABLE", "true");
|
||||
vi.stubEnv("UI_POSTHOG_ENABLED", "true");
|
||||
vi.stubEnv("UI_POSTHOG_KEY", "phc_key");
|
||||
|
||||
await expect(import("@/lib/env")).rejects.toThrow("UI_POSTHOG_HOST");
|
||||
});
|
||||
|
||||
it("resolves when PostHog is enabled with both key and host", async () => {
|
||||
vi.stubEnv("UI_POSTHOG_ENABLE", "true");
|
||||
vi.stubEnv("UI_POSTHOG_ENABLED", "true");
|
||||
vi.stubEnv("UI_POSTHOG_KEY", "phc_key");
|
||||
vi.stubEnv("UI_POSTHOG_HOST", "https://eu.i.posthog.com");
|
||||
|
||||
|
||||
+24
-24
@@ -9,15 +9,15 @@ import {
|
||||
// Every env var any gated integration reads. Cleared before each test so the
|
||||
// assertions never depend on ambient shell/CI env.
|
||||
const GATED_ENV_VARS = [
|
||||
"UI_SENTRY_ENABLE",
|
||||
"UI_SENTRY_ENABLED",
|
||||
"UI_SENTRY_DSN",
|
||||
"NEXT_PUBLIC_SENTRY_DSN",
|
||||
"UI_SENTRY_ENVIRONMENT",
|
||||
"NEXT_PUBLIC_SENTRY_ENVIRONMENT",
|
||||
"UI_GOOGLE_TAG_MANAGER_ENABLE",
|
||||
"UI_GOOGLE_TAG_MANAGER_ENABLED",
|
||||
"UI_GOOGLE_TAG_MANAGER_ID",
|
||||
"NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID",
|
||||
"UI_POSTHOG_ENABLE",
|
||||
"UI_POSTHOG_ENABLED",
|
||||
"UI_POSTHOG_KEY",
|
||||
"POSTHOG_KEY",
|
||||
"UI_POSTHOG_HOST",
|
||||
@@ -37,13 +37,13 @@ afterEach(() => {
|
||||
describe("readGatedEnv", () => {
|
||||
it("returns the primary value when the integration is enabled", () => {
|
||||
// Given
|
||||
vi.stubEnv("UI_SENTRY_ENABLE", "true");
|
||||
vi.stubEnv("UI_SENTRY_ENABLED", "true");
|
||||
vi.stubEnv("UI_SENTRY_DSN", "https://dsn.example");
|
||||
|
||||
// When / Then
|
||||
expect(
|
||||
readGatedEnv(
|
||||
"UI_SENTRY_ENABLE",
|
||||
"UI_SENTRY_ENABLED",
|
||||
"UI_SENTRY_DSN",
|
||||
"NEXT_PUBLIC_SENTRY_DSN",
|
||||
),
|
||||
@@ -52,13 +52,13 @@ describe("readGatedEnv", () => {
|
||||
|
||||
it("falls back to the legacy value when enabled and the primary is unset", () => {
|
||||
// Given
|
||||
vi.stubEnv("UI_SENTRY_ENABLE", "true");
|
||||
vi.stubEnv("UI_SENTRY_ENABLED", "true");
|
||||
vi.stubEnv("NEXT_PUBLIC_SENTRY_DSN", "https://legacy.example");
|
||||
|
||||
// When / Then
|
||||
expect(
|
||||
readGatedEnv(
|
||||
"UI_SENTRY_ENABLE",
|
||||
"UI_SENTRY_ENABLED",
|
||||
"UI_SENTRY_DSN",
|
||||
"NEXT_PUBLIC_SENTRY_DSN",
|
||||
),
|
||||
@@ -67,13 +67,13 @@ describe("readGatedEnv", () => {
|
||||
|
||||
it("ignores the primary (new) value when disabled and no legacy is set", () => {
|
||||
// Given - the new UI_* name only counts when the enable flag is "true"
|
||||
vi.stubEnv("UI_SENTRY_ENABLE", "false");
|
||||
vi.stubEnv("UI_SENTRY_ENABLED", "false");
|
||||
vi.stubEnv("UI_SENTRY_DSN", "https://dsn.example");
|
||||
|
||||
// When / Then
|
||||
expect(
|
||||
readGatedEnv(
|
||||
"UI_SENTRY_ENABLE",
|
||||
"UI_SENTRY_ENABLED",
|
||||
"UI_SENTRY_DSN",
|
||||
"NEXT_PUBLIC_SENTRY_DSN",
|
||||
),
|
||||
@@ -82,13 +82,13 @@ describe("readGatedEnv", () => {
|
||||
|
||||
it("returns the legacy value when disabled (legacy ignores the enable flag)", () => {
|
||||
// Given - legacy names stay backward compatible: they work without the flag
|
||||
vi.stubEnv("UI_SENTRY_ENABLE", "false");
|
||||
vi.stubEnv("UI_SENTRY_ENABLED", "false");
|
||||
vi.stubEnv("NEXT_PUBLIC_SENTRY_DSN", "https://legacy.example");
|
||||
|
||||
// When / Then
|
||||
expect(
|
||||
readGatedEnv(
|
||||
"UI_SENTRY_ENABLE",
|
||||
"UI_SENTRY_ENABLED",
|
||||
"UI_SENTRY_DSN",
|
||||
"NEXT_PUBLIC_SENTRY_DSN",
|
||||
),
|
||||
@@ -97,14 +97,14 @@ describe("readGatedEnv", () => {
|
||||
|
||||
it("returns the legacy value when disabled even if the new value is also set", () => {
|
||||
// Given - new value is ignored without the flag; legacy still activates
|
||||
vi.stubEnv("UI_SENTRY_ENABLE", "false");
|
||||
vi.stubEnv("UI_SENTRY_ENABLED", "false");
|
||||
vi.stubEnv("UI_SENTRY_DSN", "https://dsn.example");
|
||||
vi.stubEnv("NEXT_PUBLIC_SENTRY_DSN", "https://legacy.example");
|
||||
|
||||
// When / Then
|
||||
expect(
|
||||
readGatedEnv(
|
||||
"UI_SENTRY_ENABLE",
|
||||
"UI_SENTRY_ENABLED",
|
||||
"UI_SENTRY_DSN",
|
||||
"NEXT_PUBLIC_SENTRY_DSN",
|
||||
),
|
||||
@@ -116,7 +116,7 @@ describe("readGatedEnv", () => {
|
||||
vi.stubEnv("UI_SENTRY_DSN", "https://dsn.example");
|
||||
|
||||
// When / Then
|
||||
expect(readGatedEnv("UI_SENTRY_ENABLE", "UI_SENTRY_DSN")).toBeNull();
|
||||
expect(readGatedEnv("UI_SENTRY_ENABLED", "UI_SENTRY_DSN")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -127,7 +127,7 @@ describe("assertGatedIntegrations", () => {
|
||||
|
||||
it("throws when Sentry is enabled but the DSN is unset", () => {
|
||||
// Given
|
||||
vi.stubEnv("UI_SENTRY_ENABLE", "true");
|
||||
vi.stubEnv("UI_SENTRY_ENABLED", "true");
|
||||
|
||||
// When / Then
|
||||
expect(() => assertGatedIntegrations()).toThrow("UI_SENTRY_DSN");
|
||||
@@ -135,7 +135,7 @@ describe("assertGatedIntegrations", () => {
|
||||
|
||||
it("does not throw when Sentry is enabled and the DSN is present", () => {
|
||||
// Given
|
||||
vi.stubEnv("UI_SENTRY_ENABLE", "true");
|
||||
vi.stubEnv("UI_SENTRY_ENABLED", "true");
|
||||
vi.stubEnv("UI_SENTRY_DSN", "https://dsn.example");
|
||||
|
||||
// When / Then
|
||||
@@ -144,7 +144,7 @@ describe("assertGatedIntegrations", () => {
|
||||
|
||||
it("accepts the legacy DSN name when Sentry is enabled", () => {
|
||||
// Given
|
||||
vi.stubEnv("UI_SENTRY_ENABLE", "true");
|
||||
vi.stubEnv("UI_SENTRY_ENABLED", "true");
|
||||
vi.stubEnv("NEXT_PUBLIC_SENTRY_DSN", "https://legacy.example");
|
||||
|
||||
// When / Then
|
||||
@@ -153,7 +153,7 @@ describe("assertGatedIntegrations", () => {
|
||||
|
||||
it("does not require the optional Sentry environment when enabled", () => {
|
||||
// Given - DSN present, UI_SENTRY_ENVIRONMENT intentionally unset
|
||||
vi.stubEnv("UI_SENTRY_ENABLE", "true");
|
||||
vi.stubEnv("UI_SENTRY_ENABLED", "true");
|
||||
vi.stubEnv("UI_SENTRY_DSN", "https://dsn.example");
|
||||
|
||||
// When / Then
|
||||
@@ -162,7 +162,7 @@ describe("assertGatedIntegrations", () => {
|
||||
|
||||
it("throws when GTM is enabled without an id", () => {
|
||||
// Given
|
||||
vi.stubEnv("UI_GOOGLE_TAG_MANAGER_ENABLE", "true");
|
||||
vi.stubEnv("UI_GOOGLE_TAG_MANAGER_ENABLED", "true");
|
||||
|
||||
// When / Then
|
||||
expect(() => assertGatedIntegrations()).toThrow("UI_GOOGLE_TAG_MANAGER_ID");
|
||||
@@ -170,7 +170,7 @@ describe("assertGatedIntegrations", () => {
|
||||
|
||||
it("requires BOTH UI_POSTHOG_KEY and UI_POSTHOG_HOST when PostHog is enabled", () => {
|
||||
// Given - key set, host missing
|
||||
vi.stubEnv("UI_POSTHOG_ENABLE", "true");
|
||||
vi.stubEnv("UI_POSTHOG_ENABLED", "true");
|
||||
vi.stubEnv("UI_POSTHOG_KEY", "phc_key");
|
||||
|
||||
// When / Then
|
||||
@@ -179,7 +179,7 @@ describe("assertGatedIntegrations", () => {
|
||||
|
||||
it("does not throw when PostHog is enabled with both key and host", () => {
|
||||
// Given
|
||||
vi.stubEnv("UI_POSTHOG_ENABLE", "true");
|
||||
vi.stubEnv("UI_POSTHOG_ENABLED", "true");
|
||||
vi.stubEnv("UI_POSTHOG_KEY", "phc_key");
|
||||
vi.stubEnv("UI_POSTHOG_HOST", "https://eu.i.posthog.com");
|
||||
|
||||
@@ -196,7 +196,7 @@ describe("assertGatedIntegrations", () => {
|
||||
});
|
||||
|
||||
it("accepts the legacy PostHog names without the enable flag", () => {
|
||||
// Given - both legacy names present, no UI_POSTHOG_ENABLE
|
||||
// Given - both legacy names present, no UI_POSTHOG_ENABLED
|
||||
vi.stubEnv("POSTHOG_KEY", "phc_key");
|
||||
vi.stubEnv("POSTHOG_HOST", "https://eu.i.posthog.com");
|
||||
|
||||
@@ -224,13 +224,13 @@ describe("warnGatedIntegrationsMisconfig", () => {
|
||||
|
||||
// Then
|
||||
expect(warn).toHaveBeenCalledTimes(1);
|
||||
expect(warn.mock.calls[0][0]).toContain("UI_SENTRY_ENABLE");
|
||||
expect(warn.mock.calls[0][0]).toContain("UI_SENTRY_ENABLED");
|
||||
});
|
||||
|
||||
it("does not warn when the integration is enabled", () => {
|
||||
// Given
|
||||
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
||||
vi.stubEnv("UI_SENTRY_ENABLE", "true");
|
||||
vi.stubEnv("UI_SENTRY_ENABLED", "true");
|
||||
vi.stubEnv("UI_SENTRY_DSN", "https://dsn.example");
|
||||
|
||||
// When
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { readBoolEnv, readEnv } from "@/lib/runtime-env";
|
||||
|
||||
// Single source of truth for the third-party integrations gated behind a
|
||||
// UI_*_ENABLE flag. The same map drives boot validation (lib/env.ts) and the
|
||||
// UI_*_ENABLED flag. The same map drives boot validation (lib/env.ts) and the
|
||||
// runtime-config gating (lib/runtime-config.ts) so the two cannot drift.
|
||||
//
|
||||
// Two activation paths, applied uniformly to every integration:
|
||||
@@ -27,7 +27,7 @@ interface GatedIntegration {
|
||||
export const GATED_INTEGRATIONS: Record<string, GatedIntegration> = {
|
||||
sentry: {
|
||||
name: "Sentry",
|
||||
enableKey: "UI_SENTRY_ENABLE",
|
||||
enableKey: "UI_SENTRY_ENABLED",
|
||||
required: [{ key: "UI_SENTRY_DSN", legacy: "NEXT_PUBLIC_SENTRY_DSN" }],
|
||||
optional: [
|
||||
{
|
||||
@@ -38,7 +38,7 @@ export const GATED_INTEGRATIONS: Record<string, GatedIntegration> = {
|
||||
},
|
||||
googleTagManager: {
|
||||
name: "Google Tag Manager",
|
||||
enableKey: "UI_GOOGLE_TAG_MANAGER_ENABLE",
|
||||
enableKey: "UI_GOOGLE_TAG_MANAGER_ENABLED",
|
||||
required: [
|
||||
{
|
||||
key: "UI_GOOGLE_TAG_MANAGER_ID",
|
||||
@@ -49,7 +49,7 @@ export const GATED_INTEGRATIONS: Record<string, GatedIntegration> = {
|
||||
},
|
||||
posthog: {
|
||||
name: "PostHog",
|
||||
enableKey: "UI_POSTHOG_ENABLE",
|
||||
enableKey: "UI_POSTHOG_ENABLED",
|
||||
required: [
|
||||
{ key: "UI_POSTHOG_KEY", legacy: "POSTHOG_KEY" },
|
||||
{ key: "UI_POSTHOG_HOST", legacy: "POSTHOG_HOST" },
|
||||
|
||||
@@ -10,36 +10,36 @@ import { readBoolEnv, readEnv } from "@/lib/runtime-env";
|
||||
// only this allowlist reaches the client. Each migrated key falls back to its
|
||||
// deprecated NEXT_PUBLIC_* name during migration (see readEnv). Gated
|
||||
// integrations (Sentry/GTM/PostHog) resolve to their value only when the
|
||||
// matching UI_*_ENABLE flag is "true", else null — boot validation
|
||||
// matching UI_*_ENABLED flag is "true", else null — boot validation
|
||||
// (lib/env.ts) guarantees the value is present whenever the flag is set.
|
||||
export async function getRuntimePublicConfig(): Promise<RuntimePublicConfig> {
|
||||
await connection();
|
||||
|
||||
return {
|
||||
sentryDsn: readGatedEnv(
|
||||
"UI_SENTRY_ENABLE",
|
||||
"UI_SENTRY_ENABLED",
|
||||
"UI_SENTRY_DSN",
|
||||
"NEXT_PUBLIC_SENTRY_DSN",
|
||||
),
|
||||
sentryEnvironment: readGatedEnv(
|
||||
"UI_SENTRY_ENABLE",
|
||||
"UI_SENTRY_ENABLED",
|
||||
"UI_SENTRY_ENVIRONMENT",
|
||||
"NEXT_PUBLIC_SENTRY_ENVIRONMENT",
|
||||
),
|
||||
googleTagManagerId: readGatedEnv(
|
||||
"UI_GOOGLE_TAG_MANAGER_ENABLE",
|
||||
"UI_GOOGLE_TAG_MANAGER_ENABLED",
|
||||
"UI_GOOGLE_TAG_MANAGER_ID",
|
||||
"NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID",
|
||||
),
|
||||
apiBaseUrl: readEnv("UI_API_BASE_URL", "NEXT_PUBLIC_API_BASE_URL"),
|
||||
apiDocsUrl: readEnv("UI_API_DOCS_URL", "NEXT_PUBLIC_API_DOCS_URL"),
|
||||
posthogKey: readGatedEnv(
|
||||
"UI_POSTHOG_ENABLE",
|
||||
"UI_POSTHOG_ENABLED",
|
||||
"UI_POSTHOG_KEY",
|
||||
"POSTHOG_KEY",
|
||||
),
|
||||
posthogHost: readGatedEnv(
|
||||
"UI_POSTHOG_ENABLE",
|
||||
"UI_POSTHOG_ENABLED",
|
||||
"UI_POSTHOG_HOST",
|
||||
"POSTHOG_HOST",
|
||||
),
|
||||
|
||||
+10
-10
@@ -81,43 +81,43 @@ describe("readBoolEnv", () => {
|
||||
|
||||
it('is true only for the exact string "true"', () => {
|
||||
// Given
|
||||
vi.stubEnv("UI_SENTRY_ENABLE", "true");
|
||||
vi.stubEnv("UI_SENTRY_ENABLED", "true");
|
||||
|
||||
// When / Then
|
||||
expect(readBoolEnv("UI_SENTRY_ENABLE")).toBe(true);
|
||||
expect(readBoolEnv("UI_SENTRY_ENABLED")).toBe(true);
|
||||
});
|
||||
|
||||
it("trims surrounding whitespace before comparing", () => {
|
||||
// Given - clean() does not trim a non-empty value, so readBoolEnv must
|
||||
vi.stubEnv("UI_SENTRY_ENABLE", " true ");
|
||||
vi.stubEnv("UI_SENTRY_ENABLED", " true ");
|
||||
|
||||
// When / Then
|
||||
expect(readBoolEnv("UI_SENTRY_ENABLE")).toBe(true);
|
||||
expect(readBoolEnv("UI_SENTRY_ENABLED")).toBe(true);
|
||||
});
|
||||
|
||||
it("is false when unset", () => {
|
||||
// Given
|
||||
vi.stubEnv("UI_SENTRY_ENABLE", undefined);
|
||||
vi.stubEnv("UI_SENTRY_ENABLED", undefined);
|
||||
|
||||
// When / Then
|
||||
expect(readBoolEnv("UI_SENTRY_ENABLE")).toBe(false);
|
||||
expect(readBoolEnv("UI_SENTRY_ENABLED")).toBe(false);
|
||||
});
|
||||
|
||||
it('is false for "false"', () => {
|
||||
// Given
|
||||
vi.stubEnv("UI_SENTRY_ENABLE", "false");
|
||||
vi.stubEnv("UI_SENTRY_ENABLED", "false");
|
||||
|
||||
// When / Then
|
||||
expect(readBoolEnv("UI_SENTRY_ENABLE")).toBe(false);
|
||||
expect(readBoolEnv("UI_SENTRY_ENABLED")).toBe(false);
|
||||
});
|
||||
|
||||
it('is false for other truthy-looking values ("TRUE", "1", "yes")', () => {
|
||||
for (const value of ["TRUE", "1", "yes"]) {
|
||||
// Given
|
||||
vi.stubEnv("UI_SENTRY_ENABLE", value);
|
||||
vi.stubEnv("UI_SENTRY_ENABLED", value);
|
||||
|
||||
// When / Then
|
||||
expect(readBoolEnv("UI_SENTRY_ENABLE")).toBe(false);
|
||||
expect(readBoolEnv("UI_SENTRY_ENABLED")).toBe(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,7 +20,7 @@ describe("sentry.edge.config", () => {
|
||||
|
||||
it("should initialize with the resolved environment and reduced edge sampling", async () => {
|
||||
// Given
|
||||
vi.stubEnv("UI_SENTRY_ENABLE", "true");
|
||||
vi.stubEnv("UI_SENTRY_ENABLED", "true");
|
||||
vi.stubEnv("UI_SENTRY_DSN", "https://key@o0.ingest.sentry.io/1");
|
||||
vi.stubEnv("UI_SENTRY_ENVIRONMENT", "pro");
|
||||
|
||||
@@ -46,7 +46,7 @@ describe("sentry.edge.config", () => {
|
||||
expect(initMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should not initialize when UI_SENTRY_ENABLE is unset even if a DSN is set", async () => {
|
||||
it("should not initialize when UI_SENTRY_ENABLED is unset even if a DSN is set", async () => {
|
||||
// Given - DSN configured but the enable flag is not "true"
|
||||
vi.stubEnv("UI_SENTRY_DSN", "https://key@o0.ingest.sentry.io/1");
|
||||
|
||||
@@ -59,7 +59,7 @@ describe("sentry.edge.config", () => {
|
||||
|
||||
it("should default to a non-dev environment so an unset UI_SENTRY_ENVIRONMENT does not enable dev sampling", async () => {
|
||||
// Given - enabled with a DSN but environment unset
|
||||
vi.stubEnv("UI_SENTRY_ENABLE", "true");
|
||||
vi.stubEnv("UI_SENTRY_ENABLED", "true");
|
||||
vi.stubEnv("UI_SENTRY_DSN", "https://key@o0.ingest.sentry.io/1");
|
||||
|
||||
// When
|
||||
|
||||
@@ -3,12 +3,12 @@ import * as Sentry from "@sentry/nextjs";
|
||||
import { readGatedEnv } from "@/lib/integrations";
|
||||
|
||||
const sentryDsn = readGatedEnv(
|
||||
"UI_SENTRY_ENABLE",
|
||||
"UI_SENTRY_ENABLED",
|
||||
"UI_SENTRY_DSN",
|
||||
"NEXT_PUBLIC_SENTRY_DSN",
|
||||
);
|
||||
const sentryEnvironment = readGatedEnv(
|
||||
"UI_SENTRY_ENABLE",
|
||||
"UI_SENTRY_ENABLED",
|
||||
"UI_SENTRY_ENVIRONMENT",
|
||||
"NEXT_PUBLIC_SENTRY_ENVIRONMENT",
|
||||
);
|
||||
|
||||
@@ -21,7 +21,7 @@ describe("sentry.server.config", () => {
|
||||
|
||||
it("should initialize with the resolved environment and production sampling", async () => {
|
||||
// Given
|
||||
vi.stubEnv("UI_SENTRY_ENABLE", "true");
|
||||
vi.stubEnv("UI_SENTRY_ENABLED", "true");
|
||||
vi.stubEnv("UI_SENTRY_DSN", "https://key@o0.ingest.sentry.io/1");
|
||||
vi.stubEnv("UI_SENTRY_ENVIRONMENT", "pro");
|
||||
|
||||
@@ -48,7 +48,7 @@ describe("sentry.server.config", () => {
|
||||
expect(initMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should not initialize when UI_SENTRY_ENABLE is unset even if a DSN is set", async () => {
|
||||
it("should not initialize when UI_SENTRY_ENABLED is unset even if a DSN is set", async () => {
|
||||
// Given - DSN configured but the enable flag is not "true"
|
||||
vi.stubEnv("UI_SENTRY_DSN", "https://key@o0.ingest.sentry.io/1");
|
||||
|
||||
@@ -61,7 +61,7 @@ describe("sentry.server.config", () => {
|
||||
|
||||
it("should default to a non-dev environment so an unset UI_SENTRY_ENVIRONMENT does not enable dev sampling", async () => {
|
||||
// Given - enabled with a DSN but environment unset
|
||||
vi.stubEnv("UI_SENTRY_ENABLE", "true");
|
||||
vi.stubEnv("UI_SENTRY_ENABLED", "true");
|
||||
vi.stubEnv("UI_SENTRY_DSN", "https://key@o0.ingest.sentry.io/1");
|
||||
|
||||
// When
|
||||
|
||||
@@ -3,12 +3,12 @@ import * as Sentry from "@sentry/nextjs";
|
||||
import { readGatedEnv } from "@/lib/integrations";
|
||||
|
||||
const sentryDsn = readGatedEnv(
|
||||
"UI_SENTRY_ENABLE",
|
||||
"UI_SENTRY_ENABLED",
|
||||
"UI_SENTRY_DSN",
|
||||
"NEXT_PUBLIC_SENTRY_DSN",
|
||||
);
|
||||
const sentryEnvironment = readGatedEnv(
|
||||
"UI_SENTRY_ENABLE",
|
||||
"UI_SENTRY_ENABLED",
|
||||
"UI_SENTRY_ENVIRONMENT",
|
||||
"NEXT_PUBLIC_SENTRY_ENVIRONMENT",
|
||||
);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
**Suite ID:** `RUNTIME-CONFIG-E2E`
|
||||
**Feature:** Runtime resolution of public client config via an inert JSON data
|
||||
island injected into `<head>` (Sentry, GTM, API base/docs URL, and reserved keys). Each third-party integration
|
||||
resolves to a value only when its `UI_*_ENABLE` flag is `"true"`, so a disabled
|
||||
resolves to a value only when its `UI_*_ENABLED` flag is `"true"`, so a disabled
|
||||
integration is exposed as `null`.
|
||||
|
||||
---
|
||||
@@ -42,7 +42,7 @@ integration is exposed as `null`.
|
||||
|
||||
**Preconditions:**
|
||||
|
||||
- UI server running. Sentry may be enabled (`UI_SENTRY_ENABLE=true` with `UI_SENTRY_DSN`) or off.
|
||||
- UI server running. Sentry may be enabled (`UI_SENTRY_ENABLED=true` with `UI_SENTRY_DSN`) or off.
|
||||
|
||||
### Flow Steps
|
||||
|
||||
@@ -70,7 +70,7 @@ integration is exposed as `null`.
|
||||
|
||||
**Preconditions:**
|
||||
|
||||
- UI server running with Sentry and Google Tag Manager disabled — their `UI_SENTRY_ENABLE` and `UI_GOOGLE_TAG_MANAGER_ENABLE` flags unset, the Enterprise default — so the island exposes both as `null` (the test skips when either is enabled and configured).
|
||||
- UI server running with Sentry and Google Tag Manager disabled — their `UI_SENTRY_ENABLED` and `UI_GOOGLE_TAG_MANAGER_ENABLED` flags unset, the Enterprise default — so the island exposes both as `null` (the test skips when either is enabled and configured).
|
||||
|
||||
### Flow Steps
|
||||
|
||||
|
||||
Vendored
+3
-3
@@ -15,12 +15,12 @@ declare global {
|
||||
NEXT_PUBLIC_API_DOCS_URL?: string;
|
||||
UI_API_DOCS_URL?: string;
|
||||
|
||||
UI_GOOGLE_TAG_MANAGER_ENABLE?: "true" | "false";
|
||||
UI_GOOGLE_TAG_MANAGER_ENABLED?: "true" | "false";
|
||||
/** @deprecated use UI_GOOGLE_TAG_MANAGER_ID */
|
||||
NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID?: string;
|
||||
UI_GOOGLE_TAG_MANAGER_ID?: string;
|
||||
|
||||
UI_SENTRY_ENABLE?: "true" | "false";
|
||||
UI_SENTRY_ENABLED?: "true" | "false";
|
||||
/** @deprecated use UI_SENTRY_DSN */
|
||||
NEXT_PUBLIC_SENTRY_DSN?: string;
|
||||
UI_SENTRY_DSN?: string;
|
||||
@@ -46,7 +46,7 @@ declare global {
|
||||
SENTRY_PROJECT?: string;
|
||||
SENTRY_AUTH_TOKEN?: string;
|
||||
|
||||
UI_POSTHOG_ENABLE?: "true" | "false";
|
||||
UI_POSTHOG_ENABLED?: "true" | "false";
|
||||
/** @deprecated use UI_POSTHOG_KEY */
|
||||
POSTHOG_KEY?: string;
|
||||
UI_POSTHOG_KEY?: string;
|
||||
|
||||
Reference in New Issue
Block a user