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 dotenv from 'dotenv';
if (!process.env.ENVIRONMENT) {
console.error("❌ Missing ENVIRONMENT variable.");
process.exit(1);
}
// Load environment variables
dotenv.config();
// Determine the environment
const environment = process.env.ENVIRONMENT || 'dev';
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.");
if (!process.env.ENVIRONMENT) {
console.error("❌ ENVIRONMENT variable is missing.");
process.exit(1);
}
// Determine environment and configuration file
const nodeEnv = process.env.ENVIRONMENT || 'dev';
const configFile = `config_${nodeEnv}.json`;
// Load configuration file
let config;
try {
config = JSON.parse(fs.readFileSync(path.resolve('../', configFile), 'utf8'));
} catch (error) {
console.error(`❌ Error reading configuration file: ${error.message}`);
console.error(`Failed to read configuration file '${configFile}': ${error.message}`);
process.exit(1);
}
if (!config.OPENAPI_URL || !config.SCHEMA_PATH || !config.OUTPUT_DIRECTORY || !config.SSL_KEY_PATH || !config.SSL_CERT_PATH) {
console.error("❌ Missing essential configuration values.");
// Validate required configurations
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);
}
}
// Resolve paths from the config
const OPENAPI_URL = config.OPENAPI_URL;
const SCHEMA_PATH = path.resolve(config.SCHEMA_PATH);
const OUTPUT_DIRECTORY = path.resolve(config.OUTPUT_DIRECTORY);
const SSL_KEY_PATH = path.resolve(config.SSL_KEY_PATH);
const SSL_CERT_PATH = path.resolve(config.SSL_CERT_PATH);
console.log(`Using SCHEMA_PATH: ${SCHEMA_PATH}`);
console.log(`Using OUTPUT_DIRECTORY: ${OUTPUT_DIRECTORY}`);
// Log configuration
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);
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) {
let timer;
return (...args) => {
@ -58,16 +78,17 @@ function debounce(func, delay) {
};
}
// Main function to fetch OpenAPI schema and generate services
async function fetchAndGenerate() {
if (isGenerating) {
console.log("⚠️ Generation is already running.");
console.log("⚠️ Generation process is already running.");
return;
}
isGenerating = true;
console.log("🚀 Fetching OpenAPI schema...");
try {
// Fetch OpenAPI schema over HTTPS
console.log("🚀 Fetching OpenAPI schema...");
const options = {
rejectUnauthorized: false,
key: fs.readFileSync(SSL_KEY_PATH),
@ -85,59 +106,58 @@ async function fetchAndGenerate() {
res.on('end', async () => {
try {
// Save schema file
fs.writeFileSync(SCHEMA_PATH, data, 'utf8');
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...");
try {
await fs.promises.rm(OUTPUT_DIRECTORY, { recursive: true, force: true });
console.log(`✅ Output directory cleaned at ${OUTPUT_DIRECTORY}`);
if (fs.existsSync(OUTPUT_DIRECTORY)) {
console.error(`❌ Output directory still exists: ${OUTPUT_DIRECTORY}`);
} else {
console.log(`✅ Output directory cleaned: ${OUTPUT_DIRECTORY}`);
if (!fs.existsSync(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}`;
console.log(`Executing debug command: ${command}`);
console.log(`🔧 Executing command: ${command}`);
const { stdout, stderr } = await execPromisified(command);
if (stderr) {
console.error(`⚠️ stderr while generating services: ${stderr}`);
} else {
console.log(`Command executed successfully, output:\n${stdout}`);
console.log(`Service generation completed successfully:\n${stdout}`);
}
} catch (error) {
console.error(`❌ Error cleaning or executing command: ${error}`);
console.error(`❌ Error during schema processing or generation: ${error.message}`);
}
isGenerating = false;
});
} catch (error) {
console.error('❌ Error fetching OpenAPI schema: ' + error.message);
console.error(`❌ Failed to fetch OpenAPI schema: ${error.message}`);
isGenerating = false;
}
}
// Define backendDirectory based on ENVIRONMENT variable
const ENVIRONMENT = process.env.ENVIRONMENT || 'dev';
const backendDirectory = environment === 'test'
// Backend directory based on the environment
const backendDirectory =
nodeEnv === 'test'
? path.resolve('/home/jungfrau/heidi-v2/backend/app')
: path.resolve('/Users/gotthardg/PycharmProjects/heidi-v2/backend/app');
if (!backendDirectory) {
console.error("❌ Failed to resolve backend directory path.");
if (!fs.existsSync(backendDirectory)) {
console.error(`❌ Backend directory does not exist: ${backendDirectory}`);
process.exit(1);
}
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
.on('add', debounce(fetchAndGenerate, debounceDelay))