diff --git a/packages/backend/src/plugins/catalog.ts b/packages/backend/src/plugins/catalog.ts index e18d021395..687fd9157a 100644 --- a/packages/backend/src/plugins/catalog.ts +++ b/packages/backend/src/plugins/catalog.ts @@ -36,7 +36,7 @@ export default async function ({ logger, database }: PluginEnvironment) { ); const entitiesCatalog = new DatabaseEntitiesCatalog(db); - const locationsCatalog = new DatabaseLocationsCatalog(db); + const locationsCatalog = new DatabaseLocationsCatalog(db, reader); return await createRouter({ entitiesCatalog, locationsCatalog, logger }); } diff --git a/plugins/catalog-backend/src/catalog/DatabaseLocationsCatalog.test.ts b/plugins/catalog-backend/src/catalog/DatabaseLocationsCatalog.test.ts new file mode 100644 index 0000000000..0181b7fc28 --- /dev/null +++ b/plugins/catalog-backend/src/catalog/DatabaseLocationsCatalog.test.ts @@ -0,0 +1,82 @@ +/* + * 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 { DatabaseLocationsCatalog } from './DatabaseLocationsCatalog'; +import knex from 'knex'; +import path from 'path'; + +import { Database } from '../database'; +import { ReaderOutput } from '../ingestion/types'; +import { getVoidLogger } from '@backstage/backend-common'; + +describe('DatabaseLocationsCatalog', () => { + const database = knex({ + client: 'sqlite3', + connection: ':memory:', + useNullAsDefault: true, + }); + database.client.pool.on('createSuccess', (_eventId: any, resource: any) => { + resource.run('PRAGMA foreign_keys = ON', () => {}); + }); + let db: Database; + let catalog: DatabaseLocationsCatalog; + + const mockLocationReader = { + read: async (type: string, target: string): Promise => { + if (type !== 'valid_type') { + throw new Error(`Unknown location type ${type}`); + } + if (target === 'valid_target') { + return Promise.resolve([{ type: 'data', data: {} }]); + } + throw new Error( + `Can't read location at ${target} with error: Something is broken`, + ); + }, + }; + + beforeEach(async () => { + await database.migrate.latest({ + directory: path.resolve(__dirname, '../database/migrations'), + loadExtensions: ['.ts'], + }); + db = new Database(database, getVoidLogger()); + catalog = new DatabaseLocationsCatalog(db, mockLocationReader); + }); + + it('resolves to location with id', async () => { + return expect( + catalog.addLocation({ type: 'valid_type', target: 'valid_target' }), + ).resolves.toEqual({ + id: expect.anything(), + type: 'valid_type', + target: 'valid_target', + }); + }); + it('rejects for invalid type', async () => { + const type = 'invalid_type'; + return expect( + catalog.addLocation({ type, target: 'valid_target' }), + ).rejects.toThrow(/Unknown location type/); + }); + it('rejects for unreadable target ', async () => { + const target = 'invalid_target'; + return expect( + catalog.addLocation({ type: 'valid_type', target }), + ).rejects.toThrow( + `Can't read location at ${target} with error: Something is broken`, + ); + }); +}); diff --git a/plugins/catalog-backend/src/catalog/DatabaseLocationsCatalog.ts b/plugins/catalog-backend/src/catalog/DatabaseLocationsCatalog.ts index e9f1512074..3f5739eed0 100644 --- a/plugins/catalog-backend/src/catalog/DatabaseLocationsCatalog.ts +++ b/plugins/catalog-backend/src/catalog/DatabaseLocationsCatalog.ts @@ -16,11 +16,24 @@ import { Database } from '../database'; import { AddLocation, Location, LocationsCatalog } from './types'; +import { LocationReader } from '../ingestion'; export class DatabaseLocationsCatalog implements LocationsCatalog { - constructor(private readonly database: Database) {} + constructor( + private readonly database: Database, + private readonly reader: LocationReader, + ) {} async addLocation(location: AddLocation): Promise { + const outputs = await this.reader.read(location.type, location.target); + outputs.forEach(output => { + if (output.type === 'error') { + throw new Error( + `Can't read location at ${location.target}, ${output.error}`, + ); + } + }); + const added = await this.database.addLocation(location); return added; }