Skip to content

Commit

Permalink
feat: add systemic scaffolding schematic (#100)
Browse files Browse the repository at this point in the history
* feat: add systemic scaffolding schematic

* refactor: format errorMessage property

* fix: add chalk to dependencies

* feat: replace logging with pino

* feat: add open telemetry instrumentations

* feat: add middleware components

* refactor: update default config

* refactor: move codebase files to src folder

* refactor: remove makefile

* refactor: use opentelemetry sdk-node
  • Loading branch information
neodmy authored Dec 11, 2023
1 parent 02331f4 commit 9d2aa83
Show file tree
Hide file tree
Showing 30 changed files with 526 additions and 0 deletions.
5 changes: 5 additions & 0 deletions packages/@guidesmiths/cuckoojs-schematics/src/collection.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@
"description": "Add basic tooling to the project",
"factory": "./basic-tooling/basic-tooling.factory#main",
"schema": "./basic-tooling/schema.json"
},
"systemic-scaffolding": {
"description": "Add Systemic project scaffolding",
"factory": "./systemic-scaffolding/systemic-scaffolding.factory#main",
"schema": "./systemic-scaffolding/schema.json"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM node:18

# Create app directory
WORKDIR /usr/src/app

# Install app dependencies
COPY package*.json ./

RUN npm install

# Bundle app source
COPY . .

EXPOSE 4000

RUN npm run manifest

CMD [ "npm", "start" ]
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
process.env.SERVICE_ENV = process.env.SERVICE_ENV || 'local';

const runner = require('systemic-domain-runner');
const system = require('./system');
const openTelemetry = require('./opentelemetry');

openTelemetry.start();

const emergencyLogger = console;

const die = (message, err) => {
emergencyLogger.error(err, message);
process.exit(1);
};

runner(system(), { logger: emergencyLogger }).start((err, components) => {
if (err) die('Error starting system', err);
const { logger, pkg } = components;
logger.info(`${pkg.name} has started`);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const { defaults } = require('jest-config');
require('./src/test/env');

/** @type {import('jest').Config} */
module.exports = {
testEnvironment: 'node',
moduleFileExtensions: [...defaults.moduleFileExtensions, 'js'],
testPathIgnorePatterns: ['dist', 'config'],
collectCoverageFrom: [
'components/**/*.js',
'!test/**/*.*',
'!**/node_modules/**',
'!**/_templates/**',
'!*.config.js',
],
coverageDirectory: './test-reports/coverage',
reporters: [
'default',
['jest-junit',
{
outputDirectory: './test-reports',
},
],
],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const process = require('process');
const opentelemetry = require('@opentelemetry/sdk-node');
const { PinoInstrumentation } = require('@opentelemetry/instrumentation-pino');
const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http');
const { ExpressInstrumentation } = require('@opentelemetry/instrumentation-express');

const sdk = new opentelemetry.NodeSDK({
instrumentations: [
new PinoInstrumentation(),
new ExpressInstrumentation(),
new HttpInstrumentation(),
],
});

process.on('SIGTERM', () => {
sdk
.shutdown()
.then(
() => {},
err => process.stderr.write('Error shutting down OpenTelemetry SDK', err),
)
.finally(() => process.exit(0));
});

module.exports = sdk;
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"name": "<%=directory%>",
"version": "0.0.1",
"description": "",
"author": {
"name": "author",
"email": "[email protected]"
},
"main": "index.js",
"scripts": {
"local": "SERVICE_ENV=local node index.js",
"start": "node index.js",
"test": "cross-env SERVICE_ENV=test jest --runInBand --detectOpenHandles --forceExit",
"test:watch": "npm run test -- --watch",
"test:report": "cross-env CI=true SERVICE_ENV=test jest --runInBand --detectOpenHandles --forceExit --coverage",
"manifest": "node_modules/make-manifest/bin/make-manifest"
},
"devDependencies": {
"@types/jest": "^29.5.10",
"cross-env": "^7.0.3",
"jest": "^29.7.0",
"jest-config": "^29.7.0",
"jest-junit": "^16.0.0",
"make-manifest": "^2.0.2",
"supertest": "^6.3.3"
},
"dependencies": {
"@hapi/boom": "^9.1.0",
"@opentelemetry/instrumentation-express": "^0.33.3",
"@opentelemetry/instrumentation-http": "^0.45.1",
"@opentelemetry/instrumentation-pino": "^0.34.3",
"@opentelemetry/sdk-node": "^0.45.1",
"body-parser": "^1.19.0",
"chalk": "^5.3.0",
"confabulous": "^1.7.0",
"debug": "^4.1.1",
"helmet": "^4.1.1",
"hogan.js": "^3.0.2",
"on-headers": "^1.0.2",
"optimist": "^0.6.1",
"optional": "^0.1.4",
"pino": "^8.16.2",
"pino-http": "^8.5.1",
"pino-pretty": "^10.2.3",
"ramda": "^0.27.0",
"swagger-endpoint-validator": "^4.1.0",
"systemic": "^3.3.7",
"systemic-domain-runner": "^1.1.0",
"systemic-express": "^1.1.1"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const System = require('systemic');
const optional = require('optional');
const { join } = require('path');

const manifest = optional(join(process.cwd(), 'manifest.json')) || {};
const pkg = require(join(process.cwd(), 'package.json')); // eslint-disable-line import/no-dynamic-require

module.exports = new System({ name: '<%=directory%>' }).add('manifest', manifest).add('pkg', pkg);
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const path = require('path');
const Conf = require('packages/@guidesmiths/cuckoojs-schematics/src/systemic-scaffolding/files/src/components/config/confabulous');

module.exports = ({ confabulous } = {}) => {
const Confabulous = confabulous || Conf;
const { loaders } = Confabulous;

const start = (params, cb) =>
new Confabulous()
.add(() => loaders.require({ path: path.join(process.cwd(), 'config', 'default.js'), watch: true }))
.add(() =>
loaders.require({
path: path.join(process.cwd(), 'config', `${process.env.SERVICE_ENV}.js`),
mandatory: false,
}),
)
.add(() =>
loaders.require({ path: path.join(process.cwd(), 'secrets', 'secrets.json'), watch: true, mandatory: false }),
)
.add(() => loaders.args())
.on('loaded', cb)
.on('error', cb)
.end(cb);

return { start };
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const System = require('systemic');
const confabulous = require('./confabulous');

module.exports = new System({ name: 'config' }).add('config', confabulous(), { scoped: true });
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const System = require('systemic');
const { defaultMiddleware, app, server } = require('systemic-express');

module.exports = new System({ name: 'express' })
.add('app', app()).dependsOn('config', 'logger')
.add('middleware.default', defaultMiddleware())
.dependsOn('logger', 'app', 'routes')
.add('server', server())
.dependsOn('config', 'app', 'middleware.default');
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const System = require('systemic');
const initPino = require('./initPino');

module.exports = new System({ name: 'logging' })
.add('logger', initPino())
.dependsOn('config', 'pkg');
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const pino = require('pino');
const pinoHttp = require('pino-http');

module.exports = () => {
const start = async ({ config, pkg }) => {
const { prettyLogs, level, hiddenAttributes = [] } = config;

let loggerConfig = {
level,
base: {
name: pkg.name,
},
timestamp: pino.stdTimeFunctions.isoTime,
formatters: {
level: (label) => ({ level: label }),
},
redact: {
paths: hiddenAttributes,
censor: '(hidden)',
},
};

if (prettyLogs) {
loggerConfig = {
...loggerConfig,
transport: {
target: 'pino-pretty',
},
};
}

const logger = pino(loggerConfig);
const httpLogger = pinoHttp({ logger });

const debug = (msg, data) => logger.debug({ msg, data });
const info = (msg, data) => logger.info({ msg, data });
const warn = (msg, data) => logger.warn({ msg, data });
const error = (msg, data) => logger.error({ msg, data });
const fatal = (msg, data) => logger.fatal({ msg, data });

return {
httpLogger,
debug,
info,
warn,
error,
fatal,
};
};

return {
start,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = () => {
const start = async ({ manifest = {}, app }) => {
app.get('/__/manifest', (req, res) => res.json(manifest));
return Promise.resolve();
};

return { start };
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const System = require('systemic');
const adminRoutes = require('./admin-routes');
const initLoggerMiddleware = require('./middlewares/logger');
const initCoreMiddleware = require('./middlewares/core');

module.exports = new System({ name: 'routes' })
.add('routes.admin', adminRoutes())
.dependsOn('app', 'manifest')
.add('routes.middleware.core', initCoreMiddleware())
.dependsOn('config', 'app')
.add('routes.middleware.logger', initLoggerMiddleware())
.dependsOn('logger', 'app')
.add('routes')
.dependsOn(
'routes.middleware.core',
'routes.middleware.logger',
'routes.admin',
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const helmet = require('helmet');
const validator = require('swagger-endpoint-validator');
const bodyParser = require('body-parser');
const cors = require('cors');

module.exports = () => {
const start = async ({ app, config }) => {
const { swaggerValidator } = config;

app.use(helmet());
app.use(cors());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

await validator.init(app, swaggerValidator);

return Promise.resolve();
};

return { start };
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module.exports = () => {
const start = async ({ app, logger }) => {
app.use((req, res, next) => {
logger.httpLogger(req, res);
next();
});

return Promise.resolve();
};

return { start };
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = { logger: { transport: null } };
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module.exports = {
server: {
host: '0.0.0.0',
port: 4000,
},
routes: {
admin: {},
middleware: {
core: {
swaggerValidator: {
apiDocEndpoint: '/__/docs/api',
validateRequests: true,
validateResponses: true,
validationEndpoint: '/test',
format: 'yaml',
yaml: {
file: './docs/syncapi.yaml',
},
},
},
},
},
logger: {
prettyLogs: process.env.LOGGING_PRETTIFY,
level: process.env.LOGGING_LEVEL || 'info',
hiddenAttributes: ['req.headers', 'res.headers'],
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
logger: { transport: 'console' },
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = {};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
logger: { transport: null },
};
Loading

0 comments on commit 9d2aa83

Please sign in to comment.