Skip to content

Commit

Permalink
FE
Browse files Browse the repository at this point in the history
  • Loading branch information
adappterxyz committed Nov 5, 2024
1 parent 558ba41 commit f071c09
Show file tree
Hide file tree
Showing 15 changed files with 4,974 additions and 64 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
node_modules
fixtures
docker-image
build
env
minionbot
minionhome
.env
Empty file removed docker-image/.env
Empty file.
123 changes: 123 additions & 0 deletions minionbot/controllers/jobController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
const { scheduleJob: nodeScheduleJob } = require('node-schedule');
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');

const jobStore = new Map(); // In-memory store for jobs
const SCRIPTS_DIR = path.join(__dirname, '../scripts');

// Ensure scripts directory exists
if (!fs.existsSync(SCRIPTS_DIR)) {
fs.mkdirSync(SCRIPTS_DIR, { recursive: true });
}

const triggerJob = async (req, res) => {
try {
const { jobName, arguments } = req.body;

if (!jobStore.has(jobName)) {
return res.status(404).json({ error: 'Job not found' });
}

const job = jobStore.get(jobName);
const scriptPath = path.join(SCRIPTS_DIR, `${jobName}.spec.ts`);

// Execute the Playwright test with arguments
const result = await executePlaywrightTest(scriptPath, arguments);

res.json({
status: 'success',
jobName,
result
});
} catch (error) {
res.status(500).json({
error: error.message
});
}
};

const addJob = async (req, res) => {
try {
const { jobName, requiredArguments, script } = req.body;

if (jobStore.has(jobName)) {
return res.status(400).json({ error: 'Job already exists' });
}

// Save script to file
const scriptPath = path.join(SCRIPTS_DIR, `${jobName}.spec.ts`);
fs.writeFileSync(scriptPath, script);

// Store job metadata
jobStore.set(jobName, {
requiredArguments,
scriptPath,
created: new Date(),
});

res.json({
status: 'success',
jobName,
requiredArguments
});
} catch (error) {
res.status(500).json({
error: error.message
});
}
};

const schedulePlaywrightJob = async (req, res) => {
try {
const { jobName, cronExpression, arguments } = req.body;

if (!jobStore.has(jobName)) {
return res.status(404).json({ error: 'Job not found' });
}

const scheduledJob = nodeScheduleJob(cronExpression, async () => {
const scriptPath = path.join(SCRIPTS_DIR, `${jobName}.spec.ts`);
await executePlaywrightTest(scriptPath, arguments);
});

res.json({
status: 'success',
jobName,
nextInvocation: scheduledJob.nextInvocation()
});
} catch (error) {
res.status(500).json({
error: error.message
});
}
};

async function executePlaywrightTest(scriptPath, args = {}) {
try {
// Convert arguments to environment variables
const env = {
...process.env,
...Object.entries(args).reduce((acc, [key, value]) => {
acc[`TEST_ARG_${key.toUpperCase()}`] = value;
return acc;
}, {})
};

const result = execSync(`npx playwright test ${scriptPath}`, {
stdio: 'pipe',
env,
encoding: 'utf-8'
});

return { success: true, output: result };
} catch (error) {
throw new Error(`Test execution failed: ${error.message}`);
}
}

module.exports = {
triggerJob,
addJob,
schedulePlaywrightJob
};
File renamed without changes.
File renamed without changes.
68 changes: 68 additions & 0 deletions minionbot/html-report/index.html

Large diffs are not rendered by default.

153 changes: 153 additions & 0 deletions minionbot/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
const express = require('express');
const cors = require('cors');
const morgan = require('morgan'); // for logging
const { createLogger, format, transports } = require('winston');

// Initialize Express app
const app = express();
const router = express.Router();

// Initialize logger
const logger = createLogger({
format: format.combine(
format.timestamp(),
format.json()
),
transports: [
new transports.Console(),
new transports.File({ filename: 'playwright-service.log' })
]
});

// Middleware
app.use(cors());
app.use(express.json({ limit: '50mb' })); // Increased limit for large scripts
app.use(morgan('dev')); // HTTP request logging

// Error handling middleware
app.use((err, req, res, next) => {
logger.error('Unhandled error:', err);
res.status(500).json({
error: 'Internal Server Error',
message: err.message
});
});

// Health check endpoint
router.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});

// Import controllers
const { triggerJob, addJob, scheduleJob, listJobs, deleteJob } = require('./controllers/jobController');

// API Routes
router.post('/trigger', async (req, res) => {
try {
logger.info('Triggering job', { jobName: req.body.jobName });
const result = await triggerJob(req, res);
logger.info('Job triggered successfully', { jobName: req.body.jobName, result });
} catch (error) {
logger.error('Error triggering job', {
jobName: req.body.jobName,
error: error.message
});
res.status(500).json({ error: error.message });
}
});

router.post('/add', async (req, res) => {
try {
logger.info('Adding new job', { jobName: req.body.jobName });

// Validate required fields
const { jobName, requiredArguments, script } = req.body;
if (!jobName || !script) {
throw new Error('jobName and script are required');
}

const result = await addJob(req, res);
logger.info('Job added successfully', { jobName: req.body.jobName });
} catch (error) {
logger.error('Error adding job', {
jobName: req.body.jobName,
error: error.message
});
res.status(400).json({ error: error.message });
}
});

router.post('/schedule', async (req, res) => {
try {
logger.info('Scheduling job', {
jobName: req.body.jobName,
cronExpression: req.body.cronExpression
});

// Validate cron expression
const { jobName, cronExpression } = req.body;
if (!jobName || !cronExpression) {
throw new Error('jobName and cronExpression are required');
}

const result = await scheduleJob(req, res);
logger.info('Job scheduled successfully', {
jobName: req.body.jobName,
nextInvocation: result.nextInvocation
});
} catch (error) {
logger.error('Error scheduling job', {
jobName: req.body.jobName,
error: error.message
});
res.status(400).json({ error: error.message });
}
});

// Additional useful endpoints
router.get('/jobs', async (req, res) => {
try {
const jobs = await listJobs();
res.json(jobs);
} catch (error) {
logger.error('Error listing jobs', { error: error.message });
res.status(500).json({ error: error.message });
}
});

router.delete('/jobs/:jobName', async (req, res) => {
try {
const { jobName } = req.params;
await deleteJob(jobName);
logger.info('Job deleted successfully', { jobName });
res.json({ status: 'success', message: `Job ${jobName} deleted` });
} catch (error) {
logger.error('Error deleting job', {
jobName: req.params.jobName,
error: error.message
});
res.status(400).json({ error: error.message });
}
});

// Mount router
app.use('/api/playwright', router);

// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
logger.info(`Playwright service running on port ${PORT}`);
});

// Handle uncaught exceptions
process.on('uncaughtException', (error) => {
logger.error('Uncaught Exception:', error);
process.exit(1);
});

process.on('unhandledRejection', (error) => {
logger.error('Unhandled Rejection:', error);
process.exit(1);
});

module.exports = app;
Loading

0 comments on commit f071c09

Please sign in to comment.