diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 5ec37295ed..a5f283abba 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -6,19 +6,21 @@ "license": "Apache-2.0", "private": true, "scripts": { - "start": "tsc-watch --onFirstSuccess \"cross-env NODE_ENV=development nodemon dist/run.js\"", + "start": "tsc-watch --onFirstSuccess \"cross-env NODE_ENV=development nodemon -r esm dist/run.js\"", "build": "tsc", "lint": "backstage-cli lint", "test": "backstage-cli test", "prepack": "backstage-cli prepack", "postpack": "backstage-cli postpack", - "clean": "backstage-cli clean" + "clean": "backstage-cli clean", + "mock-db": "sh ./scripts/mock-db" }, "dependencies": { "@backstage/backend-common": "^0.1.1-alpha.6", "@backstage/catalog-model": "^0.1.1-alpha.6", "compression": "^1.7.4", "cors": "^2.8.5", + "esm": "^3.2.25", "express": "^4.17.1", "express-promise-router": "^3.0.3", "fs-extra": "^9.0.0", diff --git a/plugins/catalog-backend/scripts/mock-db b/plugins/catalog-backend/scripts/mock-db new file mode 100644 index 0000000000..af5de1cb1b --- /dev/null +++ b/plugins/catalog-backend/scripts/mock-db @@ -0,0 +1,12 @@ +curl --location --request POST 'localhost:3003/locations' \ +--header 'Content-Type: application/json' \ +--data-raw '{ + "type": "github", + "target": "https://github.com/spotify/backstage/blob/master/plugins/catalog-backend/fixtures/one_component.yaml" +}' +curl --location --request POST 'localhost:3003/locations' \ +--header 'Content-Type: application/json' \ +--data-raw '{ + "type": "github", + "target": "https://github.com/spotify/backstage/blob/master/plugins/catalog-backend/fixtures/two_components.yaml" +}' \ No newline at end of file diff --git a/plugins/catalog-backend/src/database/DatabaseManager.ts b/plugins/catalog-backend/src/database/DatabaseManager.ts index 4268dc4146..9d7a79d57a 100644 --- a/plugins/catalog-backend/src/database/DatabaseManager.ts +++ b/plugins/catalog-backend/src/database/DatabaseManager.ts @@ -14,26 +14,48 @@ * limitations under the License. */ -import type { Entity, EntityPolicy } from '@backstage/catalog-model'; +import { Entity, EntityPolicy } from '@backstage/catalog-model'; import Knex from 'knex'; import lodash from 'lodash'; import path from 'path'; import { Logger } from 'winston'; -import type { IngestionModel } from '../ingestion/types'; +import { IngestionModel } from '../ingestion/types'; import { CommonDatabase } from './CommonDatabase'; -import { DatabaseLocationUpdateLogStatus } from './types'; -import type { Database, DbEntityRequest } from './types'; +import { + DatabaseLocationUpdateLogStatus, + Database, + DbEntityRequest, +} from './types'; export class DatabaseManager { public static async createDatabase( - knex: Knex, + database: Knex, logger: Logger, ): Promise { - await knex.migrate.latest({ + await database.migrate.latest({ directory: path.resolve(__dirname, 'migrations'), loadExtensions: ['.js'], }); - return new CommonDatabase(knex, logger); + return new CommonDatabase(database, logger); + } + + public static async createInMemoryDatabase( + logger: Logger, + ): Promise { + const database = Knex({ + client: 'sqlite3', + connection: ':memory:', + useNullAsDefault: true, + }); + database.client.pool.on('createSuccess', (_eventId: any, resource: any) => { + resource.run('PRAGMA foreign_keys = ON', () => {}); + }); + + await database.migrate.latest({ + directory: path.resolve(__dirname, 'migrations'), + loadExtensions: ['.js'], + }); + return new CommonDatabase(database, logger); } private static async logUpdateSuccess( diff --git a/plugins/catalog-backend/src/service/standaloneApplication.ts b/plugins/catalog-backend/src/service/standaloneApplication.ts index 126806c751..ebd28a0a45 100644 --- a/plugins/catalog-backend/src/service/standaloneApplication.ts +++ b/plugins/catalog-backend/src/service/standaloneApplication.ts @@ -26,18 +26,26 @@ import helmet from 'helmet'; import { Logger } from 'winston'; import { EntitiesCatalog, LocationsCatalog } from '../catalog'; import { createRouter } from './router'; +import { HigherOrderOperation } from '../ingestion/types'; export interface ApplicationOptions { enableCors: boolean; entitiesCatalog: EntitiesCatalog; locationsCatalog?: LocationsCatalog; + higherOrderOperation?: HigherOrderOperation; logger: Logger; } export async function createStandaloneApplication( options: ApplicationOptions, ): Promise { - const { enableCors, entitiesCatalog, locationsCatalog, logger } = options; + const { + enableCors, + entitiesCatalog, + locationsCatalog, + logger, + higherOrderOperation, + } = options; const app = express(); app.use(helmet()); @@ -49,7 +57,12 @@ export async function createStandaloneApplication( app.use(requestLoggingHandler()); app.use( '/', - await createRouter({ entitiesCatalog, locationsCatalog, logger }), + await createRouter({ + entitiesCatalog, + locationsCatalog, + higherOrderOperation, + logger, + }), ); app.use(notFoundHandler()); app.use(errorHandler()); diff --git a/plugins/catalog-backend/src/service/standaloneServer.ts b/plugins/catalog-backend/src/service/standaloneServer.ts index 2fd4afc7cd..8bf3554510 100644 --- a/plugins/catalog-backend/src/service/standaloneServer.ts +++ b/plugins/catalog-backend/src/service/standaloneServer.ts @@ -16,8 +16,14 @@ import { Server } from 'http'; import { Logger } from 'winston'; -import { StaticEntitiesCatalog } from '../catalog'; +import knex from 'knex'; import { createStandaloneApplication } from './standaloneApplication'; +import { DatabaseEntitiesCatalog } from '../catalog/DatabaseEntitiesCatalog'; +import { DatabaseManager } from '../database/DatabaseManager'; +import { DatabaseLocationsCatalog } from '../catalog/DatabaseLocationsCatalog'; +import { LocationReaders } from '../ingestion/source/LocationReaders'; +import { IngestionModels, DescriptorParsers, HigherOrderOperations } from '..'; +import { EntityPolicies } from '@backstage/catalog-model'; export interface ServerOptions { port: number; @@ -30,25 +36,27 @@ export async function startStandaloneServer( ): Promise { const logger = options.logger.child({ service: 'catalog-backend' }); - const entitiesCatalog = new StaticEntitiesCatalog([ - { - apiVersion: 'backstage.io/v1beta1', - kind: 'Component', - metadata: { name: 'c1' }, - spec: { type: 'service' }, - }, - { - apiVersion: 'backstage.io/v1beta1', - kind: 'Component', - metadata: { name: 'c2' }, - spec: { type: 'service' }, - }, - ]); + const db = await DatabaseManager.createInMemoryDatabase(logger); + + const entitiesCatalog = new DatabaseEntitiesCatalog(db); + const locationsCatalog = new DatabaseLocationsCatalog(db); + const ingestionModel = new IngestionModels( + new LocationReaders(), + new DescriptorParsers(), + new EntityPolicies(), + ); + const higherOrderOperation = new HigherOrderOperations( + entitiesCatalog, + locationsCatalog, + ingestionModel, + ); logger.debug('Creating application...'); const app = await createStandaloneApplication({ enableCors: options.enableCors, entitiesCatalog, + locationsCatalog, + higherOrderOperation, logger, }); diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index f5fa45dc7e..35bc6c7d7c 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -17,6 +17,7 @@ import React, { FC } from 'react'; import { Component } from '../../data/component'; import { InfoCard, Progress, Table, TableColumn } from '@backstage/core'; import { Typography, Link } from '@material-ui/core'; +import EditIcon from '@material-ui/icons/Edit'; const columns: TableColumn[] = [ { @@ -66,7 +67,18 @@ const CatalogTable: FC = ({ columns={columns} options={{ paging: false }} title={`${titlePreamble} (${(components && components.length) || 0})`} - data={components} + data={components.map(({ kind, name, description }) => ({ + kind, + name, + description: ( +
+ {description} + + + +
+ ), + }))} /> ); };