From 5468b81811b82450f69514a497b8860647b3bcb7 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Fri, 3 Feb 2023 13:59:50 +0100 Subject: [PATCH] catalog-backend: move query to fullTextFilterTerm Signed-off-by: Vincenzo Scamporlino --- plugins/catalog-backend/src/catalog/types.ts | 12 +++++-- .../service/DefaultEntitiesCatalog.test.ts | 2 +- .../src/service/DefaultEntitiesCatalog.ts | 10 +++--- .../src/service/createRouter.test.ts | 6 +++- ...ts => parseEntityOrderFieldParams.test.ts} | 34 +++++++++---------- ...rams.ts => parseEntityOrderFieldParams.ts} | 12 +++---- .../request/parseFullTextFilterFields.ts | 28 +++++++++++++++ .../request/parseQueryEntitiesParams.test.ts | 17 ++++++---- .../request/parseQueryEntitiesParams.ts | 19 ++++++++--- 9 files changed, 96 insertions(+), 44 deletions(-) rename plugins/catalog-backend/src/service/request/{parseEntitySortFieldParams.test.ts => parseEntityOrderFieldParams.test.ts} (53%) rename plugins/catalog-backend/src/service/request/{parseEntitySortFieldParams.ts => parseEntityOrderFieldParams.ts} (75%) create mode 100644 plugins/catalog-backend/src/service/request/parseFullTextFilterFields.ts diff --git a/plugins/catalog-backend/src/catalog/types.ts b/plugins/catalog-backend/src/catalog/types.ts index 6b6ff5b92e..d182ffdc47 100644 --- a/plugins/catalog-backend/src/catalog/types.ts +++ b/plugins/catalog-backend/src/catalog/types.ts @@ -228,7 +228,10 @@ export interface QueryEntitiesInitialRequest { limit?: number; filter?: EntityFilter; orderFields?: EntityOrder[]; - query?: string; + fullTextFilter?: { + term: string; + fields?: string[]; + }; } /** @@ -289,9 +292,12 @@ export type Cursor = { */ isPrevious: boolean; /** - * Used for filtering the data by name. + * Used for performing full text filtering on the given fields. */ - query?: string; + fullTextFilter?: { + term: string; + fields?: string[]; + }; /** * The value of the fields of the first returned item used for paginating the data. * The catalog uses this field internally to understand if the beginning diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts index 2924c81106..5bfb0e0077 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts @@ -1057,7 +1057,7 @@ describe('DefaultEntitiesCatalog', () => { limit: 100, orderFields: [{ field: 'metadata.name', order: 'asc' }], - query: 'cAt ', + fullTextFilter: { term: 'cAt ' }, }; const response = await catalog.queryEntities(request); expect(response.items).toEqual([ diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts index 959fba1851..ba59bc55c0 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts @@ -368,11 +368,11 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog { parseFilter(cursor.filter, dbQuery, db, false, 'search.entity_id'); } - const normalizedQueryByName = cursor.query?.trim(); - if (normalizedQueryByName) { + const normalizedFullTextFilterTerm = cursor.fullTextFilter?.term?.trim(); + if (normalizedFullTextFilterTerm) { dbQuery.andWhereRaw( 'value like ?', - `%${normalizedQueryByName.toLocaleLowerCase('en-US')}%`, + `%${normalizedFullTextFilterTerm.toLocaleLowerCase('en-US')}%`, ); } @@ -707,9 +707,9 @@ function parseCursorFromRequest( const { filter, orderFields: sortFields = [defaultSortField], - query, + fullTextFilter, } = request; - return { filter, orderFields: sortFields, query }; + return { filter, orderFields: sortFields, fullTextFilter }; } if (isQueryEntitiesCursorRequest(request)) { try { diff --git a/plugins/catalog-backend/src/service/createRouter.test.ts b/plugins/catalog-backend/src/service/createRouter.test.ts index 91ede00d51..5121e800da 100644 --- a/plugins/catalog-backend/src/service/createRouter.test.ts +++ b/plugins/catalog-backend/src/service/createRouter.test.ts @@ -166,7 +166,7 @@ describe('createRouter readonly disabled', () => { totalItems: 0, }); const response = await request(app).get( - '/entities/by-query?filter=a=1,a=2,b=3&filter=c=4&sortField=metadata.name,asc&sortField=metadata.uid,desc', + '/entities/by-query?filter=a=1,a=2,b=3&filter=c=4&orderField=metadata.name,asc&orderField=metadata.uid,desc', ); expect(response.status).toEqual(200); @@ -187,6 +187,10 @@ describe('createRouter readonly disabled', () => { { field: 'metadata.name', order: 'asc' }, { field: 'metadata.uid', order: 'desc' }, ], + fullTextFilter: { + fields: undefined, + term: '', + }, }); }); diff --git a/plugins/catalog-backend/src/service/request/parseEntitySortFieldParams.test.ts b/plugins/catalog-backend/src/service/request/parseEntityOrderFieldParams.test.ts similarity index 53% rename from plugins/catalog-backend/src/service/request/parseEntitySortFieldParams.test.ts rename to plugins/catalog-backend/src/service/request/parseEntityOrderFieldParams.test.ts index 35f7c2cac0..b732163394 100644 --- a/plugins/catalog-backend/src/service/request/parseEntitySortFieldParams.test.ts +++ b/plugins/catalog-backend/src/service/request/parseEntityOrderFieldParams.test.ts @@ -14,31 +14,31 @@ * limitations under the License. */ -import { parseEntitySortFieldParams } from './parseEntitySortFieldParams'; +import { parseEntityOrderFieldParams } from './parseEntityOrderFieldParams'; -describe('parseEntitySortFieldParams', () => { - it('supports no sort fields', () => { - const result = parseEntitySortFieldParams({}); +describe('parseEntityOrderFieldParams', () => { + it('supports no order fields', () => { + const result = parseEntityOrderFieldParams({}); expect(result).toEqual(undefined); }); - it('supports single sortField', () => { - const result = parseEntitySortFieldParams({ - sortField: ['metadata.name,desc'], + it('supports single orderField', () => { + const result = parseEntityOrderFieldParams({ + orderField: ['metadata.name,desc'], })!; expect(result).toEqual([{ field: 'metadata.name', order: 'desc' }]); }); - it('supports single sortField without order', () => { - const result = parseEntitySortFieldParams({ - sortField: ['metadata.name'], + it('supports single orderField without order', () => { + const result = parseEntityOrderFieldParams({ + orderField: ['metadata.name'], })!; expect(result).toEqual([{ field: 'metadata.name' }]); }); - it('supports multiple sort fields', () => { - const result = parseEntitySortFieldParams({ - sortField: ['metadata.name,desc', 'metadata.uid,asc'], + it('supports multiple order fields', () => { + const result = parseEntityOrderFieldParams({ + orderField: ['metadata.name,desc', 'metadata.uid,asc'], }); expect(result).toEqual([ @@ -47,11 +47,11 @@ describe('parseEntitySortFieldParams', () => { ]); }); - it('throws if sortField order is not valid', () => { + it('throws if orderField order is not valid', () => { expect(() => - parseEntitySortFieldParams({ - sortField: ['metadata.name,desc', 'metadata.uid,invalid'], + parseEntityOrderFieldParams({ + orderField: ['metadata.name,desc', 'metadata.uid,invalid'], }), - ).toThrow(/Invalid sort field order/); + ).toThrow(/Invalid order field order/); }); }); diff --git a/plugins/catalog-backend/src/service/request/parseEntitySortFieldParams.ts b/plugins/catalog-backend/src/service/request/parseEntityOrderFieldParams.ts similarity index 75% rename from plugins/catalog-backend/src/service/request/parseEntitySortFieldParams.ts rename to plugins/catalog-backend/src/service/request/parseEntityOrderFieldParams.ts index 5efa2de05e..5ab8cbfcf9 100644 --- a/plugins/catalog-backend/src/service/request/parseEntitySortFieldParams.ts +++ b/plugins/catalog-backend/src/service/request/parseEntityOrderFieldParams.ts @@ -18,19 +18,19 @@ import { InputError } from '@backstage/errors'; import { EntityOrder } from '../../catalog/types'; import { parseStringsParam } from './common'; -export function parseEntitySortFieldParams( +export function parseEntityOrderFieldParams( params: Record, ): EntityOrder[] | undefined { - const sortFieldStrings = parseStringsParam(params.sortField, 'sortField'); - if (!sortFieldStrings) { + const orderFieldStrings = parseStringsParam(params.orderField, 'orderField'); + if (!orderFieldStrings) { return undefined; } - return sortFieldStrings.map(sortFieldString => { - const [field, order] = sortFieldString.split(','); + return orderFieldStrings.map(orderFieldString => { + const [field, order] = orderFieldString.split(','); if (order !== undefined && !isOrder(order)) { - throw new InputError('Invalid sort field order, must be asc or desc'); + throw new InputError('Invalid order field order, must be asc or desc'); } return { field, order }; }); diff --git a/plugins/catalog-backend/src/service/request/parseFullTextFilterFields.ts b/plugins/catalog-backend/src/service/request/parseFullTextFilterFields.ts new file mode 100644 index 0000000000..02ba5864fc --- /dev/null +++ b/plugins/catalog-backend/src/service/request/parseFullTextFilterFields.ts @@ -0,0 +1,28 @@ +import { parseStringParam } from './common'; + +/* + * Copyright 2023 The Backstage Authors + * + * 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. + */ +export function parseFullTextFilterFields(params: Record) { + const fullTextFilterFields = parseStringParam( + params.fullTextFilterFields, + 'fullTextFilterFields', + ); + if (!fullTextFilterFields) { + return undefined; + } + + return fullTextFilterFields.split(','); +} diff --git a/plugins/catalog-backend/src/service/request/parseQueryEntitiesParams.test.ts b/plugins/catalog-backend/src/service/request/parseQueryEntitiesParams.test.ts index 3a8bf1b225..da2d7e474c 100644 --- a/plugins/catalog-backend/src/service/request/parseQueryEntitiesParams.test.ts +++ b/plugins/catalog-backend/src/service/request/parseQueryEntitiesParams.test.ts @@ -28,8 +28,9 @@ describe('parseQueryEntitiesParams', () => { fields: ['kind'], limit: '3', filter: ['a=1', 'b=2'], - sortField: ['metadata.name,desc'], - query: 'query', + orderField: ['metadata.name,desc'], + fullTextFilterTerm: 'query', + fullTextFilterFields: 'metadata.name,metadata.namespace', }; const parsedObj = parseQueryEntitiesParams( validRequest, @@ -40,7 +41,10 @@ describe('parseQueryEntitiesParams', () => { { field: 'metadata.name', order: 'desc' }, ]); expect(parsedObj.filter).toBeDefined(); - expect(parsedObj.query).toBe('query'); + expect(parsedObj.fullTextFilter).toEqual({ + term: 'query', + fields: ['metadata.name', 'metadata.namespace'], + }); expect(parsedObj).not.toHaveProperty('authorizationToken'); expect(parsedObj).not.toHaveProperty('cursor'); }); @@ -52,7 +56,7 @@ describe('parseQueryEntitiesParams', () => { expect(parsedObj.fields).toBeUndefined(); expect(parsedObj.orderFields).toBeUndefined(); expect(parsedObj.filter).toBeUndefined(); - expect(parsedObj.query).toBeUndefined(); + expect(parsedObj.fullTextFilter).toEqual({ term: '', fields: undefined }); expect(parsedObj).not.toHaveProperty('authorizationToken'); expect(parsedObj).not.toHaveProperty('cursor'); }); @@ -62,9 +66,10 @@ describe('parseQueryEntitiesParams', () => { limit: 'asd', }, { filter: 3 }, - { sortField: ['metadata.uid,diagonal'] }, + { orderField: ['metadata.uid,diagonal'] }, { fields: [4] }, - { query: [] }, + { fullTextFilterTerm: [] }, + { fullTextFilterFields: 3 }, ])('should throw if some parameter is not valid %p', params => { expect(() => parseQueryEntitiesParams(params)).toThrow(); }); diff --git a/plugins/catalog-backend/src/service/request/parseQueryEntitiesParams.ts b/plugins/catalog-backend/src/service/request/parseQueryEntitiesParams.ts index 0a3da2bd48..67a4629c1c 100644 --- a/plugins/catalog-backend/src/service/request/parseQueryEntitiesParams.ts +++ b/plugins/catalog-backend/src/service/request/parseQueryEntitiesParams.ts @@ -21,8 +21,9 @@ import { } from '../../catalog/types'; import { parseIntegerParam, parseStringParam } from './common'; import { parseEntityFilterParams } from './parseEntityFilterParams'; -import { parseEntitySortFieldParams } from './parseEntitySortFieldParams'; +import { parseEntityOrderFieldParams } from './parseEntityOrderFieldParams'; import { parseEntityTransformParams } from './parseEntityTransformParams'; +import { parseFullTextFilterFields } from './parseFullTextFilterFields'; export function parseQueryEntitiesParams( params: Record, @@ -40,15 +41,23 @@ export function parseQueryEntitiesParams( } const filter = parseEntityFilterParams(params); - const query = parseStringParam(params.query, 'query'); - const sortFields = parseEntitySortFieldParams(params); + const fullTextFilterTerm = parseStringParam( + params.fullTextFilterTerm, + 'fullTextFilterTerm', + ); + const fullTextFilterFields = parseFullTextFilterFields(params); + + const orderFields = parseEntityOrderFieldParams(params); const response: Omit = { fields, filter, limit, - orderFields: sortFields, - query, + orderFields, + fullTextFilter: { + term: fullTextFilterTerm || '', + fields: fullTextFilterFields, + }, }; return response;