@backstage/plugin-search-backend-module-elasticsearch 1.8.8-next.2 → 2.0.1-next.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/alpha.cjs.js.map +1 -1
- package/dist/auth.cjs.js.map +1 -1
- 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/engines/ElasticSearchSearchEngineIndexer.cjs.js.map +1 -1
- package/dist/index.d.ts +50 -17
- package/dist/module.cjs.js.map +1 -1
- 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.1-next.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies
|
|
8
|
+
- @backstage/config@1.3.10-next.0
|
|
9
|
+
- @backstage/backend-plugin-api@1.10.2-next.0
|
|
10
|
+
- @backstage/integration-aws-node@0.2.3-next.0
|
|
11
|
+
- @backstage/plugin-search-backend-node@1.4.9-next.0
|
|
12
|
+
- @backstage/plugin-search-common@1.2.26-next.0
|
|
13
|
+
|
|
14
|
+
## 2.0.0
|
|
15
|
+
|
|
16
|
+
### Major Changes
|
|
17
|
+
|
|
18
|
+
- 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.
|
|
19
|
+
|
|
20
|
+
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.
|
|
21
|
+
|
|
22
|
+
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.
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- fe6ace6: Added optional `accountId` config to `search.elasticsearch` for resolving account-specific AWS credentials, enabling support for `webIdentityTokenFile` and `accountDefaults` when using AWS OpenSearch.
|
|
27
|
+
- b11c9b4: Fixed a bug where an empty document type list could result in querying all indices instead of returning empty results.
|
|
28
|
+
- Updated dependencies
|
|
29
|
+
- @backstage/plugin-search-backend-node@1.4.8
|
|
30
|
+
- @backstage/config@1.3.9
|
|
31
|
+
- @backstage/backend-plugin-api@1.10.1
|
|
32
|
+
- @backstage/integration-aws-node@0.2.2
|
|
33
|
+
- @backstage/plugin-search-common@1.2.25
|
|
34
|
+
|
|
3
35
|
## 1.8.8-next.2
|
|
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": [
|
package/dist/alpha.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alpha.cjs.js","sources":["../src/alpha.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 { default as feature } from './module';\n\n/** @alpha */\nconst _feature = feature;\nexport default _feature;\n"],"names":["feature"],"mappings":";;;;;;AAmBA,MAAM,QAAA,GAAWA
|
|
1
|
+
{"version":3,"file":"alpha.cjs.js","sources":["../src/alpha.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 { default as feature } from './module';\n\n/** @alpha */\nconst _feature = feature;\nexport default _feature;\n"],"names":["feature"],"mappings":";;;;;;AAmBA,MAAM,QAAA,GAAWA;;"}
|
package/dist/auth.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.cjs.js","sources":["../src/auth.ts"],"sourcesContent":["/*\n * Copyright 2026 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 { createExtensionPoint } from '@backstage/backend-plugin-api';\n\n/**\n * A provider that supplies authentication headers for Elasticsearch/OpenSearch requests.\n *\n * @remarks\n *\n * This interface allows for dynamic authentication mechanisms such as bearer tokens\n * that need to be refreshed or rotated. The `getAuthHeaders` method is called before\n * each request to the Elasticsearch/OpenSearch cluster, allowing for just-in-time\n * token retrieval and automatic rotation.\n *\n * @example\n *\n * ```ts\n * const authProvider: ElasticSearchAuthProvider = {\n * async getAuthHeaders() {\n * const token = await myTokenService.getToken();\n * return { Authorization: `Bearer ${token}` };\n * },\n * };\n * ```\n *\n * @public\n */\nexport interface ElasticSearchAuthProvider {\n /**\n * Returns authentication headers to be included in requests to Elasticsearch/OpenSearch.\n *\n * @remarks\n *\n * This method is called before each request, allowing for dynamic token refresh\n * and rotation. Implementations should handle caching internally if needed to\n * avoid excessive token generation.\n *\n * @returns A promise that resolves to a record of header names and values\n */\n getAuthHeaders(): Promise<Record<string, string>>;\n}\n\n/**\n * Extension point for providing custom authentication to the Elasticsearch search engine.\n *\n * @remarks\n *\n * Use this extension point to provide dynamic authentication mechanisms such as\n * bearer tokens with automatic rotation. When an auth provider is set, it takes\n * precedence over any static authentication configured in app-config.yaml.\n *\n * @example\n *\n * ```ts\n * import { createBackendModule } from '@backstage/backend-plugin-api';\n * import { elasticsearchAuthExtensionPoint } from '@backstage/plugin-search-backend-module-elasticsearch';\n *\n * export default createBackendModule({\n * pluginId: 'search',\n * moduleId: 'elasticsearch-custom-auth',\n * register(env) {\n * env.registerInit({\n * deps: {\n * elasticsearchAuth: elasticsearchAuthExtensionPoint,\n * },\n * async init({ elasticsearchAuth }) {\n * elasticsearchAuth.setAuthProvider({\n * async getAuthHeaders() {\n * const token = await fetchTokenFromIdentityService();\n * return { Authorization: `Bearer ${token}` };\n * },\n * });\n * },\n * });\n * },\n * });\n * ```\n *\n * @public\n */\nexport interface ElasticSearchAuthExtensionPoint {\n /**\n * Sets the authentication provider for the Elasticsearch search engine.\n *\n * @remarks\n *\n * This method can only be called once. Subsequent calls will throw an error.\n * The auth provider takes precedence over static authentication configuration.\n *\n * @param provider - The authentication provider to use\n */\n setAuthProvider(provider: ElasticSearchAuthProvider): void;\n}\n\n/**\n * Extension point used to customize Elasticsearch/OpenSearch authentication.\n *\n * @public\n */\nexport const elasticsearchAuthExtensionPoint =\n createExtensionPoint<ElasticSearchAuthExtensionPoint>({\n id: 'search.elasticsearchEngine.auth',\n });\n"],"names":["createExtensionPoint"],"mappings":";;;;AAiHO,MAAM,kCACXA,qCAAA,CAAsD;AAAA,EACpD,EAAA,EAAI;AACN,CAAC
|
|
1
|
+
{"version":3,"file":"auth.cjs.js","sources":["../src/auth.ts"],"sourcesContent":["/*\n * Copyright 2026 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 { createExtensionPoint } from '@backstage/backend-plugin-api';\n\n/**\n * A provider that supplies authentication headers for Elasticsearch/OpenSearch requests.\n *\n * @remarks\n *\n * This interface allows for dynamic authentication mechanisms such as bearer tokens\n * that need to be refreshed or rotated. The `getAuthHeaders` method is called before\n * each request to the Elasticsearch/OpenSearch cluster, allowing for just-in-time\n * token retrieval and automatic rotation.\n *\n * @example\n *\n * ```ts\n * const authProvider: ElasticSearchAuthProvider = {\n * async getAuthHeaders() {\n * const token = await myTokenService.getToken();\n * return { Authorization: `Bearer ${token}` };\n * },\n * };\n * ```\n *\n * @public\n */\nexport interface ElasticSearchAuthProvider {\n /**\n * Returns authentication headers to be included in requests to Elasticsearch/OpenSearch.\n *\n * @remarks\n *\n * This method is called before each request, allowing for dynamic token refresh\n * and rotation. Implementations should handle caching internally if needed to\n * avoid excessive token generation.\n *\n * @returns A promise that resolves to a record of header names and values\n */\n getAuthHeaders(): Promise<Record<string, string>>;\n}\n\n/**\n * Extension point for providing custom authentication to the Elasticsearch search engine.\n *\n * @remarks\n *\n * Use this extension point to provide dynamic authentication mechanisms such as\n * bearer tokens with automatic rotation. When an auth provider is set, it takes\n * precedence over any static authentication configured in app-config.yaml.\n *\n * @example\n *\n * ```ts\n * import { createBackendModule } from '@backstage/backend-plugin-api';\n * import { elasticsearchAuthExtensionPoint } from '@backstage/plugin-search-backend-module-elasticsearch';\n *\n * export default createBackendModule({\n * pluginId: 'search',\n * moduleId: 'elasticsearch-custom-auth',\n * register(env) {\n * env.registerInit({\n * deps: {\n * elasticsearchAuth: elasticsearchAuthExtensionPoint,\n * },\n * async init({ elasticsearchAuth }) {\n * elasticsearchAuth.setAuthProvider({\n * async getAuthHeaders() {\n * const token = await fetchTokenFromIdentityService();\n * return { Authorization: `Bearer ${token}` };\n * },\n * });\n * },\n * });\n * },\n * });\n * ```\n *\n * @public\n */\nexport interface ElasticSearchAuthExtensionPoint {\n /**\n * Sets the authentication provider for the Elasticsearch search engine.\n *\n * @remarks\n *\n * This method can only be called once. Subsequent calls will throw an error.\n * The auth provider takes precedence over static authentication configuration.\n *\n * @param provider - The authentication provider to use\n */\n setAuthProvider(provider: ElasticSearchAuthProvider): void;\n}\n\n/**\n * Extension point used to customize Elasticsearch/OpenSearch authentication.\n *\n * @public\n */\nexport const elasticsearchAuthExtensionPoint =\n createExtensionPoint<ElasticSearchAuthExtensionPoint>({\n id: 'search.elasticsearchEngine.auth',\n });\n"],"names":["createExtensionPoint"],"mappings":";;;;AAiHO,MAAM,kCACXA,qCAAA,CAAsD;AAAA,EACpD,EAAA,EAAI;AACN,CAAC;;"}
|
|
@@ -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":"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"}
|
|
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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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ElasticSearchSearchEngineIndexer.cjs.js","sources":["../../src/engines/ElasticSearchSearchEngineIndexer.ts"],"sourcesContent":["/*\n * Copyright 2022 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 { BatchSearchEngineIndexer } from '@backstage/plugin-search-backend-node';\nimport { ElasticSearchClientWrapper } from './ElasticSearchClientWrapper';\nimport { IndexableDocument } from '@backstage/plugin-search-common';\nimport { Readable } from 'node:stream';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\n/**\n * Options for instantiate ElasticSearchSearchEngineIndexer\n * @public\n */\nexport type ElasticSearchSearchEngineIndexerOptions = {\n type: string;\n indexPrefix: string;\n indexSeparator: string;\n alias: string;\n logger: LoggerService;\n elasticSearchClientWrapper: ElasticSearchClientWrapper;\n batchSize: number;\n batchKeyField?: string;\n skipRefresh?: boolean;\n};\n\nfunction duration(startTimestamp: [number, number]): string {\n const delta = process.hrtime(startTimestamp);\n const seconds = delta[0] + delta[1] / 1e9;\n return `${seconds.toFixed(1)}s`;\n}\n\n/**\n * Elasticsearch specific search engine indexer.\n * @public\n */\nexport class ElasticSearchSearchEngineIndexer extends BatchSearchEngineIndexer {\n private processed: number = 0;\n private removableIndices: string[] = [];\n\n private readonly startTimestamp: [number, number];\n private readonly type: string;\n public readonly indexName: string;\n private readonly indexPrefix: string;\n private readonly indexSeparator: string;\n private readonly alias: string;\n private readonly logger: LoggerService;\n private readonly sourceStream: Readable;\n private readonly elasticSearchClientWrapper: ElasticSearchClientWrapper;\n private configuredBatchSize: number;\n private bulkResult: Promise<any>;\n private bulkClientError?: Error;\n\n constructor(options: ElasticSearchSearchEngineIndexerOptions) {\n super({ batchSize: options.batchSize });\n this.configuredBatchSize = options.batchSize;\n this.logger = options.logger.child({ documentType: options.type });\n this.startTimestamp = process.hrtime();\n this.type = options.type;\n this.indexPrefix = options.indexPrefix;\n this.indexSeparator = options.indexSeparator;\n this.indexName = this.constructIndexName(`${Date.now()}`);\n this.alias = options.alias;\n this.elasticSearchClientWrapper = options.elasticSearchClientWrapper;\n\n // The ES client bulk helper supports stream-based indexing, but we have to\n // supply the stream directly to it at instantiation-time. We can't supply\n // this class itself, so instead, we create this inline stream instead.\n this.sourceStream = new Readable({ objectMode: true });\n this.sourceStream._read = () => {};\n\n // eslint-disable-next-line consistent-this\n const that = this;\n\n // Keep a reference to the ES Bulk helper so that we can know when all\n // documents have been successfully written to ES.\n this.bulkResult = this.elasticSearchClientWrapper.bulk({\n datasource: this.sourceStream,\n onDocument(doc) {\n that.processed++;\n return {\n index: { _index: that.indexName },\n ...(options.batchKeyField && doc[options.batchKeyField]\n ? { _id: doc[options.batchKeyField] }\n : {}),\n };\n },\n refreshOnCompletion:\n options.skipRefresh !== true ? that.indexName : false,\n });\n\n // Safely catch errors thrown by the bulk helper client, e.g. HTTP timeouts\n this.bulkResult.catch(e => {\n this.bulkClientError = e;\n });\n }\n\n async initialize(): Promise<void> {\n this.logger.info(`Started indexing documents for index ${this.type}`);\n\n const indices = await this.elasticSearchClientWrapper.listIndices({\n index: this.constructIndexName('*'),\n });\n\n for (const key of Object.keys(indices.body)) {\n this.removableIndices.push(key);\n }\n\n await this.elasticSearchClientWrapper.createIndex({\n index: this.indexName,\n });\n }\n\n async index(documents: IndexableDocument[]): Promise<void> {\n await this.isReady();\n documents.forEach(document => {\n this.sourceStream.push(document);\n });\n }\n\n async finalize(): Promise<void> {\n // Wait for all documents to be processed.\n await this.isReady();\n\n // Close off the underlying stream connected to ES, indicating that no more\n // documents will be written.\n this.sourceStream.push(null);\n\n // Wait for the bulk helper to finish processing.\n const result = await this.bulkResult;\n\n // Warn that no documents were indexed, early return so that alias swapping\n // does not occur, and clean up the empty index we just created.\n if (this.processed === 0) {\n this.logger.warn(\n `Index for ${this.indexName} of ${this.type} was not ${\n this.removableIndices.length ? 'replaced' : 'created'\n }: indexer received 0 documents`,\n );\n try {\n await this.elasticSearchClientWrapper.deleteIndex({\n index: this.indexName,\n });\n } catch (error) {\n this.logger.error(`Unable to clean up elastic index: ${error}`);\n }\n return;\n }\n\n // Rotate main alias upon completion. Apply permanent secondary alias so\n // stale indices can be referenced for deletion in case initial attempt\n // fails. Allow errors to bubble up so that we can clean up the created index.\n this.logger.info(\n `Indexing completed for index ${this.indexName} of ${\n this.type\n } in ${duration(this.startTimestamp)}`,\n result,\n );\n await this.elasticSearchClientWrapper.updateAliases({\n actions: [\n {\n remove: { index: this.constructIndexName('*'), alias: this.alias },\n },\n {\n add: { index: this.indexName, alias: this.alias },\n },\n ].filter(Boolean),\n });\n\n // If any indices are removable, remove them. Do not bubble up this error,\n // as doing so would delete the now aliased index. Log instead.\n if (this.removableIndices.length) {\n this.logger.info('Removing stale search indices', {\n removableIndices: this.removableIndices,\n });\n\n // Split the array into chunks of up to 50 indices to handle the case\n // where we need to delete a lot of stalled indices\n const chunks = this.removableIndices.reduce(\n (resultArray, item, index) => {\n const chunkIndex = Math.floor(index / 50);\n\n if (!resultArray[chunkIndex]) {\n resultArray[chunkIndex] = []; // start a new chunk\n }\n\n resultArray[chunkIndex].push(item);\n\n return resultArray;\n },\n [] as string[][],\n );\n\n // Call deleteIndex for each chunk\n for (const chunk of chunks) {\n try {\n await this.elasticSearchClientWrapper.deleteIndex({\n index: chunk,\n });\n } catch (e) {\n this.logger.warn(`Failed to remove stale search indices: ${e}`);\n }\n }\n }\n }\n\n /**\n * Ensures that the number of documents sent over the wire to ES matches the\n * number of documents this stream has received so far. This helps manage\n * backpressure in other parts of the indexing pipeline.\n */\n private isReady(): Promise<void> {\n // Early exit if the underlying ES client encountered an error.\n if (this.bulkClientError) {\n return Promise.reject(this.bulkClientError);\n }\n\n // Optimization: if the stream that ES reads from has fewer docs queued\n // than the configured batch size, continue early to allow more docs to be\n // queued\n if (this.sourceStream.readableLength < this.configuredBatchSize) {\n return Promise.resolve();\n }\n\n // Otherwise, continue periodically checking the stream queue to see if\n // ES has consumed the documents and continue when it's ready for more.\n return new Promise((isReady, abort) => {\n let streamLengthChecks = 0;\n const interval = setInterval(() => {\n streamLengthChecks++;\n\n if (this.sourceStream.readableLength < this.configuredBatchSize) {\n clearInterval(interval);\n isReady();\n }\n\n // Do not allow this interval to loop endlessly; anything longer than 5\n // minutes likely indicates an unrecoverable error in ES; direct the\n // user to inspect ES logs for more clues and abort in order to allow\n // the index to be cleaned up.\n if (streamLengthChecks >= 6000) {\n clearInterval(interval);\n abort(\n new Error(\n 'Exceeded 5 minutes waiting for elastic to be ready to accept more documents. Check the elastic logs for possible problems.',\n ),\n );\n }\n }, 50);\n });\n }\n\n private constructIndexName(postFix: string) {\n return `${this.indexPrefix}${this.type}${this.indexSeparator}${postFix}`;\n }\n}\n"],"names":["BatchSearchEngineIndexer","Readable"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ElasticSearchSearchEngineIndexer.cjs.js","sources":["../../src/engines/ElasticSearchSearchEngineIndexer.ts"],"sourcesContent":["/*\n * Copyright 2022 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 { BatchSearchEngineIndexer } from '@backstage/plugin-search-backend-node';\nimport { ElasticSearchClientWrapper } from './ElasticSearchClientWrapper';\nimport { IndexableDocument } from '@backstage/plugin-search-common';\nimport { Readable } from 'node:stream';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\n/**\n * Options for instantiate ElasticSearchSearchEngineIndexer\n * @public\n */\nexport type ElasticSearchSearchEngineIndexerOptions = {\n type: string;\n indexPrefix: string;\n indexSeparator: string;\n alias: string;\n logger: LoggerService;\n elasticSearchClientWrapper: ElasticSearchClientWrapper;\n batchSize: number;\n batchKeyField?: string;\n skipRefresh?: boolean;\n};\n\nfunction duration(startTimestamp: [number, number]): string {\n const delta = process.hrtime(startTimestamp);\n const seconds = delta[0] + delta[1] / 1e9;\n return `${seconds.toFixed(1)}s`;\n}\n\n/**\n * Elasticsearch specific search engine indexer.\n * @public\n */\nexport class ElasticSearchSearchEngineIndexer extends BatchSearchEngineIndexer {\n private processed: number = 0;\n private removableIndices: string[] = [];\n\n private readonly startTimestamp: [number, number];\n private readonly type: string;\n public readonly indexName: string;\n private readonly indexPrefix: string;\n private readonly indexSeparator: string;\n private readonly alias: string;\n private readonly logger: LoggerService;\n private readonly sourceStream: Readable;\n private readonly elasticSearchClientWrapper: ElasticSearchClientWrapper;\n private configuredBatchSize: number;\n private bulkResult: Promise<any>;\n private bulkClientError?: Error;\n\n constructor(options: ElasticSearchSearchEngineIndexerOptions) {\n super({ batchSize: options.batchSize });\n this.configuredBatchSize = options.batchSize;\n this.logger = options.logger.child({ documentType: options.type });\n this.startTimestamp = process.hrtime();\n this.type = options.type;\n this.indexPrefix = options.indexPrefix;\n this.indexSeparator = options.indexSeparator;\n this.indexName = this.constructIndexName(`${Date.now()}`);\n this.alias = options.alias;\n this.elasticSearchClientWrapper = options.elasticSearchClientWrapper;\n\n // The ES client bulk helper supports stream-based indexing, but we have to\n // supply the stream directly to it at instantiation-time. We can't supply\n // this class itself, so instead, we create this inline stream instead.\n this.sourceStream = new Readable({ objectMode: true });\n this.sourceStream._read = () => {};\n\n // eslint-disable-next-line consistent-this\n const that = this;\n\n // Keep a reference to the ES Bulk helper so that we can know when all\n // documents have been successfully written to ES.\n this.bulkResult = this.elasticSearchClientWrapper.bulk({\n datasource: this.sourceStream,\n onDocument(doc) {\n that.processed++;\n return {\n index: { _index: that.indexName },\n ...(options.batchKeyField && doc[options.batchKeyField]\n ? { _id: doc[options.batchKeyField] }\n : {}),\n };\n },\n refreshOnCompletion:\n options.skipRefresh !== true ? that.indexName : false,\n });\n\n // Safely catch errors thrown by the bulk helper client, e.g. HTTP timeouts\n this.bulkResult.catch(e => {\n this.bulkClientError = e;\n });\n }\n\n async initialize(): Promise<void> {\n this.logger.info(`Started indexing documents for index ${this.type}`);\n\n const indices = await this.elasticSearchClientWrapper.listIndices({\n index: this.constructIndexName('*'),\n });\n\n for (const key of Object.keys(indices.body)) {\n this.removableIndices.push(key);\n }\n\n await this.elasticSearchClientWrapper.createIndex({\n index: this.indexName,\n });\n }\n\n async index(documents: IndexableDocument[]): Promise<void> {\n await this.isReady();\n documents.forEach(document => {\n this.sourceStream.push(document);\n });\n }\n\n async finalize(): Promise<void> {\n // Wait for all documents to be processed.\n await this.isReady();\n\n // Close off the underlying stream connected to ES, indicating that no more\n // documents will be written.\n this.sourceStream.push(null);\n\n // Wait for the bulk helper to finish processing.\n const result = await this.bulkResult;\n\n // Warn that no documents were indexed, early return so that alias swapping\n // does not occur, and clean up the empty index we just created.\n if (this.processed === 0) {\n this.logger.warn(\n `Index for ${this.indexName} of ${this.type} was not ${\n this.removableIndices.length ? 'replaced' : 'created'\n }: indexer received 0 documents`,\n );\n try {\n await this.elasticSearchClientWrapper.deleteIndex({\n index: this.indexName,\n });\n } catch (error) {\n this.logger.error(`Unable to clean up elastic index: ${error}`);\n }\n return;\n }\n\n // Rotate main alias upon completion. Apply permanent secondary alias so\n // stale indices can be referenced for deletion in case initial attempt\n // fails. Allow errors to bubble up so that we can clean up the created index.\n this.logger.info(\n `Indexing completed for index ${this.indexName} of ${\n this.type\n } in ${duration(this.startTimestamp)}`,\n result,\n );\n await this.elasticSearchClientWrapper.updateAliases({\n actions: [\n {\n remove: { index: this.constructIndexName('*'), alias: this.alias },\n },\n {\n add: { index: this.indexName, alias: this.alias },\n },\n ].filter(Boolean),\n });\n\n // If any indices are removable, remove them. Do not bubble up this error,\n // as doing so would delete the now aliased index. Log instead.\n if (this.removableIndices.length) {\n this.logger.info('Removing stale search indices', {\n removableIndices: this.removableIndices,\n });\n\n // Split the array into chunks of up to 50 indices to handle the case\n // where we need to delete a lot of stalled indices\n const chunks = this.removableIndices.reduce(\n (resultArray, item, index) => {\n const chunkIndex = Math.floor(index / 50);\n\n if (!resultArray[chunkIndex]) {\n resultArray[chunkIndex] = []; // start a new chunk\n }\n\n resultArray[chunkIndex].push(item);\n\n return resultArray;\n },\n [] as string[][],\n );\n\n // Call deleteIndex for each chunk\n for (const chunk of chunks) {\n try {\n await this.elasticSearchClientWrapper.deleteIndex({\n index: chunk,\n });\n } catch (e) {\n this.logger.warn(`Failed to remove stale search indices: ${e}`);\n }\n }\n }\n }\n\n /**\n * Ensures that the number of documents sent over the wire to ES matches the\n * number of documents this stream has received so far. This helps manage\n * backpressure in other parts of the indexing pipeline.\n */\n private isReady(): Promise<void> {\n // Early exit if the underlying ES client encountered an error.\n if (this.bulkClientError) {\n return Promise.reject(this.bulkClientError);\n }\n\n // Optimization: if the stream that ES reads from has fewer docs queued\n // than the configured batch size, continue early to allow more docs to be\n // queued\n if (this.sourceStream.readableLength < this.configuredBatchSize) {\n return Promise.resolve();\n }\n\n // Otherwise, continue periodically checking the stream queue to see if\n // ES has consumed the documents and continue when it's ready for more.\n return new Promise((isReady, abort) => {\n let streamLengthChecks = 0;\n const interval = setInterval(() => {\n streamLengthChecks++;\n\n if (this.sourceStream.readableLength < this.configuredBatchSize) {\n clearInterval(interval);\n isReady();\n }\n\n // Do not allow this interval to loop endlessly; anything longer than 5\n // minutes likely indicates an unrecoverable error in ES; direct the\n // user to inspect ES logs for more clues and abort in order to allow\n // the index to be cleaned up.\n if (streamLengthChecks >= 6000) {\n clearInterval(interval);\n abort(\n new Error(\n 'Exceeded 5 minutes waiting for elastic to be ready to accept more documents. Check the elastic logs for possible problems.',\n ),\n );\n }\n }, 50);\n });\n }\n\n private constructIndexName(postFix: string) {\n return `${this.indexPrefix}${this.type}${this.indexSeparator}${postFix}`;\n }\n}\n"],"names":["BatchSearchEngineIndexer","Readable"],"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/dist/module.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.cjs.js","sources":["../src/module.ts"],"sourcesContent":["/*\n * Copyright 2023 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 {\n coreServices,\n createBackendModule,\n createExtensionPoint,\n} from '@backstage/backend-plugin-api';\nimport { searchEngineRegistryExtensionPoint } from '@backstage/plugin-search-backend-node/alpha';\nimport {\n ElasticSearchQueryTranslator,\n ElasticSearchSearchEngine,\n} from './engines/ElasticSearchSearchEngine';\nimport {\n type ElasticSearchAuthProvider,\n type ElasticSearchAuthExtensionPoint,\n elasticsearchAuthExtensionPoint,\n} from './auth';\n\n// Re-export auth types and extension point\nexport type { ElasticSearchAuthProvider, ElasticSearchAuthExtensionPoint };\nexport { elasticsearchAuthExtensionPoint };\n\n/** @public */\nexport interface ElasticSearchQueryTranslatorExtensionPoint {\n setTranslator(translator: ElasticSearchQueryTranslator): void;\n}\n\n/**\n * Extension point used to customize the ElasticSearch query translator.\n *\n * @public\n */\nexport const elasticsearchTranslatorExtensionPoint =\n createExtensionPoint<ElasticSearchQueryTranslatorExtensionPoint>({\n id: 'search.elasticsearchEngine.translator',\n });\n\n/**\n * Search backend module for the Elasticsearch engine.\n *\n * @public\n */\nexport default createBackendModule({\n pluginId: 'search',\n moduleId: 'elasticsearch-engine',\n register(env) {\n let translator: ElasticSearchQueryTranslator | undefined;\n let authProvider: ElasticSearchAuthProvider | undefined;\n\n env.registerExtensionPoint(elasticsearchTranslatorExtensionPoint, {\n setTranslator(newTranslator) {\n if (translator) {\n throw new Error(\n 'ElasticSearch query translator may only be set once',\n );\n }\n translator = newTranslator;\n },\n });\n\n env.registerExtensionPoint(elasticsearchAuthExtensionPoint, {\n setAuthProvider(provider) {\n if (authProvider) {\n throw new Error('ElasticSearch auth provider may only be set once');\n }\n authProvider = provider;\n },\n });\n\n env.registerInit({\n deps: {\n searchEngineRegistry: searchEngineRegistryExtensionPoint,\n logger: coreServices.logger,\n config: coreServices.rootConfig,\n },\n async init({ searchEngineRegistry, logger, config }) {\n const baseKey = 'search.elasticsearch';\n const baseConfig = config.getOptional(baseKey);\n if (!baseConfig) {\n logger.warn(\n 'No configuration found under \"search.elasticsearch\" key. Skipping search engine initialization.',\n );\n return;\n }\n const indexPrefix = config.getOptionalString(\n 'search.elasticsearch.indexPrefix',\n );\n if (indexPrefix) {\n logger.info(`Index prefix will be used for indices: ${indexPrefix}`);\n }\n if (authProvider) {\n logger.info(\n 'Using custom auth provider for ElasticSearch authentication.',\n );\n }\n searchEngineRegistry.setSearchEngine(\n await ElasticSearchSearchEngine.fromConfig({\n logger,\n config,\n translator,\n authProvider,\n indexPrefix,\n }),\n );\n },\n });\n },\n});\n"],"names":["createExtensionPoint","createBackendModule","elasticsearchAuthExtensionPoint","searchEngineRegistryExtensionPoint","coreServices","ElasticSearchSearchEngine"],"mappings":";;;;;;;;;AA6CO,MAAM,wCACXA,qCAAA,CAAiE;AAAA,EAC/D,EAAA,EAAI;AACN,CAAC;AAOH,cAAeC,oCAAA,CAAoB;AAAA,EACjC,QAAA,EAAU,QAAA;AAAA,EACV,QAAA,EAAU,sBAAA;AAAA,EACV,SAAS,GAAA,EAAK;AACZ,IAAA,IAAI,UAAA;AACJ,IAAA,IAAI,YAAA;AAEJ,IAAA,GAAA,CAAI,uBAAuB,qCAAA,EAAuC;AAAA,MAChE,cAAc,aAAA,EAAe;AAC3B,QAAA,IAAI,UAAA,EAAY;AACd,UAAA,MAAM,IAAI,KAAA;AAAA,YACR;AAAA,WACF;AAAA,QACF;AACA,QAAA,UAAA,GAAa,aAAA;AAAA,MACf;AAAA,KACD,CAAA;AAED,IAAA,GAAA,CAAI,uBAAuBC,oCAAA,EAAiC;AAAA,MAC1D,gBAAgB,QAAA,EAAU;AACxB,QAAA,IAAI,YAAA,EAAc;AAChB,UAAA,MAAM,IAAI,MAAM,kDAAkD,CAAA;AAAA,QACpE;AACA,QAAA,YAAA,GAAe,QAAA;AAAA,MACjB;AAAA,KACD,CAAA;AAED,IAAA,GAAA,CAAI,YAAA,CAAa;AAAA,MACf,IAAA,EAAM;AAAA,QACJ,oBAAA,EAAsBC,wCAAA;AAAA,QACtB,QAAQC,6BAAA,CAAa,MAAA;AAAA,QACrB,QAAQA,6BAAA,CAAa;AAAA,OACvB;AAAA,MACA,MAAM,IAAA,CAAK,EAAE,oBAAA,EAAsB,MAAA,EAAQ,QAAO,EAAG;AACnD,QAAA,MAAM,OAAA,GAAU,sBAAA;AAChB,QAAA,MAAM,UAAA,GAAa,MAAA,CAAO,WAAA,CAAY,OAAO,CAAA;AAC7C,QAAA,IAAI,CAAC,UAAA,EAAY;AACf,UAAA,MAAA,CAAO,IAAA;AAAA,YACL;AAAA,WACF;AACA,UAAA;AAAA,QACF;AACA,QAAA,MAAM,cAAc,MAAA,CAAO,iBAAA;AAAA,UACzB;AAAA,SACF;AACA,QAAA,IAAI,WAAA,EAAa;AACf,UAAA,MAAA,CAAO,IAAA,CAAK,CAAA,uCAAA,EAA0C,WAAW,CAAA,CAAE,CAAA;AAAA,QACrE;AACA,QAAA,IAAI,YAAA,EAAc;AAChB,UAAA,MAAA,CAAO,IAAA;AAAA,YACL;AAAA,WACF;AAAA,QACF;AACA,QAAA,oBAAA,CAAqB,eAAA;AAAA,UACnB,MAAMC,oDAA0B,UAAA,CAAW;AAAA,YACzC,MAAA;AAAA,YACA,MAAA;AAAA,YACA,UAAA;AAAA,YACA,YAAA;AAAA,YACA;AAAA,WACD;AAAA,SACH;AAAA,MACF;AAAA,KACD,CAAA;AAAA,EACH;AACF,CAAC,CAAA
|
|
1
|
+
{"version":3,"file":"module.cjs.js","sources":["../src/module.ts"],"sourcesContent":["/*\n * Copyright 2023 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 {\n coreServices,\n createBackendModule,\n createExtensionPoint,\n} from '@backstage/backend-plugin-api';\nimport { searchEngineRegistryExtensionPoint } from '@backstage/plugin-search-backend-node/alpha';\nimport {\n ElasticSearchQueryTranslator,\n ElasticSearchSearchEngine,\n} from './engines/ElasticSearchSearchEngine';\nimport {\n type ElasticSearchAuthProvider,\n type ElasticSearchAuthExtensionPoint,\n elasticsearchAuthExtensionPoint,\n} from './auth';\n\n// Re-export auth types and extension point\nexport type { ElasticSearchAuthProvider, ElasticSearchAuthExtensionPoint };\nexport { elasticsearchAuthExtensionPoint };\n\n/** @public */\nexport interface ElasticSearchQueryTranslatorExtensionPoint {\n setTranslator(translator: ElasticSearchQueryTranslator): void;\n}\n\n/**\n * Extension point used to customize the ElasticSearch query translator.\n *\n * @public\n */\nexport const elasticsearchTranslatorExtensionPoint =\n createExtensionPoint<ElasticSearchQueryTranslatorExtensionPoint>({\n id: 'search.elasticsearchEngine.translator',\n });\n\n/**\n * Search backend module for the Elasticsearch engine.\n *\n * @public\n */\nexport default createBackendModule({\n pluginId: 'search',\n moduleId: 'elasticsearch-engine',\n register(env) {\n let translator: ElasticSearchQueryTranslator | undefined;\n let authProvider: ElasticSearchAuthProvider | undefined;\n\n env.registerExtensionPoint(elasticsearchTranslatorExtensionPoint, {\n setTranslator(newTranslator) {\n if (translator) {\n throw new Error(\n 'ElasticSearch query translator may only be set once',\n );\n }\n translator = newTranslator;\n },\n });\n\n env.registerExtensionPoint(elasticsearchAuthExtensionPoint, {\n setAuthProvider(provider) {\n if (authProvider) {\n throw new Error('ElasticSearch auth provider may only be set once');\n }\n authProvider = provider;\n },\n });\n\n env.registerInit({\n deps: {\n searchEngineRegistry: searchEngineRegistryExtensionPoint,\n logger: coreServices.logger,\n config: coreServices.rootConfig,\n },\n async init({ searchEngineRegistry, logger, config }) {\n const baseKey = 'search.elasticsearch';\n const baseConfig = config.getOptional(baseKey);\n if (!baseConfig) {\n logger.warn(\n 'No configuration found under \"search.elasticsearch\" key. Skipping search engine initialization.',\n );\n return;\n }\n const indexPrefix = config.getOptionalString(\n 'search.elasticsearch.indexPrefix',\n );\n if (indexPrefix) {\n logger.info(`Index prefix will be used for indices: ${indexPrefix}`);\n }\n if (authProvider) {\n logger.info(\n 'Using custom auth provider for ElasticSearch authentication.',\n );\n }\n searchEngineRegistry.setSearchEngine(\n await ElasticSearchSearchEngine.fromConfig({\n logger,\n config,\n translator,\n authProvider,\n indexPrefix,\n }),\n );\n },\n });\n },\n});\n"],"names":["createExtensionPoint","createBackendModule","elasticsearchAuthExtensionPoint","searchEngineRegistryExtensionPoint","coreServices","ElasticSearchSearchEngine"],"mappings":";;;;;;;;;AA6CO,MAAM,wCACXA,qCAAA,CAAiE;AAAA,EAC/D,EAAA,EAAI;AACN,CAAC;AAOH,cAAeC,oCAAA,CAAoB;AAAA,EACjC,QAAA,EAAU,QAAA;AAAA,EACV,QAAA,EAAU,sBAAA;AAAA,EACV,SAAS,GAAA,EAAK;AACZ,IAAA,IAAI,UAAA;AACJ,IAAA,IAAI,YAAA;AAEJ,IAAA,GAAA,CAAI,uBAAuB,qCAAA,EAAuC;AAAA,MAChE,cAAc,aAAA,EAAe;AAC3B,QAAA,IAAI,UAAA,EAAY;AACd,UAAA,MAAM,IAAI,KAAA;AAAA,YACR;AAAA,WACF;AAAA,QACF;AACA,QAAA,UAAA,GAAa,aAAA;AAAA,MACf;AAAA,KACD,CAAA;AAED,IAAA,GAAA,CAAI,uBAAuBC,oCAAA,EAAiC;AAAA,MAC1D,gBAAgB,QAAA,EAAU;AACxB,QAAA,IAAI,YAAA,EAAc;AAChB,UAAA,MAAM,IAAI,MAAM,kDAAkD,CAAA;AAAA,QACpE;AACA,QAAA,YAAA,GAAe,QAAA;AAAA,MACjB;AAAA,KACD,CAAA;AAED,IAAA,GAAA,CAAI,YAAA,CAAa;AAAA,MACf,IAAA,EAAM;AAAA,QACJ,oBAAA,EAAsBC,wCAAA;AAAA,QACtB,QAAQC,6BAAA,CAAa,MAAA;AAAA,QACrB,QAAQA,6BAAA,CAAa;AAAA,OACvB;AAAA,MACA,MAAM,IAAA,CAAK,EAAE,oBAAA,EAAsB,MAAA,EAAQ,QAAO,EAAG;AACnD,QAAA,MAAM,OAAA,GAAU,sBAAA;AAChB,QAAA,MAAM,UAAA,GAAa,MAAA,CAAO,WAAA,CAAY,OAAO,CAAA;AAC7C,QAAA,IAAI,CAAC,UAAA,EAAY;AACf,UAAA,MAAA,CAAO,IAAA;AAAA,YACL;AAAA,WACF;AACA,UAAA;AAAA,QACF;AACA,QAAA,MAAM,cAAc,MAAA,CAAO,iBAAA;AAAA,UACzB;AAAA,SACF;AACA,QAAA,IAAI,WAAA,EAAa;AACf,UAAA,MAAA,CAAO,IAAA,CAAK,CAAA,uCAAA,EAA0C,WAAW,CAAA,CAAE,CAAA;AAAA,QACrE;AACA,QAAA,IAAI,YAAA,EAAc;AAChB,UAAA,MAAA,CAAO,IAAA;AAAA,YACL;AAAA,WACF;AAAA,QACF;AACA,QAAA,oBAAA,CAAqB,eAAA;AAAA,UACnB,MAAMC,oDAA0B,UAAA,CAAW;AAAA,YACzC,MAAA;AAAA,YACA,MAAA;AAAA,YACA,UAAA;AAAA,YACA,YAAA;AAAA,YACA;AAAA,WACD;AAAA,SACH;AAAA,MACF;AAAA,KACD,CAAA;AAAA,EACH;AACF,CAAC,CAAA;;;;"}
|
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.1-next.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.
|
|
66
|
-
"@backstage/config": "1.3.
|
|
67
|
-
"@backstage/integration-aws-node": "0.2.
|
|
68
|
-
"@backstage/plugin-search-backend-node": "1.4.
|
|
69
|
-
"@backstage/plugin-search-common": "1.2.
|
|
70
|
-
"@elastic/elasticsearch": "^
|
|
65
|
+
"@backstage/backend-plugin-api": "1.10.2-next.0",
|
|
66
|
+
"@backstage/config": "1.3.10-next.0",
|
|
67
|
+
"@backstage/integration-aws-node": "0.2.3-next.0",
|
|
68
|
+
"@backstage/plugin-search-backend-node": "1.4.9-next.0",
|
|
69
|
+
"@backstage/plugin-search-common": "1.2.26-next.0",
|
|
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.
|
|
78
|
-
"@backstage/cli": "0.36.
|
|
79
|
-
"@elastic/elasticsearch-mock": "^
|
|
77
|
+
"@backstage/backend-test-utils": "1.11.8-next.0",
|
|
78
|
+
"@backstage/cli": "0.36.7-next.0",
|
|
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
|
},
|