backend-app-api: refactor RestrictedIndexedRouter -> DefaultRootHttpRouter

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-10 15:06:04 +01:00
committed by blam
parent 8f9d0c8fca
commit df6a5a9264
5 changed files with 77 additions and 16 deletions
+16
View File
@@ -12,6 +12,7 @@ import { CorsOptions } from 'cors';
import { ErrorRequestHandler } from 'express';
import { Express as Express_2 } from 'express';
import { ExtensionPoint } from '@backstage/backend-plugin-api';
import { Handler } from 'express';
import { HelmetOptions } from 'helmet';
import * as http from 'http';
import { HttpRouterService } from '@backstage/backend-plugin-api';
@@ -79,6 +80,21 @@ export const databaseFactory: (
options?: undefined,
) => ServiceFactory<PluginDatabaseManager>;
// @public
export class DefaultRootHttpRouter implements RootHttpRouterService {
// (undocumented)
static create(options?: DefaultRootHttpRouterOptions): DefaultRootHttpRouter;
// (undocumented)
handler(): Handler;
// (undocumented)
use(path: string, handler: Handler): void;
}
// @public
export interface DefaultRootHttpRouterOptions {
indexPath?: string | false;
}
// @public (undocumented)
export const discoveryFactory: (
options?: undefined,
@@ -14,9 +14,9 @@
* limitations under the License.
*/
import { RestrictedIndexedRouter } from './RestrictedIndexedRouter';
import { DefaultRootHttpRouter } from './DefaultRootHttpRouter';
describe('RestrictedIndexedRouter', () => {
describe('DefaultRootHttpRouter', () => {
it.each([
[['/b'], '/a'],
[['/a'], '/aa/b'],
@@ -25,7 +25,7 @@ describe('RestrictedIndexedRouter', () => {
[['/b/a'], '/a'],
[['/a'], '/aa'],
])(`with existing paths %s, adds %s without conflict`, (existing, added) => {
const router = new RestrictedIndexedRouter(false);
const router = DefaultRootHttpRouter.create();
for (const path of existing) {
router.use(path, () => {});
}
@@ -39,7 +39,7 @@ describe('RestrictedIndexedRouter', () => {
])(
`find conflict when existing paths %s, adds %s`,
(existing, added, conflict) => {
const router = new RestrictedIndexedRouter(false);
const router = DefaultRootHttpRouter.create();
for (const path of existing) {
router.use(path, () => {});
}
@@ -48,4 +48,10 @@ describe('RestrictedIndexedRouter', () => {
);
},
);
it('should not be possible to supply an empty indexPath', () => {
expect(() => DefaultRootHttpRouter.create({ indexPath: '' })).toThrow(
'indexPath option may not be an empty string',
);
});
});
@@ -21,18 +21,53 @@ function normalizePath(path: string): string {
return path.replace(/\/*$/, '/');
}
export class RestrictedIndexedRouter implements RootHttpRouterService {
#indexPath?: false | string;
/**
* Options for the {@link DefaultRootHttpRouter} class.
*
* @public
*/
export interface DefaultRootHttpRouterOptions {
/**
* The path to forward all unmatched requests to. Defaults to '/api/app' if
* not given. Disables index path behavior if false is given.
*/
indexPath?: string | false;
}
/**
* The default implementation of the {@link @backstage/backend-plugin-api#RootHttpRouterService} interface for
* {@link @backstage/backend-plugin-api#coreServices.rootHttpRouter}.
*
* @public
*/
export class DefaultRootHttpRouter implements RootHttpRouterService {
#indexPath?: string;
#router = Router();
#namedRoutes = Router();
#indexRouter = Router();
#existingPaths = new Array<string>();
constructor(indexPath?: false | string) {
static create(options?: DefaultRootHttpRouterOptions) {
let indexPath;
if (options?.indexPath === false) {
indexPath = undefined;
} else if (options?.indexPath === undefined) {
indexPath = '/api/app';
} else if (options?.indexPath === '') {
throw new Error('indexPath option may not be an empty string');
} else {
indexPath = options.indexPath;
}
return new DefaultRootHttpRouter(indexPath);
}
private constructor(indexPath?: string) {
this.#indexPath = indexPath;
this.#router.use(this.#namedRoutes);
this.#router.use(this.#indexRouter);
if (this.#indexPath) {
this.#router.use(this.#indexRouter);
}
}
use(path: string, handler: Handler) {
@@ -14,8 +14,12 @@
* limitations under the License.
*/
export { rootHttpRouterFactory } from './rootHttpRouterFactory';
export type {
RootHttpRouterFactoryOptions,
RootHttpRouterConfigureOptions,
export {
rootHttpRouterFactory,
type RootHttpRouterFactoryOptions,
type RootHttpRouterConfigureOptions,
} from './rootHttpRouterFactory';
export {
DefaultRootHttpRouter,
type DefaultRootHttpRouterOptions,
} from './DefaultRootHttpRouter';
@@ -27,7 +27,7 @@ import {
MiddlewareFactory,
readHttpServerOptions,
} from '../../../http';
import { RestrictedIndexedRouter } from './RestrictedIndexedRouter';
import { DefaultRootHttpRouter } from './DefaultRootHttpRouter';
/**
* @public
@@ -46,7 +46,8 @@ export interface RootHttpRouterConfigureOptions {
*/
export type RootHttpRouterFactoryOptions = {
/**
* The path to forward all unmatched requests to. Defaults to '/api/app'
* The path to forward all unmatched requests to. Defaults to '/api/app' if
* not given. Disables index path behavior if false is given.
*/
indexPath?: string | false;
@@ -82,11 +83,10 @@ export const rootHttpRouterFactory = createServiceFactory({
configure = defaultConfigure,
}: RootHttpRouterFactoryOptions = {},
) {
const router = new RestrictedIndexedRouter(indexPath ?? '/api/app');
const logger = rootLogger.child({ service: 'rootHttpRouter' });
const app = express();
const router = DefaultRootHttpRouter.create({ indexPath });
const middleware = MiddlewareFactory.create({ config, logger });
configure({