refactor(api): tidy the api and shared packages (#2733)

This commit is contained in:
Hampus authored and GitHub committed 2026-09-13 17:37:48 +02:00
1 parent 580401d2dc
commit 5afbf67a70
333 files changed
+10180 -7910

No files matched your search

@@ -1,7 +1,15 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import type {Client} from '@elastic/elasticsearch';
import type {SearchRequest, SortCombinations, SortResults} from '@elastic/elasticsearch/lib/api/types';
import type {
BulkOperationType,
BulkResponse,
DeleteByQueryResponse,
ErrorCause,
SearchRequest,
SortCombinations,
SortResults,
} from '@elastic/elasticsearch/lib/api/types';
import type {ISearchAdapter, SearchOptions, SearchResult} from '@fluxer/schema/src/contracts/search/SearchAdapterTypes';
import type {ElasticsearchFilter} from '../ElasticsearchFilterUtils';
import {compactFilters} from '../ElasticsearchFilterUtils';
@@ -11,6 +19,7 @@ const ELASTICSEARCH_MAX_RESULT_WINDOW = 10000;
const DEEP_PAGINATION_BATCH_SIZE = 1000;
const MAX_SEARCH_LIMIT = 1000;
const FACET_TERM_LIMIT = 200;
const ERROR_TYPE_MAX_LENGTH = 128;
interface ElasticsearchSearchHit<TResult> {
_source?: TResult;
@@ -191,7 +200,8 @@ export class ElasticsearchIndexAdapter<
}
this.assertInitialised();
const operations = docs.flatMap((doc) => [{index: {_index: this.indexDefinition.indexName, _id: doc.id}}, doc]);
await this.client.bulk({operations, refresh: false});
const response = await this.client.bulk({operations, refresh: false});
this.assertBulkSucceeded(response, 'index');
}
async updateDocument(doc: TResult): Promise<void> {
@@ -205,12 +215,7 @@ export class ElasticsearchIndexAdapter<
}
async bulkIndexDocuments(docs: Array<TResult>): Promise<void> {
if (docs.length === 0) {
return;
}
this.assertInitialised();
const operations = docs.flatMap((doc) => [{index: {_index: this.indexDefinition.indexName, _id: doc.id}}, doc]);
await this.client.bulk({operations, refresh: false});
await this.indexDocuments(docs);
}
async refreshIndex(): Promise<void> {
@@ -228,25 +233,28 @@ export class ElasticsearchIndexAdapter<
}
this.assertInitialised();
const operations = ids.map((id) => ({delete: {_index: this.indexDefinition.indexName, _id: id}}));
await this.client.bulk({operations, refresh: false});
const response = await this.client.bulk({operations, refresh: false});
this.assertBulkSucceeded(response, 'delete');
}
async deleteByQuery(query: Record<string, unknown>): Promise<void> {
this.assertInitialised();
await this.client.deleteByQuery({
const response = await this.client.deleteByQuery({
index: this.indexDefinition.indexName,
query,
refresh: false,
});
this.assertDeleteByQuerySucceeded(response);
}
async deleteAllDocuments(): Promise<void> {
this.assertInitialised();
await this.client.deleteByQuery({
const response = await this.client.deleteByQuery({
index: this.indexDefinition.indexName,
query: {match_all: {}},
refresh: true,
});
this.assertDeleteByQuerySucceeded(response);
}
async search(query: string, filters: TFilters, options?: SearchOptions): Promise<SearchResult<TResult>> {
@@ -307,6 +315,29 @@ export class ElasticsearchIndexAdapter<
}
}
private assertBulkSucceeded(response: BulkResponse, operation: BulkOperationType): void {
if (!response.errors) return;
const context = `Elasticsearch bulk ${operation} failed for index ${this.indexDefinition.indexName}`;
for (const item of response.items ?? []) {
const failure = item[operation];
if (failure?.error) {
throw new Error(`${context}: ${summarizeOperationFailure(failure.status, failure.error)}`);
}
}
throw new Error(`${context}: response reported errors without ${operation} item error details`);
}
private assertDeleteByQuerySucceeded(response: DeleteByQueryResponse): void {
const context = `Elasticsearch delete by query failed for index ${this.indexDefinition.indexName}`;
if (response.timed_out) {
throw new Error(`${context}: timed out`);
}
const failure = response.failures?.[0];
if (failure) {
throw new Error(`${context}: ${summarizeOperationFailure(failure.status, failure.cause)}`);
}
}
private async executeDeepPaginationSearch(
searchParams: ElasticsearchBaseSearchRequest,
offset: number,
@@ -403,6 +434,12 @@ export class ElasticsearchIndexAdapter<
}
}
function summarizeOperationFailure(status: number, error: ErrorCause): string {
const errorType =
typeof error.type === 'string' ? JSON.stringify(error.type.slice(0, ERROR_TYPE_MAX_LENGTH)) : 'missing error type';
return `HTTP ${status}, ${errorType}`;
}
function isResourceAlreadyExistsError(error: unknown): boolean {
if (error == null || typeof error !== 'object') {
return false;