Skip to content

Commit

Permalink
refactor: modularize bootstrap tasks in index.js (#1527)
Browse files Browse the repository at this point in the history
  • Loading branch information
Matte22 authored Feb 18, 2025
1 parent ea22add commit d2f4e44
Show file tree
Hide file tree
Showing 10 changed files with 467 additions and 299 deletions.
52 changes: 52 additions & 0 deletions api/source/bootstrap/bootstrapUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const path = require('node:path')
const logger = require('../utils/logger')
const packageJson = require("../package.json")

function modulePathResolver( handlersPath, route, apiDoc ) {
const pathKey = route.openApiRoute.substring(route.basePath.length)
const schema = apiDoc.paths[pathKey][route.method.toLowerCase()]
const controller = schema.tags[0]
const method = schema['operationId']
const modulePath = path.join(handlersPath, controller)
const handler = require(modulePath)
if (handler[method] === undefined) {
throw new Error(
`Could not find a [${method}] function in ${modulePath} when trying to route [${route.method} ${route.expressRoute}].`,
)
}
return handler[method]
}

function buildResponseValidationConfig(willValidateResponse) {
if (willValidateResponse){
return {
onError: (error, body, req) => {
logger.writeError('rest', 'responseValidation', {
error,
request: logger.serializeRequest(req),
body
})
}
}
}
else {
return false
}
}

function logAppConfig(config) {
logger.writeInfo('bootstrapUtils', 'starting bootstrap', {
version: packageJson.version,
env: logger.serializeEnvironment(),
dirname: __dirname,
cwd: process.cwd()
})
logger.writeInfo('bootstrapUtils', 'configuration', config)

}

module.exports = {
modulePathResolver,
buildResponseValidationConfig,
logAppConfig
}
80 changes: 80 additions & 0 deletions api/source/bootstrap/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
const express = require('express')
const path = require('path')
const writer = require('../utils/writer')
const logger = require('../utils/logger')
const config = require('../utils/config')

function serveClient(app) {

if (config.client.disabled) {
logger.writeDebug('serveClient', 'client', {message: 'client disabled'})
return
}
try {
serveClientEnv(app)
serveStaticFiles(app)
logger.writeDebug('serveClient', 'client', { message: 'succeeded setting up client' })
}
catch (err) {
logger.writeError('serveClient', 'client', {message: err.message, stack: err.stack})
}
}

function getClientEnv(){
const envJS =
`const STIGMAN = {
Env: {
version: "${config.version}",
apiBase: "${config.client.apiBase}",
displayAppManagers: ${config.client.displayAppManagers},
welcome: {
image: "${config.client.welcome.image}",
title: "${config.client.welcome.title.replace(/"/g, '\\"')}",
message: "${config.client.welcome.message.replace(/"/g, '\\"')}",
link: "${config.client.welcome.link}"
},
commit: {
branch: "${config.commit.branch}",
sha: "${config.commit.sha}",
tag: "${config.commit.tag}",
describe: "${config.commit.describe}"
},
oauth: {
authority: "${config.client.authority}",
clientId: "${config.client.clientId}",
refreshToken: {
disabled: ${config.client.refreshToken.disabled}
},
extraScopes: "${config.client.extraScopes ?? ''}",
scopePrefix: "${config.client.scopePrefix ?? ''}"
},
experimental: {
appData: "${config.experimental.appData}"
}
}
}`
return envJS
}

function serveClientEnv(app){
const envJS = getClientEnv()
app.get('/js/Env.js', function (req, res) {
req.component = 'static'
writer.writeWithContentType(res, { payload: envJS, contentType: "application/javascript" })
})
}

function serveStaticFiles(app){
const staticPath = path.join(__dirname, "../", config.client.directory)
logger.writeDebug('serveStaticFiles', 'client', {client_static: staticPath})
const expressStatic = express.static(staticPath)

app.use('/', (req, res, next) => {
req.component = 'static'
expressStatic(req, res, next)
})
}

module.exports = {
serveClient,
}
38 changes: 38 additions & 0 deletions api/source/bootstrap/dependencies.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const logger = require('../utils/logger')
const auth = require('../utils/auth')
const db = require('../service/utils')
const { serializeError } = require('../utils/serializeError')

let depStatus = {
db: 'waiting',
auth: 'waiting'
}

async function initializeDependencies() {
try {
await Promise.all([
auth.initializeAuth(setDepStatus),
db.initializeDatabase(setDepStatus)
])
} catch (e) {
logger.writeError('dependencies', 'shutdown', {message:'Failed to setup dependencies', error: serializeError(e)})
process.exit(1)
}
}

function setDepStatus(service, status) {
if (depStatus.hasOwnProperty(service)) {
depStatus[service] = status
} else {
throw new Error(`Service ${service} is not recognized in depStatus`)
}
}

function getDepStatus() {
return depStatus
}

module.exports = {
getDepStatus,
initializeDependencies
}
62 changes: 62 additions & 0 deletions api/source/bootstrap/docs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@

const express = require('express')
const path = require('path')
const fs = require('fs')
const logger = require('../utils/logger')
const config = require('../utils/config')
const swaggerUi = require('swagger-ui-express')
const jsyaml = require('js-yaml')

function serveDocs(app) {
if (config.docs.disabled) {
logger.writeDebug('serveDocs', 'client', {message: 'documentation disabled'})
return
}
try {
app.use('/docs', express.static(path.join(__dirname, "../", config.docs.docsDirectory)))
logger.writeDebug('serveDocs', 'client', {message: 'succeeded setting up documentation'})
}
catch (err) {
logger.writeError('serveDocs', 'client', {message: err.message, stack: err.stack})
}
}

function serveApiDocs(app) {

if (config.swaggerUi.enabled) {
const oasDoc = getOAS()
configureSwaggerUI(app, oasDoc)
}
else
{
logger.writeDebug('serveApiDocs', 'SwaggerUI', { message: 'Swagger UI is disabled in configuration' })
}
}

function getOAS(){
// Read and modify OpenAPI specification
const apiSpecPath = path.join(__dirname, '../specification/stig-manager.yaml')
let spec = fs.readFileSync(apiSpecPath, 'utf8')
let oasDoc = jsyaml.load(spec)
// Replace with config values
oasDoc.info.version = config.version
oasDoc.servers[0].url = config.swaggerUi.server
oasDoc.components.securitySchemes.oauth.openIdConnectUrl = `${config.client.authority}/.well-known/openid-configuration`
config.definition = oasDoc
return oasDoc
}

function configureSwaggerUI(app, oasDoc){
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(oasDoc, null, {
oauth2RedirectUrl: config.swaggerUi.oauth2RedirectUrl,
oauth: {
usePkceWithAuthorizationCodeGrant: true
}
}))
app.get(['/swagger.json','/openapi.json'], function(req, res) {
res.json(oasDoc)
})
logger.writeDebug('configureSwaggerUI', 'client', {message: 'succeeded setting up swagger-ui'})
}

module.exports = { serveDocs, serveApiDocs }
32 changes: 32 additions & 0 deletions api/source/bootstrap/errorHandlers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const logger = require('../utils/logger')
const smErrors = require('../utils/error')
const { serializeError } = require('../utils/serializeError')
const path = require('path')

function configureErrorHandlers(app) {
// express-openapi-validator does not expose top-level HttpError in their index.js.
// We can get it from framework.types.js
// CAUTION: We break here if express-openapi-validator changes this
const eovPath = path.dirname(require.resolve('express-openapi-validator'))
const eovErrors = require(path.join(eovPath, 'framework', 'types.js'))
app.use((err, req, res, next) => {
if (!(err instanceof smErrors.SmError) && !(err instanceof eovErrors.HttpError)) {
logger.writeError('rest', 'error', {
request: logger.serializeRequest(req),
error: serializeError(err)
})
}

res.errorBody = { error: err.message, code: err.code, detail: err.detail}
if (err.status === 500 || !(err.status)) res.errorBody.stack = err.stack
if (!res._headerSent) {
res.status(err.status || 500).header(err.headers).json(res.errorBody)
}
else {
res.write(JSON.stringify(res.errorBody) + '\n')
res.end()
}
})
}

module.exports = configureErrorHandlers
Loading

0 comments on commit d2f4e44

Please sign in to comment.