Skip to content

Commit 46b910e

Browse files
dev(api): add build options to test env (freeCodeCamp#59957)
1 parent f9edfe3 commit 46b910e

4 files changed

Lines changed: 77 additions & 75 deletions

File tree

api/jest.utils.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import request from 'supertest';
22

3-
import { build } from './src/app';
3+
import { build, buildOptions } from './src/app';
44
import { createUserInput } from './src/utils/create-user';
55
import { examJson } from './__mocks__/exam';
66
import { CSRF_COOKIE, CSRF_HEADER } from './src/plugins/csrf';
@@ -158,7 +158,11 @@ const indexData: IndexData[] = [
158158
export function setupServer(): void {
159159
let fastify: FastifyTestInstance;
160160
beforeAll(async () => {
161-
fastify = await build();
161+
if (process.env.FCC_ENABLE_TEST_LOGGING !== 'true') {
162+
// @ts-expect-error Disable logging by unsetting logger
163+
buildOptions.logger = undefined;
164+
}
165+
fastify = await build(buildOptions);
162166
await fastify.ready();
163167

164168
// Prisma does not support TTL indexes in the schema yet, so, to avoid

api/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
"develop": "tsx watch --clear-screen=false src/server.ts",
7171
"start": "FREECODECAMP_NODE_ENV=production node dist/server.js",
7272
"test": "jest --force-exit",
73+
"test-with-logging": "FCC_ENABLE_TEST_LOGGING=true pnpm run test",
7374
"prisma": "dotenv -e ../.env prisma",
7475
"postinstall": "prisma generate",
7576
"generate-exams": "tsx tools/exam-environment/generate/index.ts",

api/src/app.ts

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { randomBytes } from 'crypto';
2+
import { isEmpty } from 'lodash';
13
import fastifyAccepts from '@fastify/accepts';
24
import fastifySwagger from '@fastify/swagger';
35
import fastifySwaggerUI from '@fastify/swagger-ui';
@@ -9,6 +11,7 @@ import Fastify, {
911
FastifyBaseLogger,
1012
FastifyHttpOptions,
1113
FastifyInstance,
14+
FastifyRequest,
1215
RawReplyDefaultExpression,
1316
RawRequestDefaultExpression,
1417
RawServerDefault
@@ -42,7 +45,9 @@ import {
4245
FCC_ENABLE_EXAM_ENVIRONMENT,
4346
FCC_ENABLE_SENTRY_ROUTES,
4447
GROWTHBOOK_FASTIFY_API_HOST,
45-
GROWTHBOOK_FASTIFY_CLIENT_KEY
48+
GROWTHBOOK_FASTIFY_CLIENT_KEY,
49+
FREECODECAMP_NODE_ENV,
50+
FCC_API_LOG_LEVEL
4651
} from './utils/env';
4752
import { isObjectID } from './utils/validation';
4853
import {
@@ -78,6 +83,67 @@ ajv.addFormat('objectid', {
7883
validate: (str: string) => isObjectID(str)
7984
});
8085

86+
const requestSerializer = (req: FastifyRequest) => {
87+
const method = req.method || 'METHOD not found';
88+
const url = req.url || 'URL not found';
89+
const headers = req.headers || 'HEADERS not found';
90+
const xForwardedFor = Array.isArray(req.headers['x-forwarded-for'])
91+
? req.headers['x-forwarded-for'][0]
92+
: req.headers['x-forwarded-for'];
93+
const ip =
94+
xForwardedFor || req.headers['x-real-ip'] || req.ip || 'IP not found';
95+
const query = isEmpty(req.query) ? 'QUERY not found' : req.query;
96+
const hostname = req.hostname || 'HOSTNAME not found';
97+
const remotePort = req.socket.remotePort || 'REMOTE_PORT not found';
98+
99+
return {
100+
REQ_ID: req.id,
101+
METHOD: method,
102+
URL: url,
103+
IP: ip,
104+
HOSTNAME: hostname,
105+
REMOTE_PORT: remotePort,
106+
QUERY: query,
107+
HEADERS: headers
108+
};
109+
};
110+
111+
const envToLogger = {
112+
development: {
113+
transport: {
114+
target: 'pino-pretty',
115+
options: {
116+
singleLine: true,
117+
translateTime: 'HH:MM:ss Z',
118+
ignore: 'pid,hostname'
119+
}
120+
},
121+
level: FCC_API_LOG_LEVEL || 'info',
122+
serializers: {
123+
req: (req: FastifyRequest) => {
124+
return {
125+
method: req.method,
126+
url: req.url
127+
};
128+
}
129+
}
130+
// No need to redact in development
131+
},
132+
production: {
133+
level: FCC_API_LOG_LEVEL || 'info',
134+
serializers: {
135+
req: requestSerializer
136+
},
137+
redact: ['req.HEADERS.cookie']
138+
}
139+
};
140+
141+
export const buildOptions = {
142+
logger: envToLogger[FREECODECAMP_NODE_ENV] ?? true,
143+
genReqId: () => randomBytes(8).toString('hex'),
144+
disableRequestLogging: true
145+
};
146+
81147
/**
82148
* Top-level wrapper to instantiate the API server. This is where all middleware and
83149
* routes should be mounted.

api/src/server.ts

Lines changed: 3 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,10 @@
11
import './instrument';
22

3-
import { randomBytes } from 'crypto';
4-
5-
import { FastifyRequest } from 'fastify';
6-
import { isEmpty } from 'lodash';
7-
8-
import { build } from './app';
9-
import {
10-
FREECODECAMP_NODE_ENV,
11-
FCC_API_LOG_LEVEL,
12-
HOST,
13-
PORT
14-
} from './utils/env';
15-
16-
const requestSerializer = (req: FastifyRequest) => {
17-
const method = req.method || 'METHOD not found';
18-
const url = req.url || 'URL not found';
19-
const headers = req.headers || 'HEADERS not found';
20-
const xForwardedFor = Array.isArray(req.headers['x-forwarded-for'])
21-
? req.headers['x-forwarded-for'][0]
22-
: req.headers['x-forwarded-for'];
23-
const ip =
24-
xForwardedFor || req.headers['x-real-ip'] || req.ip || 'IP not found';
25-
const query = isEmpty(req.query) ? 'QUERY not found' : req.query;
26-
const hostname = req.hostname || 'HOSTNAME not found';
27-
const remotePort = req.socket.remotePort || 'REMOTE_PORT not found';
28-
29-
return {
30-
REQ_ID: req.id,
31-
METHOD: method,
32-
URL: url,
33-
IP: ip,
34-
HOSTNAME: hostname,
35-
REMOTE_PORT: remotePort,
36-
QUERY: query,
37-
HEADERS: headers
38-
};
39-
};
40-
41-
const envToLogger = {
42-
development: {
43-
transport: {
44-
target: 'pino-pretty',
45-
options: {
46-
singleLine: true,
47-
translateTime: 'HH:MM:ss Z',
48-
ignore: 'pid,hostname'
49-
}
50-
},
51-
level: FCC_API_LOG_LEVEL || 'info',
52-
serializers: {
53-
req: (req: FastifyRequest) => {
54-
return {
55-
method: req.method,
56-
url: req.url
57-
};
58-
}
59-
}
60-
// No need to redact in development
61-
},
62-
production: {
63-
level: FCC_API_LOG_LEVEL || 'info',
64-
serializers: {
65-
req: requestSerializer
66-
},
67-
redact: ['req.HEADERS.cookie']
68-
}
69-
};
3+
import { build, buildOptions } from './app';
4+
import { HOST, PORT } from './utils/env';
705

716
const start = async () => {
72-
const fastify = await build({
73-
logger: envToLogger[FREECODECAMP_NODE_ENV] ?? true,
74-
genReqId: () => randomBytes(8).toString('hex'),
75-
disableRequestLogging: true
76-
});
7+
const fastify = await build(buildOptions);
778
try {
789
const port = Number(PORT);
7910
fastify.log.info(`Starting server on port ${port}`);

0 commit comments

Comments
 (0)