72 lines
2.5 KiB
JavaScript
72 lines
2.5 KiB
JavaScript
const { randomBytes } = require('node:crypto');
|
|
const { readFile, rename, rm, writeFile } = require('node:fs/promises');
|
|
const { resolve } = require('node:path');
|
|
|
|
// Relative to the working directory, as before: the contract test runs the script in a temporary directory.
|
|
const target = resolve(process.cwd(), 'openapi.json');
|
|
const checkOnly = process.argv.includes('--check');
|
|
|
|
// Schema generation needs no deployment configuration or real credentials.
|
|
Object.assign(process.env, {
|
|
DATABASE_URL: 'file::memory:',
|
|
FRONTEND_ORIGIN: 'http://localhost',
|
|
JWT_SECRET: randomBytes(32).toString('hex'),
|
|
TOKEN_ENCRYPTION_KEY: randomBytes(32).toString('base64'),
|
|
PORT: '3000',
|
|
LOG_LEVEL: 'error',
|
|
TBANK_GRPC_ENDPOINT: 'localhost:443',
|
|
TBANK_RATE_LIMIT_PER_SECOND: '5',
|
|
TBANK_CA_CERT_PATH: '',
|
|
});
|
|
|
|
const { NestFactory } = require('@nestjs/core');
|
|
|
|
const { AppModule } = require('../dist/src/app.module.js');
|
|
const { API_PREFIX, createOpenApiDocument } = require('../dist/src/openapi.js');
|
|
|
|
async function render() {
|
|
const app = await NestFactory.create(AppModule, {
|
|
preview: true,
|
|
logger: false,
|
|
});
|
|
try {
|
|
app.setGlobalPrefix(API_PREFIX);
|
|
return JSON.stringify(createOpenApiDocument(app), null, 2) + '\n';
|
|
} finally {
|
|
await app.close();
|
|
}
|
|
}
|
|
|
|
// Read-only: compares the runtime document with the committed artifact and never writes.
|
|
async function check(content) {
|
|
const current = await readFile(target, 'utf8').catch(() => null);
|
|
if (current !== content) {
|
|
console.error(
|
|
'openapi.json is out of date with the runtime schema. Run `npm run openapi:generate`.',
|
|
);
|
|
process.exitCode = 1;
|
|
return;
|
|
}
|
|
console.log('openapi.json matches the runtime schema.');
|
|
}
|
|
|
|
// Atomic update: write a sibling temp file, then rename over the target, so a failed run never leaves partial output.
|
|
async function update(content) {
|
|
const temp = `${target}.${process.pid}.tmp`;
|
|
try {
|
|
// eslint-disable-next-line security/detect-non-literal-fs-filename -- Operator-selected working directory and fixed artifact name; no request input.
|
|
await writeFile(temp, content);
|
|
// eslint-disable-next-line security/detect-non-literal-fs-filename -- Operator-selected working directory and fixed artifact name; no request input.
|
|
await rename(temp, target);
|
|
} finally {
|
|
await rm(temp, { force: true });
|
|
}
|
|
}
|
|
|
|
render()
|
|
.then((content) => (checkOnly ? check(content) : update(content)))
|
|
.catch((error) => {
|
|
console.error(error);
|
|
process.exitCode = 1;
|
|
});
|