diff --git a/packages/catalog-client/src/CatalogClient.ts b/packages/catalog-client/src/CatalogClient.ts index fa8a88c1ca..62111abf9e 100644 --- a/packages/catalog-client/src/CatalogClient.ts +++ b/packages/catalog-client/src/CatalogClient.ts @@ -359,13 +359,17 @@ export class CatalogClient implements CatalogApi { const body: QueryEntitiesByPredicateRequest = {}; if (isQueryEntitiesInitialRequest(request)) { - const { query, limit, orderFields, fullTextFilter, fields } = request; + const { query, limit, offset, orderFields, fullTextFilter, fields } = + request; if (query && typeof query === 'object') { body.query = query; } if (limit !== undefined) { body.limit = limit; } + if (offset !== undefined) { + body.offset = offset; + } if (orderFields !== undefined) { body.orderBy = [orderFields].flat(); } diff --git a/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts b/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts index 818a780b4a..17a9c9962f 100644 --- a/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts +++ b/packages/catalog-client/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts @@ -27,6 +27,7 @@ import { QueryEntitiesByPredicateRequestOrderByInner } from '../models/QueryEnti export interface QueryEntitiesByPredicateRequest { cursor?: string; limit?: number; + offset?: number; orderBy?: Array; fullTextFilter?: QueryEntitiesByPredicateRequestFullTextFilter; fields?: Array; diff --git a/plugins/catalog-backend/src/schema/openapi.yaml b/plugins/catalog-backend/src/schema/openapi.yaml index 41738c1148..0531a9a1d3 100644 --- a/plugins/catalog-backend/src/schema/openapi.yaml +++ b/plugins/catalog-backend/src/schema/openapi.yaml @@ -1125,6 +1125,8 @@ paths: type: string limit: type: number + offset: + type: number orderBy: type: array items: diff --git a/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts b/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts index 818a780b4a..17a9c9962f 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts +++ b/plugins/catalog-backend/src/schema/openapi/generated/models/QueryEntitiesByPredicateRequest.model.ts @@ -27,6 +27,7 @@ import { QueryEntitiesByPredicateRequestOrderByInner } from '../models/QueryEnti export interface QueryEntitiesByPredicateRequest { cursor?: string; limit?: number; + offset?: number; orderBy?: Array; fullTextFilter?: QueryEntitiesByPredicateRequestFullTextFilter; fields?: Array; diff --git a/plugins/catalog-backend/src/schema/openapi/generated/router.ts b/plugins/catalog-backend/src/schema/openapi/generated/router.ts index ed9536d6b9..6c739eb2ee 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/router.ts +++ b/plugins/catalog-backend/src/schema/openapi/generated/router.ts @@ -1269,6 +1269,9 @@ export const spec = { limit: { type: 'number', }, + offset: { + type: 'number', + }, orderBy: { type: 'array', items: { diff --git a/plugins/catalog-backend/src/service/createRouter.test.ts b/plugins/catalog-backend/src/service/createRouter.test.ts index ebfadc66c7..acfac3b8d1 100644 --- a/plugins/catalog-backend/src/service/createRouter.test.ts +++ b/plugins/catalog-backend/src/service/createRouter.test.ts @@ -509,6 +509,31 @@ describe('createRouter readonly disabled', () => { ); }); + it('queries entities with an offset', async () => { + const items: Entity[] = [ + { apiVersion: 'a', kind: 'b', metadata: { name: 'n' } }, + ]; + entitiesCatalog.queryEntities.mockResolvedValue({ + items: { type: 'object', entities: items }, + pageInfo: {}, + totalItems: 5, + }); + + const response = await request(app) + .post('/entities/by-query') + .send({ query: { kind: 'b' }, limit: 2, offset: 3 }); + + expect(response.status).toEqual(200); + expect(entitiesCatalog.queryEntities).toHaveBeenCalledWith( + expect.objectContaining({ + query: { kind: 'b' }, + limit: 2, + offset: 3, + credentials: mockCredentials.user(), + }), + ); + }); + it('paginates with a cursor in the body', async () => { const items: Entity[] = [ { apiVersion: 'a', kind: 'b', metadata: { name: 'n' } }, diff --git a/plugins/catalog-backend/src/service/request/parseEntityQuery.test.ts b/plugins/catalog-backend/src/service/request/parseEntityQuery.test.ts index b95c4c6c0e..7380259188 100644 --- a/plugins/catalog-backend/src/service/request/parseEntityQuery.test.ts +++ b/plugins/catalog-backend/src/service/request/parseEntityQuery.test.ts @@ -28,6 +28,7 @@ describe('parseEntityQuery', () => { fullTextFilter: undefined, fields: undefined, limit: undefined, + offset: undefined, }); }); @@ -68,6 +69,18 @@ describe('parseEntityQuery', () => { expect(result).toEqual(expect.objectContaining({ limit: 50 })); }); + it('passes through offset', () => { + const result = parseEntityQuery({ offset: 100 }); + expect(result).toEqual(expect.objectContaining({ offset: 100 })); + }); + + it('passes through limit and offset together', () => { + const result = parseEntityQuery({ limit: 50, offset: 100 }); + expect(result).toEqual( + expect.objectContaining({ limit: 50, offset: 100 }), + ); + }); + it('passes through fields', () => { const result = parseEntityQuery({ fields: ['metadata.name', 'kind'], diff --git a/plugins/catalog-backend/src/service/request/parseEntityQuery.ts b/plugins/catalog-backend/src/service/request/parseEntityQuery.ts index c33492e740..27992c273a 100644 --- a/plugins/catalog-backend/src/service/request/parseEntityQuery.ts +++ b/plugins/catalog-backend/src/service/request/parseEntityQuery.ts @@ -110,5 +110,6 @@ export function parseEntityQuery( : undefined, fields: request.fields, limit: request.limit, + offset: request.offset, }; }