Skip to content

Commit

Permalink
refactor(gate): add err msg for missing env vars (#827)
Browse files Browse the repository at this point in the history
<!--
Pull requests are squashed and merged using:
- their title as the commit message
- their description as the commit body

Having a good title and description is important for the users to get
readable changelog.
-->

<!-- 1. Explain WHAT the change is about -->

-

<!-- 2. Explain WHY the change cannot be made simpler -->

-

<!-- 3. Explain HOW users should update their code -->

#### Migration notes

...

- [ ] The change comes with new or modified tests
- [ ] Hard-to-understand functions have explanatory comments
- [ ] End-user documentation is updated to reflect the change
  • Loading branch information
destifo authored Sep 9, 2024
1 parent 6706680 commit 8da5053
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 45 deletions.
2 changes: 1 addition & 1 deletion .ghjk/lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -1978,7 +1978,7 @@
]
}
},
"defaultEnv": "ci",
"defaultEnv": "dev",
"envsNamed": {
"main": "bciqkx2kodz5dx5pv3hjcc4vpirphyetattk4oks5pucb7dsqrgwzqwa",
"_wasm": "bciqk7aif25pqtsyncbnbdapte4lixsodypfxvmkepsuey74vgr4zb5q",
Expand Down
3 changes: 0 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3591,6 +3591,3 @@ _N/A_
- [ ] End-user documentation is updated to reflect the change

</details>



69 changes: 36 additions & 33 deletions src/typegate/src/config/types.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,41 @@
// Copyright Metatype OÜ, licensed under the Elastic License 2.0.
// SPDX-License-Identifier: Elastic-2.0

import { z } from "zod";
import { z, RefinementCtx } from "zod";
import { decodeBase64 } from "@std/encoding/base64";
import type { RedisConnectOptions } from "redis";
import type { S3ClientConfig } from "aws-sdk/client-s3";

const zBooleanString = z.preprocess(
(a: unknown) => z.coerce.string().parse(a) === "true",
z.boolean(),
z.boolean()
);

const addMissingEnvVarIssue = (envVar: string, ctx: RefinementCtx) => {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Error: Env var ${envVar} is not configured.`,
});

return z.NEVER;
};

const refineEnvVar = (envVar: string) => z.string().optional().transform((s: string | undefined, ctx) => {
if (s === undefined) {
return addMissingEnvVarIssue(envVar, ctx);
}

return s;
});

const refineURLEnvVar = (envVar: string) => z.string().optional().transform((s: string | undefined, ctx) => {
if (s === undefined) {
return addMissingEnvVarIssue(envVar, ctx);
}

return new URL(s);
});

export const globalConfigSchema = z.object({
debug: zBooleanString,
// To be set to false when running from source.
Expand All @@ -37,44 +62,22 @@ export const typegateConfigBaseSchema = z.object({
.optional()
.transform((s: string | undefined, ctx) => {
if (s === undefined) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message:
"Error: Env var TG_SECRET is not configured. Exiting from the application.",
});

return z.NEVER;
return addMissingEnvVarIssue("TG_SECRET", ctx);
}

const bytes = decodeBase64(s);
if (bytes.length != 64) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message:
`Base64 contains ${bytes.length} instead of 64 bytes (use openssl rand -base64 64 | tr -d '\n')`,
message: `Base64 contains ${bytes.length} instead of 64 bytes (use openssl rand -base64 64 | tr -d '\n')`,
});
}
return bytes;
}),
timer_max_timeout_ms: z.coerce.number().positive().max(60000),
timer_destroy_resources: z.boolean(),
timer_policy_eval_retries: z.number().nonnegative().max(5),
tg_admin_password: z
.string()
.optional()
.transform((s: string | undefined, ctx) => {
if (s === undefined) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message:
"Error: Env var TG_ADMIN_PASSWORD is not configured. Exiting from the application.",
});

return z.NEVER;
}

return s;
}),
tg_admin_password: refineEnvVar("TG_ADMIN_PASSWORD"),
tmp_dir: z.string(),
jwt_max_duration_sec: z.coerce.number().positive(),
jwt_refresh_duration_sec: z.coerce.number().positive(),
Expand All @@ -88,12 +91,12 @@ export type TypegateConfigBase = z.infer<typeof typegateConfigBaseSchema>;
// These config entries are only accessible on a Typegate instance.
// They are not read from a global variable to enable test isolation and configurability.
export const syncConfigSchema = z.object({
redis_url: z.string().transform((s) => new URL(s)),
s3_host: z.string().transform((s) => new URL(s)),
s3_region: z.string(),
s3_bucket: z.string(),
s3_access_key: z.string(),
s3_secret_key: z.string(),
redis_url: refineURLEnvVar("SYNC_REDIS_URL"),
s3_host: refineURLEnvVar("SYNC_S3_HOST"),
s3_region: refineEnvVar("SYNC_S3_REGION"),
s3_bucket: refineEnvVar("SYNC_S3_BUCKET"),
s3_access_key: refineEnvVar("SYNC_S3_ACCESS_KEY"),
s3_secret_key: refineEnvVar("SYNC_S3_SECRET_KEY"),
s3_path_style: zBooleanString.default(false),
});
export type SyncConfig = z.infer<typeof syncConfigSchema>;
Expand Down
12 changes: 4 additions & 8 deletions tests/sync/sync_config_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,9 @@ Deno.test("test sync config", async (t) => {
"s3_access_key",
"s3_secret_key",
].map((k) => ({
code: "invalid_type",
expected: "string",
message: "Required",
code: "custom",
message: `Error: Env var SYNC_${k.toUpperCase()} is not configured.`,
path: [k],
received: "undefined",
})),
);

Expand All @@ -90,11 +88,9 @@ Deno.test("test sync config", async (t) => {
Deno.env.set("SYNC_S3_BUCKET", "bucket");
assertInvalidSyncConfig([
{
code: "invalid_type",
expected: "string",
message: "Required",
code: "custom",
message: "Error: Env var SYNC_S3_SECRET_KEY is not configured.",
path: ["s3_secret_key"],
received: "undefined",
},
]);
});
Expand Down

0 comments on commit 8da5053

Please sign in to comment.