Skip to content

Commit 4c7334e

Browse files
authored
Merge pull request #126 from microservices-suite/repo-engineering/universal-cli
Repo engineering/universal cli
2 parents e372a0c + 5acf980 commit 4c7334e

File tree

8 files changed

+250
-80
lines changed

8 files changed

+250
-80
lines changed
Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,53 @@
11
module.exports = ({ answers }) => `
2-
const services = require('../services')
3-
const { asyncErrorHandler, APIError } = require('${answers.project_base}/utilities')
4-
const hello = asyncErrorHandler(async (req, res) => {
5-
res.status(200).json({ data:'Hello from ${answers.project_base}/${answers.service_name}' })
6-
})
2+
const services = require('../services/services');
3+
const { asyncErrorHandler, APIError } = require('${answers.project_base}/utilities');
4+
5+
const create${answers.service_name.charAt(0).toUpperCase()+answers.service_name.slice(1)} = asyncErrorHandler(async (req, res) => {
6+
const { body } = req;
7+
const { ${answers.service_name.toLowerCase()}: data } = await services.create${answers.service_name.charAt(0).toUpperCase()+answers.service_name.slice(1)}({ body });
8+
res.status(201).json({ data });
9+
});
10+
11+
const get${answers.service_name.charAt(0).toUpperCase()+answers.service_name.slice(1)}s = asyncErrorHandler(async (req, res) => {
12+
const { ${answers.service_name.toLowerCase()}s: data } = await services.get${answers.service_name.charAt(0).toUpperCase()+answers.service_name.slice(1)}s();
13+
res.status(200).json({ data });
14+
});
15+
16+
const get${answers.service_name.charAt(0).toUpperCase()+answers.service_name.slice(1)} = asyncErrorHandler(async (req, res) => {
17+
const { id } = req.params;
18+
const { ${answers.service_name.toLowerCase()}: data } = await services.get${answers.service_name.charAt(0).toUpperCase()+answers.service_name.slice(1)}ById({ id });
19+
if (!data) {
20+
throw new APIError(404, '${answers.service_name.toLowerCase()} not found');
21+
}
22+
res.status(200).json({ data });
23+
});
24+
25+
const update${answers.service_name.charAt(0).toUpperCase()+answers.service_name.slice(1)} = asyncErrorHandler(async (req, res) => {
26+
const { id } = req.params;
27+
const { body } = req;
28+
const { ${answers.service_name.toLowerCase()} } = await services.get${answers.service_name.charAt(0).toUpperCase()+answers.service_name.slice(1)}ById({ id });
29+
if (!${answers.service_name.toLowerCase()}) {
30+
throw new APIError(404, '${answers.service_name.toLowerCase()} not found');
31+
}
32+
const { upserted_${answers.service_name.toLowerCase()}: data } = await services.update${answers.service_name.charAt(0).toUpperCase()+answers.service_name.slice(1)}Profile({ id, body });
33+
res.status(200).json({ data });
34+
});
35+
36+
const delete${answers.service_name.charAt(0).toUpperCase()+answers.service_name.slice(1)} = asyncErrorHandler(async (req, res) => {
37+
const { id } = req.params;
38+
const { ${answers.service_name.toLowerCase()} } = await services.get${answers.service_name.charAt(0).toUpperCase()+answers.service_name.slice(1)}ById({ id });
39+
if (!${answers.service_name.toLowerCase()}) {
40+
throw new APIError(404, '${answers.service_name.toLowerCase()} not found');
41+
}
42+
await services.delete${answers.service_name.charAt(0).toUpperCase()+answers.service_name.slice(1)}({ id });
43+
res.status(200).json({ message: 'Deletion successful' });
44+
});
745
module.exports = {
8-
hello,
9-
}`;
46+
create${answers.service_name.charAt(0).toUpperCase()+answers.service_name.slice(1)},
47+
get${answers.service_name.charAt(0).toUpperCase()+answers.service_name.slice(1)}s,
48+
get${answers.service_name.charAt(0).toUpperCase()+answers.service_name.slice(1)},
49+
update${answers.service_name.charAt(0).toUpperCase()+answers.service_name.slice(1)},
50+
delete${answers.service_name.charAt(0).toUpperCase()+answers.service_name.slice(1)}
51+
};
52+
53+
`;
Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,17 @@
1-
module.exports = () => 'module.exports = {};'; // Example content for model
1+
module.exports = ({ answers }) => `
2+
const mongoose = require('mongoose');
3+
4+
const ${answers.service_name}Schema = new mongoose.Schema({
5+
name: {
6+
type: String,
7+
required: true,
8+
}
9+
}
10+
, {
11+
timestamps: true
12+
});
13+
14+
const ${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)} = mongoose.model('${answers.service_name}', ${answers.service_name}Schema);
15+
16+
module.exports = ${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)};
17+
`;
Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,26 @@
1-
module.exports = () => 'const express = require(\'express\');\nconst router = express.Router();\nmodule.exports = { router };';
1+
module.exports = ({ answers }) => `
2+
const express = require('express');
3+
const { validate } = require('${answers.project_base}/utilities');
4+
const {
5+
create${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)}Validation,
6+
get${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)}Validation,
7+
update${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)}Validation,
8+
delete${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)}Validation
9+
} = require('${answers.project_base}/validations');
10+
const ${answers.service_name.toLowerCase()}Controller = require('../controllers/controllers');
11+
12+
const router = express.Router();
13+
14+
router
15+
.route('/${answers.service_name.toLowerCase()}s')
16+
.post(validate(create${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)}Validation), ${answers.service_name.toLowerCase()}Controller.create${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)})
17+
.get(${answers.service_name.toLowerCase()}Controller.get${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)}s);
18+
19+
router
20+
.route('/${answers.service_name.toLowerCase()}s/:id')
21+
.get(validate(get${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)}Validation), ${answers.service_name.toLowerCase()}Controller.get${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)})
22+
.patch(validate(update${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)}Validation), ${answers.service_name.toLowerCase()}Controller.update${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)})
23+
.delete(validate(delete${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)}Validation), ${answers.service_name.toLowerCase()}Controller.delete${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)});
24+
25+
module.exports = router;
26+
`;
Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,35 @@
1-
module.exports = () => 'module.exports = {};';
1+
module.exports = ({ answers }) => `
2+
const { ObjectId } = require('mongodb');
3+
const ${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)} = require('../models/models');
4+
5+
const create${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)} = async ({ body }) => {
6+
const ${answers.service_name.toLowerCase()} = await ${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)}.create(body);
7+
return { ${answers.service_name.toLowerCase()} };
8+
};
9+
10+
const get${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)}s = async () => {
11+
const ${answers.service_name.toLowerCase()}s = await ${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)}.find({});
12+
return { ${answers.service_name.toLowerCase()}s };
13+
};
14+
15+
const get${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)}ById = async ({ id }) => {
16+
const ${answers.service_name.toLowerCase()} = await ${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)}.findById(id);
17+
return { ${answers.service_name.toLowerCase()} };
18+
};
19+
20+
const update${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)}Profile = async ({ id, body }) => {
21+
const upserted_${answers.service_name.toLowerCase()} = await ${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)}.updateOne({ _id: ObjectId(id) }, { ...body }, { upsert: false });
22+
return { upserted_${answers.service_name.toLowerCase()} };
23+
};
24+
25+
const delete${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)} = async ({ id }) => {
26+
await ${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)}.deleteOne({ _id: ObjectId(id) });
27+
};
28+
module.exports = {
29+
create${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)},
30+
get${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)}s,
31+
get${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)}ById,
32+
update${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)}Profile,
33+
delete${answers.service_name.charAt(0).toUpperCase() + answers.service_name.slice(1)}
34+
};
35+
`;

.suite-cli/cli/scripts/scripts.module.js

Lines changed: 83 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,53 @@ const startAll = async ({ options }) => {
363363

364364
}
365365

366+
// /**
367+
// * Starts services with nodemon in development mode by default, otherwise with PM2.
368+
// * @param {Object} options - Environment settings for running the services.
369+
// * @param {string[]} options.serviceDirectories - List of service directories under `options.microservicesDir`.
370+
// * @param {string} options.microservicesDir - The root directory of the services.
371+
// * @param {string} [options.mode='dev'] - The environment mode for running the services. Defaults to 'dev'.
372+
// * @returns {void} Starts the services and logs their startup status.
373+
// */
374+
// const spinVanillaServices = async ({ serviceDirectories, microservicesDir, mode = 'dev' }) => {
375+
// const spinner = ora('Starting all services in ' + mode + ' mode...').start();
376+
377+
// try {
378+
// // Simulate delay before starting services
379+
// await delay(1);
380+
381+
// await Promise.all(serviceDirectories.map(async (dir) => {
382+
// const serviceSpinner = ora('Starting service concurrently in: ' + dir).start();
383+
// const processes = await exec(`yarn ${mode}`, { cwd: join(microservicesDir, dir) }, async (error, stdout, stderr) => {
384+
// if (error) {
385+
// const errorMessage = getErrorMessage(error, dir, microservicesDir);
386+
// serviceSpinner.fail(errorMessage);
387+
// } else {
388+
// serviceSpinner.succeed(`Service in directory ${dir} started successfully`);
389+
// }
390+
// });
391+
// processes.stdout.on('data', data => {
392+
// const output = data.toString();
393+
// // Check if the output contains the "yarn run" message
394+
// if (!output.includes('yarn run')) {
395+
// // Stop the spinner before printing the output
396+
// serviceSpinner.stop();
397+
// spinner.succeed(output);
398+
// // Restart the spinner after printing the output
399+
// // serviceSpinner.start();
400+
// }
401+
// });
402+
// }));
403+
404+
// spinner.succeed(`service${serviceDirectories.length > 0 ? 's' : ''} started successfully: ${serviceDirectories}`);
405+
// } catch (error) {
406+
// spinner.fail('An error occurred while starting services');
407+
// console.error(error);
408+
// exit(1);
409+
// }
410+
// };
411+
412+
366413
/**
367414
* Starts services with nodemon in development mode by default, otherwise with PM2.
368415
* @param {Object} options - Environment settings for running the services.
@@ -375,42 +422,49 @@ const spinVanillaServices = async ({ serviceDirectories, microservicesDir, mode
375422
const spinner = ora('Starting all services in ' + mode + ' mode...').start();
376423

377424
try {
378-
// Simulate delay before starting services
379-
await delay(1);
380-
381425
await Promise.all(serviceDirectories.map(async (dir) => {
382-
const serviceSpinner = ora('Starting service concurrently in: ' + dir).start();
383-
const processes = await exec(`yarn ${mode}`, { cwd: join(microservicesDir, dir) }, async (error, stdout, stderr) => {
384-
if (error) {
385-
const errorMessage = getErrorMessage(error, dir, microservicesDir);
386-
serviceSpinner.fail(errorMessage);
387-
} else {
388-
serviceSpinner.succeed(`Service in directory ${dir} started successfully`);
389-
}
390-
});
391-
processes.stdout.on('data', data => {
426+
const servicePath = join(microservicesDir, dir);
427+
// TODO: check if the yarn.cmd works in windows really
428+
const command = process.platform === 'win32' ? 'yarn.cmd' : 'yarn';
429+
const args = [mode];
430+
431+
const child = spawn(command, args, { cwd: servicePath, shell: true });
432+
433+
child.stdout.on('data', (data) => {
392434
const output = data.toString();
393435
// Check if the output contains the "yarn run" message
394-
if (!output.includes('yarn run')) {
436+
if (!output.includes('yarn run') && !output.includes('NODE_ENV')) {
395437
// Stop the spinner before printing the output
396-
serviceSpinner.stop();
397-
spinner.succeed(output);
438+
console.log(output.trim());
398439
// Restart the spinner after printing the output
399-
// serviceSpinner.start();
440+
}
441+
});
442+
443+
child.stderr.on('data', (data) => {
444+
const output = data.toString();
445+
// Handle stderr output
446+
spinner.fail(`Error in service ${dir}: ${output.trim()}`);
447+
});
448+
449+
child.on('close', (code) => {
450+
if (code !== 0) {
451+
spinner.fail(`Service in directory ${dir} exited with code ${code}`);
452+
} else {
453+
spinner.succeed(`Service in directory ${dir} started successfully`);
454+
400455
}
401456
});
402457
}));
403458

404-
spinner.succeed(`service${serviceDirectories.length > 0 ? 's' : ''} started successfully: ${serviceDirectories}`);
459+
spinner.succeed(`Service${serviceDirectories.length > 1 ? 's' : ''} started successfully: ${serviceDirectories.join(', ')}`);
460+
console.log('\n')
405461
} catch (error) {
406462
spinner.fail('An error occurred while starting services');
407463
console.error(error);
408-
exit(1);
464+
process.exit(1);
409465
}
410466
};
411467

412-
413-
414468
const getErrorMessage = (error, dir, microservicesDir) => {
415469
const errorMessageParts = error.message.split('\n');
416470
let errorMessage = '';
@@ -862,7 +916,7 @@ const addPackageJson = async ({ project_root, answers }) => {
862916
"winston",
863917
"mongoose"
864918
];
865-
const devDependencies = ["nodemon","jest"];
919+
const devDependencies = ["nodemon", "jest"];
866920
const configDependencies = ['dotenv', 'joi', 'morgan', 'winston']
867921
const utilitiesDependencies = ['joi']
868922
// Join dependencies into a single string for the command
@@ -1196,12 +1250,11 @@ const generateMCSHelper = ({ project_root, answers }) => {
11961250
// Correct the file extension based on directory
11971251
const mcsPath = `${project_root}/microservices/${answers.service_name}/src/${mcs}`
11981252
mkdirSync(mcsPath, { recursive: true })
1199-
const fileExtension = mcs === 'models' ? 'model.js' : mcs === 'routes' ? 'routes.js' : 'controllers.js';
1253+
const fileExtension = `${mcs}.js`;
12001254

12011255
// Write main file content
1202-
const mainContent = mcs === 'models' ? assets.modelContent() : mcs === 'routes' ? assets.routesContent() : mcs === 'controllers' ? assets.controllersContent({ answers }) : assets.servicesContent();
1256+
const mainContent = mcs === 'models' ? assets.modelContent({ answers }) : mcs === 'routes' ? assets.routesContent({ answers }) : mcs === 'controllers' ? assets.controllersContent({ answers }) : assets.servicesContent({ answers });
12031257
writeFileSync(join(mcsPath, `${fileExtension}`), mainContent);
1204-
12051258
// Write index file content
12061259
const indexContent = mcs === 'models' ? assets.modelIndexContent() : mcs === 'routes' ? assets.routesIndexContent() : mcs === 'controllers' ? assets.controllersIndexContent() : assets.servicesIndexContent();
12071260
writeFileSync(join(mcsPath, 'index.js'), indexContent);
@@ -1223,17 +1276,17 @@ const releasePackage = async ({ package }) => {
12231276
const { workspace_name } = retrieveWorkSpaceName({ package_json_path });
12241277
if (package) {
12251278
logInfo({ message: `Looking for package: ${workspace_name}/${package}` });
1226-
await executeCommand('yarn', ['workspace', `${workspace_name}/${package}`, 'release'],{stdio:'inherit',shell:true});
1279+
await executeCommand('yarn', ['workspace', `${workspace_name}/${package}`, 'release'], { stdio: 'inherit', shell: true });
12271280
} else {
1228-
await executeCommand('yarn', ['generate:release'], { cwd: cwd(),stdio:'inherit',shell:true });
1281+
await executeCommand('yarn', ['generate:release'], { cwd: cwd(), stdio: 'inherit', shell: true });
12291282
}
12301283
} catch (error) {
1231-
ora().fail('Command failed to run');
1284+
ora().fail('Command failed to run');
12321285
}
12331286
}
12341287

1235-
const executeCommand =(command, args, options) => {
1236-
return new Promise(async(resolve, reject) => {
1288+
const executeCommand = (command, args, options) => {
1289+
return new Promise(async (resolve, reject) => {
12371290
const child = await spawn(command, args, options);
12381291
child.on('close', (code) => {
12391292
if (code !== 0) {

microservices/user-service/index.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,23 @@ mongoose.connect(config.db).then(() => {
1717
});
1818

1919
const app = express();
20+
app.set('trust proxy', 1);
2021

2122
app.use(express.json());
23+
app.use((req, res, next) => {
24+
const clientIp = req.headers['x-forwarded-for'] ||req.socket.remoteAddress || req.ip || req.connection.remoteAddress;
25+
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url} - IP: ${clientIp}`);
26+
next();
27+
});
2228

2329
app.get('/', (req, res) => {
24-
res.json({ messae: 'hello from @microservices-suite/user-service' });
30+
res.json({
31+
messae: 'hello from @microservices-suite/user-service',
32+
socket:req.socket.remoteAddress,
33+
connection:req.connection.remoteAddress,
34+
ip:req.ip,
35+
forwarded: req.headers['x-forwarded-for']||'not fwd'
36+
});
2537
});
2638

2739
const server = http.createServer(app);

microservices/user-service/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"author": "Gilbert Andanje",
1515
"license": "ISC",
1616
"dependencies": {
17-
"@microservices-suite/config": "1.0.3",
17+
"@microservices-suite/config": "1.0.9",
1818
"@microservices-suite/errors": "1.1.6",
1919
"@microservices-suite/utilities": "1.0.6",
2020
"@microservices-suite/validations": "1.0.9",

0 commit comments

Comments
 (0)