chore(catalog-backend): use code instead of db to implement similarity rule
This commit is contained in:
@@ -61,7 +61,6 @@ function serializeSpec(spec: Entity['spec']): DbEntitiesRow['spec'] {
|
||||
function toEntityRow(
|
||||
locationId: string | undefined,
|
||||
entity: Entity,
|
||||
normalize: (value: string) => string,
|
||||
): DbEntitiesRow {
|
||||
return {
|
||||
id: entity.metadata.uid!,
|
||||
@@ -72,9 +71,6 @@ function toEntityRow(
|
||||
kind: entity.kind,
|
||||
name: entity.metadata.name,
|
||||
namespace: entity.metadata.namespace || null,
|
||||
kind_normalized: normalize(entity.kind),
|
||||
name_normalized: normalize(entity.metadata.name),
|
||||
namespace_normalized: normalize(entity.metadata.namespace || ''),
|
||||
metadata: serializeMetadata(entity.metadata),
|
||||
spec: serializeSpec(entity.spec),
|
||||
};
|
||||
@@ -162,6 +158,8 @@ export class CommonDatabase implements Database {
|
||||
throw new InputError('May not specify generation for new entities');
|
||||
}
|
||||
|
||||
await this.ensureNoSimilarNames(tx, request.entity);
|
||||
|
||||
const newEntity = lodash.cloneDeep(request.entity);
|
||||
newEntity.metadata = {
|
||||
...newEntity.metadata,
|
||||
@@ -170,7 +168,7 @@ export class CommonDatabase implements Database {
|
||||
generation: 1,
|
||||
};
|
||||
|
||||
const newRow = toEntityRow(request.locationId, newEntity, this.normalize);
|
||||
const newRow = toEntityRow(request.locationId, newEntity);
|
||||
await tx<DbEntitiesRow>('entities').insert(newRow);
|
||||
await this.updateEntitiesSearch(tx, newRow.id, newEntity);
|
||||
|
||||
@@ -259,9 +257,11 @@ export class CommonDatabase implements Database {
|
||||
}
|
||||
}
|
||||
|
||||
await this.ensureNoSimilarNames(tx, newEntity);
|
||||
|
||||
// Store the updated entity; select on the old etag to ensure that we do
|
||||
// not lose to another writer
|
||||
const newRow = toEntityRow(request.locationId, newEntity, this.normalize);
|
||||
const newRow = toEntityRow(request.locationId, newEntity);
|
||||
const updatedRows = await tx<DbEntitiesRow>('entities')
|
||||
.where({ id: oldRow.id, etag: oldRow.etag })
|
||||
.update(newRow);
|
||||
@@ -451,4 +451,46 @@ export class CommonDatabase implements Database {
|
||||
// we got around to writing the entries
|
||||
}
|
||||
}
|
||||
|
||||
private async ensureNoSimilarNames(
|
||||
tx: Knex.Transaction<any, any>,
|
||||
data: Entity,
|
||||
): Promise<void> {
|
||||
const newKind = data.kind;
|
||||
const newName = data.metadata.name;
|
||||
const newNamespace = data.metadata.namespace;
|
||||
const newKindNorm = this.normalize(newKind);
|
||||
const newNameNorm = this.normalize(newName);
|
||||
const newNamespaceNorm = this.normalize(newNamespace || '');
|
||||
|
||||
for (const item of await this.entities(tx)) {
|
||||
if (data.metadata.uid === item.entity.metadata.uid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const oldKind = item.entity.kind;
|
||||
const oldName = item.entity.metadata.name;
|
||||
const oldNamespace = item.entity.metadata.namespace;
|
||||
const oldKindNorm = this.normalize(oldKind);
|
||||
const oldNameNorm = this.normalize(oldName);
|
||||
const oldNamespaceNorm = this.normalize(oldNamespace || '');
|
||||
|
||||
if (
|
||||
oldKindNorm === newKindNorm &&
|
||||
oldNameNorm === newNameNorm &&
|
||||
oldNamespaceNorm === newNamespaceNorm
|
||||
) {
|
||||
// Only throw if things were actually different - for completely equal
|
||||
// things, we let the database handle the conflict
|
||||
if (
|
||||
oldKind !== newKind ||
|
||||
oldName !== newName ||
|
||||
oldNamespace !== newNamespace
|
||||
) {
|
||||
const message = `Kind, namespace, name are too similar to an existing entity`;
|
||||
throw new ConflictError(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* 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<any> {
|
||||
return knex.schema.alterTable('entities', table => {
|
||||
// these are added as nullable because sqlite does not support alter column
|
||||
table
|
||||
.string('kind_normalized')
|
||||
.nullable()
|
||||
.comment('The kind field of the entity, normalized');
|
||||
table
|
||||
.string('name_normalized')
|
||||
.nullable()
|
||||
.comment('The metadata.name field of the entity, normalized');
|
||||
table
|
||||
.string('namespace_normalized')
|
||||
.nullable()
|
||||
.comment('The metadata.namespace field of the entity, normalized');
|
||||
table.dropUnique([], 'entities_unique_name');
|
||||
table.unique(
|
||||
['kind_normalized', 'name_normalized', 'namespace_normalized'],
|
||||
'entities_unique_name_normalized',
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<any> {
|
||||
return knex.schema.alterTable('entities', table => {
|
||||
table.dropUnique([], 'entities_unique_name_normalized');
|
||||
table.unique(['kind', 'name', 'namespace'], 'entities_unique_name');
|
||||
table.dropColumns(
|
||||
'kind_normalized',
|
||||
'name_normalized',
|
||||
'namespace_normalized',
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -23,9 +23,6 @@ export type DbEntitiesRow = {
|
||||
kind: string;
|
||||
name: string | null;
|
||||
namespace: string | null;
|
||||
kind_normalized: string;
|
||||
name_normalized: string;
|
||||
namespace_normalized: string;
|
||||
etag: string;
|
||||
generation: number;
|
||||
metadata: string;
|
||||
|
||||
Reference in New Issue
Block a user