@backstage/plugin-search-backend-module-elasticsearch 1.8.8-next.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/config.schema.json +8 -8
- package/dist/engines/ElasticSearchAuthTransport.cjs.js +3 -26
- package/dist/engines/ElasticSearchAuthTransport.cjs.js.map +1 -1
- package/dist/engines/ElasticSearchClientOptions.cjs.js.map +1 -1
- package/dist/engines/ElasticSearchClientWrapper.cjs.js +39 -19
- package/dist/engines/ElasticSearchClientWrapper.cjs.js.map +1 -1
- package/dist/engines/ElasticSearchSearchEngine.cjs.js +6 -2
- package/dist/engines/ElasticSearchSearchEngine.cjs.js.map +1 -1
- package/dist/index.d.ts +50 -17
- package/package.json +10 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# @backstage/plugin-search-backend-module-elasticsearch
|
|
2
2
|
|
|
3
|
+
## 2.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 3753c36: **BREAKING**: Upgraded the Elasticsearch client to version 8, requiring at least version 8.19 - the only 8.x release line still under support, [following the end of life of Elasticsearch 7.x and earlier 8.x lines](https://endoflife.date/elasticsearch). Elasticsearch 7.x clusters are no longer supported.
|
|
8
|
+
|
|
9
|
+
If you use the `elastic` provider or connect to a plain Elasticsearch node, make sure your cluster is running Elasticsearch 8.19 or later before upgrading. The `aws` and `opensearch` providers keep using the OpenSearch client and are unaffected. Existing TLS settings continue to work without any configuration changes.
|
|
10
|
+
|
|
11
|
+
The methods of the `ElasticSearchClientWrapper` now return a normalized response that exposes `body`, `statusCode`, `headers` and `warnings` for both providers, instead of the client specific response types. Code that relies on the raw client types or reads the `meta` property of a response needs to be adjusted.
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- fe6ace6: Added optional `accountId` config to `search.elasticsearch` for resolving account-specific AWS credentials, enabling support for `webIdentityTokenFile` and `accountDefaults` when using AWS OpenSearch.
|
|
16
|
+
- b11c9b4: Fixed a bug where an empty document type list could result in querying all indices instead of returning empty results.
|
|
17
|
+
- Updated dependencies
|
|
18
|
+
- @backstage/plugin-search-backend-node@1.4.8
|
|
19
|
+
- @backstage/config@1.3.9
|
|
20
|
+
- @backstage/backend-plugin-api@1.10.1
|
|
21
|
+
- @backstage/integration-aws-node@0.2.2
|
|
22
|
+
- @backstage/plugin-search-common@1.2.25
|
|
23
|
+
|
|
24
|
+
## 1.8.8-next.2
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- Updated dependencies
|
|
29
|
+
- @backstage/config@1.3.9-next.0
|
|
30
|
+
- @backstage/backend-plugin-api@1.10.1-next.1
|
|
31
|
+
- @backstage/integration-aws-node@0.2.2-next.0
|
|
32
|
+
- @backstage/plugin-search-backend-node@1.4.8-next.1
|
|
33
|
+
- @backstage/plugin-search-common@1.2.25-next.0
|
|
34
|
+
|
|
3
35
|
## 1.8.8-next.1
|
|
4
36
|
|
|
5
37
|
### Patch Changes
|
package/config.schema.json
CHANGED
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"description": "Delimiter string used to concatenate fragments. Defaults to \" ... \"."
|
|
63
63
|
}
|
|
64
64
|
},
|
|
65
|
-
"description": "Options for configuring highlight settings See https://www.elastic.co/guide/en/elasticsearch/reference/
|
|
65
|
+
"description": "Options for configuring highlight settings See https://www.elastic.co/guide/en/elasticsearch/reference/8.19/highlighting.html"
|
|
66
66
|
},
|
|
67
67
|
"queryOptions": {
|
|
68
68
|
"type": "object",
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
"template": {
|
|
114
114
|
"type": "object",
|
|
115
115
|
"additionalProperties": {},
|
|
116
|
-
"description": "See available properties of template https://www.elastic.co/guide/en/elasticsearch/reference/
|
|
116
|
+
"description": "See available properties of template https://www.elastic.co/guide/en/elasticsearch/reference/8.19/indices-put-template.html#put-index-template-api-request-body"
|
|
117
117
|
}
|
|
118
118
|
},
|
|
119
119
|
"required": [
|
|
@@ -202,7 +202,7 @@
|
|
|
202
202
|
"description": "Delimiter string used to concatenate fragments. Defaults to \" ... \"."
|
|
203
203
|
}
|
|
204
204
|
},
|
|
205
|
-
"description": "Options for configuring highlight settings See https://www.elastic.co/guide/en/elasticsearch/reference/
|
|
205
|
+
"description": "Options for configuring highlight settings See https://www.elastic.co/guide/en/elasticsearch/reference/8.19/highlighting.html"
|
|
206
206
|
},
|
|
207
207
|
"queryOptions": {
|
|
208
208
|
"type": "object",
|
|
@@ -253,7 +253,7 @@
|
|
|
253
253
|
"template": {
|
|
254
254
|
"type": "object",
|
|
255
255
|
"additionalProperties": {},
|
|
256
|
-
"description": "See available properties of template https://www.elastic.co/guide/en/elasticsearch/reference/
|
|
256
|
+
"description": "See available properties of template https://www.elastic.co/guide/en/elasticsearch/reference/8.19/indices-put-template.html#put-index-template-api-request-body"
|
|
257
257
|
}
|
|
258
258
|
},
|
|
259
259
|
"required": [
|
|
@@ -369,7 +369,7 @@
|
|
|
369
369
|
"description": "Delimiter string used to concatenate fragments. Defaults to \" ... \"."
|
|
370
370
|
}
|
|
371
371
|
},
|
|
372
|
-
"description": "Options for configuring highlight settings See https://www.elastic.co/guide/en/elasticsearch/reference/
|
|
372
|
+
"description": "Options for configuring highlight settings See https://www.elastic.co/guide/en/elasticsearch/reference/8.19/highlighting.html"
|
|
373
373
|
},
|
|
374
374
|
"queryOptions": {
|
|
375
375
|
"type": "object",
|
|
@@ -420,7 +420,7 @@
|
|
|
420
420
|
"template": {
|
|
421
421
|
"type": "object",
|
|
422
422
|
"additionalProperties": {},
|
|
423
|
-
"description": "See available properties of template https://www.elastic.co/guide/en/elasticsearch/reference/
|
|
423
|
+
"description": "See available properties of template https://www.elastic.co/guide/en/elasticsearch/reference/8.19/indices-put-template.html#put-index-template-api-request-body"
|
|
424
424
|
}
|
|
425
425
|
},
|
|
426
426
|
"required": [
|
|
@@ -538,7 +538,7 @@
|
|
|
538
538
|
"description": "Delimiter string used to concatenate fragments. Defaults to \" ... \"."
|
|
539
539
|
}
|
|
540
540
|
},
|
|
541
|
-
"description": "Options for configuring highlight settings See https://www.elastic.co/guide/en/elasticsearch/reference/
|
|
541
|
+
"description": "Options for configuring highlight settings See https://www.elastic.co/guide/en/elasticsearch/reference/8.19/highlighting.html"
|
|
542
542
|
},
|
|
543
543
|
"queryOptions": {
|
|
544
544
|
"type": "object",
|
|
@@ -589,7 +589,7 @@
|
|
|
589
589
|
"template": {
|
|
590
590
|
"type": "object",
|
|
591
591
|
"additionalProperties": {},
|
|
592
|
-
"description": "See available properties of template https://www.elastic.co/guide/en/elasticsearch/reference/
|
|
592
|
+
"description": "See available properties of template https://www.elastic.co/guide/en/elasticsearch/reference/8.19/indices-put-template.html#put-index-template-api-request-body"
|
|
593
593
|
}
|
|
594
594
|
},
|
|
595
595
|
"required": [
|
|
@@ -41,32 +41,9 @@ function createOpenSearchAuthTransport(authProvider) {
|
|
|
41
41
|
}
|
|
42
42
|
function createElasticSearchAuthTransport(authProvider) {
|
|
43
43
|
class AuthTransport extends elasticsearch.Transport {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
authProvider.getAuthHeaders().then((authHeaders) => {
|
|
48
|
-
const mergedOptions = {
|
|
49
|
-
headers: authHeaders
|
|
50
|
-
};
|
|
51
|
-
return super.request(params, mergedOptions, cb);
|
|
52
|
-
}).catch(cb);
|
|
53
|
-
return { abort: () => {
|
|
54
|
-
} };
|
|
55
|
-
}
|
|
56
|
-
if (callback !== void 0) {
|
|
57
|
-
authProvider.getAuthHeaders().then((authHeaders) => {
|
|
58
|
-
const mergedOptions = {
|
|
59
|
-
...options,
|
|
60
|
-
headers: {
|
|
61
|
-
...options?.headers,
|
|
62
|
-
...authHeaders
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
return super.request(params, mergedOptions, callback);
|
|
66
|
-
}).catch(callback);
|
|
67
|
-
return { abort: () => {
|
|
68
|
-
} };
|
|
69
|
-
}
|
|
44
|
+
// The Elasticsearch v8 client only supports the promise-based transport
|
|
45
|
+
// signature, so there is no callback variant to handle here.
|
|
46
|
+
request(params, options) {
|
|
70
47
|
const result = authProvider.getAuthHeaders().then((authHeaders) => {
|
|
71
48
|
const mergedOptions = {
|
|
72
49
|
...options,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ElasticSearchAuthTransport.cjs.js","sources":["../../src/engines/ElasticSearchAuthTransport.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Transport as OpenSearchTransport } from '@opensearch-project/opensearch';\nimport { Transport as ElasticSearchTransport } from '@elastic/elasticsearch';\nimport { ElasticSearchAuthProvider } from '../auth';\nimport { ElasticSearchTransportConstructor } from './ElasticSearchClientOptions';\n\n/**\n * Creates a custom OpenSearch Transport class that injects authentication headers\n * from the provided auth provider before each request.\n *\n * @param authProvider - The authentication provider to use for getting headers\n * @returns A Transport class constructor that can be used with the OpenSearch client\n *\n * @internal\n */\nexport function createOpenSearchAuthTransport(\n authProvider: ElasticSearchAuthProvider,\n): ElasticSearchTransportConstructor {\n class AuthTransport extends OpenSearchTransport {\n request(\n params: any,\n optionsOrCallback: any = {},\n maybeCallback?: any,\n ): any {\n // options is optional, so if it's a function, it's the callback\n const isOptionsCallback = typeof optionsOrCallback === 'function';\n const options = isOptionsCallback ? {} : optionsOrCallback;\n const callback = isOptionsCallback ? optionsOrCallback : maybeCallback;\n\n // If callback style, wrap it to inject headers\n if (callback !== undefined) {\n authProvider\n .getAuthHeaders()\n .then(authHeaders => {\n const mergedOptions = {\n ...options,\n headers: {\n ...options.headers,\n ...authHeaders,\n },\n };\n return super.request(params, mergedOptions, callback);\n })\n .catch(callback);\n\n // Return an abort-able object similar to the AWS transport\n return {\n abort: () => {},\n };\n }\n\n // Promise style\n return authProvider.getAuthHeaders().then(authHeaders => {\n const mergedOptions = {\n ...options,\n headers: {\n ...options.headers,\n ...authHeaders,\n },\n };\n return super.request(params, mergedOptions);\n });\n }\n }\n\n return AuthTransport as unknown as ElasticSearchTransportConstructor;\n}\n\n/**\n * Creates a custom Elasticsearch Transport class that injects authentication headers\n * from the provided auth provider before each request.\n *\n * @param authProvider - The authentication provider to use for getting headers\n * @returns A Transport class constructor that can be used with the Elasticsearch client\n *\n * @internal\n */\nexport function createElasticSearchAuthTransport(\n authProvider: ElasticSearchAuthProvider,\n): ElasticSearchTransportConstructor {\n class AuthTransport extends ElasticSearchTransport {\n
|
|
1
|
+
{"version":3,"file":"ElasticSearchAuthTransport.cjs.js","sources":["../../src/engines/ElasticSearchAuthTransport.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Transport as OpenSearchTransport } from '@opensearch-project/opensearch';\nimport { Transport as ElasticSearchTransport } from '@elastic/elasticsearch';\nimport { ElasticSearchAuthProvider } from '../auth';\nimport { ElasticSearchTransportConstructor } from './ElasticSearchClientOptions';\n\n/**\n * Creates a custom OpenSearch Transport class that injects authentication headers\n * from the provided auth provider before each request.\n *\n * @param authProvider - The authentication provider to use for getting headers\n * @returns A Transport class constructor that can be used with the OpenSearch client\n *\n * @internal\n */\nexport function createOpenSearchAuthTransport(\n authProvider: ElasticSearchAuthProvider,\n): ElasticSearchTransportConstructor {\n class AuthTransport extends OpenSearchTransport {\n request(\n params: any,\n optionsOrCallback: any = {},\n maybeCallback?: any,\n ): any {\n // options is optional, so if it's a function, it's the callback\n const isOptionsCallback = typeof optionsOrCallback === 'function';\n const options = isOptionsCallback ? {} : optionsOrCallback;\n const callback = isOptionsCallback ? optionsOrCallback : maybeCallback;\n\n // If callback style, wrap it to inject headers\n if (callback !== undefined) {\n authProvider\n .getAuthHeaders()\n .then(authHeaders => {\n const mergedOptions = {\n ...options,\n headers: {\n ...options.headers,\n ...authHeaders,\n },\n };\n return super.request(params, mergedOptions, callback);\n })\n .catch(callback);\n\n // Return an abort-able object similar to the AWS transport\n return {\n abort: () => {},\n };\n }\n\n // Promise style\n return authProvider.getAuthHeaders().then(authHeaders => {\n const mergedOptions = {\n ...options,\n headers: {\n ...options.headers,\n ...authHeaders,\n },\n };\n return super.request(params, mergedOptions);\n });\n }\n }\n\n return AuthTransport as unknown as ElasticSearchTransportConstructor;\n}\n\n/**\n * Creates a custom Elasticsearch Transport class that injects authentication headers\n * from the provided auth provider before each request.\n *\n * @param authProvider - The authentication provider to use for getting headers\n * @returns A Transport class constructor that can be used with the Elasticsearch client\n *\n * @internal\n */\nexport function createElasticSearchAuthTransport(\n authProvider: ElasticSearchAuthProvider,\n): ElasticSearchTransportConstructor {\n class AuthTransport extends ElasticSearchTransport {\n // The Elasticsearch v8 client only supports the promise-based transport\n // signature, so there is no callback variant to handle here.\n request(params: any, options?: any): any {\n const result = authProvider.getAuthHeaders().then(authHeaders => {\n const mergedOptions = {\n ...options,\n headers: {\n ...options?.headers,\n ...authHeaders,\n },\n };\n return super.request(params, mergedOptions);\n });\n\n // Add abort method to match TransportRequestPromise interface\n (result as any).abort = () => {};\n return result;\n }\n }\n\n return AuthTransport as unknown as ElasticSearchTransportConstructor;\n}\n"],"names":["OpenSearchTransport","ElasticSearchTransport"],"mappings":";;;;;AA8BO,SAAS,8BACd,YAAA,EACmC;AAAA,EACnC,MAAM,sBAAsBA,oBAAA,CAAoB;AAAA,IAC9C,OAAA,CACE,MAAA,EACA,iBAAA,GAAyB,IACzB,aAAA,EACK;AAEL,MAAA,MAAM,iBAAA,GAAoB,OAAO,iBAAA,KAAsB,UAAA;AACvD,MAAA,MAAM,OAAA,GAAU,iBAAA,GAAoB,EAAC,GAAI,iBAAA;AACzC,MAAA,MAAM,QAAA,GAAW,oBAAoB,iBAAA,GAAoB,aAAA;AAGzD,MAAA,IAAI,aAAa,MAAA,EAAW;AAC1B,QAAA,YAAA,CACG,cAAA,EAAe,CACf,IAAA,CAAK,CAAA,WAAA,KAAe;AACnB,UAAA,MAAM,aAAA,GAAgB;AAAA,YACpB,GAAG,OAAA;AAAA,YACH,OAAA,EAAS;AAAA,cACP,GAAG,OAAA,CAAQ,OAAA;AAAA,cACX,GAAG;AAAA;AACL,WACF;AACA,UAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,MAAA,EAAQ,aAAA,EAAe,QAAQ,CAAA;AAAA,QACtD,CAAC,CAAA,CACA,KAAA,CAAM,QAAQ,CAAA;AAGjB,QAAA,OAAO;AAAA,UACL,OAAO,MAAM;AAAA,UAAC;AAAA,SAChB;AAAA,MACF;AAGA,MAAA,OAAO,YAAA,CAAa,cAAA,EAAe,CAAE,IAAA,CAAK,CAAA,WAAA,KAAe;AACvD,QAAA,MAAM,aAAA,GAAgB;AAAA,UACpB,GAAG,OAAA;AAAA,UACH,OAAA,EAAS;AAAA,YACP,GAAG,OAAA,CAAQ,OAAA;AAAA,YACX,GAAG;AAAA;AACL,SACF;AACA,QAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,MAAA,EAAQ,aAAa,CAAA;AAAA,MAC5C,CAAC,CAAA;AAAA,IACH;AAAA;AAGF,EAAA,OAAO,aAAA;AACT;AAWO,SAAS,iCACd,YAAA,EACmC;AAAA,EACnC,MAAM,sBAAsBC,uBAAA,CAAuB;AAAA;AAAA;AAAA,IAGjD,OAAA,CAAQ,QAAa,OAAA,EAAoB;AACvC,MAAA,MAAM,MAAA,GAAS,YAAA,CAAa,cAAA,EAAe,CAAE,KAAK,CAAA,WAAA,KAAe;AAC/D,QAAA,MAAM,aAAA,GAAgB;AAAA,UACpB,GAAG,OAAA;AAAA,UACH,OAAA,EAAS;AAAA,YACP,GAAG,OAAA,EAAS,OAAA;AAAA,YACZ,GAAG;AAAA;AACL,SACF;AACA,QAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,MAAA,EAAQ,aAAa,CAAA;AAAA,MAC5C,CAAC,CAAA;AAGD,MAAC,MAAA,CAAe,QAAQ,MAAM;AAAA,MAAC,CAAA;AAC/B,MAAA,OAAO,MAAA;AAAA,IACT;AAAA;AAGF,EAAA,OAAO,aAAA;AACT;;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ElasticSearchClientOptions.cjs.js","sources":["../../src/engines/ElasticSearchClientOptions.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { ConnectionOptions as TLSConnectionOptions } from 'node:tls';\n\n/**\n * Typeguard to differentiate ElasticSearch client options which are compatible\n * with OpenSearch vs. ElasticSearch clients. Useful when calling the\n * {@link ElasticSearchSearchEngine.newClient} method.\n *\n * @public\n */\nexport const isOpenSearchCompatible = (\n opts: ElasticSearchClientOptions,\n): opts is OpenSearchElasticSearchClientOptions => {\n return ['aws', 'opensearch'].includes(opts?.provider ?? '');\n};\n\n/**\n * Options used to configure the `@elastic/elasticsearch` client or the\n * `@opensearch-project/opensearch` client, depending on the given config. It\n * will be passed as an argument to the\n * {@link ElasticSearchSearchEngine.newClient} method.\n *\n * @public\n */\nexport type ElasticSearchClientOptions =\n | ElasticSearchElasticSearchClientOptions\n | OpenSearchElasticSearchClientOptions;\n\n/**\n * Options used to configure the `@opensearch-project/opensearch` client.\n *\n * They are drawn from the `ClientOptions` class of `@opensearch-project/opensearch`,\n * but are maintained separately so that this interface is not coupled to it.\n *\n * @public\n */\nexport interface OpenSearchElasticSearchClientOptions\n extends BaseElasticSearchClientOptions {\n provider?: 'aws' | 'opensearch';\n region?: string;\n service?: 'es' | 'aoss';\n auth?: OpenSearchAuth;\n connection?: OpenSearchConnectionConstructor;\n node?: string | string[] | OpenSearchNodeOptions | OpenSearchNodeOptions[];\n nodes?: string | string[] | OpenSearchNodeOptions | OpenSearchNodeOptions[];\n}\n\n/**\n * Options used to configure the `@elastic/elasticsearch` client.\n *\n * They are drawn from the `ClientOptions` class of `@elastic/elasticsearch`,\n * but are maintained separately so that this interface is not coupled to it.\n *\n * @public\n */\nexport interface ElasticSearchElasticSearchClientOptions\n extends BaseElasticSearchClientOptions {\n provider?: 'elastic';\n auth?: ElasticSearchAuth;\n Connection?: ElasticSearchConnectionConstructor;\n node?:\n | string\n | string[]\n | ElasticSearchNodeOptions\n | ElasticSearchNodeOptions[];\n nodes?:\n | string\n | string[]\n | ElasticSearchNodeOptions\n | ElasticSearchNodeOptions[];\n cloud?: {\n id: string;\n username?: string;\n password?: string;\n };\n}\n\n/**\n * Base client options that are shared across `@opensearch-project/opensearch`\n * and `@elastic/elasticsearch` clients.\n *\n * @public\n */\nexport interface BaseElasticSearchClientOptions {\n Transport?: ElasticSearchTransportConstructor;\n maxRetries?: number;\n requestTimeout?: number;\n pingTimeout?: number;\n sniffInterval?: number | boolean;\n sniffOnStart?: boolean;\n sniffEndpoint?: string;\n sniffOnConnectionFault?: boolean;\n resurrectStrategy?: 'ping' | 'optimistic' | 'none';\n suggestCompression?: boolean;\n compression?: 'gzip';\n ssl?: TLSConnectionOptions;\n agent?: ElasticSearchAgentOptions | ((opts?: any) => unknown) | false;\n nodeFilter?: (connection: any) => boolean;\n nodeSelector?: ((connections: any[]) => any) | string;\n headers?: Record<string, any>;\n opaqueIdPrefix?: string;\n name?: string | symbol;\n proxy?: string | URL;\n enableMetaHeader?: boolean;\n disablePrototypePoisoningProtection?: boolean | 'proto' | 'constructor';\n}\n\n/**\n * @public\n */\nexport type OpenSearchAuth = {\n username: string;\n password: string;\n};\n\n/**\n * @public\n */\nexport type ElasticSearchAuth =\n | OpenSearchAuth\n | {\n apiKey:\n | string\n | {\n id: string;\n api_key: string;\n };\n };\n\n/**\n * @public\n */\nexport interface ElasticSearchNodeOptions {\n url: URL;\n id?: string;\n agent?: ElasticSearchAgentOptions;\n ssl?: TLSConnectionOptions;\n headers?: Record<string, any>;\n roles?: {\n master: boolean;\n data: boolean;\n ingest: boolean;\n ml: boolean;\n };\n}\n\n/**\n * @public\n */\nexport interface OpenSearchNodeOptions {\n url: URL;\n id?: string;\n agent?: ElasticSearchAgentOptions;\n ssl?: TLSConnectionOptions;\n headers?: Record<string, any>;\n roles?: {\n master: boolean;\n data: boolean;\n ingest: boolean;\n };\n}\n\n/**\n * @public\n */\nexport interface ElasticSearchAgentOptions {\n keepAlive?: boolean;\n keepAliveMsecs?: number;\n maxSockets?: number;\n maxFreeSockets?: number;\n}\n\n/**\n * @public\n */\nexport interface ElasticSearchConnectionConstructor {\n new (opts?: any): any;\n statuses: {\n ALIVE: string;\n DEAD: string;\n };\n roles
|
|
1
|
+
{"version":3,"file":"ElasticSearchClientOptions.cjs.js","sources":["../../src/engines/ElasticSearchClientOptions.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { ConnectionOptions as TLSConnectionOptions } from 'node:tls';\n\n/**\n * Typeguard to differentiate ElasticSearch client options which are compatible\n * with OpenSearch vs. ElasticSearch clients. Useful when calling the\n * {@link ElasticSearchSearchEngine.newClient} method.\n *\n * @public\n */\nexport const isOpenSearchCompatible = (\n opts: ElasticSearchClientOptions,\n): opts is OpenSearchElasticSearchClientOptions => {\n return ['aws', 'opensearch'].includes(opts?.provider ?? '');\n};\n\n/**\n * Options used to configure the `@elastic/elasticsearch` client or the\n * `@opensearch-project/opensearch` client, depending on the given config. It\n * will be passed as an argument to the\n * {@link ElasticSearchSearchEngine.newClient} method.\n *\n * @public\n */\nexport type ElasticSearchClientOptions =\n | ElasticSearchElasticSearchClientOptions\n | OpenSearchElasticSearchClientOptions;\n\n/**\n * Options used to configure the `@opensearch-project/opensearch` client.\n *\n * They are drawn from the `ClientOptions` class of `@opensearch-project/opensearch`,\n * but are maintained separately so that this interface is not coupled to it.\n *\n * @public\n */\nexport interface OpenSearchElasticSearchClientOptions\n extends BaseElasticSearchClientOptions {\n provider?: 'aws' | 'opensearch';\n region?: string;\n service?: 'es' | 'aoss';\n auth?: OpenSearchAuth;\n connection?: OpenSearchConnectionConstructor;\n node?: string | string[] | OpenSearchNodeOptions | OpenSearchNodeOptions[];\n nodes?: string | string[] | OpenSearchNodeOptions | OpenSearchNodeOptions[];\n}\n\n/**\n * Options used to configure the `@elastic/elasticsearch` client.\n *\n * They are drawn from the `ClientOptions` class of `@elastic/elasticsearch`,\n * but are maintained separately so that this interface is not coupled to it.\n *\n * @public\n */\nexport interface ElasticSearchElasticSearchClientOptions\n extends BaseElasticSearchClientOptions {\n provider?: 'elastic';\n auth?: ElasticSearchAuth;\n Connection?: ElasticSearchConnectionConstructor;\n node?:\n | string\n | string[]\n | ElasticSearchNodeOptions\n | ElasticSearchNodeOptions[];\n nodes?:\n | string\n | string[]\n | ElasticSearchNodeOptions\n | ElasticSearchNodeOptions[];\n cloud?: {\n id: string;\n username?: string;\n password?: string;\n };\n}\n\n/**\n * Base client options that are shared across `@opensearch-project/opensearch`\n * and `@elastic/elasticsearch` clients.\n *\n * @public\n */\nexport interface BaseElasticSearchClientOptions {\n Transport?: ElasticSearchTransportConstructor;\n maxRetries?: number;\n requestTimeout?: number;\n pingTimeout?: number;\n sniffInterval?: number | boolean;\n sniffOnStart?: boolean;\n sniffEndpoint?: string;\n sniffOnConnectionFault?: boolean;\n resurrectStrategy?: 'ping' | 'optimistic' | 'none';\n suggestCompression?: boolean;\n compression?: 'gzip';\n ssl?: TLSConnectionOptions;\n agent?: ElasticSearchAgentOptions | ((opts?: any) => unknown) | false;\n nodeFilter?: (connection: any) => boolean;\n nodeSelector?: ((connections: any[]) => any) | string;\n headers?: Record<string, any>;\n opaqueIdPrefix?: string;\n name?: string | symbol;\n proxy?: string | URL;\n enableMetaHeader?: boolean;\n disablePrototypePoisoningProtection?: boolean | 'proto' | 'constructor';\n}\n\n/**\n * @public\n */\nexport type OpenSearchAuth = {\n username: string;\n password: string;\n};\n\n/**\n * @public\n */\nexport type ElasticSearchAuth =\n | OpenSearchAuth\n | {\n apiKey:\n | string\n | {\n id: string;\n api_key: string;\n };\n };\n\n/**\n * @public\n */\nexport interface ElasticSearchNodeOptions {\n url: URL;\n id?: string;\n agent?: ElasticSearchAgentOptions;\n ssl?: TLSConnectionOptions;\n headers?: Record<string, any>;\n roles?: {\n master: boolean;\n data: boolean;\n ingest: boolean;\n ml: boolean;\n };\n}\n\n/**\n * @public\n */\nexport interface OpenSearchNodeOptions {\n url: URL;\n id?: string;\n agent?: ElasticSearchAgentOptions;\n ssl?: TLSConnectionOptions;\n headers?: Record<string, any>;\n roles?: {\n master: boolean;\n data: boolean;\n ingest: boolean;\n };\n}\n\n/**\n * @public\n */\nexport interface ElasticSearchAgentOptions {\n keepAlive?: boolean;\n keepAliveMsecs?: number;\n maxSockets?: number;\n maxFreeSockets?: number;\n}\n\n/**\n * @public\n */\nexport interface ElasticSearchConnectionConstructor {\n new (opts?: any): any;\n statuses: {\n ALIVE: string;\n DEAD: string;\n };\n // The Elasticsearch v8 client exposes connection roles as an instance\n // property rather than a static, so this is optional for compatibility.\n roles?: {\n MASTER: string;\n DATA: string;\n INGEST: string;\n ML: string;\n };\n}\n\n/**\n * @public\n */\nexport interface OpenSearchConnectionConstructor {\n new (opts?: any): any;\n statuses: {\n ALIVE: string;\n DEAD: string;\n };\n roles: {\n MASTER: string;\n DATA: string;\n INGEST: string;\n };\n}\n\n/**\n * @public\n */\nexport interface ElasticSearchTransportConstructor {\n new (opts?: any): any;\n sniffReasons: {\n SNIFF_ON_START: string;\n SNIFF_INTERVAL: string;\n SNIFF_ON_CONNECTION_FAULT: string;\n DEFAULT: string;\n };\n}\n\n// todo(iamEAP) implement canary types to ensure we remain compatible through upgrades.\n"],"names":[],"mappings":";;AAwBO,MAAM,sBAAA,GAAyB,CACpC,IAAA,KACiD;AACjD,EAAA,OAAO,CAAC,KAAA,EAAO,YAAY,EAAE,QAAA,CAAS,IAAA,EAAM,YAAY,EAAE,CAAA;AAC5D;;;;"}
|
|
@@ -4,6 +4,13 @@ var elasticsearch = require('@elastic/elasticsearch');
|
|
|
4
4
|
var opensearch = require('@opensearch-project/opensearch');
|
|
5
5
|
var ElasticSearchClientOptions = require('./ElasticSearchClientOptions.cjs.js');
|
|
6
6
|
|
|
7
|
+
function toElasticSearchClientOptions(options) {
|
|
8
|
+
const { ssl, ...rest } = options;
|
|
9
|
+
return {
|
|
10
|
+
...rest,
|
|
11
|
+
...ssl ? { tls: ssl } : {}
|
|
12
|
+
};
|
|
13
|
+
}
|
|
7
14
|
class ElasticSearchClientWrapper {
|
|
8
15
|
elasticSearchClient;
|
|
9
16
|
openSearchClient;
|
|
@@ -18,7 +25,9 @@ class ElasticSearchClientWrapper {
|
|
|
18
25
|
});
|
|
19
26
|
}
|
|
20
27
|
return new ElasticSearchClientWrapper({
|
|
21
|
-
elasticSearchClient: new elasticsearch.Client(
|
|
28
|
+
elasticSearchClient: new elasticsearch.Client(
|
|
29
|
+
toElasticSearchClientOptions(options)
|
|
30
|
+
)
|
|
22
31
|
});
|
|
23
32
|
}
|
|
24
33
|
search(options) {
|
|
@@ -33,10 +42,13 @@ class ElasticSearchClientWrapper {
|
|
|
33
42
|
});
|
|
34
43
|
}
|
|
35
44
|
if (this.elasticSearchClient) {
|
|
36
|
-
return this.elasticSearchClient.search(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
45
|
+
return this.elasticSearchClient.search(
|
|
46
|
+
{
|
|
47
|
+
...options,
|
|
48
|
+
...searchOptions
|
|
49
|
+
},
|
|
50
|
+
{ meta: true }
|
|
51
|
+
);
|
|
40
52
|
}
|
|
41
53
|
throw new Error("No client defined");
|
|
42
54
|
}
|
|
@@ -54,7 +66,9 @@ class ElasticSearchClientWrapper {
|
|
|
54
66
|
return this.openSearchClient.indices.putIndexTemplate(template);
|
|
55
67
|
}
|
|
56
68
|
if (this.elasticSearchClient) {
|
|
57
|
-
return this.elasticSearchClient.indices.putIndexTemplate(template
|
|
69
|
+
return this.elasticSearchClient.indices.putIndexTemplate(template, {
|
|
70
|
+
meta: true
|
|
71
|
+
});
|
|
58
72
|
}
|
|
59
73
|
throw new Error("No client defined");
|
|
60
74
|
}
|
|
@@ -63,7 +77,7 @@ class ElasticSearchClientWrapper {
|
|
|
63
77
|
return this.openSearchClient.indices.get(options);
|
|
64
78
|
}
|
|
65
79
|
if (this.elasticSearchClient) {
|
|
66
|
-
return this.elasticSearchClient.indices.get(options);
|
|
80
|
+
return this.elasticSearchClient.indices.get(options, { meta: true });
|
|
67
81
|
}
|
|
68
82
|
throw new Error("No client defined");
|
|
69
83
|
}
|
|
@@ -72,7 +86,7 @@ class ElasticSearchClientWrapper {
|
|
|
72
86
|
return this.openSearchClient.indices.exists(options);
|
|
73
87
|
}
|
|
74
88
|
if (this.elasticSearchClient) {
|
|
75
|
-
return this.elasticSearchClient.indices.exists(options);
|
|
89
|
+
return this.elasticSearchClient.indices.exists(options, { meta: true });
|
|
76
90
|
}
|
|
77
91
|
throw new Error("No client defined");
|
|
78
92
|
}
|
|
@@ -81,7 +95,7 @@ class ElasticSearchClientWrapper {
|
|
|
81
95
|
return this.openSearchClient.indices.delete(options);
|
|
82
96
|
}
|
|
83
97
|
if (this.elasticSearchClient) {
|
|
84
|
-
return this.elasticSearchClient.indices.delete(options);
|
|
98
|
+
return this.elasticSearchClient.indices.delete(options, { meta: true });
|
|
85
99
|
}
|
|
86
100
|
throw new Error("No client defined");
|
|
87
101
|
}
|
|
@@ -97,10 +111,13 @@ class ElasticSearchClientWrapper {
|
|
|
97
111
|
});
|
|
98
112
|
}
|
|
99
113
|
if (this.elasticSearchClient) {
|
|
100
|
-
return this.elasticSearchClient.cat.aliases(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
114
|
+
return this.elasticSearchClient.cat.aliases(
|
|
115
|
+
{
|
|
116
|
+
format: "json",
|
|
117
|
+
name: aliases
|
|
118
|
+
},
|
|
119
|
+
{ meta: true }
|
|
120
|
+
);
|
|
104
121
|
}
|
|
105
122
|
throw new Error("No client defined");
|
|
106
123
|
}
|
|
@@ -109,7 +126,7 @@ class ElasticSearchClientWrapper {
|
|
|
109
126
|
return this.openSearchClient.indices.create(options);
|
|
110
127
|
}
|
|
111
128
|
if (this.elasticSearchClient) {
|
|
112
|
-
return this.elasticSearchClient.indices.create(options);
|
|
129
|
+
return this.elasticSearchClient.indices.create(options, { meta: true });
|
|
113
130
|
}
|
|
114
131
|
throw new Error("No client defined");
|
|
115
132
|
}
|
|
@@ -123,11 +140,14 @@ class ElasticSearchClientWrapper {
|
|
|
123
140
|
});
|
|
124
141
|
}
|
|
125
142
|
if (this.elasticSearchClient) {
|
|
126
|
-
return this.elasticSearchClient.indices.updateAliases(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
143
|
+
return this.elasticSearchClient.indices.updateAliases(
|
|
144
|
+
{
|
|
145
|
+
body: {
|
|
146
|
+
actions: filteredActions
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
{ meta: true }
|
|
150
|
+
);
|
|
131
151
|
}
|
|
132
152
|
throw new Error("No client defined");
|
|
133
153
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ElasticSearchClientWrapper.cjs.js","sources":["../../src/engines/ElasticSearchClientWrapper.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Client as ElasticSearchClient } from '@elastic/elasticsearch';\nimport { Client as OpenSearchClient } from '@opensearch-project/opensearch';\nimport { Readable } from 'node:stream';\nimport {\n ElasticSearchClientOptions,\n isOpenSearchCompatible,\n} from './ElasticSearchClientOptions';\nimport { ElasticSearchCustomIndexTemplate } from './types';\n\n/**\n * @public\n */\nexport type ElasticSearchAliasAction =\n | {\n remove: { index: any; alias: any };\n add?: undefined;\n }\n | {\n add: { indices: any; alias: any; index?: undefined };\n remove?: undefined;\n }\n | {\n add: { index: any; alias: any; indices?: undefined };\n remove?: undefined;\n }\n | undefined;\n\n/**\n * @public\n */\nexport type ElasticSearchIndexAction = {\n index: {\n _index: string;\n [key: string]: any;\n };\n};\n\n/**\n * A wrapper class that exposes logical methods that are conditionally fired\n * against either a configured Elasticsearch client or a configured Opensearch\n * client.\n *\n * This is necessary because, despite its intention to be API-compatible, the\n * opensearch client does not support API key-based authentication. This is\n * also the sanest way to accomplish this while making typescript happy.\n *\n * In the future, if the differences between implementations become\n * unmaintainably divergent, we should split out the Opensearch and\n * Elasticsearch search engine implementations.\n *\n * @public\n */\nexport class ElasticSearchClientWrapper {\n private readonly elasticSearchClient: ElasticSearchClient | undefined;\n private readonly openSearchClient: OpenSearchClient | undefined;\n\n private constructor(options: {\n openSearchClient?: OpenSearchClient;\n elasticSearchClient?: ElasticSearchClient;\n }) {\n this.openSearchClient = options.openSearchClient;\n this.elasticSearchClient = options.elasticSearchClient;\n }\n\n static fromClientOptions(options: ElasticSearchClientOptions) {\n if (isOpenSearchCompatible(options)) {\n return new ElasticSearchClientWrapper({\n openSearchClient: new OpenSearchClient(options),\n });\n }\n\n return new ElasticSearchClientWrapper({\n elasticSearchClient: new ElasticSearchClient(options),\n });\n }\n\n search(options: { index: string | string[]; body: Object }) {\n const searchOptions = {\n ignore_unavailable: true,\n allow_no_indices: true,\n };\n\n if (this.openSearchClient) {\n return this.openSearchClient.search({\n ...options,\n ...searchOptions,\n });\n }\n\n if (this.elasticSearchClient) {\n return this.elasticSearchClient.search({\n ...options,\n ...searchOptions,\n });\n }\n\n throw new Error('No client defined');\n }\n\n bulk(bulkOptions: {\n datasource: Readable;\n onDocument: (doc: any) => ElasticSearchIndexAction;\n refreshOnCompletion?: string | boolean;\n }) {\n if (this.openSearchClient) {\n return this.openSearchClient.helpers.bulk(bulkOptions);\n }\n\n if (this.elasticSearchClient) {\n return this.elasticSearchClient.helpers.bulk(bulkOptions);\n }\n\n throw new Error('No client defined');\n }\n\n putIndexTemplate(template: ElasticSearchCustomIndexTemplate) {\n if (this.openSearchClient) {\n return this.openSearchClient.indices.putIndexTemplate(template);\n }\n\n if (this.elasticSearchClient) {\n return this.elasticSearchClient.indices.putIndexTemplate(template);\n }\n\n throw new Error('No client defined');\n }\n\n listIndices(options: { index: string }) {\n if (this.openSearchClient) {\n return this.openSearchClient.indices.get(options);\n }\n\n if (this.elasticSearchClient) {\n return this.elasticSearchClient.indices.get(options);\n }\n\n throw new Error('No client defined');\n }\n\n indexExists(options: { index: string | string[] }) {\n if (this.openSearchClient) {\n return this.openSearchClient.indices.exists(options);\n }\n\n if (this.elasticSearchClient) {\n return this.elasticSearchClient.indices.exists(options);\n }\n\n throw new Error('No client defined');\n }\n\n deleteIndex(options: { index: string | string[] }) {\n if (this.openSearchClient) {\n return this.openSearchClient.indices.delete(options);\n }\n\n if (this.elasticSearchClient) {\n return this.elasticSearchClient.indices.delete(options);\n }\n\n throw new Error('No client defined');\n }\n\n /**\n * @deprecated unused by the ElasticSearch Engine, will be removed in the future\n */\n getAliases(options: { aliases: string[] }) {\n const { aliases } = options;\n\n if (this.openSearchClient) {\n return this.openSearchClient.cat.aliases({\n format: 'json',\n name: aliases,\n });\n }\n\n if (this.elasticSearchClient) {\n return this.elasticSearchClient.cat.aliases({\n format: 'json',\n name: aliases,\n });\n }\n\n throw new Error('No client defined');\n }\n\n createIndex(options: { index: string }) {\n if (this.openSearchClient) {\n return this.openSearchClient.indices.create(options);\n }\n\n if (this.elasticSearchClient) {\n return this.elasticSearchClient.indices.create(options);\n }\n\n throw new Error('No client defined');\n }\n\n updateAliases(options: { actions: ElasticSearchAliasAction[] }) {\n const filteredActions = options.actions.filter(Boolean);\n\n if (this.openSearchClient) {\n return this.openSearchClient.indices.updateAliases({\n body: {\n actions: filteredActions,\n },\n });\n }\n\n if (this.elasticSearchClient) {\n return this.elasticSearchClient.indices.updateAliases({\n body: {\n actions: filteredActions,\n },\n });\n }\n\n throw new Error('No client defined');\n }\n}\n"],"names":["isOpenSearchCompatible","OpenSearchClient","ElasticSearchClient"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ElasticSearchClientWrapper.cjs.js","sources":["../../src/engines/ElasticSearchClientWrapper.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Client as ElasticSearchClient,\n ClientOptions as ElasticSearchClientLibOptions,\n} from '@elastic/elasticsearch';\nimport { Client as OpenSearchClient } from '@opensearch-project/opensearch';\nimport { Readable } from 'node:stream';\nimport {\n ElasticSearchClientOptions,\n isOpenSearchCompatible,\n} from './ElasticSearchClientOptions';\nimport { ElasticSearchCustomIndexTemplate } from './types';\n\n/**\n * @public\n */\nexport type ElasticSearchAliasAction =\n | {\n remove: { index: any; alias: any };\n add?: undefined;\n }\n | {\n add: { indices: any; alias: any; index?: undefined };\n remove?: undefined;\n }\n | {\n add: { index: any; alias: any; indices?: undefined };\n remove?: undefined;\n }\n | undefined;\n\n/**\n * @public\n */\nexport type ElasticSearchIndexAction = {\n index: {\n _index: string;\n [key: string]: any;\n };\n};\n\n/**\n * The normalized response shape returned by the wrapped client methods.\n *\n * Both the Elasticsearch and Opensearch clients expose the response payload\n * under a `body` property together with the HTTP status code, the response\n * headers and any deprecation warnings, so consumers can read these regardless\n * of the configured provider. The client specific `meta` envelope is not part\n * of this type because its shape differs between the two clients.\n *\n * @public\n */\nexport type ElasticSearchClientResponse<TBody = any> = {\n body: TBody;\n headers: Record<string, any> | null;\n statusCode: number | null;\n warnings: string[] | null;\n};\n\n/**\n * The promise returned by the wrapped client methods.\n *\n * The Opensearch client returns a promise that additionally exposes an\n * `abort` method, whereas the Elasticsearch v8 client returns a plain\n * promise, so `abort` is only present when the Opensearch client is\n * configured.\n *\n * @public\n */\nexport type ElasticSearchClientResponsePromise<TBody = any> = Promise<\n ElasticSearchClientResponse<TBody>\n> & {\n abort?: () => void;\n};\n\n/**\n * Adapts the provider-agnostic client options to the shape expected by the\n * `@elastic/elasticsearch` v8 client.\n *\n * The v8 client reads TLS configuration from a `tls` property; the `ssl`\n * property used by the Opensearch client (and by earlier Elasticsearch client\n * versions) is no longer recognized. Map it across so that TLS settings keep\n * being honored.\n */\nfunction toElasticSearchClientOptions(\n options: ElasticSearchClientOptions,\n): ElasticSearchClientLibOptions {\n const { ssl, ...rest } = options;\n return {\n ...rest,\n ...(ssl ? { tls: ssl } : {}),\n } as ElasticSearchClientLibOptions;\n}\n\n/**\n * A wrapper class that exposes logical methods that are conditionally fired\n * against either a configured Elasticsearch client or a configured Opensearch\n * client.\n *\n * This is necessary because, despite its intention to be API-compatible, the\n * opensearch client does not support API key-based authentication. This is\n * also the sanest way to accomplish this while making typescript happy.\n *\n * In the future, if the differences between implementations become\n * unmaintainably divergent, we should split out the Opensearch and\n * Elasticsearch search engine implementations.\n *\n * The Elasticsearch client calls below pass `{ meta: true }`. As of v8 the\n * `@elastic/elasticsearch` client returns the response body directly, whereas\n * the Opensearch client (forked from the v7 Elasticsearch client) still wraps\n * it in `{ body, statusCode, headers, ... }`. Requesting the meta envelope\n * keeps both clients returning the same shape, so consumers can continue to\n * read `response.body` regardless of the configured provider.\n *\n * @public\n */\nexport class ElasticSearchClientWrapper {\n private readonly elasticSearchClient: ElasticSearchClient | undefined;\n private readonly openSearchClient: OpenSearchClient | undefined;\n\n private constructor(options: {\n openSearchClient?: OpenSearchClient;\n elasticSearchClient?: ElasticSearchClient;\n }) {\n this.openSearchClient = options.openSearchClient;\n this.elasticSearchClient = options.elasticSearchClient;\n }\n\n static fromClientOptions(options: ElasticSearchClientOptions) {\n if (isOpenSearchCompatible(options)) {\n return new ElasticSearchClientWrapper({\n openSearchClient: new OpenSearchClient(options),\n });\n }\n\n return new ElasticSearchClientWrapper({\n elasticSearchClient: new ElasticSearchClient(\n toElasticSearchClientOptions(options),\n ),\n });\n }\n\n search(options: {\n index: string | string[];\n body: Object;\n }): ElasticSearchClientResponsePromise {\n const searchOptions = {\n ignore_unavailable: true,\n allow_no_indices: true,\n };\n\n if (this.openSearchClient) {\n return this.openSearchClient.search({\n ...options,\n ...searchOptions,\n });\n }\n\n if (this.elasticSearchClient) {\n return this.elasticSearchClient.search(\n {\n ...options,\n ...searchOptions,\n },\n { meta: true },\n );\n }\n\n throw new Error('No client defined');\n }\n\n bulk(bulkOptions: {\n datasource: Readable;\n onDocument: (doc: any) => ElasticSearchIndexAction;\n refreshOnCompletion?: string | boolean;\n }) {\n if (this.openSearchClient) {\n return this.openSearchClient.helpers.bulk(bulkOptions);\n }\n\n if (this.elasticSearchClient) {\n return this.elasticSearchClient.helpers.bulk(bulkOptions);\n }\n\n throw new Error('No client defined');\n }\n\n putIndexTemplate(\n template: ElasticSearchCustomIndexTemplate,\n ): ElasticSearchClientResponsePromise {\n if (this.openSearchClient) {\n return this.openSearchClient.indices.putIndexTemplate(template);\n }\n\n if (this.elasticSearchClient) {\n return this.elasticSearchClient.indices.putIndexTemplate(template, {\n meta: true,\n });\n }\n\n throw new Error('No client defined');\n }\n\n listIndices(options: { index: string }): ElasticSearchClientResponsePromise {\n if (this.openSearchClient) {\n return this.openSearchClient.indices.get(options);\n }\n\n if (this.elasticSearchClient) {\n return this.elasticSearchClient.indices.get(options, { meta: true });\n }\n\n throw new Error('No client defined');\n }\n\n indexExists(options: {\n index: string | string[];\n }): ElasticSearchClientResponsePromise<boolean> {\n if (this.openSearchClient) {\n return this.openSearchClient.indices.exists(options);\n }\n\n if (this.elasticSearchClient) {\n return this.elasticSearchClient.indices.exists(options, { meta: true });\n }\n\n throw new Error('No client defined');\n }\n\n deleteIndex(options: {\n index: string | string[];\n }): ElasticSearchClientResponsePromise {\n if (this.openSearchClient) {\n return this.openSearchClient.indices.delete(options);\n }\n\n if (this.elasticSearchClient) {\n return this.elasticSearchClient.indices.delete(options, { meta: true });\n }\n\n throw new Error('No client defined');\n }\n\n /**\n * @deprecated unused by the ElasticSearch Engine, will be removed in the future\n */\n getAliases(options: {\n aliases: string[];\n }): ElasticSearchClientResponsePromise {\n const { aliases } = options;\n\n if (this.openSearchClient) {\n return this.openSearchClient.cat.aliases({\n format: 'json',\n name: aliases,\n });\n }\n\n if (this.elasticSearchClient) {\n return this.elasticSearchClient.cat.aliases(\n {\n format: 'json',\n name: aliases,\n },\n { meta: true },\n );\n }\n\n throw new Error('No client defined');\n }\n\n createIndex(options: { index: string }): ElasticSearchClientResponsePromise {\n if (this.openSearchClient) {\n return this.openSearchClient.indices.create(options);\n }\n\n if (this.elasticSearchClient) {\n return this.elasticSearchClient.indices.create(options, { meta: true });\n }\n\n throw new Error('No client defined');\n }\n\n updateAliases(options: {\n actions: ElasticSearchAliasAction[];\n }): ElasticSearchClientResponsePromise {\n const filteredActions = options.actions.filter(Boolean);\n\n if (this.openSearchClient) {\n return this.openSearchClient.indices.updateAliases({\n body: {\n actions: filteredActions,\n },\n });\n }\n\n if (this.elasticSearchClient) {\n return this.elasticSearchClient.indices.updateAliases(\n {\n body: {\n actions: filteredActions as any[],\n },\n },\n { meta: true },\n );\n }\n\n throw new Error('No client defined');\n }\n}\n"],"names":["isOpenSearchCompatible","OpenSearchClient","ElasticSearchClient"],"mappings":"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"}
|
|
@@ -58,9 +58,13 @@ class ElasticSearchSearchEngine {
|
|
|
58
58
|
authProvider
|
|
59
59
|
} = options;
|
|
60
60
|
const credentialProvider = integrationAwsNode.DefaultAwsCredentialsManager.fromConfig(config);
|
|
61
|
+
const esConfig = config.getConfig("search.elasticsearch");
|
|
62
|
+
const awsAccountId = esConfig.getOptionalString("accountId");
|
|
61
63
|
const clientOptions = await this.createElasticSearchClientOptions(
|
|
62
|
-
await credentialProvider?.getCredentialProvider(
|
|
63
|
-
|
|
64
|
+
await credentialProvider?.getCredentialProvider(
|
|
65
|
+
awsAccountId ? { accountId: awsAccountId } : void 0
|
|
66
|
+
),
|
|
67
|
+
esConfig,
|
|
64
68
|
authProvider
|
|
65
69
|
);
|
|
66
70
|
if (clientOptions.provider === "elastic") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ElasticSearchSearchEngine.cjs.js","sources":["../../src/engines/ElasticSearchSearchEngine.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n IndexableDocument,\n IndexableResult,\n IndexableResultSet,\n SearchQuery,\n} from '@backstage/plugin-search-common';\nimport { SearchEngine } from '@backstage/plugin-search-backend-node';\nimport { isEmpty, isNumber, isNaN as nan } from 'lodash';\n\nimport { AwsSigv4Signer } from '@opensearch-project/opensearch/aws';\nimport { RequestSigner } from 'aws4';\nimport { Config } from '@backstage/config';\nimport {\n ElasticSearchClientOptions,\n OpenSearchElasticSearchClientOptions,\n} from './ElasticSearchClientOptions';\nimport { ElasticSearchClientWrapper } from './ElasticSearchClientWrapper';\nimport { ElasticSearchCustomIndexTemplate } from './types';\nimport { ElasticSearchSearchEngineIndexer } from './ElasticSearchSearchEngineIndexer';\nimport { MissingIndexError } from '@backstage/plugin-search-backend-node';\nimport esb from 'elastic-builder';\nimport { randomUUID as uuid } from 'node:crypto';\nimport {\n AwsCredentialProvider,\n DefaultAwsCredentialsManager,\n} from '@backstage/integration-aws-node';\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { ElasticSearchAuthProvider } from '../auth';\nimport {\n createOpenSearchAuthTransport,\n createElasticSearchAuthTransport,\n} from './ElasticSearchAuthTransport';\n\nexport type { ElasticSearchClientOptions };\n\n/**\n * Search query that the elasticsearch engine understands.\n * @public\n */\nexport type ElasticSearchConcreteQuery = {\n documentTypes?: string[];\n elasticSearchQuery: Object;\n pageSize: number;\n};\n\n/**\n * Options available for the Elasticsearch specific query translator.\n * @public\n */\nexport type ElasticSearchQueryTranslatorOptions = {\n highlightOptions?: ElasticSearchHighlightConfig;\n queryOptions?: ElasticSearchQueryConfig;\n};\n\n/**\n * Elasticsearch specific query translator.\n * @public\n */\nexport type ElasticSearchQueryTranslator = (\n query: SearchQuery,\n options?: ElasticSearchQueryTranslatorOptions,\n) => ElasticSearchConcreteQuery;\n\n/**\n * Options for instantiate ElasticSearchSearchEngine\n * @public\n */\nexport type ElasticSearchOptions = {\n logger: LoggerService;\n config: Config;\n aliasPostfix?: string;\n indexPrefix?: string;\n translator?: ElasticSearchQueryTranslator;\n /**\n * An optional authentication provider for dynamic authentication.\n *\n * @remarks\n *\n * When provided, this auth provider will be used to inject authentication\n * headers into each request to Elasticsearch/OpenSearch. This is useful\n * for scenarios requiring dynamic tokens (e.g., bearer tokens with rotation).\n *\n * The auth provider takes precedence over static authentication configured\n * in app-config.yaml.\n */\n authProvider?: ElasticSearchAuthProvider;\n};\n\n/**\n * @public\n */\nexport type ElasticSearchHighlightOptions = {\n fragmentDelimiter?: string;\n fragmentSize?: number;\n numFragments?: number;\n};\n\n/**\n * @public\n */\nexport type ElasticSearchQueryConfig = {\n fuzziness?: string | number;\n prefixLength?: number;\n};\n\n/**\n * @public\n */\nexport type ElasticSearchHighlightConfig = {\n fragmentDelimiter: string;\n fragmentSize: number;\n numFragments: number;\n preTag: string;\n postTag: string;\n};\n\ntype ElasticSearchResult = {\n _index: string;\n _type: string;\n _score: number;\n _source: IndexableDocument;\n highlight?: {\n [field: string]: string[];\n };\n};\n\nfunction isBlank(str: string) {\n return (isEmpty(str) && !isNumber(str)) || nan(str);\n}\n\nconst DEFAULT_INDEXER_BATCH_SIZE = 1000;\n\n/**\n * @public\n */\nexport class ElasticSearchSearchEngine implements SearchEngine {\n private readonly elasticSearchClientWrapper: ElasticSearchClientWrapper;\n private readonly highlightOptions: ElasticSearchHighlightConfig;\n private readonly queryOptions?: ElasticSearchQueryConfig;\n\n private readonly elasticSearchClientOptions: ElasticSearchClientOptions;\n private readonly aliasPostfix: string;\n private readonly indexPrefix: string;\n private readonly logger: LoggerService;\n private readonly batchSize: number;\n private readonly batchKeyField?: string;\n\n constructor(\n elasticSearchClientOptions: ElasticSearchClientOptions,\n aliasPostfix: string,\n indexPrefix: string,\n logger: LoggerService,\n batchSize: number,\n batchKeyField?: string,\n highlightOptions?: ElasticSearchHighlightOptions,\n queryOptions?: ElasticSearchQueryConfig,\n ) {\n this.elasticSearchClientOptions = elasticSearchClientOptions;\n this.aliasPostfix = aliasPostfix;\n this.indexPrefix = indexPrefix;\n this.logger = logger;\n this.batchSize = batchSize;\n this.batchKeyField = batchKeyField;\n this.elasticSearchClientWrapper =\n ElasticSearchClientWrapper.fromClientOptions(elasticSearchClientOptions);\n const uuidTag = uuid();\n this.highlightOptions = {\n preTag: `<${uuidTag}>`,\n postTag: `</${uuidTag}>`,\n fragmentSize: 1000,\n numFragments: 1,\n fragmentDelimiter: ' ... ',\n ...highlightOptions,\n };\n this.queryOptions = queryOptions;\n }\n\n static async fromConfig(options: ElasticSearchOptions) {\n const {\n logger,\n config,\n aliasPostfix = `search`,\n indexPrefix = ``,\n translator,\n authProvider,\n } = options;\n const credentialProvider = DefaultAwsCredentialsManager.fromConfig(config);\n const clientOptions = await this.createElasticSearchClientOptions(\n await credentialProvider?.getCredentialProvider(),\n config.getConfig('search.elasticsearch'),\n authProvider,\n );\n if (clientOptions.provider === 'elastic') {\n logger.info('Initializing Elastic.co ElasticSearch search engine.');\n } else if (clientOptions.provider === 'aws') {\n logger.info('Initializing AWS OpenSearch search engine.');\n } else if (clientOptions.provider === 'opensearch') {\n logger.info('Initializing OpenSearch search engine.');\n } else {\n logger.info('Initializing ElasticSearch search engine.');\n }\n\n const engine = new ElasticSearchSearchEngine(\n clientOptions,\n aliasPostfix,\n indexPrefix,\n logger,\n config.getOptionalNumber('search.elasticsearch.batchSize') ??\n DEFAULT_INDEXER_BATCH_SIZE,\n config.getOptionalString('search.elasticsearch.batchKeyField'),\n config.getOptional<ElasticSearchHighlightOptions>(\n 'search.elasticsearch.highlightOptions',\n ),\n config.getOptional<ElasticSearchQueryConfig>(\n 'search.elasticsearch.queryOptions',\n ),\n );\n\n for (const indexTemplate of this.readIndexTemplateConfig(\n config.getConfig('search.elasticsearch'),\n )) {\n await engine.setIndexTemplate(indexTemplate);\n }\n\n if (translator) {\n await engine.setTranslator(translator);\n }\n\n return engine;\n }\n\n /**\n * Create a custom search client from the derived search client configuration.\n * This need not be the same client that the engine uses internally.\n *\n * @example Instantiate an instance of an Elasticsearch client.\n *\n * ```ts\n * import { isOpenSearchCompatible } from '@backstage/plugin-search-backend-module-elasticsearch';\n * import { Client } from '@elastic/elasticsearch';\n *\n * const client = searchEngine.newClient<Client>(options => {\n * // This type guard ensures options are compatible with either OpenSearch\n * // or Elasticsearch client constructors.\n * if (!isOpenSearchCompatible(options)) {\n * return new Client(options);\n * }\n * throw new Error('Incompatible options provided');\n * });\n * ```\n */\n newClient<T>(create: (options: ElasticSearchClientOptions) => T): T {\n return create(this.elasticSearchClientOptions);\n }\n\n protected translator(\n query: SearchQuery,\n options?: ElasticSearchQueryTranslatorOptions,\n ): ElasticSearchConcreteQuery {\n const { term, filters = {}, types, pageCursor } = query;\n\n const filter = Object.entries(filters)\n .filter(([_, value]) => Boolean(value))\n .map(([key, value]: [key: string, value: any]) => {\n if (['string', 'number', 'boolean'].includes(typeof value)) {\n // Use exact matching for string datatype fields\n const keyword = typeof value === 'string' ? `${key}.keyword` : key;\n return esb.matchQuery(keyword, value.toString());\n }\n if (Array.isArray(value)) {\n return esb\n .boolQuery()\n .should(value.map(it => esb.matchQuery(key, it.toString())));\n }\n this.logger.error('Failed to query, unrecognized filter type', {\n key,\n value,\n });\n throw new Error(\n 'Failed to add filters to query. Unrecognized filter type',\n );\n });\n\n const esbQueries = [];\n // https://regex101.com/r/Lr0MqS/1\n const phraseTerms = term.match(/\"[^\"]*\"/g);\n\n if (isBlank(term)) {\n const esbQuery = esb.matchAllQuery();\n esbQueries.push(esbQuery);\n } else if (phraseTerms && phraseTerms.length > 0) {\n let restTerm = term;\n for (const phraseTerm of phraseTerms) {\n restTerm = restTerm.replace(phraseTerm, '');\n const esbPhraseQuery = esb\n .multiMatchQuery(['*'], phraseTerm.replace(/\"/g, ''))\n .type('phrase');\n esbQueries.push(esbPhraseQuery);\n }\n if (restTerm?.length > 0) {\n const esbRestQuery = esb\n .multiMatchQuery(['*'], restTerm.trim())\n .fuzziness(options?.queryOptions?.fuzziness ?? 'auto')\n .prefixLength(options?.queryOptions?.prefixLength ?? 0);\n esbQueries.push(esbRestQuery);\n }\n } else {\n const esbQuery = esb\n .multiMatchQuery(['*'], term)\n .fuzziness(options?.queryOptions?.fuzziness ?? 'auto')\n .prefixLength(options?.queryOptions?.prefixLength ?? 0);\n esbQueries.push(esbQuery);\n }\n\n const pageSize = query.pageLimit || 25;\n const { page } = decodePageCursor(pageCursor);\n\n let esbRequestBodySearch = esb\n .requestBodySearch()\n .query(esb.boolQuery().filter(filter).should(esbQueries))\n .from(page * pageSize)\n .size(pageSize);\n\n if (options?.highlightOptions) {\n esbRequestBodySearch = esbRequestBodySearch.highlight(\n esb\n .highlight('*')\n .numberOfFragments(options.highlightOptions.numFragments as number)\n .fragmentSize(options.highlightOptions.fragmentSize as number)\n .preTags(options.highlightOptions.preTag)\n .postTags(options.highlightOptions.postTag),\n );\n }\n\n return {\n elasticSearchQuery: esbRequestBodySearch.toJSON(),\n documentTypes: types,\n pageSize,\n };\n }\n\n setTranslator(translator: ElasticSearchQueryTranslator) {\n this.translator = translator;\n }\n\n async setIndexTemplate(template: ElasticSearchCustomIndexTemplate) {\n try {\n await this.elasticSearchClientWrapper.putIndexTemplate(template);\n this.logger.info('Custom index template set');\n } catch (error) {\n this.logger.error(`Unable to set custom index template: ${error}`);\n }\n }\n\n async getIndexer(type: string) {\n const alias = this.constructSearchAlias(type);\n const indexerLogger = this.logger.child({ documentType: type });\n\n const indexer = new ElasticSearchSearchEngineIndexer({\n type,\n indexPrefix: this.indexPrefix,\n indexSeparator: this.indexSeparator,\n alias,\n elasticSearchClientWrapper: this.elasticSearchClientWrapper,\n logger: indexerLogger,\n batchSize: this.batchSize,\n batchKeyField: this.batchKeyField,\n skipRefresh:\n (\n this\n .elasticSearchClientOptions as OpenSearchElasticSearchClientOptions\n )?.service === 'aoss',\n });\n\n // Attempt cleanup upon failure.\n // todo(@backstage/search-maintainers): Consider introducing a more\n // formal mechanism for handling such errors in BatchSearchEngineIndexer and\n // replacing this handler with it. See: #17291\n indexer.on('error', async e => {\n indexerLogger.error(`Failed to index documents for type ${type}`, e);\n let cleanupError: Error | undefined;\n\n // In some cases, a failure may have occurred before the indexer was able\n // to complete initialization. Try up to 5 times to remove the dangling\n // index.\n await new Promise<void>(async done => {\n const maxAttempts = 5;\n let attempts = 0;\n\n while (attempts < maxAttempts) {\n try {\n await this.elasticSearchClientWrapper.deleteIndex({\n index: indexer.indexName,\n });\n\n attempts = maxAttempts;\n cleanupError = undefined;\n done();\n } catch (err) {\n cleanupError = err;\n }\n\n // Wait 1 second between retries.\n await new Promise(okay => setTimeout(okay, 1000));\n\n attempts++;\n }\n done();\n });\n\n if (cleanupError) {\n indexerLogger.error(\n `Unable to clean up elastic index ${indexer.indexName}: ${cleanupError}`,\n );\n } else {\n indexerLogger.info(\n `Removed partial, failed index ${indexer.indexName}`,\n );\n }\n });\n\n return indexer;\n }\n\n async query(query: SearchQuery): Promise<IndexableResultSet> {\n const { elasticSearchQuery, documentTypes, pageSize } = this.translator(\n query,\n {\n highlightOptions: this.highlightOptions,\n queryOptions: this.queryOptions,\n },\n );\n if (documentTypes && documentTypes.length === 0) {\n return {\n results: [],\n nextPageCursor: undefined,\n previousPageCursor: undefined,\n numberOfResults: undefined,\n };\n }\n const queryIndices = documentTypes\n ? documentTypes.map(it => this.constructSearchAlias(it))\n : this.constructSearchAlias('*');\n try {\n const result = await this.elasticSearchClientWrapper.search({\n index: queryIndices,\n body: elasticSearchQuery,\n });\n const { page } = decodePageCursor(query.pageCursor);\n const hasNextPage = result.body.hits.total.value > (page + 1) * pageSize;\n const hasPreviousPage = page > 0;\n const nextPageCursor = hasNextPage\n ? encodePageCursor({ page: page + 1 })\n : undefined;\n const previousPageCursor = hasPreviousPage\n ? encodePageCursor({ page: page - 1 })\n : undefined;\n\n return {\n results: result.body.hits.hits.map(\n (d: ElasticSearchResult, index: number) => {\n const resultItem: IndexableResult = {\n type: this.getTypeFromIndex(d._index),\n document: d._source,\n rank: pageSize * page + index + 1,\n };\n\n if (d.highlight) {\n resultItem.highlight = {\n preTag: this.highlightOptions.preTag as string,\n postTag: this.highlightOptions.postTag as string,\n fields: Object.fromEntries(\n Object.entries(d.highlight).map(([field, fragments]) => [\n field,\n fragments.join(this.highlightOptions.fragmentDelimiter),\n ]),\n ),\n };\n }\n\n return resultItem;\n },\n ),\n nextPageCursor,\n previousPageCursor,\n numberOfResults: result.body.hits.total.value,\n };\n } catch (error) {\n if (error.meta?.body?.error?.type === 'index_not_found_exception') {\n throw new MissingIndexError(\n `Missing index for ${queryIndices}. This means there are no documents to search through.`,\n error,\n );\n }\n this.logger.error(\n `Failed to query documents for indices ${queryIndices}`,\n error,\n );\n return Promise.reject({ results: [] });\n }\n }\n\n private readonly indexSeparator = '-index__';\n\n private getTypeFromIndex(index: string) {\n return index\n .substring(this.indexPrefix.length)\n .split(this.indexSeparator)[0];\n }\n\n private constructSearchAlias(type: string) {\n const postFix = this.aliasPostfix ? `__${this.aliasPostfix}` : '';\n return `${this.indexPrefix}${type}${postFix}`;\n }\n\n private static async createElasticSearchClientOptions(\n credentialProvider: AwsCredentialProvider,\n config?: Config,\n authProvider?: ElasticSearchAuthProvider,\n ): Promise<ElasticSearchClientOptions> {\n if (!config) {\n throw new Error('No elastic search config found');\n }\n const clientOptionsConfig = config.getOptionalConfig('clientOptions');\n const sslConfig = clientOptionsConfig?.getOptionalConfig('ssl');\n\n if (config.getOptionalString('provider') === 'elastic') {\n const authConfig = authProvider ? undefined : config.getConfig('auth');\n return {\n provider: 'elastic',\n cloud: {\n id: config.getString('cloudId'),\n },\n // When using authProvider, we inject auth via custom Transport, not static auth\n ...(authConfig\n ? {\n auth: {\n username: authConfig.getString('username'),\n password: authConfig.getString('password'),\n },\n }\n : {}),\n ...(authProvider\n ? {\n Transport: createElasticSearchAuthTransport(authProvider),\n }\n : {}),\n ...(sslConfig\n ? {\n ssl: {\n rejectUnauthorized:\n sslConfig?.getOptionalBoolean('rejectUnauthorized'),\n },\n }\n : {}),\n };\n }\n if (config.getOptionalString('provider') === 'aws') {\n // AWS provider uses SigV4 signing; authProvider is not applicable here\n if (authProvider) {\n throw new Error(\n 'Custom auth provider is not supported with AWS provider. AWS uses SigV4 signing.',\n );\n }\n const requestSigner = new RequestSigner(config.getString('node'));\n const service =\n config.getOptionalString('service') ?? requestSigner.service;\n if (service !== 'es' && service !== 'aoss')\n throw new Error(`Unrecognized service type: ${service}`);\n return {\n provider: 'aws',\n node: config.getString('node'),\n region: config.getOptionalString('region'),\n service,\n ...(sslConfig\n ? {\n ssl: {\n rejectUnauthorized:\n sslConfig?.getOptionalBoolean('rejectUnauthorized'),\n },\n }\n : {}),\n ...AwsSigv4Signer({\n region: config.getOptionalString('region') ?? requestSigner.region, // for backwards compatibility\n service: service,\n getCredentials: async () =>\n await credentialProvider.sdkCredentialProvider(),\n }),\n };\n }\n if (config.getOptionalString('provider') === 'opensearch') {\n const authConfig = authProvider\n ? undefined\n : config.getOptionalConfig('auth');\n return {\n provider: 'opensearch',\n node: config.getString('node'),\n // When using authProvider, we inject auth via custom Transport, not static auth\n ...(authConfig\n ? {\n auth: {\n username: authConfig.getString('username'),\n password: authConfig.getString('password'),\n },\n }\n : {}),\n ...(authProvider\n ? {\n Transport: createOpenSearchAuthTransport(authProvider),\n }\n : {}),\n ...(sslConfig\n ? {\n ssl: {\n rejectUnauthorized:\n sslConfig?.getOptionalBoolean('rejectUnauthorized'),\n },\n }\n : {}),\n };\n }\n // Default provider (standard Elasticsearch)\n const authConfig = authProvider\n ? undefined\n : config.getOptionalConfig('auth');\n const auth =\n authConfig &&\n (authConfig.has('apiKey')\n ? {\n apiKey: authConfig.getString('apiKey'),\n }\n : {\n username: authConfig.getString('username'),\n password: authConfig.getString('password'),\n });\n return {\n node: config.getString('node'),\n ...(auth ? { auth } : {}),\n ...(authProvider\n ? {\n Transport: createElasticSearchAuthTransport(authProvider),\n }\n : {}),\n ...(sslConfig\n ? {\n ssl: {\n rejectUnauthorized:\n sslConfig?.getOptionalBoolean('rejectUnauthorized'),\n },\n }\n : {}),\n };\n }\n\n private static readIndexTemplateConfig(\n config: Config,\n ): ElasticSearchCustomIndexTemplate[] {\n return (\n config.getOptionalConfigArray('indexTemplates')?.map(templateConfig => {\n const bodyConfig = templateConfig.getConfig('body');\n return {\n name: templateConfig.getString('name'),\n body: {\n index_patterns: bodyConfig.getStringArray('index_patterns'),\n composed_of: bodyConfig.getOptionalStringArray('composed_of'),\n template: bodyConfig.getOptionalConfig('template')?.get(),\n },\n };\n }) ?? []\n );\n }\n}\n\n/**\n * @public\n */\nexport function decodePageCursor(pageCursor?: string): { page: number } {\n if (!pageCursor) {\n return { page: 0 };\n }\n\n return {\n page: Number(Buffer.from(pageCursor, 'base64').toString('utf-8')),\n };\n}\n\nexport function encodePageCursor({ page }: { page: number }): string {\n return Buffer.from(`${page}`, 'utf-8').toString('base64');\n}\n"],"names":["isEmpty","isNumber","nan","ElasticSearchClientWrapper","uuid","DefaultAwsCredentialsManager","esb","ElasticSearchSearchEngineIndexer","MissingIndexError","authConfig","createElasticSearchAuthTransport","RequestSigner","AwsSigv4Signer","createOpenSearchAuthTransport"],"mappings":";;;;;;;;;;;;;;;;;AA8IA,SAAS,QAAQ,GAAA,EAAa;AAC5B,EAAA,OAAQA,cAAA,CAAQ,GAAG,CAAA,IAAK,CAACC,gBAAS,GAAG,CAAA,IAAMC,aAAI,GAAG,CAAA;AACpD;AAEA,MAAM,0BAAA,GAA6B,GAAA;AAK5B,MAAM,yBAAA,CAAkD;AAAA,EAC5C,0BAAA;AAAA,EACA,gBAAA;AAAA,EACA,YAAA;AAAA,EAEA,0BAAA;AAAA,EACA,YAAA;AAAA,EACA,WAAA;AAAA,EACA,MAAA;AAAA,EACA,SAAA;AAAA,EACA,aAAA;AAAA,EAEjB,WAAA,CACE,4BACA,YAAA,EACA,WAAA,EACA,QACA,SAAA,EACA,aAAA,EACA,kBACA,YAAA,EACA;AACA,IAAA,IAAA,CAAK,0BAAA,GAA6B,0BAAA;AAClC,IAAA,IAAA,CAAK,YAAA,GAAe,YAAA;AACpB,IAAA,IAAA,CAAK,WAAA,GAAc,WAAA;AACnB,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,SAAA,GAAY,SAAA;AACjB,IAAA,IAAA,CAAK,aAAA,GAAgB,aAAA;AACrB,IAAA,IAAA,CAAK,0BAAA,GACHC,qDAAA,CAA2B,iBAAA,CAAkB,0BAA0B,CAAA;AACzE,IAAA,MAAM,UAAUC,sBAAA,EAAK;AACrB,IAAA,IAAA,CAAK,gBAAA,GAAmB;AAAA,MACtB,MAAA,EAAQ,IAAI,OAAO,CAAA,CAAA,CAAA;AAAA,MACnB,OAAA,EAAS,KAAK,OAAO,CAAA,CAAA,CAAA;AAAA,MACrB,YAAA,EAAc,GAAA;AAAA,MACd,YAAA,EAAc,CAAA;AAAA,MACd,iBAAA,EAAmB,OAAA;AAAA,MACnB,GAAG;AAAA,KACL;AACA,IAAA,IAAA,CAAK,YAAA,GAAe,YAAA;AAAA,EACtB;AAAA,EAEA,aAAa,WAAW,OAAA,EAA+B;AACrD,IAAA,MAAM;AAAA,MACJ,MAAA;AAAA,MACA,MAAA;AAAA,MACA,YAAA,GAAe,CAAA,MAAA,CAAA;AAAA,MACf,WAAA,GAAc,CAAA,CAAA;AAAA,MACd,UAAA;AAAA,MACA;AAAA,KACF,GAAI,OAAA;AACJ,IAAA,MAAM,kBAAA,GAAqBC,+CAAA,CAA6B,UAAA,CAAW,MAAM,CAAA;AACzE,IAAA,MAAM,aAAA,GAAgB,MAAM,IAAA,CAAK,gCAAA;AAAA,MAC/B,MAAM,oBAAoB,qBAAA,EAAsB;AAAA,MAChD,MAAA,CAAO,UAAU,sBAAsB,CAAA;AAAA,MACvC;AAAA,KACF;AACA,IAAA,IAAI,aAAA,CAAc,aAAa,SAAA,EAAW;AACxC,MAAA,MAAA,CAAO,KAAK,sDAAsD,CAAA;AAAA,IACpE,CAAA,MAAA,IAAW,aAAA,CAAc,QAAA,KAAa,KAAA,EAAO;AAC3C,MAAA,MAAA,CAAO,KAAK,4CAA4C,CAAA;AAAA,IAC1D,CAAA,MAAA,IAAW,aAAA,CAAc,QAAA,KAAa,YAAA,EAAc;AAClD,MAAA,MAAA,CAAO,KAAK,wCAAwC,CAAA;AAAA,IACtD,CAAA,MAAO;AACL,MAAA,MAAA,CAAO,KAAK,2CAA2C,CAAA;AAAA,IACzD;AAEA,IAAA,MAAM,SAAS,IAAI,yBAAA;AAAA,MACjB,aAAA;AAAA,MACA,YAAA;AAAA,MACA,WAAA;AAAA,MACA,MAAA;AAAA,MACA,MAAA,CAAO,iBAAA,CAAkB,gCAAgC,CAAA,IACvD,0BAAA;AAAA,MACF,MAAA,CAAO,kBAAkB,oCAAoC,CAAA;AAAA,MAC7D,MAAA,CAAO,WAAA;AAAA,QACL;AAAA,OACF;AAAA,MACA,MAAA,CAAO,WAAA;AAAA,QACL;AAAA;AACF,KACF;AAEA,IAAA,KAAA,MAAW,iBAAiB,IAAA,CAAK,uBAAA;AAAA,MAC/B,MAAA,CAAO,UAAU,sBAAsB;AAAA,KACzC,EAAG;AACD,MAAA,MAAM,MAAA,CAAO,iBAAiB,aAAa,CAAA;AAAA,IAC7C;AAEA,IAAA,IAAI,UAAA,EAAY;AACd,MAAA,MAAM,MAAA,CAAO,cAAc,UAAU,CAAA;AAAA,IACvC;AAEA,IAAA,OAAO,MAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBA,UAAa,MAAA,EAAuD;AAClE,IAAA,OAAO,MAAA,CAAO,KAAK,0BAA0B,CAAA;AAAA,EAC/C;AAAA,EAEU,UAAA,CACR,OACA,OAAA,EAC4B;AAC5B,IAAA,MAAM,EAAE,IAAA,EAAM,OAAA,GAAU,EAAC,EAAG,KAAA,EAAO,YAAW,GAAI,KAAA;AAElD,IAAA,MAAM,MAAA,GAAS,OAAO,OAAA,CAAQ,OAAO,EAClC,MAAA,CAAO,CAAC,CAAC,CAAA,EAAG,KAAK,MAAM,OAAA,CAAQ,KAAK,CAAC,CAAA,CACrC,GAAA,CAAI,CAAC,CAAC,GAAA,EAAK,KAAK,CAAA,KAAiC;AAChD,MAAA,IAAI,CAAC,UAAU,QAAA,EAAU,SAAS,EAAE,QAAA,CAAS,OAAO,KAAK,CAAA,EAAG;AAE1D,QAAA,MAAM,UAAU,OAAO,KAAA,KAAU,QAAA,GAAW,CAAA,EAAG,GAAG,CAAA,QAAA,CAAA,GAAa,GAAA;AAC/D,QAAA,OAAOC,oBAAA,CAAI,UAAA,CAAW,OAAA,EAAS,KAAA,CAAM,UAAU,CAAA;AAAA,MACjD;AACA,MAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACxB,QAAA,OAAOA,oBAAA,CACJ,SAAA,EAAU,CACV,MAAA,CAAO,MAAM,GAAA,CAAI,CAAA,EAAA,KAAMA,oBAAA,CAAI,UAAA,CAAW,GAAA,EAAK,EAAA,CAAG,QAAA,EAAU,CAAC,CAAC,CAAA;AAAA,MAC/D;AACA,MAAA,IAAA,CAAK,MAAA,CAAO,MAAM,2CAAA,EAA6C;AAAA,QAC7D,GAAA;AAAA,QACA;AAAA,OACD,CAAA;AACD,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF,CAAC,CAAA;AAEH,IAAA,MAAM,aAAa,EAAC;AAEpB,IAAA,MAAM,WAAA,GAAc,IAAA,CAAK,KAAA,CAAM,UAAU,CAAA;AAEzC,IAAA,IAAI,OAAA,CAAQ,IAAI,CAAA,EAAG;AACjB,MAAA,MAAM,QAAA,GAAWA,qBAAI,aAAA,EAAc;AACnC,MAAA,UAAA,CAAW,KAAK,QAAQ,CAAA;AAAA,IAC1B,CAAA,MAAA,IAAW,WAAA,IAAe,WAAA,CAAY,MAAA,GAAS,CAAA,EAAG;AAChD,MAAA,IAAI,QAAA,GAAW,IAAA;AACf,MAAA,KAAA,MAAW,cAAc,WAAA,EAAa;AACpC,QAAA,QAAA,GAAW,QAAA,CAAS,OAAA,CAAQ,UAAA,EAAY,EAAE,CAAA;AAC1C,QAAA,MAAM,cAAA,GAAiBA,oBAAA,CACpB,eAAA,CAAgB,CAAC,GAAG,CAAA,EAAG,UAAA,CAAW,OAAA,CAAQ,IAAA,EAAM,EAAE,CAAC,CAAA,CACnD,KAAK,QAAQ,CAAA;AAChB,QAAA,UAAA,CAAW,KAAK,cAAc,CAAA;AAAA,MAChC;AACA,MAAA,IAAI,QAAA,EAAU,SAAS,CAAA,EAAG;AACxB,QAAA,MAAM,YAAA,GAAeA,qBAClB,eAAA,CAAgB,CAAC,GAAG,CAAA,EAAG,QAAA,CAAS,MAAM,CAAA,CACtC,UAAU,OAAA,EAAS,YAAA,EAAc,aAAa,MAAM,CAAA,CACpD,aAAa,OAAA,EAAS,YAAA,EAAc,gBAAgB,CAAC,CAAA;AACxD,QAAA,UAAA,CAAW,KAAK,YAAY,CAAA;AAAA,MAC9B;AAAA,IACF,CAAA,MAAO;AACL,MAAA,MAAM,WAAWA,oBAAA,CACd,eAAA,CAAgB,CAAC,GAAG,CAAA,EAAG,IAAI,CAAA,CAC3B,SAAA,CAAU,OAAA,EAAS,YAAA,EAAc,aAAa,MAAM,CAAA,CACpD,aAAa,OAAA,EAAS,YAAA,EAAc,gBAAgB,CAAC,CAAA;AACxD,MAAA,UAAA,CAAW,KAAK,QAAQ,CAAA;AAAA,IAC1B;AAEA,IAAA,MAAM,QAAA,GAAW,MAAM,SAAA,IAAa,EAAA;AACpC,IAAA,MAAM,EAAE,IAAA,EAAK,GAAI,gBAAA,CAAiB,UAAU,CAAA;AAE5C,IAAA,IAAI,oBAAA,GAAuBA,qBACxB,iBAAA,EAAkB,CAClB,MAAMA,oBAAA,CAAI,SAAA,GAAY,MAAA,CAAO,MAAM,EAAE,MAAA,CAAO,UAAU,CAAC,CAAA,CACvD,IAAA,CAAK,OAAO,QAAQ,CAAA,CACpB,KAAK,QAAQ,CAAA;AAEhB,IAAA,IAAI,SAAS,gBAAA,EAAkB;AAC7B,MAAA,oBAAA,GAAuB,oBAAA,CAAqB,SAAA;AAAA,QAC1CA,oBAAA,CACG,UAAU,GAAG,CAAA,CACb,kBAAkB,OAAA,CAAQ,gBAAA,CAAiB,YAAsB,CAAA,CACjE,YAAA,CAAa,OAAA,CAAQ,iBAAiB,YAAsB,CAAA,CAC5D,QAAQ,OAAA,CAAQ,gBAAA,CAAiB,MAAM,CAAA,CACvC,QAAA,CAAS,OAAA,CAAQ,gBAAA,CAAiB,OAAO;AAAA,OAC9C;AAAA,IACF;AAEA,IAAA,OAAO;AAAA,MACL,kBAAA,EAAoB,qBAAqB,MAAA,EAAO;AAAA,MAChD,aAAA,EAAe,KAAA;AAAA,MACf;AAAA,KACF;AAAA,EACF;AAAA,EAEA,cAAc,UAAA,EAA0C;AACtD,IAAA,IAAA,CAAK,UAAA,GAAa,UAAA;AAAA,EACpB;AAAA,EAEA,MAAM,iBAAiB,QAAA,EAA4C;AACjE,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,CAAK,0BAAA,CAA2B,gBAAA,CAAiB,QAAQ,CAAA;AAC/D,MAAA,IAAA,CAAK,MAAA,CAAO,KAAK,2BAA2B,CAAA;AAAA,IAC9C,SAAS,KAAA,EAAO;AACd,MAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,CAAA,qCAAA,EAAwC,KAAK,CAAA,CAAE,CAAA;AAAA,IACnE;AAAA,EACF;AAAA,EAEA,MAAM,WAAW,IAAA,EAAc;AAC7B,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,oBAAA,CAAqB,IAAI,CAAA;AAC5C,IAAA,MAAM,gBAAgB,IAAA,CAAK,MAAA,CAAO,MAAM,EAAE,YAAA,EAAc,MAAM,CAAA;AAE9D,IAAA,MAAM,OAAA,GAAU,IAAIC,iEAAA,CAAiC;AAAA,MACnD,IAAA;AAAA,MACA,aAAa,IAAA,CAAK,WAAA;AAAA,MAClB,gBAAgB,IAAA,CAAK,cAAA;AAAA,MACrB,KAAA;AAAA,MACA,4BAA4B,IAAA,CAAK,0BAAA;AAAA,MACjC,MAAA,EAAQ,aAAA;AAAA,MACR,WAAW,IAAA,CAAK,SAAA;AAAA,MAChB,eAAe,IAAA,CAAK,aAAA;AAAA,MACpB,WAAA,EAEI,IAAA,CACG,0BAAA,EACF,OAAA,KAAY;AAAA,KAClB,CAAA;AAMD,IAAA,OAAA,CAAQ,EAAA,CAAG,OAAA,EAAS,OAAM,CAAA,KAAK;AAC7B,MAAA,aAAA,CAAc,KAAA,CAAM,CAAA,mCAAA,EAAsC,IAAI,CAAA,CAAA,EAAI,CAAC,CAAA;AACnE,MAAA,IAAI,YAAA;AAKJ,MAAA,MAAM,IAAI,OAAA,CAAc,OAAM,IAAA,KAAQ;AACpC,QAAA,MAAM,WAAA,GAAc,CAAA;AACpB,QAAA,IAAI,QAAA,GAAW,CAAA;AAEf,QAAA,OAAO,WAAW,WAAA,EAAa;AAC7B,UAAA,IAAI;AACF,YAAA,MAAM,IAAA,CAAK,2BAA2B,WAAA,CAAY;AAAA,cAChD,OAAO,OAAA,CAAQ;AAAA,aAChB,CAAA;AAED,YAAA,QAAA,GAAW,WAAA;AACX,YAAA,YAAA,GAAe,KAAA,CAAA;AACf,YAAA,IAAA,EAAK;AAAA,UACP,SAAS,GAAA,EAAK;AACZ,YAAA,YAAA,GAAe,GAAA;AAAA,UACjB;AAGA,UAAA,MAAM,IAAI,OAAA,CAAQ,CAAA,IAAA,KAAQ,UAAA,CAAW,IAAA,EAAM,GAAI,CAAC,CAAA;AAEhD,UAAA,QAAA,EAAA;AAAA,QACF;AACA,QAAA,IAAA,EAAK;AAAA,MACP,CAAC,CAAA;AAED,MAAA,IAAI,YAAA,EAAc;AAChB,QAAA,aAAA,CAAc,KAAA;AAAA,UACZ,CAAA,iCAAA,EAAoC,OAAA,CAAQ,SAAS,CAAA,EAAA,EAAK,YAAY,CAAA;AAAA,SACxE;AAAA,MACF,CAAA,MAAO;AACL,QAAA,aAAA,CAAc,IAAA;AAAA,UACZ,CAAA,8BAAA,EAAiC,QAAQ,SAAS,CAAA;AAAA,SACpD;AAAA,MACF;AAAA,IACF,CAAC,CAAA;AAED,IAAA,OAAO,OAAA;AAAA,EACT;AAAA,EAEA,MAAM,MAAM,KAAA,EAAiD;AAC3D,IAAA,MAAM,EAAE,kBAAA,EAAoB,aAAA,EAAe,QAAA,KAAa,IAAA,CAAK,UAAA;AAAA,MAC3D,KAAA;AAAA,MACA;AAAA,QACE,kBAAkB,IAAA,CAAK,gBAAA;AAAA,QACvB,cAAc,IAAA,CAAK;AAAA;AACrB,KACF;AACA,IAAA,IAAI,aAAA,IAAiB,aAAA,CAAc,MAAA,KAAW,CAAA,EAAG;AAC/C,MAAA,OAAO;AAAA,QACL,SAAS,EAAC;AAAA,QACV,cAAA,EAAgB,MAAA;AAAA,QAChB,kBAAA,EAAoB,MAAA;AAAA,QACpB,eAAA,EAAiB;AAAA,OACnB;AAAA,IACF;AACA,IAAA,MAAM,YAAA,GAAe,aAAA,GACjB,aAAA,CAAc,GAAA,CAAI,CAAA,EAAA,KAAM,IAAA,CAAK,oBAAA,CAAqB,EAAE,CAAC,CAAA,GACrD,IAAA,CAAK,oBAAA,CAAqB,GAAG,CAAA;AACjC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,0BAAA,CAA2B,MAAA,CAAO;AAAA,QAC1D,KAAA,EAAO,YAAA;AAAA,QACP,IAAA,EAAM;AAAA,OACP,CAAA;AACD,MAAA,MAAM,EAAE,IAAA,EAAK,GAAI,gBAAA,CAAiB,MAAM,UAAU,CAAA;AAClD,MAAA,MAAM,cAAc,MAAA,CAAO,IAAA,CAAK,KAAK,KAAA,CAAM,KAAA,GAAA,CAAS,OAAO,CAAA,IAAK,QAAA;AAChE,MAAA,MAAM,kBAAkB,IAAA,GAAO,CAAA;AAC/B,MAAA,MAAM,cAAA,GAAiB,cACnB,gBAAA,CAAiB,EAAE,MAAM,IAAA,GAAO,CAAA,EAAG,CAAA,GACnC,KAAA,CAAA;AACJ,MAAA,MAAM,kBAAA,GAAqB,kBACvB,gBAAA,CAAiB,EAAE,MAAM,IAAA,GAAO,CAAA,EAAG,CAAA,GACnC,KAAA,CAAA;AAEJ,MAAA,OAAO;AAAA,QACL,OAAA,EAAS,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,IAAA,CAAK,GAAA;AAAA,UAC7B,CAAC,GAAwB,KAAA,KAAkB;AACzC,YAAA,MAAM,UAAA,GAA8B;AAAA,cAClC,IAAA,EAAM,IAAA,CAAK,gBAAA,CAAiB,CAAA,CAAE,MAAM,CAAA;AAAA,cACpC,UAAU,CAAA,CAAE,OAAA;AAAA,cACZ,IAAA,EAAM,QAAA,GAAW,IAAA,GAAO,KAAA,GAAQ;AAAA,aAClC;AAEA,YAAA,IAAI,EAAE,SAAA,EAAW;AACf,cAAA,UAAA,CAAW,SAAA,GAAY;AAAA,gBACrB,MAAA,EAAQ,KAAK,gBAAA,CAAiB,MAAA;AAAA,gBAC9B,OAAA,EAAS,KAAK,gBAAA,CAAiB,OAAA;AAAA,gBAC/B,QAAQ,MAAA,CAAO,WAAA;AAAA,kBACb,MAAA,CAAO,OAAA,CAAQ,CAAA,CAAE,SAAS,CAAA,CAAE,IAAI,CAAC,CAAC,KAAA,EAAO,SAAS,CAAA,KAAM;AAAA,oBACtD,KAAA;AAAA,oBACA,SAAA,CAAU,IAAA,CAAK,IAAA,CAAK,gBAAA,CAAiB,iBAAiB;AAAA,mBACvD;AAAA;AACH,eACF;AAAA,YACF;AAEA,YAAA,OAAO,UAAA;AAAA,UACT;AAAA,SACF;AAAA,QACA,cAAA;AAAA,QACA,kBAAA;AAAA,QACA,eAAA,EAAiB,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,KAAA,CAAM;AAAA,OAC1C;AAAA,IACF,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,KAAA,CAAM,IAAA,EAAM,IAAA,EAAM,KAAA,EAAO,SAAS,2BAAA,EAA6B;AACjE,QAAA,MAAM,IAAIC,yCAAA;AAAA,UACR,qBAAqB,YAAY,CAAA,sDAAA,CAAA;AAAA,UACjC;AAAA,SACF;AAAA,MACF;AACA,MAAA,IAAA,CAAK,MAAA,CAAO,KAAA;AAAA,QACV,yCAAyC,YAAY,CAAA,CAAA;AAAA,QACrD;AAAA,OACF;AACA,MAAA,OAAO,QAAQ,MAAA,CAAO,EAAE,OAAA,EAAS,IAAI,CAAA;AAAA,IACvC;AAAA,EACF;AAAA,EAEiB,cAAA,GAAiB,UAAA;AAAA,EAE1B,iBAAiB,KAAA,EAAe;AACtC,IAAA,OAAO,KAAA,CACJ,SAAA,CAAU,IAAA,CAAK,WAAA,CAAY,MAAM,EACjC,KAAA,CAAM,IAAA,CAAK,cAAc,CAAA,CAAE,CAAC,CAAA;AAAA,EACjC;AAAA,EAEQ,qBAAqB,IAAA,EAAc;AACzC,IAAA,MAAM,UAAU,IAAA,CAAK,YAAA,GAAe,CAAA,EAAA,EAAK,IAAA,CAAK,YAAY,CAAA,CAAA,GAAK,EAAA;AAC/D,IAAA,OAAO,GAAG,IAAA,CAAK,WAAW,CAAA,EAAG,IAAI,GAAG,OAAO,CAAA,CAAA;AAAA,EAC7C;AAAA,EAEA,aAAqB,gCAAA,CACnB,kBAAA,EACA,MAAA,EACA,YAAA,EACqC;AACrC,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,MAAM,IAAI,MAAM,gCAAgC,CAAA;AAAA,IAClD;AACA,IAAA,MAAM,mBAAA,GAAsB,MAAA,CAAO,iBAAA,CAAkB,eAAe,CAAA;AACpE,IAAA,MAAM,SAAA,GAAY,mBAAA,EAAqB,iBAAA,CAAkB,KAAK,CAAA;AAE9D,IAAA,IAAI,MAAA,CAAO,iBAAA,CAAkB,UAAU,CAAA,KAAM,SAAA,EAAW;AACtD,MAAA,MAAMC,WAAAA,GAAa,YAAA,GAAe,MAAA,GAAY,MAAA,CAAO,UAAU,MAAM,CAAA;AACrE,MAAA,OAAO;AAAA,QACL,QAAA,EAAU,SAAA;AAAA,QACV,KAAA,EAAO;AAAA,UACL,EAAA,EAAI,MAAA,CAAO,SAAA,CAAU,SAAS;AAAA,SAChC;AAAA;AAAA,QAEA,GAAIA,WAAAA,GACA;AAAA,UACE,IAAA,EAAM;AAAA,YACJ,QAAA,EAAUA,WAAAA,CAAW,SAAA,CAAU,UAAU,CAAA;AAAA,YACzC,QAAA,EAAUA,WAAAA,CAAW,SAAA,CAAU,UAAU;AAAA;AAC3C,YAEF,EAAC;AAAA,QACL,GAAI,YAAA,GACA;AAAA,UACE,SAAA,EAAWC,4DAAiC,YAAY;AAAA,YAE1D,EAAC;AAAA,QACL,GAAI,SAAA,GACA;AAAA,UACE,GAAA,EAAK;AAAA,YACH,kBAAA,EACE,SAAA,EAAW,kBAAA,CAAmB,oBAAoB;AAAA;AACtD,YAEF;AAAC,OACP;AAAA,IACF;AACA,IAAA,IAAI,MAAA,CAAO,iBAAA,CAAkB,UAAU,CAAA,KAAM,KAAA,EAAO;AAElD,MAAA,IAAI,YAAA,EAAc;AAChB,QAAA,MAAM,IAAI,KAAA;AAAA,UACR;AAAA,SACF;AAAA,MACF;AACA,MAAA,MAAM,gBAAgB,IAAIC,kBAAA,CAAc,MAAA,CAAO,SAAA,CAAU,MAAM,CAAC,CAAA;AAChE,MAAA,MAAM,OAAA,GACJ,MAAA,CAAO,iBAAA,CAAkB,SAAS,KAAK,aAAA,CAAc,OAAA;AACvD,MAAA,IAAI,OAAA,KAAY,QAAQ,OAAA,KAAY,MAAA;AAClC,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,2BAAA,EAA8B,OAAO,CAAA,CAAE,CAAA;AACzD,MAAA,OAAO;AAAA,QACL,QAAA,EAAU,KAAA;AAAA,QACV,IAAA,EAAM,MAAA,CAAO,SAAA,CAAU,MAAM,CAAA;AAAA,QAC7B,MAAA,EAAQ,MAAA,CAAO,iBAAA,CAAkB,QAAQ,CAAA;AAAA,QACzC,OAAA;AAAA,QACA,GAAI,SAAA,GACA;AAAA,UACE,GAAA,EAAK;AAAA,YACH,kBAAA,EACE,SAAA,EAAW,kBAAA,CAAmB,oBAAoB;AAAA;AACtD,YAEF,EAAC;AAAA,QACL,GAAGC,kBAAA,CAAe;AAAA,UAChB,MAAA,EAAQ,MAAA,CAAO,iBAAA,CAAkB,QAAQ,KAAK,aAAA,CAAc,MAAA;AAAA;AAAA,UAC5D,OAAA;AAAA,UACA,cAAA,EAAgB,YACd,MAAM,kBAAA,CAAmB,qBAAA;AAAsB,SAClD;AAAA,OACH;AAAA,IACF;AACA,IAAA,IAAI,MAAA,CAAO,iBAAA,CAAkB,UAAU,CAAA,KAAM,YAAA,EAAc;AACzD,MAAA,MAAMH,WAAAA,GAAa,YAAA,GACf,MAAA,GACA,MAAA,CAAO,kBAAkB,MAAM,CAAA;AACnC,MAAA,OAAO;AAAA,QACL,QAAA,EAAU,YAAA;AAAA,QACV,IAAA,EAAM,MAAA,CAAO,SAAA,CAAU,MAAM,CAAA;AAAA;AAAA,QAE7B,GAAIA,WAAAA,GACA;AAAA,UACE,IAAA,EAAM;AAAA,YACJ,QAAA,EAAUA,WAAAA,CAAW,SAAA,CAAU,UAAU,CAAA;AAAA,YACzC,QAAA,EAAUA,WAAAA,CAAW,SAAA,CAAU,UAAU;AAAA;AAC3C,YAEF,EAAC;AAAA,QACL,GAAI,YAAA,GACA;AAAA,UACE,SAAA,EAAWI,yDAA8B,YAAY;AAAA,YAEvD,EAAC;AAAA,QACL,GAAI,SAAA,GACA;AAAA,UACE,GAAA,EAAK;AAAA,YACH,kBAAA,EACE,SAAA,EAAW,kBAAA,CAAmB,oBAAoB;AAAA;AACtD,YAEF;AAAC,OACP;AAAA,IACF;AAEA,IAAA,MAAM,UAAA,GAAa,YAAA,GACf,MAAA,GACA,MAAA,CAAO,kBAAkB,MAAM,CAAA;AACnC,IAAA,MAAM,IAAA,GACJ,UAAA,KACC,UAAA,CAAW,GAAA,CAAI,QAAQ,CAAA,GACpB;AAAA,MACE,MAAA,EAAQ,UAAA,CAAW,SAAA,CAAU,QAAQ;AAAA,KACvC,GACA;AAAA,MACE,QAAA,EAAU,UAAA,CAAW,SAAA,CAAU,UAAU,CAAA;AAAA,MACzC,QAAA,EAAU,UAAA,CAAW,SAAA,CAAU,UAAU;AAAA,KAC3C,CAAA;AACN,IAAA,OAAO;AAAA,MACL,IAAA,EAAM,MAAA,CAAO,SAAA,CAAU,MAAM,CAAA;AAAA,MAC7B,GAAI,IAAA,GAAO,EAAE,IAAA,KAAS,EAAC;AAAA,MACvB,GAAI,YAAA,GACA;AAAA,QACE,SAAA,EAAWH,4DAAiC,YAAY;AAAA,UAE1D,EAAC;AAAA,MACL,GAAI,SAAA,GACA;AAAA,QACE,GAAA,EAAK;AAAA,UACH,kBAAA,EACE,SAAA,EAAW,kBAAA,CAAmB,oBAAoB;AAAA;AACtD,UAEF;AAAC,KACP;AAAA,EACF;AAAA,EAEA,OAAe,wBACb,MAAA,EACoC;AACpC,IAAA,OACE,MAAA,CAAO,sBAAA,CAAuB,gBAAgB,CAAA,EAAG,IAAI,CAAA,cAAA,KAAkB;AACrE,MAAA,MAAM,UAAA,GAAa,cAAA,CAAe,SAAA,CAAU,MAAM,CAAA;AAClD,MAAA,OAAO;AAAA,QACL,IAAA,EAAM,cAAA,CAAe,SAAA,CAAU,MAAM,CAAA;AAAA,QACrC,IAAA,EAAM;AAAA,UACJ,cAAA,EAAgB,UAAA,CAAW,cAAA,CAAe,gBAAgB,CAAA;AAAA,UAC1D,WAAA,EAAa,UAAA,CAAW,sBAAA,CAAuB,aAAa,CAAA;AAAA,UAC5D,QAAA,EAAU,UAAA,CAAW,iBAAA,CAAkB,UAAU,GAAG,GAAA;AAAI;AAC1D,OACF;AAAA,IACF,CAAC,KAAK,EAAC;AAAA,EAEX;AACF;AAKO,SAAS,iBAAiB,UAAA,EAAuC;AACtE,EAAA,IAAI,CAAC,UAAA,EAAY;AACf,IAAA,OAAO,EAAE,MAAM,CAAA,EAAE;AAAA,EACnB;AAEA,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,OAAO,MAAA,CAAO,IAAA,CAAK,YAAY,QAAQ,CAAA,CAAE,QAAA,CAAS,OAAO,CAAC;AAAA,GAClE;AACF;AAEO,SAAS,gBAAA,CAAiB,EAAE,IAAA,EAAK,EAA6B;AACnE,EAAA,OAAO,MAAA,CAAO,KAAK,CAAA,EAAG,IAAI,IAAI,OAAO,CAAA,CAAE,SAAS,QAAQ,CAAA;AAC1D;;;;;;"}
|
|
1
|
+
{"version":3,"file":"ElasticSearchSearchEngine.cjs.js","sources":["../../src/engines/ElasticSearchSearchEngine.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n IndexableDocument,\n IndexableResult,\n IndexableResultSet,\n SearchQuery,\n} from '@backstage/plugin-search-common';\nimport { SearchEngine } from '@backstage/plugin-search-backend-node';\nimport { isEmpty, isNumber, isNaN as nan } from 'lodash';\n\nimport { AwsSigv4Signer } from '@opensearch-project/opensearch/aws';\nimport { RequestSigner } from 'aws4';\nimport { Config } from '@backstage/config';\nimport {\n ElasticSearchClientOptions,\n OpenSearchElasticSearchClientOptions,\n} from './ElasticSearchClientOptions';\nimport { ElasticSearchClientWrapper } from './ElasticSearchClientWrapper';\nimport { ElasticSearchCustomIndexTemplate } from './types';\nimport { ElasticSearchSearchEngineIndexer } from './ElasticSearchSearchEngineIndexer';\nimport { MissingIndexError } from '@backstage/plugin-search-backend-node';\nimport esb from 'elastic-builder';\nimport { randomUUID as uuid } from 'node:crypto';\nimport {\n AwsCredentialProvider,\n DefaultAwsCredentialsManager,\n} from '@backstage/integration-aws-node';\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { ElasticSearchAuthProvider } from '../auth';\nimport {\n createOpenSearchAuthTransport,\n createElasticSearchAuthTransport,\n} from './ElasticSearchAuthTransport';\n\nexport type { ElasticSearchClientOptions };\n\n/**\n * Search query that the elasticsearch engine understands.\n * @public\n */\nexport type ElasticSearchConcreteQuery = {\n documentTypes?: string[];\n elasticSearchQuery: Object;\n pageSize: number;\n};\n\n/**\n * Options available for the Elasticsearch specific query translator.\n * @public\n */\nexport type ElasticSearchQueryTranslatorOptions = {\n highlightOptions?: ElasticSearchHighlightConfig;\n queryOptions?: ElasticSearchQueryConfig;\n};\n\n/**\n * Elasticsearch specific query translator.\n * @public\n */\nexport type ElasticSearchQueryTranslator = (\n query: SearchQuery,\n options?: ElasticSearchQueryTranslatorOptions,\n) => ElasticSearchConcreteQuery;\n\n/**\n * Options for instantiate ElasticSearchSearchEngine\n * @public\n */\nexport type ElasticSearchOptions = {\n logger: LoggerService;\n config: Config;\n aliasPostfix?: string;\n indexPrefix?: string;\n translator?: ElasticSearchQueryTranslator;\n /**\n * An optional authentication provider for dynamic authentication.\n *\n * @remarks\n *\n * When provided, this auth provider will be used to inject authentication\n * headers into each request to Elasticsearch/OpenSearch. This is useful\n * for scenarios requiring dynamic tokens (e.g., bearer tokens with rotation).\n *\n * The auth provider takes precedence over static authentication configured\n * in app-config.yaml.\n */\n authProvider?: ElasticSearchAuthProvider;\n};\n\n/**\n * @public\n */\nexport type ElasticSearchHighlightOptions = {\n fragmentDelimiter?: string;\n fragmentSize?: number;\n numFragments?: number;\n};\n\n/**\n * @public\n */\nexport type ElasticSearchQueryConfig = {\n fuzziness?: string | number;\n prefixLength?: number;\n};\n\n/**\n * @public\n */\nexport type ElasticSearchHighlightConfig = {\n fragmentDelimiter: string;\n fragmentSize: number;\n numFragments: number;\n preTag: string;\n postTag: string;\n};\n\ntype ElasticSearchResult = {\n _index: string;\n _type: string;\n _score: number;\n _source: IndexableDocument;\n highlight?: {\n [field: string]: string[];\n };\n};\n\nfunction isBlank(str: string) {\n return (isEmpty(str) && !isNumber(str)) || nan(str);\n}\n\nconst DEFAULT_INDEXER_BATCH_SIZE = 1000;\n\n/**\n * @public\n */\nexport class ElasticSearchSearchEngine implements SearchEngine {\n private readonly elasticSearchClientWrapper: ElasticSearchClientWrapper;\n private readonly highlightOptions: ElasticSearchHighlightConfig;\n private readonly queryOptions?: ElasticSearchQueryConfig;\n\n private readonly elasticSearchClientOptions: ElasticSearchClientOptions;\n private readonly aliasPostfix: string;\n private readonly indexPrefix: string;\n private readonly logger: LoggerService;\n private readonly batchSize: number;\n private readonly batchKeyField?: string;\n\n constructor(\n elasticSearchClientOptions: ElasticSearchClientOptions,\n aliasPostfix: string,\n indexPrefix: string,\n logger: LoggerService,\n batchSize: number,\n batchKeyField?: string,\n highlightOptions?: ElasticSearchHighlightOptions,\n queryOptions?: ElasticSearchQueryConfig,\n ) {\n this.elasticSearchClientOptions = elasticSearchClientOptions;\n this.aliasPostfix = aliasPostfix;\n this.indexPrefix = indexPrefix;\n this.logger = logger;\n this.batchSize = batchSize;\n this.batchKeyField = batchKeyField;\n this.elasticSearchClientWrapper =\n ElasticSearchClientWrapper.fromClientOptions(elasticSearchClientOptions);\n const uuidTag = uuid();\n this.highlightOptions = {\n preTag: `<${uuidTag}>`,\n postTag: `</${uuidTag}>`,\n fragmentSize: 1000,\n numFragments: 1,\n fragmentDelimiter: ' ... ',\n ...highlightOptions,\n };\n this.queryOptions = queryOptions;\n }\n\n static async fromConfig(options: ElasticSearchOptions) {\n const {\n logger,\n config,\n aliasPostfix = `search`,\n indexPrefix = ``,\n translator,\n authProvider,\n } = options;\n const credentialProvider = DefaultAwsCredentialsManager.fromConfig(config);\n const esConfig = config.getConfig('search.elasticsearch');\n const awsAccountId = esConfig.getOptionalString('accountId');\n const clientOptions = await this.createElasticSearchClientOptions(\n await credentialProvider?.getCredentialProvider(\n awsAccountId ? { accountId: awsAccountId } : undefined,\n ),\n esConfig,\n authProvider,\n );\n if (clientOptions.provider === 'elastic') {\n logger.info('Initializing Elastic.co ElasticSearch search engine.');\n } else if (clientOptions.provider === 'aws') {\n logger.info('Initializing AWS OpenSearch search engine.');\n } else if (clientOptions.provider === 'opensearch') {\n logger.info('Initializing OpenSearch search engine.');\n } else {\n logger.info('Initializing ElasticSearch search engine.');\n }\n\n const engine = new ElasticSearchSearchEngine(\n clientOptions,\n aliasPostfix,\n indexPrefix,\n logger,\n config.getOptionalNumber('search.elasticsearch.batchSize') ??\n DEFAULT_INDEXER_BATCH_SIZE,\n config.getOptionalString('search.elasticsearch.batchKeyField'),\n config.getOptional<ElasticSearchHighlightOptions>(\n 'search.elasticsearch.highlightOptions',\n ),\n config.getOptional<ElasticSearchQueryConfig>(\n 'search.elasticsearch.queryOptions',\n ),\n );\n\n for (const indexTemplate of this.readIndexTemplateConfig(\n config.getConfig('search.elasticsearch'),\n )) {\n await engine.setIndexTemplate(indexTemplate);\n }\n\n if (translator) {\n await engine.setTranslator(translator);\n }\n\n return engine;\n }\n\n /**\n * Create a custom search client from the derived search client configuration.\n * This need not be the same client that the engine uses internally.\n *\n * @example Instantiate an instance of an Elasticsearch client.\n *\n * ```ts\n * import { isOpenSearchCompatible } from '@backstage/plugin-search-backend-module-elasticsearch';\n * import { Client } from '@elastic/elasticsearch';\n *\n * const client = searchEngine.newClient<Client>(options => {\n * // This type guard ensures options are compatible with either OpenSearch\n * // or Elasticsearch client constructors.\n * if (!isOpenSearchCompatible(options)) {\n * return new Client(options);\n * }\n * throw new Error('Incompatible options provided');\n * });\n * ```\n */\n newClient<T>(create: (options: ElasticSearchClientOptions) => T): T {\n return create(this.elasticSearchClientOptions);\n }\n\n protected translator(\n query: SearchQuery,\n options?: ElasticSearchQueryTranslatorOptions,\n ): ElasticSearchConcreteQuery {\n const { term, filters = {}, types, pageCursor } = query;\n\n const filter = Object.entries(filters)\n .filter(([_, value]) => Boolean(value))\n .map(([key, value]: [key: string, value: any]) => {\n if (['string', 'number', 'boolean'].includes(typeof value)) {\n // Use exact matching for string datatype fields\n const keyword = typeof value === 'string' ? `${key}.keyword` : key;\n return esb.matchQuery(keyword, value.toString());\n }\n if (Array.isArray(value)) {\n return esb\n .boolQuery()\n .should(value.map(it => esb.matchQuery(key, it.toString())));\n }\n this.logger.error('Failed to query, unrecognized filter type', {\n key,\n value,\n });\n throw new Error(\n 'Failed to add filters to query. Unrecognized filter type',\n );\n });\n\n const esbQueries = [];\n // https://regex101.com/r/Lr0MqS/1\n const phraseTerms = term.match(/\"[^\"]*\"/g);\n\n if (isBlank(term)) {\n const esbQuery = esb.matchAllQuery();\n esbQueries.push(esbQuery);\n } else if (phraseTerms && phraseTerms.length > 0) {\n let restTerm = term;\n for (const phraseTerm of phraseTerms) {\n restTerm = restTerm.replace(phraseTerm, '');\n const esbPhraseQuery = esb\n .multiMatchQuery(['*'], phraseTerm.replace(/\"/g, ''))\n .type('phrase');\n esbQueries.push(esbPhraseQuery);\n }\n if (restTerm?.length > 0) {\n const esbRestQuery = esb\n .multiMatchQuery(['*'], restTerm.trim())\n .fuzziness(options?.queryOptions?.fuzziness ?? 'auto')\n .prefixLength(options?.queryOptions?.prefixLength ?? 0);\n esbQueries.push(esbRestQuery);\n }\n } else {\n const esbQuery = esb\n .multiMatchQuery(['*'], term)\n .fuzziness(options?.queryOptions?.fuzziness ?? 'auto')\n .prefixLength(options?.queryOptions?.prefixLength ?? 0);\n esbQueries.push(esbQuery);\n }\n\n const pageSize = query.pageLimit || 25;\n const { page } = decodePageCursor(pageCursor);\n\n let esbRequestBodySearch = esb\n .requestBodySearch()\n .query(esb.boolQuery().filter(filter).should(esbQueries))\n .from(page * pageSize)\n .size(pageSize);\n\n if (options?.highlightOptions) {\n esbRequestBodySearch = esbRequestBodySearch.highlight(\n esb\n .highlight('*')\n .numberOfFragments(options.highlightOptions.numFragments as number)\n .fragmentSize(options.highlightOptions.fragmentSize as number)\n .preTags(options.highlightOptions.preTag)\n .postTags(options.highlightOptions.postTag),\n );\n }\n\n return {\n elasticSearchQuery: esbRequestBodySearch.toJSON(),\n documentTypes: types,\n pageSize,\n };\n }\n\n setTranslator(translator: ElasticSearchQueryTranslator) {\n this.translator = translator;\n }\n\n async setIndexTemplate(template: ElasticSearchCustomIndexTemplate) {\n try {\n await this.elasticSearchClientWrapper.putIndexTemplate(template);\n this.logger.info('Custom index template set');\n } catch (error) {\n this.logger.error(`Unable to set custom index template: ${error}`);\n }\n }\n\n async getIndexer(type: string) {\n const alias = this.constructSearchAlias(type);\n const indexerLogger = this.logger.child({ documentType: type });\n\n const indexer = new ElasticSearchSearchEngineIndexer({\n type,\n indexPrefix: this.indexPrefix,\n indexSeparator: this.indexSeparator,\n alias,\n elasticSearchClientWrapper: this.elasticSearchClientWrapper,\n logger: indexerLogger,\n batchSize: this.batchSize,\n batchKeyField: this.batchKeyField,\n skipRefresh:\n (\n this\n .elasticSearchClientOptions as OpenSearchElasticSearchClientOptions\n )?.service === 'aoss',\n });\n\n // Attempt cleanup upon failure.\n // todo(@backstage/search-maintainers): Consider introducing a more\n // formal mechanism for handling such errors in BatchSearchEngineIndexer and\n // replacing this handler with it. See: #17291\n indexer.on('error', async e => {\n indexerLogger.error(`Failed to index documents for type ${type}`, e);\n let cleanupError: Error | undefined;\n\n // In some cases, a failure may have occurred before the indexer was able\n // to complete initialization. Try up to 5 times to remove the dangling\n // index.\n await new Promise<void>(async done => {\n const maxAttempts = 5;\n let attempts = 0;\n\n while (attempts < maxAttempts) {\n try {\n await this.elasticSearchClientWrapper.deleteIndex({\n index: indexer.indexName,\n });\n\n attempts = maxAttempts;\n cleanupError = undefined;\n done();\n } catch (err) {\n cleanupError = err;\n }\n\n // Wait 1 second between retries.\n await new Promise(okay => setTimeout(okay, 1000));\n\n attempts++;\n }\n done();\n });\n\n if (cleanupError) {\n indexerLogger.error(\n `Unable to clean up elastic index ${indexer.indexName}: ${cleanupError}`,\n );\n } else {\n indexerLogger.info(\n `Removed partial, failed index ${indexer.indexName}`,\n );\n }\n });\n\n return indexer;\n }\n\n async query(query: SearchQuery): Promise<IndexableResultSet> {\n const { elasticSearchQuery, documentTypes, pageSize } = this.translator(\n query,\n {\n highlightOptions: this.highlightOptions,\n queryOptions: this.queryOptions,\n },\n );\n if (documentTypes && documentTypes.length === 0) {\n return {\n results: [],\n nextPageCursor: undefined,\n previousPageCursor: undefined,\n numberOfResults: undefined,\n };\n }\n const queryIndices = documentTypes\n ? documentTypes.map(it => this.constructSearchAlias(it))\n : this.constructSearchAlias('*');\n try {\n const result = await this.elasticSearchClientWrapper.search({\n index: queryIndices,\n body: elasticSearchQuery,\n });\n const { page } = decodePageCursor(query.pageCursor);\n const hasNextPage = result.body.hits.total.value > (page + 1) * pageSize;\n const hasPreviousPage = page > 0;\n const nextPageCursor = hasNextPage\n ? encodePageCursor({ page: page + 1 })\n : undefined;\n const previousPageCursor = hasPreviousPage\n ? encodePageCursor({ page: page - 1 })\n : undefined;\n\n return {\n results: result.body.hits.hits.map(\n (d: ElasticSearchResult, index: number) => {\n const resultItem: IndexableResult = {\n type: this.getTypeFromIndex(d._index),\n document: d._source,\n rank: pageSize * page + index + 1,\n };\n\n if (d.highlight) {\n resultItem.highlight = {\n preTag: this.highlightOptions.preTag as string,\n postTag: this.highlightOptions.postTag as string,\n fields: Object.fromEntries(\n Object.entries(d.highlight).map(([field, fragments]) => [\n field,\n fragments.join(this.highlightOptions.fragmentDelimiter),\n ]),\n ),\n };\n }\n\n return resultItem;\n },\n ),\n nextPageCursor,\n previousPageCursor,\n numberOfResults: result.body.hits.total.value,\n };\n } catch (error) {\n if (error.meta?.body?.error?.type === 'index_not_found_exception') {\n throw new MissingIndexError(\n `Missing index for ${queryIndices}. This means there are no documents to search through.`,\n error,\n );\n }\n this.logger.error(\n `Failed to query documents for indices ${queryIndices}`,\n error,\n );\n return Promise.reject({ results: [] });\n }\n }\n\n private readonly indexSeparator = '-index__';\n\n private getTypeFromIndex(index: string) {\n return index\n .substring(this.indexPrefix.length)\n .split(this.indexSeparator)[0];\n }\n\n private constructSearchAlias(type: string) {\n const postFix = this.aliasPostfix ? `__${this.aliasPostfix}` : '';\n return `${this.indexPrefix}${type}${postFix}`;\n }\n\n private static async createElasticSearchClientOptions(\n credentialProvider: AwsCredentialProvider,\n config?: Config,\n authProvider?: ElasticSearchAuthProvider,\n ): Promise<ElasticSearchClientOptions> {\n if (!config) {\n throw new Error('No elastic search config found');\n }\n const clientOptionsConfig = config.getOptionalConfig('clientOptions');\n const sslConfig = clientOptionsConfig?.getOptionalConfig('ssl');\n\n if (config.getOptionalString('provider') === 'elastic') {\n const authConfig = authProvider ? undefined : config.getConfig('auth');\n return {\n provider: 'elastic',\n cloud: {\n id: config.getString('cloudId'),\n },\n // When using authProvider, we inject auth via custom Transport, not static auth\n ...(authConfig\n ? {\n auth: {\n username: authConfig.getString('username'),\n password: authConfig.getString('password'),\n },\n }\n : {}),\n ...(authProvider\n ? {\n Transport: createElasticSearchAuthTransport(authProvider),\n }\n : {}),\n ...(sslConfig\n ? {\n ssl: {\n rejectUnauthorized:\n sslConfig?.getOptionalBoolean('rejectUnauthorized'),\n },\n }\n : {}),\n };\n }\n if (config.getOptionalString('provider') === 'aws') {\n // AWS provider uses SigV4 signing; authProvider is not applicable here\n if (authProvider) {\n throw new Error(\n 'Custom auth provider is not supported with AWS provider. AWS uses SigV4 signing.',\n );\n }\n const requestSigner = new RequestSigner(config.getString('node'));\n const service =\n config.getOptionalString('service') ?? requestSigner.service;\n if (service !== 'es' && service !== 'aoss')\n throw new Error(`Unrecognized service type: ${service}`);\n return {\n provider: 'aws',\n node: config.getString('node'),\n region: config.getOptionalString('region'),\n service,\n ...(sslConfig\n ? {\n ssl: {\n rejectUnauthorized:\n sslConfig?.getOptionalBoolean('rejectUnauthorized'),\n },\n }\n : {}),\n ...AwsSigv4Signer({\n region: config.getOptionalString('region') ?? requestSigner.region, // for backwards compatibility\n service: service,\n getCredentials: async () =>\n await credentialProvider.sdkCredentialProvider(),\n }),\n };\n }\n if (config.getOptionalString('provider') === 'opensearch') {\n const authConfig = authProvider\n ? undefined\n : config.getOptionalConfig('auth');\n return {\n provider: 'opensearch',\n node: config.getString('node'),\n // When using authProvider, we inject auth via custom Transport, not static auth\n ...(authConfig\n ? {\n auth: {\n username: authConfig.getString('username'),\n password: authConfig.getString('password'),\n },\n }\n : {}),\n ...(authProvider\n ? {\n Transport: createOpenSearchAuthTransport(authProvider),\n }\n : {}),\n ...(sslConfig\n ? {\n ssl: {\n rejectUnauthorized:\n sslConfig?.getOptionalBoolean('rejectUnauthorized'),\n },\n }\n : {}),\n };\n }\n // Default provider (standard Elasticsearch)\n const authConfig = authProvider\n ? undefined\n : config.getOptionalConfig('auth');\n const auth =\n authConfig &&\n (authConfig.has('apiKey')\n ? {\n apiKey: authConfig.getString('apiKey'),\n }\n : {\n username: authConfig.getString('username'),\n password: authConfig.getString('password'),\n });\n return {\n node: config.getString('node'),\n ...(auth ? { auth } : {}),\n ...(authProvider\n ? {\n Transport: createElasticSearchAuthTransport(authProvider),\n }\n : {}),\n ...(sslConfig\n ? {\n ssl: {\n rejectUnauthorized:\n sslConfig?.getOptionalBoolean('rejectUnauthorized'),\n },\n }\n : {}),\n };\n }\n\n private static readIndexTemplateConfig(\n config: Config,\n ): ElasticSearchCustomIndexTemplate[] {\n return (\n config.getOptionalConfigArray('indexTemplates')?.map(templateConfig => {\n const bodyConfig = templateConfig.getConfig('body');\n return {\n name: templateConfig.getString('name'),\n body: {\n index_patterns: bodyConfig.getStringArray('index_patterns'),\n composed_of: bodyConfig.getOptionalStringArray('composed_of'),\n template: bodyConfig.getOptionalConfig('template')?.get(),\n },\n };\n }) ?? []\n );\n }\n}\n\n/**\n * @public\n */\nexport function decodePageCursor(pageCursor?: string): { page: number } {\n if (!pageCursor) {\n return { page: 0 };\n }\n\n return {\n page: Number(Buffer.from(pageCursor, 'base64').toString('utf-8')),\n };\n}\n\nexport function encodePageCursor({ page }: { page: number }): string {\n return Buffer.from(`${page}`, 'utf-8').toString('base64');\n}\n"],"names":["isEmpty","isNumber","nan","ElasticSearchClientWrapper","uuid","DefaultAwsCredentialsManager","esb","ElasticSearchSearchEngineIndexer","MissingIndexError","authConfig","createElasticSearchAuthTransport","RequestSigner","AwsSigv4Signer","createOpenSearchAuthTransport"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,11 +4,7 @@ import { IndexableDocument, SearchQuery, IndexableResultSet } from '@backstage/p
|
|
|
4
4
|
import { BatchSearchEngineIndexer, SearchEngine } from '@backstage/plugin-search-backend-node';
|
|
5
5
|
import { Config } from '@backstage/config';
|
|
6
6
|
import { ConnectionOptions } from 'node:tls';
|
|
7
|
-
import * as
|
|
8
|
-
import * as _elastic_elasticsearch_lib_Transport from '@elastic/elasticsearch/lib/Transport';
|
|
9
|
-
import * as _elastic_elasticsearch from '@elastic/elasticsearch';
|
|
10
|
-
import * as _opensearch_project_opensearch_lib_Transport_js from '@opensearch-project/opensearch/lib/Transport.js';
|
|
11
|
-
import * as _opensearch_project_opensearch from '@opensearch-project/opensearch';
|
|
7
|
+
import * as _opensearch_project_opensearch_lib_Helpers_js from '@opensearch-project/opensearch/lib/Helpers.js';
|
|
12
8
|
import { Readable } from 'node:stream';
|
|
13
9
|
|
|
14
10
|
/**
|
|
@@ -159,7 +155,7 @@ interface ElasticSearchConnectionConstructor {
|
|
|
159
155
|
ALIVE: string;
|
|
160
156
|
DEAD: string;
|
|
161
157
|
};
|
|
162
|
-
roles
|
|
158
|
+
roles?: {
|
|
163
159
|
MASTER: string;
|
|
164
160
|
DATA: string;
|
|
165
161
|
INGEST: string;
|
|
@@ -219,7 +215,7 @@ type ElasticSearchCustomIndexTemplateBody = {
|
|
|
219
215
|
composed_of?: string[];
|
|
220
216
|
/**
|
|
221
217
|
* See available properties of template
|
|
222
|
-
* https://www.elastic.co/guide/en/elasticsearch/reference/
|
|
218
|
+
* https://www.elastic.co/guide/en/elasticsearch/reference/8.19/indices-put-template.html#put-index-template-api-request-body
|
|
223
219
|
*/
|
|
224
220
|
template?: Record<string, any>;
|
|
225
221
|
};
|
|
@@ -257,6 +253,36 @@ type ElasticSearchIndexAction = {
|
|
|
257
253
|
[key: string]: any;
|
|
258
254
|
};
|
|
259
255
|
};
|
|
256
|
+
/**
|
|
257
|
+
* The normalized response shape returned by the wrapped client methods.
|
|
258
|
+
*
|
|
259
|
+
* Both the Elasticsearch and Opensearch clients expose the response payload
|
|
260
|
+
* under a `body` property together with the HTTP status code, the response
|
|
261
|
+
* headers and any deprecation warnings, so consumers can read these regardless
|
|
262
|
+
* of the configured provider. The client specific `meta` envelope is not part
|
|
263
|
+
* of this type because its shape differs between the two clients.
|
|
264
|
+
*
|
|
265
|
+
* @public
|
|
266
|
+
*/
|
|
267
|
+
type ElasticSearchClientResponse<TBody = any> = {
|
|
268
|
+
body: TBody;
|
|
269
|
+
headers: Record<string, any> | null;
|
|
270
|
+
statusCode: number | null;
|
|
271
|
+
warnings: string[] | null;
|
|
272
|
+
};
|
|
273
|
+
/**
|
|
274
|
+
* The promise returned by the wrapped client methods.
|
|
275
|
+
*
|
|
276
|
+
* The Opensearch client returns a promise that additionally exposes an
|
|
277
|
+
* `abort` method, whereas the Elasticsearch v8 client returns a plain
|
|
278
|
+
* promise, so `abort` is only present when the Opensearch client is
|
|
279
|
+
* configured.
|
|
280
|
+
*
|
|
281
|
+
* @public
|
|
282
|
+
*/
|
|
283
|
+
type ElasticSearchClientResponsePromise<TBody = any> = Promise<ElasticSearchClientResponse<TBody>> & {
|
|
284
|
+
abort?: () => void;
|
|
285
|
+
};
|
|
260
286
|
/**
|
|
261
287
|
* A wrapper class that exposes logical methods that are conditionally fired
|
|
262
288
|
* against either a configured Elasticsearch client or a configured Opensearch
|
|
@@ -270,6 +296,13 @@ type ElasticSearchIndexAction = {
|
|
|
270
296
|
* unmaintainably divergent, we should split out the Opensearch and
|
|
271
297
|
* Elasticsearch search engine implementations.
|
|
272
298
|
*
|
|
299
|
+
* The Elasticsearch client calls below pass `{ meta: true }`. As of v8 the
|
|
300
|
+
* `@elastic/elasticsearch` client returns the response body directly, whereas
|
|
301
|
+
* the Opensearch client (forked from the v7 Elasticsearch client) still wraps
|
|
302
|
+
* it in `{ body, statusCode, headers, ... }`. Requesting the meta envelope
|
|
303
|
+
* keeps both clients returning the same shape, so consumers can continue to
|
|
304
|
+
* read `response.body` regardless of the configured provider.
|
|
305
|
+
*
|
|
273
306
|
* @public
|
|
274
307
|
*/
|
|
275
308
|
declare class ElasticSearchClientWrapper {
|
|
@@ -280,34 +313,34 @@ declare class ElasticSearchClientWrapper {
|
|
|
280
313
|
search(options: {
|
|
281
314
|
index: string | string[];
|
|
282
315
|
body: Object;
|
|
283
|
-
}):
|
|
316
|
+
}): ElasticSearchClientResponsePromise;
|
|
284
317
|
bulk(bulkOptions: {
|
|
285
318
|
datasource: Readable;
|
|
286
319
|
onDocument: (doc: any) => ElasticSearchIndexAction;
|
|
287
320
|
refreshOnCompletion?: string | boolean;
|
|
288
|
-
}):
|
|
289
|
-
putIndexTemplate(template: ElasticSearchCustomIndexTemplate):
|
|
321
|
+
}): _opensearch_project_opensearch_lib_Helpers_js.BulkHelper<_opensearch_project_opensearch_lib_Helpers_js.BulkStats>;
|
|
322
|
+
putIndexTemplate(template: ElasticSearchCustomIndexTemplate): ElasticSearchClientResponsePromise;
|
|
290
323
|
listIndices(options: {
|
|
291
324
|
index: string;
|
|
292
|
-
}):
|
|
325
|
+
}): ElasticSearchClientResponsePromise;
|
|
293
326
|
indexExists(options: {
|
|
294
327
|
index: string | string[];
|
|
295
|
-
}):
|
|
328
|
+
}): ElasticSearchClientResponsePromise<boolean>;
|
|
296
329
|
deleteIndex(options: {
|
|
297
330
|
index: string | string[];
|
|
298
|
-
}):
|
|
331
|
+
}): ElasticSearchClientResponsePromise;
|
|
299
332
|
/**
|
|
300
333
|
* @deprecated unused by the ElasticSearch Engine, will be removed in the future
|
|
301
334
|
*/
|
|
302
335
|
getAliases(options: {
|
|
303
336
|
aliases: string[];
|
|
304
|
-
}):
|
|
337
|
+
}): ElasticSearchClientResponsePromise;
|
|
305
338
|
createIndex(options: {
|
|
306
339
|
index: string;
|
|
307
|
-
}):
|
|
340
|
+
}): ElasticSearchClientResponsePromise;
|
|
308
341
|
updateAliases(options: {
|
|
309
342
|
actions: ElasticSearchAliasAction[];
|
|
310
|
-
}):
|
|
343
|
+
}): ElasticSearchClientResponsePromise;
|
|
311
344
|
}
|
|
312
345
|
|
|
313
346
|
/**
|
|
@@ -595,4 +628,4 @@ declare const elasticsearchTranslatorExtensionPoint: _backstage_backend_plugin_a
|
|
|
595
628
|
declare const _default: _backstage_backend_plugin_api.BackendFeature;
|
|
596
629
|
|
|
597
630
|
export { ElasticSearchClientWrapper, ElasticSearchSearchEngine, ElasticSearchSearchEngineIndexer, decodePageCursor as decodeElasticSearchPageCursor, _default as default, elasticsearchAuthExtensionPoint, elasticsearchTranslatorExtensionPoint, isOpenSearchCompatible };
|
|
598
|
-
export type { BaseElasticSearchClientOptions, ElasticSearchAgentOptions, ElasticSearchAliasAction, ElasticSearchAuth, ElasticSearchAuthExtensionPoint, ElasticSearchAuthProvider, ElasticSearchClientOptions, ElasticSearchConcreteQuery, ElasticSearchConnectionConstructor, ElasticSearchCustomIndexTemplate, ElasticSearchCustomIndexTemplateBody, ElasticSearchElasticSearchClientOptions, ElasticSearchHighlightConfig, ElasticSearchHighlightOptions, ElasticSearchIndexAction, ElasticSearchNodeOptions, ElasticSearchOptions, ElasticSearchQueryConfig, ElasticSearchQueryTranslator, ElasticSearchQueryTranslatorExtensionPoint, ElasticSearchQueryTranslatorOptions, ElasticSearchSearchEngineIndexerOptions, ElasticSearchTransportConstructor, OpenSearchAuth, OpenSearchConnectionConstructor, OpenSearchElasticSearchClientOptions, OpenSearchNodeOptions };
|
|
631
|
+
export type { BaseElasticSearchClientOptions, ElasticSearchAgentOptions, ElasticSearchAliasAction, ElasticSearchAuth, ElasticSearchAuthExtensionPoint, ElasticSearchAuthProvider, ElasticSearchClientOptions, ElasticSearchClientResponse, ElasticSearchClientResponsePromise, ElasticSearchConcreteQuery, ElasticSearchConnectionConstructor, ElasticSearchCustomIndexTemplate, ElasticSearchCustomIndexTemplateBody, ElasticSearchElasticSearchClientOptions, ElasticSearchHighlightConfig, ElasticSearchHighlightOptions, ElasticSearchIndexAction, ElasticSearchNodeOptions, ElasticSearchOptions, ElasticSearchQueryConfig, ElasticSearchQueryTranslator, ElasticSearchQueryTranslatorExtensionPoint, ElasticSearchQueryTranslatorOptions, ElasticSearchSearchEngineIndexerOptions, ElasticSearchTransportConstructor, OpenSearchAuth, OpenSearchConnectionConstructor, OpenSearchElasticSearchClientOptions, OpenSearchNodeOptions };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/plugin-search-backend-module-elasticsearch",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "A module for the search backend that implements search using ElasticSearch",
|
|
5
5
|
"backstage": {
|
|
6
6
|
"role": "backend-plugin-module",
|
|
@@ -62,21 +62,21 @@
|
|
|
62
62
|
"test": "backstage-cli package test"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@backstage/backend-plugin-api": "1.10.1
|
|
66
|
-
"@backstage/config": "1.3.
|
|
67
|
-
"@backstage/integration-aws-node": "0.2.
|
|
68
|
-
"@backstage/plugin-search-backend-node": "1.4.8
|
|
69
|
-
"@backstage/plugin-search-common": "1.2.
|
|
70
|
-
"@elastic/elasticsearch": "^
|
|
65
|
+
"@backstage/backend-plugin-api": "^1.10.1",
|
|
66
|
+
"@backstage/config": "^1.3.9",
|
|
67
|
+
"@backstage/integration-aws-node": "^0.2.2",
|
|
68
|
+
"@backstage/plugin-search-backend-node": "^1.4.8",
|
|
69
|
+
"@backstage/plugin-search-common": "^1.2.25",
|
|
70
|
+
"@elastic/elasticsearch": "^8.19.0",
|
|
71
71
|
"@opensearch-project/opensearch": "^2.2.1",
|
|
72
72
|
"aws4": "^1.12.0",
|
|
73
73
|
"elastic-builder": "^2.16.0",
|
|
74
74
|
"lodash": "^4.17.21"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
|
-
"@backstage/backend-test-utils": "1.11.7
|
|
78
|
-
"@backstage/cli": "0.36.6
|
|
79
|
-
"@elastic/elasticsearch-mock": "^
|
|
77
|
+
"@backstage/backend-test-utils": "^1.11.7",
|
|
78
|
+
"@backstage/cli": "^0.36.6",
|
|
79
|
+
"@elastic/elasticsearch-mock": "^2.0.0",
|
|
80
80
|
"@short.io/opensearch-mock": "^0.4.0",
|
|
81
81
|
"@types/aws4": "^1.5.1"
|
|
82
82
|
},
|