diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 629f4c6d18..477cf5e54f 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -21,6 +21,7 @@ "helmet": "^3.22.0", "knex": "^0.21.1", "morgan": "^1.10.0", + "sqlite3": "^4.2.0", "uuid": "^8.0.0", "winston": "^3.2.1", "yaml": "^1.9.2", diff --git a/plugins/catalog-backend/src/catalog/DatabaseCatalog.test.ts b/plugins/catalog-backend/src/catalog/DatabaseCatalog.test.ts index 98a5e9acd4..e244831bfd 100644 --- a/plugins/catalog-backend/src/catalog/DatabaseCatalog.test.ts +++ b/plugins/catalog-backend/src/catalog/DatabaseCatalog.test.ts @@ -60,4 +60,20 @@ describe('DatabaseCatalog', () => { /Found no location/, ); }); + + it('instead of adding second location with the same target, returns existing one', async () => { + // Prepare + const catalog = new DatabaseCatalog(database, logger); + const input = { type: 'a', target: 'b' }; + const output1 = await catalog.addLocation(input); + + // Try to insert the same location + const output2 = await catalog.addLocation(input); + const locations = await catalog.locations(); + + // Output is the same + expect(output2).toEqual(output1); + // Locations contain only one record + expect(locations).toEqual([output1]); + }); }); diff --git a/plugins/catalog-backend/src/catalog/DatabaseCatalog.ts b/plugins/catalog-backend/src/catalog/DatabaseCatalog.ts index 87ff5a503f..acf5e02272 100644 --- a/plugins/catalog-backend/src/catalog/DatabaseCatalog.ts +++ b/plugins/catalog-backend/src/catalog/DatabaseCatalog.ts @@ -53,7 +53,7 @@ export class DatabaseCatalog implements Catalog { name: descriptor.metadata.name, }; - await this.database.transaction(async (tx) => { + await this.database.transaction(async tx => { // TODO(freben): Currently, several locations can compete for the same component const count = await tx('components') .where({ name: component.name }) @@ -74,7 +74,9 @@ export class DatabaseCatalog implements Catalog { // eslint-disable-next-line @typescript-eslint/no-unused-vars async component(name: string): Promise { - const items = await this.database('components').where({ name }).select(); + const items = await this.database('components') + .where({ name }) + .select(); if (!items.length) { throw new NotFoundError(`Found no component with name ${name}`); } @@ -82,6 +84,10 @@ export class DatabaseCatalog implements Catalog { } async addLocation(location: AddLocationRequest): Promise { + const existingLocation = await this.locationByTarget(location.target); + if (existingLocation) { + return existingLocation; + } const id = uuidv4(); const { type, target } = location; await this.database('locations').insert({ id, type, target }); @@ -89,7 +95,9 @@ export class DatabaseCatalog implements Catalog { } async removeLocation(id: string): Promise { - const result = await this.database('locations').where({ id }).del(); + const result = await this.database('locations') + .where({ id }) + .del(); if (!result) { throw new NotFoundError(`Found no location with ID ${id}`); @@ -97,13 +105,21 @@ export class DatabaseCatalog implements Catalog { } async location(id: string): Promise { - const items = await this.database('locations').where({ id }).select(); + const items = await this.database('locations') + .where({ id }) + .select(); if (!items.length) { throw new NotFoundError(`Found no location with ID ${id}`); } return items[0]; } + private async locationByTarget( + target: string, + ): Promise { + return (await this.locations()).find(l => l.target === target); + } + async locations(): Promise { return await this.database('locations').select(); }