From ec4ae194f3c839ec67cff90801d5c8e767d6fe40 Mon Sep 17 00:00:00 2001 From: Pierre Merlet Date: Tue, 5 May 2026 16:24:20 +0200 Subject: [PATCH] fix(ds-elasticsearch): propagate enableCount option to the underlying model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `enableCount` option exposed on `addCollectionFromIndex` and `addCollectionFromTemplate` was never destructured nor forwarded to `ModelElasticsearch`, so it was silently dropped. As a result, consumers setting `enableCount: true` still ended up with a non-countable collection — Forest Admin's UI then has no total to drive pagination and effectively shows only the first page of records. Forward the flag through `addCollectionFromIndex`, `addCollectionFromTemplate` and `introspectTemplate` to the `ModelElasticsearch` constructor, and add tests asserting both the collection-level wiring and the builder propagation. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/introspection/builder.ts | 12 ++++++- .../introspection/template-introspector.ts | 3 ++ .../test/collection.test.ts | 36 +++++++++++++++++++ .../introspection/builder.integration.test.ts | 29 +++++++++++++++ 4 files changed, 79 insertions(+), 1 deletion(-) diff --git a/packages/datasource-elasticsearch/src/introspection/builder.ts b/packages/datasource-elasticsearch/src/introspection/builder.ts index 4ad5de5a..726a5127 100644 --- a/packages/datasource-elasticsearch/src/introspection/builder.ts +++ b/packages/datasource-elasticsearch/src/introspection/builder.ts @@ -61,7 +61,12 @@ export interface ElasticsearchDatasourceOptionsBuilder { /** * Add a collection from an index */ - addCollectionFromIndex({ name, indexName }: ElasticsearchCollectionFromIndexOptions): this; + addCollectionFromIndex({ + name, + indexName, + overrideTypeConverter, + enableCount, + }: ElasticsearchCollectionFromIndexOptions): this; /** * Add a collection from a template @@ -71,6 +76,7 @@ export interface ElasticsearchDatasourceOptionsBuilder { templateName, generateIndexName, overrideTypeConverter, + enableCount, }: ElasticsearchCollectionFromTemplateOptions): this; } @@ -90,6 +96,7 @@ export class ElasticsearchDatasourceBuilder implements ElasticsearchDatasourceOp name, indexName, overrideTypeConverter, + enableCount, }: ElasticsearchCollectionFromIndexOptions): this { this.collectionsPromises.push( (async () => { @@ -108,6 +115,7 @@ export class ElasticsearchDatasourceBuilder implements ElasticsearchDatasourceOp mapping[indexName].mappings, () => indexName, overrideTypeConverter, + enableCount, ); })(), ); @@ -120,6 +128,7 @@ export class ElasticsearchDatasourceBuilder implements ElasticsearchDatasourceOp templateName, generateIndexName, overrideTypeConverter, + enableCount, }: ElasticsearchCollectionFromTemplateOptions): this { this.collectionsPromises.push( (async () => { @@ -129,6 +138,7 @@ export class ElasticsearchDatasourceBuilder implements ElasticsearchDatasourceOp name, typeof generateIndexName === 'string' ? () => generateIndexName : generateIndexName, overrideTypeConverter, + enableCount, ); return modelFromTemplate; diff --git a/packages/datasource-elasticsearch/src/introspection/template-introspector.ts b/packages/datasource-elasticsearch/src/introspection/template-introspector.ts index 79d91b36..8a1159b8 100644 --- a/packages/datasource-elasticsearch/src/introspection/template-introspector.ts +++ b/packages/datasource-elasticsearch/src/introspection/template-introspector.ts @@ -12,6 +12,7 @@ export default async function introspectTemplate( overrideName?: string, generateIndexName?: (record?: unknown) => string, overrideTypeConverter?: OverrideTypeConverter, + enableCount?: boolean, ) { const isIndexTemplate = await elasticsearchClient.indices.existsIndexTemplate({ name: templateName, @@ -40,6 +41,7 @@ export default async function introspectTemplate( mappings, generateIndexName, overrideTypeConverter, + enableCount, ); } @@ -59,5 +61,6 @@ export default async function introspectTemplate( mappings, generateIndexName, overrideTypeConverter, + enableCount, ); } diff --git a/packages/datasource-elasticsearch/test/collection.test.ts b/packages/datasource-elasticsearch/test/collection.test.ts index b1c058a7..3a650b8e 100644 --- a/packages/datasource-elasticsearch/test/collection.test.ts +++ b/packages/datasource-elasticsearch/test/collection.test.ts @@ -51,6 +51,42 @@ describe('ElasticsearchDataSource > Collection', () => { expect(elasticsearchCollection.nativeDriver).toBeDefined(); }); + describe('count capability', () => { + const buildCollection = (enableCount?: boolean) => { + const dataSource = Symbol('datasource') as unknown as DataSource; + const mockClient = new MockClient(); + const client = new Client({ + node: 'http://localhost:9200', + Connection: mockClient.getConnection(), + }); + + const model = new ModelElasticsearch( + client, + '__collection__', + ['indexPattern'], + ['alias'], + { properties: { name: { type: 'integer' } } }, + undefined, + undefined, + enableCount, + ); + + return new ElasticsearchCollection(dataSource, model, jest.fn(), client); + }; + + it('exposes a countable schema when the model has enableCount = true', () => { + const collection = buildCollection(true); + + expect(collection.schema.countable).toBe(true); + }); + + it('exposes a non-countable schema by default', () => { + const collection = buildCollection(); + + expect(collection.schema.countable).toBe(false); + }); + }); + // describe('create', () => { // }); diff --git a/packages/datasource-elasticsearch/test/introspection/builder.integration.test.ts b/packages/datasource-elasticsearch/test/introspection/builder.integration.test.ts index d1f7d08f..21097e78 100644 --- a/packages/datasource-elasticsearch/test/introspection/builder.integration.test.ts +++ b/packages/datasource-elasticsearch/test/introspection/builder.integration.test.ts @@ -161,5 +161,34 @@ describe('introspection > builder', () => { await deleteElasticsearchTemplate('test-template', 'test-template-*'); }); + + it('should propagate enableCount to the produced model', async () => { + const { client } = await createElasticsearchTemplate( + 'test-template-count', + { + indexPattern: 'test-template-count-*', + alias: 'test-template-count-alias', + mapping: { + dynamic: 'strict', + properties: { id: { type: 'integer' } }, + }, + }, + [{ id: 1, index: 'test-template-count-index' }], + ); + + const elasticsearchDatasourceBuilder = new ElasticsearchDatasourceBuilder(client); + + elasticsearchDatasourceBuilder.addCollectionFromTemplate({ + name: 'countable', + templateName: 'test-template-count', + enableCount: true, + }); + + const [model] = await elasticsearchDatasourceBuilder.createCollectionsFromConfiguration(); + + expect(model.enableCount).toBe(true); + + await deleteElasticsearchTemplate('test-template-count', 'test-template-count-*'); + }); }); });