Skip to content

Commit d272a5e

Browse files
authored
Merge pull request #4874 from Infisical/dynamic-import-nock
Move nock to dev deps and include the route only for dev/test builds
2 parents 00ec4f9 + c5802c6 commit d272a5e

File tree

18 files changed

+170
-123
lines changed

18 files changed

+170
-123
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,4 @@ cli/test/infisical-merge
7474
backend/bdd/.bdd-infisical-bootstrap-result.json
7575

7676
/npm/bin
77+
__pycache__

backend/bdd/features/steps/pki_acme.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -615,13 +615,14 @@ def step_impl(context: Context, var_path: str, jq_query: str, expected: str):
615615

616616
@then('the value {var_path} with jq "{jq_query}" should match pattern {regex}')
617617
def step_impl(context: Context, var_path: str, jq_query: str, regex: str):
618+
actual_regex = replace_vars(regex, context.vars)
618619
value, result = apply_value_with_jq(
619620
context=context,
620621
var_path=var_path,
621622
jq_query=jq_query,
622623
)
623-
assert re.match(replace_vars(regex, context.vars), result), (
624-
f"{json.dumps(value)!r} with jq {jq_query!r}, the result {json.dumps(result)!r} does not match {regex!r}"
624+
assert re.match(actual_regex, result), (
625+
f"{json.dumps(value)!r} with jq {jq_query!r}, the result {json.dumps(result)!r} does not match {actual_regex!r}"
625626
)
626627

627628

backend/bdd/features/steps/utils.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
ACC_KEY_BITS = 2048
1717
ACC_KEY_PUBLIC_EXPONENT = 65537
18+
NOCK_API_PREFIX = "/api/__bdd_nock__"
1819
logger = logging.getLogger(__name__)
1920
faker = Faker()
2021

@@ -265,7 +266,7 @@ def extension_to_dict(ext):
265266
def define_nock(context: Context, definitions: list[dict]):
266267
jwt_token = context.vars["AUTH_TOKEN"]
267268
response = context.http_client.post(
268-
"/api/v1/bdd-nock/define",
269+
f"{NOCK_API_PREFIX}/define",
269270
headers=dict(authorization="Bearer {}".format(jwt_token)),
270271
json=dict(definitions=definitions),
271272
)
@@ -275,7 +276,7 @@ def define_nock(context: Context, definitions: list[dict]):
275276
def restore_nock(context: Context):
276277
jwt_token = context.vars["AUTH_TOKEN"]
277278
response = context.http_client.post(
278-
"/api/v1/bdd-nock/restore",
279+
f"{NOCK_API_PREFIX}/restore",
279280
headers=dict(authorization="Bearer {}".format(jwt_token)),
280281
json=dict(),
281282
)
@@ -285,7 +286,7 @@ def restore_nock(context: Context):
285286
def clean_all_nock(context: Context):
286287
jwt_token = context.vars["AUTH_TOKEN"]
287288
response = context.http_client.post(
288-
"/api/v1/bdd-nock/clean-all",
289+
f"{NOCK_API_PREFIX}/clean-all",
289290
headers=dict(authorization="Bearer {}".format(jwt_token)),
290291
json=dict(),
291292
)

backend/nodemon.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
{
2-
"watch": ["src"],
2+
"watch": [
3+
"src"
4+
],
35
"ext": ".ts,.js",
46
"ignore": [],
5-
"exec": "tsx ./src/main.ts | pino-pretty --colorize --colorizeObjects --singleLine"
6-
}
7+
"exec": "tsx --tsconfig=./tsconfig.dev.json --inspect=0.0.0.0:9229 ./src/main.ts | pino-pretty --colorize --colorizeObjects --singleLine"
8+
}

backend/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"binary:clean": "rm -rf ./dist && rm -rf ./binary",
3333
"binary:rename-imports": "ts-node ./scripts/rename-mjs.ts",
3434
"test": "echo \"Error: no test specified\" && exit 1",
35-
"dev": "tsx watch --clear-screen=false ./src/main.ts | pino-pretty --colorize --colorizeObjects --singleLine",
35+
"dev": "tsx watch --clear-screen=false ./src/main.ts --config tsconfig.dev.json | pino-pretty --colorize --colorizeObjects --singleLine",
3636
"dev:docker": "nodemon",
3737
"build": "tsup --sourcemap",
3838
"build:frontend": "npm run build --prefix ../frontend",
@@ -266,4 +266,4 @@
266266
"zod": "^3.22.4",
267267
"zod-to-json-schema": "^3.24.5"
268268
}
269-
}
269+
}

backend/src/lib/config/env.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ const envSchema = z
400400
isAcmeDevelopmentMode: data.NODE_ENV === "development" && data.ACME_DEVELOPMENT_MODE,
401401
isProductionMode: data.NODE_ENV === "production" || IS_PACKAGED,
402402
isRedisSentinelMode: Boolean(data.REDIS_SENTINEL_HOSTS),
403-
isBddNockApiEnabled: data.NODE_ENV === "development" && data.BDD_NOCK_API_ENABLED,
403+
isBddNockApiEnabled: data.NODE_ENV !== "production" && data.BDD_NOCK_API_ENABLED,
404404
REDIS_SENTINEL_HOSTS: data.REDIS_SENTINEL_HOSTS?.trim()
405405
?.split(",")
406406
.map((el) => {
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import type { Definition } from "nock";
2+
import { z } from "zod";
3+
4+
import { getConfig } from "@app/lib/config/env";
5+
import { ForbiddenRequestError } from "@app/lib/errors";
6+
import { logger } from "@app/lib/logger";
7+
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
8+
import { AuthMode } from "@app/services/auth/auth-type";
9+
10+
// When running in production, we don't want to even import nock, because it's not needed and it increases memory usage a lots.
11+
// It once caused an outage in the production environment.
12+
// This is why we would rather to crash the app if it's not in development mode (in that case, Kubernetes should stop it from rolling out).
13+
if (process.env.NODE_ENV === "production") {
14+
throw new Error("BDD Nock API can only be enabled in development or test mode");
15+
}
16+
17+
export const registerBddNockRouter = async (server: FastifyZodProvider) => {
18+
const appCfg = getConfig();
19+
const importNock = async () => {
20+
// eslint-disable-next-line import/no-extraneous-dependencies
21+
const { default: nock } = await import("nock");
22+
return nock;
23+
};
24+
25+
const checkIfBddNockApiEnabled = () => {
26+
// Note: Please note that this API is only available in development mode and only for BDD tests.
27+
// This endpoint should NEVER BE ENABLED IN PRODUCTION!
28+
if (appCfg.NODE_ENV === "production" || !appCfg.isBddNockApiEnabled) {
29+
throw new ForbiddenRequestError({ message: "BDD Nock API is not enabled" });
30+
}
31+
};
32+
33+
server.route({
34+
method: "POST",
35+
url: "/define",
36+
schema: {
37+
body: z.object({ definitions: z.unknown().array() }),
38+
response: {
39+
200: z.object({ status: z.string() })
40+
}
41+
},
42+
onRequest: verifyAuth([AuthMode.JWT]),
43+
handler: async (req) => {
44+
checkIfBddNockApiEnabled();
45+
const { body } = req;
46+
const { definitions } = body;
47+
logger.info(definitions, "Defining nock");
48+
const processedDefinitions = definitions.map((definition: unknown) => {
49+
const { path, ...rest } = definition as Definition;
50+
return {
51+
...rest,
52+
path:
53+
path !== undefined && typeof path === "string"
54+
? path
55+
: new RegExp((path as unknown as { regex: string }).regex ?? "")
56+
} as Definition;
57+
});
58+
59+
const nock = await importNock();
60+
nock.define(processedDefinitions);
61+
// Ensure we are activating the nocks, because we could have called `nock.restore()` before this call.
62+
if (!nock.isActive()) {
63+
nock.activate();
64+
}
65+
return { status: "ok" };
66+
}
67+
});
68+
69+
server.route({
70+
method: "POST",
71+
url: "/clean-all",
72+
schema: {
73+
response: {
74+
200: z.object({ status: z.string() })
75+
}
76+
},
77+
onRequest: verifyAuth([AuthMode.JWT]),
78+
handler: async () => {
79+
checkIfBddNockApiEnabled();
80+
logger.info("Cleaning all nocks");
81+
const nock = await importNock();
82+
nock.cleanAll();
83+
return { status: "ok" };
84+
}
85+
});
86+
87+
server.route({
88+
method: "POST",
89+
url: "/restore",
90+
schema: {
91+
response: {
92+
200: z.object({ status: z.string() })
93+
}
94+
},
95+
onRequest: verifyAuth([AuthMode.JWT]),
96+
handler: async () => {
97+
checkIfBddNockApiEnabled();
98+
logger.info("Restore network requests from nock");
99+
const nock = await importNock();
100+
nock.restore();
101+
return { status: "ok" };
102+
}
103+
});
104+
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export const registerBddNockRouter = async () => {
2+
// This route is only available in development or test mode.
3+
// The actual implementation is in the dev.ts file and will be aliased to that file in development or test mode.
4+
// And if somehow we try to enable it in production, we will throw an error.
5+
throw new Error("BDD Nock should not be enabled in production");
6+
};

backend/src/server/routes/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { registerBddNockRouter } from "@bdd_routes/bdd-nock-router";
12
import { CronJob } from "cron";
23
import { Knex } from "knex";
34
import { monitorEventLoopDelay } from "perf_hooks";
@@ -2698,6 +2699,12 @@ export const registerRoutes = async (
26982699
await server.register(registerV3Routes, { prefix: "/api/v3" });
26992700
await server.register(registerV4Routes, { prefix: "/api/v4" });
27002701

2702+
// Note: This is a special route for BDD tests. It's only available in development mode and only for BDD tests.
2703+
// This route should NEVER BE ENABLED IN PRODUCTION!
2704+
if (getConfig().isBddNockApiEnabled) {
2705+
await server.register(registerBddNockRouter, { prefix: "/api/__bdd_nock__" });
2706+
}
2707+
27012708
server.addHook("onClose", async () => {
27022709
cronJobs.forEach((job) => job.stop());
27032710
await telemetryService.flushAll();

backend/src/server/routes/v1/bdd-nock-router.ts

Lines changed: 0 additions & 87 deletions
This file was deleted.

0 commit comments

Comments
 (0)