backend-common: reimplement middleware using MiddlewareFactory

Co-authored-by: Fredrik Adelöw <freben@gmail.com>
Co-authored-by: blam <ben@blam.sh>
Co-authored-by: Johan Haals <johan.haals@gmail.com>
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2023-01-09 16:04:25 +01:00
parent eb62b5be74
commit c44dc6225f
6 changed files with 26 additions and 92 deletions
+1
View File
@@ -20,5 +20,6 @@
* @packageDocumentation
*/
export * from './lib/http';
export * from './wiring';
export * from './services/implementations';
+1
View File
@@ -34,6 +34,7 @@
"test:kubernetes": "backstage-cli package test -t KubernetesContainerRunner --no-watch"
},
"dependencies": {
"@backstage/backend-app-api": "workspace:^",
"@backstage/backend-plugin-api": "workspace:^",
"@backstage/cli-common": "workspace:^",
"@backstage/config": "workspace:^",
@@ -14,19 +14,11 @@
* limitations under the License.
*/
import {
AuthenticationError,
ConflictError,
ErrorResponseBody,
InputError,
NotAllowedError,
NotFoundError,
NotModifiedError,
serializeError,
} from '@backstage/errors';
import { ErrorRequestHandler, NextFunction, Request, Response } from 'express';
import { ErrorRequestHandler } from 'express';
import { LoggerService } from '@backstage/backend-plugin-api';
import { getRootLogger } from '../logging';
import { ConfigReader } from '@backstage/config';
import { MiddlewareFactory } from '@backstage/backend-app-api';
/**
* Options passed to the {@link errorHandler} middleware.
@@ -73,69 +65,11 @@ export type ErrorHandlerOptions = {
export function errorHandler(
options: ErrorHandlerOptions = {},
): ErrorRequestHandler {
const showStackTraces =
options.showStackTraces ?? process.env.NODE_ENV === 'development';
const logger = (options.logger || getRootLogger()).child({
type: 'errorHandler',
return MiddlewareFactory.create({
config: new ConfigReader({}),
logger: options.logger ?? getRootLogger(),
}).error({
logAllErrors: options.logClientErrors,
showStackTraces: options.showStackTraces,
});
return (error: Error, req: Request, res: Response, next: NextFunction) => {
const statusCode = getStatusCode(error);
if (options.logClientErrors || statusCode >= 500) {
logger.error(`Request failed with status ${statusCode}`, error);
}
if (res.headersSent) {
// If the headers have already been sent, do not send the response again
// as this will throw an error in the backend.
next(error);
return;
}
const body: ErrorResponseBody = {
error: serializeError(error, { includeStack: showStackTraces }),
request: { method: req.method, url: req.url },
response: { statusCode },
};
res.status(statusCode).json(body);
};
}
function getStatusCode(error: Error): number {
// Look for common http library status codes
const knownStatusCodeFields = ['statusCode', 'status'];
for (const field of knownStatusCodeFields) {
const statusCode = (error as any)[field];
if (
typeof statusCode === 'number' &&
(statusCode | 0) === statusCode && // is whole integer
statusCode >= 100 &&
statusCode <= 599
) {
return statusCode;
}
}
// Handle well-known error types
switch (error.name) {
case NotModifiedError.name:
return 304;
case InputError.name:
return 400;
case AuthenticationError.name:
return 401;
case NotAllowedError.name:
return 403;
case NotFoundError.name:
return 404;
case ConflictError.name:
return 409;
default:
break;
}
// Fall back to internal server error
return 500;
}
@@ -14,7 +14,10 @@
* limitations under the License.
*/
import { NextFunction, Request, RequestHandler, Response } from 'express';
import { MiddlewareFactory } from '@backstage/backend-app-api';
import { ConfigReader } from '@backstage/config';
import { RequestHandler } from 'express';
import { getRootLogger } from '../logging';
/**
* Express middleware to handle requests for missing routes.
@@ -26,8 +29,8 @@ import { NextFunction, Request, RequestHandler, Response } from 'express';
* @returns An Express request handler
*/
export function notFoundHandler(): RequestHandler {
/* eslint-disable @typescript-eslint/no-unused-vars */
return (_request: Request, response: Response, _next: NextFunction) => {
response.status(404).end();
};
return MiddlewareFactory.create({
config: new ConfigReader({}),
logger: getRootLogger(),
}).notFound();
}
@@ -14,10 +14,11 @@
* limitations under the License.
*/
import { MiddlewareFactory } from '@backstage/backend-app-api';
import { RequestHandler } from 'express';
import { LoggerService } from '@backstage/backend-plugin-api';
import morgan from 'morgan';
import { getRootLogger } from '../logging';
import { ConfigReader } from '@backstage/config';
/**
* Logs incoming requests.
@@ -27,15 +28,8 @@ import { getRootLogger } from '../logging';
* @returns An Express request handler
*/
export function requestLoggingHandler(logger?: LoggerService): RequestHandler {
const actualLogger = (logger || getRootLogger()).child({
type: 'incomingRequest',
});
return morgan('combined', {
stream: {
write(message: string) {
actualLogger.info(message.trimEnd());
},
},
});
return MiddlewareFactory.create({
config: new ConfigReader({}),
logger: logger ?? getRootLogger(),
}).logging();
}
+1
View File
@@ -3413,6 +3413,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@backstage/backend-common@workspace:packages/backend-common"
dependencies:
"@backstage/backend-app-api": "workspace:^"
"@backstage/backend-plugin-api": "workspace:^"
"@backstage/backend-test-utils": "workspace:^"
"@backstage/cli": "workspace:^"