Merge pull request #686 from spotify/freben/inventory-again

Make the inventory backend standalone runnable
This commit is contained in:
Fredrik Adelöw
2020-05-01 22:30:59 +02:00
committed by GitHub
15 changed files with 328 additions and 36 deletions
+1 -1
View File
@@ -24,8 +24,8 @@ import {
const app = express();
app.use(requestLoggingHandler());
app.use('/home', myHomeRouter);
app.use(errorHandler());
app.use(notFoundHandler());
app.use(errorHandler());
app.listen(PORT, () => {
getRootLogger().info(`Listening on port ${PORT}`);
+24 -18
View File
@@ -28,32 +28,38 @@ import {
notFoundHandler,
requestLoggingHandler,
} from '@backstage/backend-common';
import { router as inventoryRouter } from '@backstage/plugin-inventory-backend';
import { createRouter as inventoryRouter } from '@backstage/plugin-inventory-backend';
import { createScaffolder } from '@backstage/plugin-scaffolder-backend';
import compression from 'compression';
import cors from 'cors';
import express from 'express';
import helmet from 'helmet';
import { testRouter } from './test';
import { createScaffolder } from '@backstage/plugin-scaffolder-backend';
const DEFAULT_PORT = 7000;
const PORT = parseInt(process.env.PORT ?? '', 10) || DEFAULT_PORT;
const app = express();
const logger = getRootLogger().child({ type: 'plugin' });
const scaffolder = createScaffolder();
async function main() {
const inventory = await inventoryRouter({ logger });
const scaffolder = createScaffolder();
app.use(helmet());
app.use(cors());
app.use(compression());
app.use(express.json());
app.use(requestLoggingHandler());
app.use('/test', testRouter);
app.use('/inventory', inventoryRouter);
app.use('/scaffolder', scaffolder);
app.use(errorHandler());
app.use(notFoundHandler());
const app = express();
app.listen(PORT, () => {
getRootLogger().info(`Listening on port ${PORT}`);
});
app.use(helmet());
app.use(cors());
app.use(compression());
app.use(express.json());
app.use(requestLoggingHandler());
app.use('/test', testRouter);
app.use('/inventory', inventory);
app.use('/scaffolder', scaffolder);
app.use(notFoundHandler());
app.use(errorHandler());
app.listen(PORT, () => {
getRootLogger().info(`Listening on port ${PORT}`);
});
}
main();
+15 -3
View File
@@ -5,18 +5,30 @@
"license": "Apache-2.0",
"private": true,
"scripts": {
"start": "tsc-watch --onFirstSuccess \"nodemon dist/run.js\"",
"build": "tsc",
"lint": "backstage-cli lint",
"test": "backstage-cli test",
"clean": "backstage-cli clean"
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.4"
"@backstage/cli": "^0.1.1-alpha.4",
"jest-fetch-mock": "^3.0.3",
"tsc-watch": "^4.2.3"
},
"dependencies": {
"express": "^4.17.1"
"@backstage/backend-common": "0.1.1-alpha.4",
"compression": "^1.7.4",
"cors": "^2.8.5",
"express": "^4.17.1",
"helmet": "^3.22.0",
"morgan": "^1.10.0",
"winston": "^3.2.1"
},
"files": [
"dist"
]
],
"nodemonConfig": {
"watch": "./dist"
}
}
+1 -1
View File
@@ -14,4 +14,4 @@
* limitations under the License.
*/
export { router } from './plugin';
export * from './service/router';
@@ -0,0 +1,35 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, Inventory } from './types';
export class AggregatorInventory implements Inventory {
inventories: Inventory[] = [];
list(): Promise<Array<Component>> {
return Promise.all(this.inventories.map(i => i.list())).then(lists =>
lists.flat(),
);
}
item(id: string): Promise<Component | undefined> {
return this.list().then(items => items.find(i => i.id === id));
}
enlist(inventory: Inventory) {
this.inventories.push(inventory);
}
}
@@ -0,0 +1,29 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, Inventory } from './types';
export class StaticInventory implements Inventory {
constructor(private components: Component[]) {}
list(): Promise<Array<Component>> {
return Promise.resolve([...this.components]);
}
item(id: string): Promise<Component | undefined> {
return this.list().then(items => items.find(i => i.id === id));
}
}
@@ -0,0 +1,19 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './AggregatorInventory';
export * from './StaticInventory';
export * from './types';
@@ -14,17 +14,11 @@
* limitations under the License.
*/
import express from 'express';
export type Component = {
id: string;
};
export const router = express.Router();
router.get('/', async (_, res) => {
res
.status(200)
.send([
{ id: 'component1' },
{ id: 'component2' },
{ id: 'component3' },
{ id: 'component4' },
]);
});
export type Inventory = {
list: () => Promise<Component[]>;
item: (id: string) => Promise<Component | undefined>;
};
+34
View File
@@ -0,0 +1,34 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { getRootLogger } from '@backstage/backend-common';
import { startServer } from './service/server';
startServer({
port: process.env.PLUGIN_PORT ? Number(process.env.PLUGIN_PORT) : 3003,
enableCors: process.env.PLUGIN_CORS
? Boolean(process.env.PLUGIN_CORS)
: false,
logger: getRootLogger(),
}).catch(err => {
getRootLogger().error(err);
process.exit(1);
});
process.on('SIGINT', () => {
getRootLogger().info('CTRL+C pressed; exiting.');
process.exit(0);
});
@@ -0,0 +1,51 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
errorHandler,
notFoundHandler,
requestLoggingHandler,
} from '@backstage/backend-common';
import compression from 'compression';
import cors from 'cors';
import express from 'express';
import helmet from 'helmet';
import { Logger } from 'winston';
import { createRouter } from './router';
export interface ApplicationOptions {
enableCors: boolean;
logger: Logger;
}
export async function createApplication(
options: ApplicationOptions,
): Promise<express.Application> {
const app = express();
app.use(helmet());
if (options.enableCors) {
app.use(cors());
}
app.use(compression());
app.use(express.json());
app.use(requestLoggingHandler());
app.use('/', await createRouter({ logger: options.logger }));
app.use(notFoundHandler());
app.use(errorHandler());
return app;
}
@@ -0,0 +1,61 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import express from 'express';
import { Logger } from 'winston';
import { AggregatorInventory, StaticInventory } from '../inventory';
export interface RouterOptions {
logger: Logger;
}
export async function createRouter(
options: RouterOptions,
): Promise<express.Router> {
const logger = options.logger.child({ plugin: 'inventory' });
const inventory = new AggregatorInventory();
inventory.enlist(
new StaticInventory([
{ id: 'component1' },
{ id: 'component2' },
{ id: 'component3' },
{ id: 'component4' },
]),
);
const router = express.Router();
router
.get('/', async (req, res) => {
const components = await inventory.list();
res.status(200).send(components);
})
.get('/:id', async (req, res) => {
const { id } = req.params;
const component = await inventory.item(id);
if (component) {
res.status(200).send(component);
} else {
res.status(404).send();
}
});
const app = express();
app.set('logger', logger);
app.use('/', router);
return app;
}
@@ -0,0 +1,48 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Server } from 'http';
import { Logger } from 'winston';
import { createApplication } from './application';
export interface ServerOptions {
port: number;
enableCors: boolean;
logger: Logger;
}
export async function startServer(options: ServerOptions): Promise<Server> {
const logger = options.logger.child({ service: 'inventory-backend' });
logger.debug('Creating application...');
const app = await createApplication({
enableCors: options.enableCors,
logger,
});
logger.debug('Starting application server...');
return await new Promise((resolve, reject) => {
const server = app.listen(options.port, (err?: Error) => {
if (err) {
reject(err);
return;
}
logger.info(`Listening on port ${options.port}`);
resolve(server);
});
});
}
@@ -13,3 +13,5 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
require('jest-fetch-mock').enableMocks();
+1
View File
@@ -10,6 +10,7 @@
"target": "es5",
"module": "commonjs",
"esModuleInterop": true,
"lib": ["es2019"],
"types": ["node", "jest"]
}
}