diff --git a/plugins/catalog-backend/src/catalog/DatabaseLocationsCatalog.ts b/plugins/catalog-backend/src/catalog/DatabaseLocationsCatalog.ts index d79769a5a7..93a4a77622 100644 --- a/plugins/catalog-backend/src/catalog/DatabaseLocationsCatalog.ts +++ b/plugins/catalog-backend/src/catalog/DatabaseLocationsCatalog.ts @@ -18,7 +18,7 @@ import { LocationReader } from '../ingestion'; import { Database, DatabaseLocationUpdateLogEvent } from '../database'; import { AddLocation, - LocationEnvelope, + LocationResponse, Location, LocationsCatalog, } from './types'; @@ -47,10 +47,10 @@ export class DatabaseLocationsCatalog implements LocationsCatalog { await this.database.removeLocation(id); } - async locations(): Promise { + async locations(): Promise { const items = await this.database.locations(); return items.map(({ message, status, timestamp, ...data }) => ({ - lastUpdate: { + currentStatus: { message, status, timestamp, @@ -63,7 +63,7 @@ export class DatabaseLocationsCatalog implements LocationsCatalog { return this.database.locationHistory(id); } - async location(id: string): Promise { + async location(id: string): Promise { const { message, status, @@ -71,7 +71,7 @@ export class DatabaseLocationsCatalog implements LocationsCatalog { ...data } = await this.database.location(id); return { - lastUpdate: { + currentStatus: { message, status, timestamp, diff --git a/plugins/catalog-backend/src/catalog/types.ts b/plugins/catalog-backend/src/catalog/types.ts index 1c51448f0b..32d4edd27e 100644 --- a/plugins/catalog-backend/src/catalog/types.ts +++ b/plugins/catalog-backend/src/catalog/types.ts @@ -42,10 +42,10 @@ export type EntitiesCatalog = { // Locations // -export type status = { - timestamp: DatabaseLocationUpdateLogEvent['created_at'] | null; - status: DatabaseLocationUpdateLogEvent['status'] | null; - message: DatabaseLocationUpdateLogEvent['message'] | null; +export type LocationUpdateStatus = { + timestamp: string | null; + status: string | null; + message: string | null; }; export type Location = { @@ -54,9 +54,9 @@ export type Location = { target: string; }; -export type LocationEnvelope = { +export type LocationResponse = { data: Location; - lastUpdate: status; + currentStatus: LocationUpdateStatus; }; export type AddLocation = { @@ -74,7 +74,7 @@ export const addLocationSchema: yup.Schema = yup export type LocationsCatalog = { addLocation(location: AddLocation): Promise; removeLocation(id: string): Promise; - locations(): Promise; - location(id: string): Promise; + locations(): Promise; + location(id: string): Promise; locationHistory(id: string): Promise; }; diff --git a/plugins/catalog-backend/src/database/Database.test.ts b/plugins/catalog-backend/src/database/Database.test.ts index 070e138258..00be7d5d1e 100644 --- a/plugins/catalog-backend/src/database/Database.test.ts +++ b/plugins/catalog-backend/src/database/Database.test.ts @@ -78,7 +78,6 @@ describe('Database', () => { labels: { e: 'f' }, annotations: { g: 'h', - 'backstage.io/managed-by-location': undefined, }, }, spec: { i: 'j' }, diff --git a/plugins/catalog-backend/src/database/Database.ts b/plugins/catalog-backend/src/database/Database.ts index 674624c9c6..d4d3ec842e 100644 --- a/plugins/catalog-backend/src/database/Database.ts +++ b/plugins/catalog-backend/src/database/Database.ts @@ -195,7 +195,9 @@ export class Database { generation: 1, annotations: { ...(newEntity.metadata?.annotations ?? {}), - 'backstage.io/managed-by-location': request.locationId, + ...(request.locationId + ? { 'backstage.io/managed-by-location': request.locationId } + : {}), }, }; @@ -396,19 +398,15 @@ export class Database { async location(id: string): Promise { const items = await this.database('locations') .where('locations.id', id) - .leftJoin( - 'location_update_log', + .leftOuterJoin( + 'location_update_log_latest', 'locations.id', - 'location_update_log.location_id', + 'location_update_log_latest.location_id', ) - .orderBy('location_update_log.created_at', 'desc') - .select({ - status: 'location_update_log.status', - timestamp: 'location_update_log.created_at', - message: 'location_update_log.message', - id: 'locations.id', - type: 'locations.type', - target: 'locations.target', + .select('locations.*', { + status: 'location_update_log_latest.status', + timestamp: 'location_update_log_latest.created_at', + message: 'location_update_log_latest.message', }); if (!items.length) { @@ -418,28 +416,19 @@ export class Database { } async locations(): Promise { - const query = this.database - .select({ - status: 'location_update_log.status', - timestamp: 'location_update_log.created_at', - message: 'location_update_log.message', - id: 'locations.id', - type: 'locations.type', - target: 'locations.target', - }) - .from('locations') - .leftJoin( - 'location_update_log', + const locations = await this.database('locations') + .leftOuterJoin( + 'location_update_log_latest', 'locations.id', - 'location_update_log.location_id', + 'location_update_log_latest.location_id', ) - .orderBy('location_update_log.created_at', 'desc'); + .select('locations.*', { + status: 'location_update_log_latest.status', + timestamp: 'location_update_log_latest.created_at', + message: 'location_update_log_latest.message', + }); - const result = await this.database(query) - .select() - .groupBy('id'); - - return result; + return locations; } async locationHistory(id: string): Promise { diff --git a/plugins/catalog-backend/src/database/migrations/20200527114117_location_update_log_latest_view.ts b/plugins/catalog-backend/src/database/migrations/20200527114117_location_update_log_latest_view.ts new file mode 100644 index 0000000000..6da9eeed9b --- /dev/null +++ b/plugins/catalog-backend/src/database/migrations/20200527114117_location_update_log_latest_view.ts @@ -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 * as Knex from 'knex'; + +export async function up(knex: Knex): Promise { + // Need to first order by date of creation + const query = knex + .select() + .from('location_update_log') + .orderBy('location_update_log.created_at', 'desc'); + + // And only then to do the grouping to get the latest per location + const groupedQuery = knex(query).groupBy('location_id').select(); + + await knex.schema.raw( + `CREATE VIEW location_update_log_latest AS ${groupedQuery.toString()};`, + ); +} + +export async function down(knex: Knex): Promise { + return knex.schema.raw(`DROP VIEW location_update_log_latest;`); +} diff --git a/plugins/catalog-backend/src/database/types.ts b/plugins/catalog-backend/src/database/types.ts index e20ba59517..cf49adb32e 100644 --- a/plugins/catalog-backend/src/database/types.ts +++ b/plugins/catalog-backend/src/database/types.ts @@ -53,9 +53,9 @@ export type DbLocationsRow = { }; export type DbLocationsRowWithStatus = DbLocationsRow & { - status: DatabaseLocationUpdateLogEvent['status'] | null; - timestamp: DatabaseLocationUpdateLogEvent['created_at'] | null; - message: DatabaseLocationUpdateLogEvent['message'] | null; + status: string | null; + timestamp: string | null; + message: string | null; }; export type AddDatabaseLocation = { diff --git a/plugins/catalog-backend/src/ingestion/types.ts b/plugins/catalog-backend/src/ingestion/types.ts index 87a6f19f1f..e5083a65f1 100644 --- a/plugins/catalog-backend/src/ingestion/types.ts +++ b/plugins/catalog-backend/src/ingestion/types.ts @@ -79,7 +79,7 @@ export type EntityMeta = { * Key/value pairs of non-identifying auxiliary information attached to the * entity. */ - annotations?: Record; + annotations?: Record; }; /**