Refactor OpenAPI fetcher for improved clarity and robustness

Reorganized and enhanced the OpenAPI fetch logic for better maintainability and error handling. Key updates include improved environment variable validation, more detailed error messages, streamlined configuration loading, and additional safety checks for file paths and directories. Added proper logging and ensured the process flow is easy to trace.
This commit is contained in:
GotthardG 2024-12-17 15:38:26 +01:00
parent 25528811dc
commit 7e72871ad7

View File

@ -6,48 +6,68 @@ import path from 'path';
import util from 'util'; import util from 'util';
import dotenv from 'dotenv'; import dotenv from 'dotenv';
if (!process.env.ENVIRONMENT) { // Load environment variables
console.error("❌ Missing ENVIRONMENT variable.");
process.exit(1);
}
dotenv.config(); dotenv.config();
// Determine the environment if (!process.env.ENVIRONMENT) {
const environment = process.env.ENVIRONMENT || 'dev'; console.error("❌ ENVIRONMENT variable is missing.");
const nodeEnv = process.env.NODE_ENV || 'dev';
const configFile = `config_${nodeEnv}.json`;
// Load the appropriate configuration
if (!configFile) {
console.error("❌ Configuration file path is missing.");
process.exit(1); process.exit(1);
} }
// Determine environment and configuration file
const nodeEnv = process.env.ENVIRONMENT || 'dev';
const configFile = `config_${nodeEnv}.json`;
// Load configuration file
let config; let config;
try { try {
config = JSON.parse(fs.readFileSync(path.resolve('../', configFile), 'utf8')); config = JSON.parse(fs.readFileSync(path.resolve('../', configFile), 'utf8'));
} catch (error) { } catch (error) {
console.error(`❌ Error reading configuration file: ${error.message}`); console.error(`Failed to read configuration file '${configFile}': ${error.message}`);
process.exit(1); process.exit(1);
} }
if (!config.OPENAPI_URL || !config.SCHEMA_PATH || !config.OUTPUT_DIRECTORY || !config.SSL_KEY_PATH || !config.SSL_CERT_PATH) { // Validate required configurations
console.error("❌ Missing essential configuration values."); const requiredFields = [
'OPENAPI_URL',
'SCHEMA_PATH',
'OUTPUT_DIRECTORY',
'SSL_KEY_PATH',
'SSL_CERT_PATH',
];
for (const field of requiredFields) {
if (!config[field]) {
console.error(`❌ Missing required configuration: ${field}`);
process.exit(1); process.exit(1);
}
} }
// Resolve paths from the config
const OPENAPI_URL = config.OPENAPI_URL; const OPENAPI_URL = config.OPENAPI_URL;
const SCHEMA_PATH = path.resolve(config.SCHEMA_PATH); const SCHEMA_PATH = path.resolve(config.SCHEMA_PATH);
const OUTPUT_DIRECTORY = path.resolve(config.OUTPUT_DIRECTORY); const OUTPUT_DIRECTORY = path.resolve(config.OUTPUT_DIRECTORY);
const SSL_KEY_PATH = path.resolve(config.SSL_KEY_PATH); const SSL_KEY_PATH = path.resolve(config.SSL_KEY_PATH);
const SSL_CERT_PATH = path.resolve(config.SSL_CERT_PATH); const SSL_CERT_PATH = path.resolve(config.SSL_CERT_PATH);
console.log(`Using SCHEMA_PATH: ${SCHEMA_PATH}`); // Log configuration
console.log(`Using OUTPUT_DIRECTORY: ${OUTPUT_DIRECTORY}`); console.log(`[INFO] Environment: ${nodeEnv}`);
console.log(`[INFO] Using SCHEMA_PATH: ${SCHEMA_PATH}`);
console.log(`[INFO] Using OUTPUT_DIRECTORY: ${OUTPUT_DIRECTORY}`);
// Verify SSL files
if (!fs.existsSync(SSL_KEY_PATH) || !fs.existsSync(SSL_CERT_PATH)) {
console.error(`❌ SSL files not found:
Key Path: ${SSL_KEY_PATH} (exists: ${fs.existsSync(SSL_KEY_PATH)})
Cert Path: ${SSL_CERT_PATH} (exists: ${fs.existsSync(SSL_CERT_PATH)})`);
process.exit(1);
}
const execPromisified = util.promisify(exec); const execPromisified = util.promisify(exec);
let isGenerating = false; let isGenerating = false;
const debounceDelay = 500; // 500ms debounce const debounceDelay = 500; // Debounce interval in milliseconds
// Debounce function to control rapid re-triggering
function debounce(func, delay) { function debounce(func, delay) {
let timer; let timer;
return (...args) => { return (...args) => {
@ -58,16 +78,17 @@ function debounce(func, delay) {
}; };
} }
// Main function to fetch OpenAPI schema and generate services
async function fetchAndGenerate() { async function fetchAndGenerate() {
if (isGenerating) { if (isGenerating) {
console.log("⚠️ Generation is already running."); console.log("⚠️ Generation process is already running.");
return; return;
} }
isGenerating = true; isGenerating = true;
console.log("🚀 Fetching OpenAPI schema...");
try { try {
// Fetch OpenAPI schema over HTTPS
console.log("🚀 Fetching OpenAPI schema...");
const options = { const options = {
rejectUnauthorized: false, rejectUnauthorized: false,
key: fs.readFileSync(SSL_KEY_PATH), key: fs.readFileSync(SSL_KEY_PATH),
@ -85,59 +106,58 @@ async function fetchAndGenerate() {
res.on('end', async () => { res.on('end', async () => {
try { try {
// Save schema file
fs.writeFileSync(SCHEMA_PATH, data, 'utf8'); fs.writeFileSync(SCHEMA_PATH, data, 'utf8');
console.log(`✅ OpenAPI schema saved to ${SCHEMA_PATH}`); console.log(`✅ OpenAPI schema saved to ${SCHEMA_PATH}`);
} catch (writeError) {
console.error(`❌ Error saving OpenAPI schema: ${writeError}`);
isGenerating = false;
return;
}
console.log("🧼 Cleaning output directory..."); console.log("🧼 Cleaning output directory...");
try {
await fs.promises.rm(OUTPUT_DIRECTORY, { recursive: true, force: true }); await fs.promises.rm(OUTPUT_DIRECTORY, { recursive: true, force: true });
console.log(`✅ Output directory cleaned at ${OUTPUT_DIRECTORY}`);
if (fs.existsSync(OUTPUT_DIRECTORY)) { console.log(`✅ Output directory cleaned: ${OUTPUT_DIRECTORY}`);
console.error(`❌ Output directory still exists: ${OUTPUT_DIRECTORY}`); if (!fs.existsSync(OUTPUT_DIRECTORY)) {
} else {
console.log(`✅ Confirmed removal of ${OUTPUT_DIRECTORY}`); console.log(`✅ Confirmed removal of ${OUTPUT_DIRECTORY}`);
} else {
console.error(`❌ Failed to remove output directory: ${OUTPUT_DIRECTORY}`);
} }
// Generate services
const command = `npx openapi -i ${SCHEMA_PATH} -o ${OUTPUT_DIRECTORY}`; const command = `npx openapi -i ${SCHEMA_PATH} -o ${OUTPUT_DIRECTORY}`;
console.log(`Executing debug command: ${command}`); console.log(`🔧 Executing command: ${command}`);
const { stdout, stderr } = await execPromisified(command); const { stdout, stderr } = await execPromisified(command);
if (stderr) { if (stderr) {
console.error(`⚠️ stderr while generating services: ${stderr}`); console.error(`⚠️ stderr while generating services: ${stderr}`);
} else { } else {
console.log(`Command executed successfully, output:\n${stdout}`); console.log(`Service generation completed successfully:\n${stdout}`);
} }
} catch (error) { } catch (error) {
console.error(`❌ Error cleaning or executing command: ${error}`); console.error(`❌ Error during schema processing or generation: ${error.message}`);
} }
isGenerating = false; isGenerating = false;
}); });
} catch (error) { } catch (error) {
console.error('❌ Error fetching OpenAPI schema: ' + error.message); console.error(`❌ Failed to fetch OpenAPI schema: ${error.message}`);
isGenerating = false; isGenerating = false;
} }
} }
// Define backendDirectory based on ENVIRONMENT variable // Backend directory based on the environment
const ENVIRONMENT = process.env.ENVIRONMENT || 'dev'; const backendDirectory =
const backendDirectory = environment === 'test' nodeEnv === 'test'
? path.resolve('/home/jungfrau/heidi-v2/backend/app') ? path.resolve('/home/jungfrau/heidi-v2/backend/app')
: path.resolve('/Users/gotthardg/PycharmProjects/heidi-v2/backend/app'); : path.resolve('/Users/gotthardg/PycharmProjects/heidi-v2/backend/app');
if (!backendDirectory) { if (!fs.existsSync(backendDirectory)) {
console.error("❌ Failed to resolve backend directory path."); console.error(`❌ Backend directory does not exist: ${backendDirectory}`);
process.exit(1); process.exit(1);
} }
console.log(`👀 Watching for changes in ${backendDirectory}`); console.log(`👀 Watching for changes in ${backendDirectory}`);
const watcher = chokidar.watch(backendDirectory, { persistent: true, ignored: [SCHEMA_PATH, OUTPUT_DIRECTORY] }); // Watcher for change detection
const watcher = chokidar.watch(backendDirectory, {
persistent: true,
ignored: [SCHEMA_PATH, OUTPUT_DIRECTORY],
});
watcher watcher
.on('add', debounce(fetchAndGenerate, debounceDelay)) .on('add', debounce(fetchAndGenerate, debounceDelay))