@backstage/plugin-search-backend-module-elasticsearch 0.1.6-next.0 → 0.2.0-next.1

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 CHANGED
@@ -1,5 +1,36 @@
1
1
  # @backstage/plugin-search-backend-module-elasticsearch
2
2
 
3
+ ## 0.2.0-next.1
4
+
5
+ ### Minor Changes
6
+
7
+ - c5af773757: **BREAKING**: In order to remain interoperable with all currently supported
8
+ deployments of Elasticsearch, this package will now conditionally use either
9
+ the `@elastic/elasticsearch` or `@opensearch-project/opensearch` client when
10
+ communicating with your deployed cluster.
11
+
12
+ If you do not rely on types exported from this package, or if you do not make
13
+ use of the `createElasticSearchClientOptions` or `newClient` methods on the
14
+ `ElasticSearchSearchEngine` class, then upgrading to this version should not
15
+ require any further action on your part. Everything will continue to work as it
16
+ always has.
17
+
18
+ If you _do_ rely on either of the above methods or any underlying types, some
19
+ changes may be needed to your custom code. A type guard is now exported by this
20
+ package (`isOpenSearchCompatible(options)`), which you may use to help clarify
21
+ which client options are compatible with which client constructors.
22
+
23
+ If you are using this package with the `search.elasticsearch.provider` set to
24
+ `aws`, and you are making use of the `newClient` method in particular, you may
25
+ wish to start using the `@opensearch-project/opensearch` client in your custom
26
+ code.
27
+
28
+ ### Patch Changes
29
+
30
+ - Updated dependencies
31
+ - @backstage/plugin-search-backend-node@0.6.3-next.1
32
+ - @backstage/plugin-search-common@0.3.6-next.0
33
+
3
34
  ## 0.1.6-next.0
4
35
 
5
36
  ### Patch Changes
package/dist/index.cjs.js CHANGED
@@ -2,13 +2,14 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var awsEsConnection = require('@acuris/aws-es-connection');
6
- var elasticsearch = require('@elastic/elasticsearch');
5
+ var awsOsConnection = require('aws-os-connection');
7
6
  var esb = require('elastic-builder');
8
7
  var lodash = require('lodash');
9
8
  var uuid = require('uuid');
10
9
  var pluginSearchBackendNode = require('@backstage/plugin-search-backend-node');
11
10
  var stream = require('stream');
11
+ var elasticsearch = require('@elastic/elasticsearch');
12
+ var opensearch = require('@opensearch-project/opensearch');
12
13
 
13
14
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
14
15
 
@@ -33,12 +34,12 @@ class ElasticSearchSearchEngineIndexer extends pluginSearchBackendNode.BatchSear
33
34
  this.indexName = this.constructIndexName(`${Date.now()}`);
34
35
  this.alias = options.alias;
35
36
  this.removableAlias = `${this.alias}_removable`;
36
- this.elasticSearchClient = options.elasticSearchClient;
37
+ this.elasticSearchClientWrapper = options.elasticSearchClientWrapper;
37
38
  this.sourceStream = new stream.Readable({ objectMode: true });
38
39
  this.sourceStream._read = () => {
39
40
  };
40
41
  const that = this;
41
- this.bulkResult = this.elasticSearchClient.helpers.bulk({
42
+ this.bulkResult = this.elasticSearchClientWrapper.bulk({
42
43
  datasource: this.sourceStream,
43
44
  onDocument() {
44
45
  that.processed++;
@@ -51,14 +52,13 @@ class ElasticSearchSearchEngineIndexer extends pluginSearchBackendNode.BatchSear
51
52
  }
52
53
  async initialize() {
53
54
  this.logger.info(`Started indexing documents for index ${this.type}`);
54
- const aliases = await this.elasticSearchClient.cat.aliases({
55
- format: "json",
56
- name: [this.alias, this.removableAlias]
55
+ const aliases = await this.elasticSearchClientWrapper.getAliases({
56
+ aliases: [this.alias, this.removableAlias]
57
57
  });
58
58
  this.removableIndices = [
59
59
  ...new Set(aliases.body.map((r) => r.index))
60
60
  ];
61
- await this.elasticSearchClient.indices.create({
61
+ await this.elasticSearchClientWrapper.createIndex({
62
62
  index: this.indexName
63
63
  });
64
64
  }
@@ -74,28 +74,26 @@ class ElasticSearchSearchEngineIndexer extends pluginSearchBackendNode.BatchSear
74
74
  this.sourceStream.push(null);
75
75
  const result = await this.bulkResult;
76
76
  this.logger.info(`Indexing completed for index ${this.type} in ${duration(this.startTimestamp)}`, result);
77
- await this.elasticSearchClient.indices.updateAliases({
78
- body: {
79
- actions: [
80
- {
81
- remove: { index: this.constructIndexName("*"), alias: this.alias }
82
- },
83
- this.removableIndices.length ? {
84
- add: {
85
- indices: this.removableIndices,
86
- alias: this.removableAlias
87
- }
88
- } : void 0,
89
- {
90
- add: { index: this.indexName, alias: this.alias }
77
+ await this.elasticSearchClientWrapper.updateAliases({
78
+ actions: [
79
+ {
80
+ remove: { index: this.constructIndexName("*"), alias: this.alias }
81
+ },
82
+ this.removableIndices.length ? {
83
+ add: {
84
+ indices: this.removableIndices,
85
+ alias: this.removableAlias
91
86
  }
92
- ].filter(Boolean)
93
- }
87
+ } : void 0,
88
+ {
89
+ add: { index: this.indexName, alias: this.alias }
90
+ }
91
+ ].filter(Boolean)
94
92
  });
95
93
  if (this.removableIndices.length) {
96
94
  this.logger.info("Removing stale search indices", this.removableIndices);
97
95
  try {
98
- await this.elasticSearchClient.indices.delete({
96
+ await this.elasticSearchClientWrapper.deleteIndex({
99
97
  index: this.removableIndices
100
98
  });
101
99
  } catch (e) {
@@ -118,6 +116,117 @@ class ElasticSearchSearchEngineIndexer extends pluginSearchBackendNode.BatchSear
118
116
  }
119
117
  }
120
118
 
119
+ const isOpenSearchCompatible = (opts) => {
120
+ return (opts == null ? void 0 : opts.provider) === "aws";
121
+ };
122
+
123
+ class ElasticSearchClientWrapper {
124
+ constructor({
125
+ openSearchClient,
126
+ elasticSearchClient
127
+ }) {
128
+ this.openSearchClient = openSearchClient;
129
+ this.elasticSearchClient = elasticSearchClient;
130
+ }
131
+ static fromClientOptions(options) {
132
+ if (isOpenSearchCompatible(options)) {
133
+ return new ElasticSearchClientWrapper({
134
+ openSearchClient: new opensearch.Client(options)
135
+ });
136
+ }
137
+ return new ElasticSearchClientWrapper({
138
+ elasticSearchClient: new elasticsearch.Client(options)
139
+ });
140
+ }
141
+ search({ index, body }) {
142
+ if (this.openSearchClient) {
143
+ return this.openSearchClient.search({ index, body });
144
+ }
145
+ if (this.elasticSearchClient) {
146
+ return this.elasticSearchClient.search({ index, body });
147
+ }
148
+ throw new Error("No client defined");
149
+ }
150
+ bulk(bulkOptions) {
151
+ if (this.openSearchClient) {
152
+ return this.openSearchClient.helpers.bulk(bulkOptions);
153
+ }
154
+ if (this.elasticSearchClient) {
155
+ return this.elasticSearchClient.helpers.bulk(bulkOptions);
156
+ }
157
+ throw new Error("No client defined");
158
+ }
159
+ putIndexTemplate(template) {
160
+ if (this.openSearchClient) {
161
+ return this.openSearchClient.indices.putIndexTemplate(template);
162
+ }
163
+ if (this.elasticSearchClient) {
164
+ return this.elasticSearchClient.indices.putIndexTemplate(template);
165
+ }
166
+ throw new Error("No client defined");
167
+ }
168
+ indexExists({ index }) {
169
+ if (this.openSearchClient) {
170
+ return this.openSearchClient.indices.exists({ index });
171
+ }
172
+ if (this.elasticSearchClient) {
173
+ return this.elasticSearchClient.indices.exists({ index });
174
+ }
175
+ throw new Error("No client defined");
176
+ }
177
+ deleteIndex({ index }) {
178
+ if (this.openSearchClient) {
179
+ return this.openSearchClient.indices.delete({ index });
180
+ }
181
+ if (this.elasticSearchClient) {
182
+ return this.elasticSearchClient.indices.delete({ index });
183
+ }
184
+ throw new Error("No client defined");
185
+ }
186
+ createIndex({ index }) {
187
+ if (this.openSearchClient) {
188
+ return this.openSearchClient.indices.create({ index });
189
+ }
190
+ if (this.elasticSearchClient) {
191
+ return this.elasticSearchClient.indices.create({ index });
192
+ }
193
+ throw new Error("No client defined");
194
+ }
195
+ getAliases({ aliases }) {
196
+ if (this.openSearchClient) {
197
+ return this.openSearchClient.cat.aliases({
198
+ format: "json",
199
+ name: aliases
200
+ });
201
+ }
202
+ if (this.elasticSearchClient) {
203
+ return this.elasticSearchClient.cat.aliases({
204
+ format: "json",
205
+ name: aliases
206
+ });
207
+ }
208
+ throw new Error("No client defined");
209
+ }
210
+ updateAliases({ actions }) {
211
+ const filteredActions = actions.filter(Boolean);
212
+ if (this.openSearchClient) {
213
+ return this.openSearchClient.indices.updateAliases({
214
+ body: {
215
+ actions: filteredActions
216
+ }
217
+ });
218
+ }
219
+ if (this.elasticSearchClient) {
220
+ return this.elasticSearchClient.indices.updateAliases({
221
+ body: {
222
+ actions: filteredActions
223
+ }
224
+ });
225
+ }
226
+ throw new Error("No client defined");
227
+ }
228
+ }
229
+
121
230
  function isBlank(str) {
122
231
  return lodash.isEmpty(str) && !lodash.isNumber(str) || lodash.isNaN(str);
123
232
  }
@@ -128,7 +237,7 @@ class ElasticSearchSearchEngine {
128
237
  this.indexPrefix = indexPrefix;
129
238
  this.logger = logger;
130
239
  this.indexSeparator = "-index__";
131
- this.elasticSearchClient = this.newClient((options) => new elasticsearch.Client(options));
240
+ this.elasticSearchClientWrapper = ElasticSearchClientWrapper.fromClientOptions(elasticSearchClientOptions);
132
241
  const uuidTag = uuid.v4();
133
242
  this.highlightOptions = {
134
243
  preTag: `<${uuidTag}>`,
@@ -149,7 +258,7 @@ class ElasticSearchSearchEngine {
149
258
  if (options.provider === "elastic") {
150
259
  logger.info("Initializing Elastic.co ElasticSearch search engine.");
151
260
  } else if (options.provider === "aws") {
152
- logger.info("Initializing AWS ElasticSearch search engine.");
261
+ logger.info("Initializing AWS OpenSearch search engine.");
153
262
  } else {
154
263
  logger.info("Initializing ElasticSearch search engine.");
155
264
  }
@@ -189,7 +298,7 @@ class ElasticSearchSearchEngine {
189
298
  }
190
299
  async setIndexTemplate(template) {
191
300
  try {
192
- await this.elasticSearchClient.indices.putIndexTemplate(template);
301
+ await this.elasticSearchClientWrapper.putIndexTemplate(template);
193
302
  this.logger.info("Custom index template set");
194
303
  } catch (error) {
195
304
  this.logger.error(`Unable to set custom index template: ${error}`);
@@ -202,19 +311,19 @@ class ElasticSearchSearchEngine {
202
311
  indexPrefix: this.indexPrefix,
203
312
  indexSeparator: this.indexSeparator,
204
313
  alias,
205
- elasticSearchClient: this.elasticSearchClient,
314
+ elasticSearchClientWrapper: this.elasticSearchClientWrapper,
206
315
  logger: this.logger
207
316
  });
208
317
  indexer.on("error", async (e) => {
209
318
  this.logger.error(`Failed to index documents for type ${type}`, e);
210
319
  try {
211
- const response = await this.elasticSearchClient.indices.exists({
320
+ const response = await this.elasticSearchClientWrapper.indexExists({
212
321
  index: indexer.indexName
213
322
  });
214
323
  const indexCreated = response.body;
215
324
  if (indexCreated) {
216
325
  this.logger.info(`Removing created index ${indexer.indexName}`);
217
- await this.elasticSearchClient.indices.delete({
326
+ await this.elasticSearchClientWrapper.deleteIndex({
218
327
  index: indexer.indexName
219
328
  });
220
329
  }
@@ -228,7 +337,7 @@ class ElasticSearchSearchEngine {
228
337
  const { elasticSearchQuery, documentTypes, pageSize } = this.translator(query, { highlightOptions: this.highlightOptions });
229
338
  const queryIndices = documentTypes ? documentTypes.map((it) => this.constructSearchAlias(it)) : this.constructSearchAlias("*");
230
339
  try {
231
- const result = await this.elasticSearchClient.search({
340
+ const result = await this.elasticSearchClientWrapper.search({
232
341
  index: queryIndices,
233
342
  body: elasticSearchQuery
234
343
  });
@@ -308,8 +417,8 @@ async function createElasticSearchClientOptions(config) {
308
417
  };
309
418
  }
310
419
  if (config.getOptionalString("provider") === "aws") {
311
- const awsCredentials = await awsEsConnection.awsGetCredentials();
312
- const AWSConnection = awsEsConnection.createAWSConnection(awsCredentials);
420
+ const awsCredentials = await awsOsConnection.awsGetCredentials();
421
+ const AWSConnection = awsOsConnection.createAWSConnection(awsCredentials);
313
422
  return {
314
423
  provider: "aws",
315
424
  node: config.getString("node"),
@@ -340,4 +449,5 @@ async function createElasticSearchClientOptions(config) {
340
449
  }
341
450
 
342
451
  exports.ElasticSearchSearchEngine = ElasticSearchSearchEngine;
452
+ exports.isOpenSearchCompatible = isOpenSearchCompatible;
343
453
  //# sourceMappingURL=index.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../src/engines/ElasticSearchSearchEngineIndexer.ts","../src/engines/ElasticSearchSearchEngine.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 { IndexableDocument } from '@backstage/plugin-search-common';\nimport { Client } from '@elastic/elasticsearch';\nimport { Readable } from 'stream';\nimport { Logger } from 'winston';\n\n/**\n * Options for instansiate ElasticSearchSearchEngineIndexer\n * @public\n */\nexport type ElasticSearchSearchEngineIndexerOptions = {\n type: string;\n indexPrefix: string;\n indexSeparator: string;\n alias: string;\n logger: Logger;\n elasticSearchClient: Client;\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 received: number = 0;\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 removableAlias: string;\n private readonly logger: Logger;\n private readonly sourceStream: Readable;\n private readonly elasticSearchClient: Client;\n private bulkResult: Promise<any>;\n\n constructor(options: ElasticSearchSearchEngineIndexerOptions) {\n super({ batchSize: 1000 });\n this.logger = options.logger;\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.removableAlias = `${this.alias}_removable`;\n this.elasticSearchClient = options.elasticSearchClient;\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.elasticSearchClient.helpers.bulk({\n datasource: this.sourceStream,\n onDocument() {\n that.processed++;\n return {\n index: { _index: that.indexName },\n };\n },\n refreshOnCompletion: that.indexName,\n });\n }\n\n async initialize(): Promise<void> {\n this.logger.info(`Started indexing documents for index ${this.type}`);\n\n const aliases = await this.elasticSearchClient.cat.aliases({\n format: 'json',\n name: [this.alias, this.removableAlias],\n });\n\n this.removableIndices = [\n ...new Set(aliases.body.map((r: Record<string, any>) => r.index)),\n ] as string[];\n\n await this.elasticSearchClient.indices.create({\n index: this.indexName,\n });\n }\n\n async index(documents: IndexableDocument[]): Promise<void> {\n await this.isReady();\n documents.forEach(document => {\n this.received++;\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 // 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.type} in ${duration(\n this.startTimestamp,\n )}`,\n result,\n );\n await this.elasticSearchClient.indices.updateAliases({\n body: {\n actions: [\n {\n remove: { index: this.constructIndexName('*'), alias: this.alias },\n },\n this.removableIndices.length\n ? {\n add: {\n indices: this.removableIndices,\n alias: this.removableAlias,\n },\n }\n : undefined,\n {\n add: { index: this.indexName, alias: this.alias },\n },\n ].filter(Boolean),\n },\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', this.removableIndices);\n try {\n await this.elasticSearchClient.indices.delete({\n index: this.removableIndices,\n });\n } catch (e) {\n this.logger.warn(`Failed to remove stale search indices: ${e}`);\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 return new Promise(resolve => {\n const interval = setInterval(() => {\n if (this.received === this.processed) {\n clearInterval(interval);\n resolve();\n }\n }, 50);\n });\n }\n\n private constructIndexName(postFix: string) {\n return `${this.indexPrefix}${this.type}${this.indexSeparator}${postFix}`;\n }\n}\n","/*\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 awsGetCredentials,\n createAWSConnection,\n} from '@acuris/aws-es-connection';\nimport { Config } from '@backstage/config';\nimport {\n IndexableDocument,\n IndexableResult,\n IndexableResultSet,\n SearchEngine,\n SearchQuery,\n} from '@backstage/plugin-search-common';\nimport { Client } from '@elastic/elasticsearch';\nimport esb from 'elastic-builder';\nimport { isEmpty, isNaN as nan, isNumber } from 'lodash';\nimport { v4 as uuid } from 'uuid';\nimport { Logger } from 'winston';\nimport type { ElasticSearchClientOptions } from './ElasticSearchClientOptions';\nimport { ElasticSearchSearchEngineIndexer } from './ElasticSearchSearchEngineIndexer';\n\nexport type { ElasticSearchClientOptions };\n\n/**\n * Elasticsearch specific index template\n * @public\n */\nexport type ElasticSearchCustomIndexTemplate = {\n name: string;\n body: ElasticSearchCustomIndexTemplateBody;\n};\n\n/**\n * Elasticsearch specific index template body\n * @public\n */\nexport type ElasticSearchCustomIndexTemplateBody = {\n /**\n * Array of wildcard (*) expressions used to match the names of data streams and indices during creation.\n */\n index_patterns: string[];\n /**\n * An ordered list of component template names.\n * Component templates are merged in the order specified,\n * meaning that the last component template specified has the highest precedence.\n */\n composed_of?: string[];\n /**\n * See available properties of template\n * https://www.elastic.co/guide/en/elasticsearch/reference/7.15/indices-put-template.html#put-index-template-api-request-body\n */\n template?: Record<string, any>;\n};\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};\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 instansiate ElasticSearchSearchEngine\n * @public\n */\nexport type ElasticSearchOptions = {\n logger: Logger;\n config: Config;\n aliasPostfix?: string;\n indexPrefix?: string;\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 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\n/**\n * @public\n */\nexport class ElasticSearchSearchEngine implements SearchEngine {\n private readonly elasticSearchClient: Client;\n private readonly highlightOptions: ElasticSearchHighlightConfig;\n\n constructor(\n private readonly elasticSearchClientOptions: ElasticSearchClientOptions,\n private readonly aliasPostfix: string,\n private readonly indexPrefix: string,\n private readonly logger: Logger,\n highlightOptions?: ElasticSearchHighlightOptions,\n ) {\n this.elasticSearchClient = this.newClient(options => new Client(options));\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 }\n\n static async fromConfig({\n logger,\n config,\n aliasPostfix = `search`,\n indexPrefix = ``,\n }: ElasticSearchOptions) {\n const options = await createElasticSearchClientOptions(\n config.getConfig('search.elasticsearch'),\n );\n if (options.provider === 'elastic') {\n logger.info('Initializing Elastic.co ElasticSearch search engine.');\n } else if (options.provider === 'aws') {\n logger.info('Initializing AWS ElasticSearch search engine.');\n } else {\n logger.info('Initializing ElasticSearch search engine.');\n }\n\n return new ElasticSearchSearchEngine(\n options,\n aliasPostfix,\n indexPrefix,\n logger,\n config.getOptional<ElasticSearchHighlightOptions>(\n 'search.elasticsearch.highlightOptions',\n ),\n );\n }\n\n /**\n * Create a custom search client from the derived elastic search\n * configuration. This need not be the same client that the engine uses\n * internally.\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(\n '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 const esbQuery = isBlank(term)\n ? esb.matchAllQuery()\n : esb\n .multiMatchQuery(['*'], term)\n .fuzziness('auto')\n .minimumShouldMatch(1);\n const pageSize = 25;\n const { page } = decodePageCursor(pageCursor);\n\n let esbRequestBodySearch = esb\n .requestBodySearch()\n .query(esb.boolQuery().filter(filter).must([esbQuery]))\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.elasticSearchClient.indices.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\n const indexer = new ElasticSearchSearchEngineIndexer({\n type,\n indexPrefix: this.indexPrefix,\n indexSeparator: this.indexSeparator,\n alias,\n elasticSearchClient: this.elasticSearchClient,\n logger: this.logger,\n });\n\n // Attempt cleanup upon failure.\n indexer.on('error', async e => {\n this.logger.error(`Failed to index documents for type ${type}`, e);\n try {\n const response = await this.elasticSearchClient.indices.exists({\n index: indexer.indexName,\n });\n const indexCreated = response.body;\n if (indexCreated) {\n this.logger.info(`Removing created index ${indexer.indexName}`);\n await this.elasticSearchClient.indices.delete({\n index: indexer.indexName,\n });\n }\n } catch (error) {\n this.logger.error(`Unable to clean up elastic index: ${error}`);\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 { highlightOptions: this.highlightOptions },\n );\n const queryIndices = documentTypes\n ? documentTypes.map(it => this.constructSearchAlias(it))\n : this.constructSearchAlias('*');\n try {\n const result = await this.elasticSearchClient.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 };\n } catch (e) {\n this.logger.error(\n `Failed to query documents for indices ${queryIndices}`,\n e,\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\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\nasync function createElasticSearchClientOptions(\n config?: Config,\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 = config.getConfig('auth');\n return {\n provider: 'elastic',\n cloud: {\n id: config.getString('cloudId'),\n },\n auth: {\n username: authConfig.getString('username'),\n password: authConfig.getString('password'),\n },\n ...(sslConfig\n ? {\n ssl: {\n rejectUnauthorized:\n sslConfig?.getOptionalBoolean('rejectUnauthorized'),\n },\n }\n : {}),\n };\n }\n if (config.getOptionalString('provider') === 'aws') {\n const awsCredentials = await awsGetCredentials();\n const AWSConnection = createAWSConnection(awsCredentials);\n return {\n provider: 'aws',\n node: config.getString('node'),\n ...AWSConnection,\n ...(sslConfig\n ? {\n ssl: {\n rejectUnauthorized:\n sslConfig?.getOptionalBoolean('rejectUnauthorized'),\n },\n }\n : {}),\n };\n }\n const authConfig = 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,\n ...(sslConfig\n ? {\n ssl: {\n rejectUnauthorized:\n sslConfig?.getOptionalBoolean('rejectUnauthorized'),\n },\n }\n : {}),\n };\n}\n"],"names":["BatchSearchEngineIndexer","Readable","isEmpty","isNumber","nan","Client","uuid","esb","awsGetCredentials","createAWSConnection"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/engines/ElasticSearchSearchEngineIndexer.ts","../src/engines/ElasticSearchClientOptions.ts","../src/engines/ElasticSearchClientWrapper.ts","../src/engines/ElasticSearchSearchEngine.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 { IndexableDocument } from '@backstage/plugin-search-common';\nimport { Readable } from 'stream';\nimport { Logger } from 'winston';\nimport { ElasticSearchClientWrapper } from './ElasticSearchClientWrapper';\n\n/**\n * Options for instansiate ElasticSearchSearchEngineIndexer\n * @public\n */\nexport type ElasticSearchSearchEngineIndexerOptions = {\n type: string;\n indexPrefix: string;\n indexSeparator: string;\n alias: string;\n logger: Logger;\n elasticSearchClientWrapper: ElasticSearchClientWrapper;\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 received: number = 0;\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 removableAlias: string;\n private readonly logger: Logger;\n private readonly sourceStream: Readable;\n private readonly elasticSearchClientWrapper: ElasticSearchClientWrapper;\n private bulkResult: Promise<any>;\n\n constructor(options: ElasticSearchSearchEngineIndexerOptions) {\n super({ batchSize: 1000 });\n this.logger = options.logger;\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.removableAlias = `${this.alias}_removable`;\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() {\n that.processed++;\n return {\n index: { _index: that.indexName },\n };\n },\n refreshOnCompletion: that.indexName,\n });\n }\n\n async initialize(): Promise<void> {\n this.logger.info(`Started indexing documents for index ${this.type}`);\n\n const aliases = await this.elasticSearchClientWrapper.getAliases({\n aliases: [this.alias, this.removableAlias],\n });\n\n this.removableIndices = [\n ...new Set(aliases.body.map((r: Record<string, any>) => r.index)),\n ] as string[];\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.received++;\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 // 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.type} in ${duration(\n this.startTimestamp,\n )}`,\n result,\n );\n await this.elasticSearchClientWrapper.updateAliases({\n actions: [\n {\n remove: { index: this.constructIndexName('*'), alias: this.alias },\n },\n this.removableIndices.length\n ? {\n add: {\n indices: this.removableIndices,\n alias: this.removableAlias,\n },\n }\n : undefined,\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', this.removableIndices);\n try {\n await this.elasticSearchClientWrapper.deleteIndex({\n index: this.removableIndices,\n });\n } catch (e) {\n this.logger.warn(`Failed to remove stale search indices: ${e}`);\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 return new Promise(resolve => {\n const interval = setInterval(() => {\n if (this.received === this.processed) {\n clearInterval(interval);\n resolve();\n }\n }, 50);\n });\n }\n\n private constructIndexName(postFix: string) {\n return `${this.indexPrefix}${this.type}${this.indexSeparator}${postFix}`;\n }\n}\n","/*\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 '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 opts?.provider === 'aws';\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';\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: {\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","/*\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 { Client as ElasticSearchClient } from '@elastic/elasticsearch';\nimport { Client as OpenSearchClient } from '@opensearch-project/opensearch';\nimport { Readable } from '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({\n openSearchClient,\n elasticSearchClient,\n }: {\n openSearchClient?: OpenSearchClient;\n elasticSearchClient?: ElasticSearchClient;\n }) {\n this.openSearchClient = openSearchClient;\n this.elasticSearchClient = 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({ index, body }: { index: string | string[]; body: Object }) {\n if (this.openSearchClient) {\n return this.openSearchClient.search({ index, body });\n }\n\n if (this.elasticSearchClient) {\n return this.elasticSearchClient.search({ index, body });\n }\n\n throw new Error('No client defined');\n }\n\n bulk(bulkOptions: {\n datasource: Readable;\n onDocument: () => 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 indexExists({ index }: { index: string | string[] }) {\n if (this.openSearchClient) {\n return this.openSearchClient.indices.exists({ index });\n }\n\n if (this.elasticSearchClient) {\n return this.elasticSearchClient.indices.exists({ index });\n }\n\n throw new Error('No client defined');\n }\n\n deleteIndex({ index }: { index: string | string[] }) {\n if (this.openSearchClient) {\n return this.openSearchClient.indices.delete({ index });\n }\n\n if (this.elasticSearchClient) {\n return this.elasticSearchClient.indices.delete({ index });\n }\n\n throw new Error('No client defined');\n }\n\n createIndex({ index }: { index: string }) {\n if (this.openSearchClient) {\n return this.openSearchClient.indices.create({ index });\n }\n\n if (this.elasticSearchClient) {\n return this.elasticSearchClient.indices.create({ index });\n }\n\n throw new Error('No client defined');\n }\n\n getAliases({ aliases }: { aliases: string[] }) {\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 updateAliases({ actions }: { actions: ElasticSearchAliasAction[] }) {\n const filteredActions = actions.filter(Boolean);\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","/*\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 { awsGetCredentials, createAWSConnection } from 'aws-os-connection';\nimport { Config } from '@backstage/config';\nimport {\n IndexableDocument,\n IndexableResult,\n IndexableResultSet,\n SearchEngine,\n SearchQuery,\n} from '@backstage/plugin-search-common';\nimport esb from 'elastic-builder';\nimport { isEmpty, isNaN as nan, isNumber } from 'lodash';\nimport { v4 as uuid } from 'uuid';\nimport { Logger } from 'winston';\nimport { ElasticSearchClientOptions } from './ElasticSearchClientOptions';\nimport { ElasticSearchSearchEngineIndexer } from './ElasticSearchSearchEngineIndexer';\nimport { ElasticSearchCustomIndexTemplate } from './types';\nimport { ElasticSearchClientWrapper } from './ElasticSearchClientWrapper';\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};\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 instansiate ElasticSearchSearchEngine\n * @public\n */\nexport type ElasticSearchOptions = {\n logger: Logger;\n config: Config;\n aliasPostfix?: string;\n indexPrefix?: string;\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 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\n/**\n * @public\n */\nexport class ElasticSearchSearchEngine implements SearchEngine {\n private readonly elasticSearchClientWrapper: ElasticSearchClientWrapper;\n private readonly highlightOptions: ElasticSearchHighlightConfig;\n\n constructor(\n private readonly elasticSearchClientOptions: ElasticSearchClientOptions,\n private readonly aliasPostfix: string,\n private readonly indexPrefix: string,\n private readonly logger: Logger,\n highlightOptions?: ElasticSearchHighlightOptions,\n ) {\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 }\n\n static async fromConfig({\n logger,\n config,\n aliasPostfix = `search`,\n indexPrefix = ``,\n }: ElasticSearchOptions) {\n const options = await createElasticSearchClientOptions(\n config.getConfig('search.elasticsearch'),\n );\n if (options.provider === 'elastic') {\n logger.info('Initializing Elastic.co ElasticSearch search engine.');\n } else if (options.provider === 'aws') {\n logger.info('Initializing AWS OpenSearch search engine.');\n } else {\n logger.info('Initializing ElasticSearch search engine.');\n }\n\n return new ElasticSearchSearchEngine(\n options,\n aliasPostfix,\n indexPrefix,\n logger,\n config.getOptional<ElasticSearchHighlightOptions>(\n 'search.elasticsearch.highlightOptions',\n ),\n );\n }\n\n /**\n * Create a custom search client from the derived elastic search\n * configuration. This need not be the same client that the engine uses\n * internally.\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(\n '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 const esbQuery = isBlank(term)\n ? esb.matchAllQuery()\n : esb\n .multiMatchQuery(['*'], term)\n .fuzziness('auto')\n .minimumShouldMatch(1);\n const pageSize = 25;\n const { page } = decodePageCursor(pageCursor);\n\n let esbRequestBodySearch = esb\n .requestBodySearch()\n .query(esb.boolQuery().filter(filter).must([esbQuery]))\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\n const indexer = new ElasticSearchSearchEngineIndexer({\n type,\n indexPrefix: this.indexPrefix,\n indexSeparator: this.indexSeparator,\n alias,\n elasticSearchClientWrapper: this.elasticSearchClientWrapper,\n logger: this.logger,\n });\n\n // Attempt cleanup upon failure.\n indexer.on('error', async e => {\n this.logger.error(`Failed to index documents for type ${type}`, e);\n try {\n const response = await this.elasticSearchClientWrapper.indexExists({\n index: indexer.indexName,\n });\n const indexCreated = response.body;\n if (indexCreated) {\n this.logger.info(`Removing created index ${indexer.indexName}`);\n await this.elasticSearchClientWrapper.deleteIndex({\n index: indexer.indexName,\n });\n }\n } catch (error) {\n this.logger.error(`Unable to clean up elastic index: ${error}`);\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 { highlightOptions: this.highlightOptions },\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 };\n } catch (e) {\n this.logger.error(\n `Failed to query documents for indices ${queryIndices}`,\n e,\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\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\nexport async function createElasticSearchClientOptions(\n config?: Config,\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 = config.getConfig('auth');\n return {\n provider: 'elastic',\n cloud: {\n id: config.getString('cloudId'),\n },\n auth: {\n username: authConfig.getString('username'),\n password: authConfig.getString('password'),\n },\n ...(sslConfig\n ? {\n ssl: {\n rejectUnauthorized:\n sslConfig?.getOptionalBoolean('rejectUnauthorized'),\n },\n }\n : {}),\n };\n }\n if (config.getOptionalString('provider') === 'aws') {\n const awsCredentials = await awsGetCredentials();\n const AWSConnection = createAWSConnection(awsCredentials);\n return {\n provider: 'aws',\n node: config.getString('node'),\n ...AWSConnection,\n ...(sslConfig\n ? {\n ssl: {\n rejectUnauthorized:\n sslConfig?.getOptionalBoolean('rejectUnauthorized'),\n },\n }\n : {}),\n };\n }\n const authConfig = 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,\n ...(sslConfig\n ? {\n ssl: {\n rejectUnauthorized:\n sslConfig?.getOptionalBoolean('rejectUnauthorized'),\n },\n }\n : {}),\n };\n}\n"],"names":["BatchSearchEngineIndexer","Readable","OpenSearchClient","ElasticSearchClient","isEmpty","isNumber","nan","uuid","esb","awsGetCredentials","createAWSConnection"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -4,24 +4,71 @@ import { IndexableDocument, SearchQuery, SearchEngine, IndexableResultSet } from
4
4
  import { Logger } from 'winston';
5
5
  import { ConnectionOptions } from 'tls';
6
6
  import { BatchSearchEngineIndexer } from '@backstage/plugin-search-backend-node';
7
- import { Client } from '@elastic/elasticsearch';
7
+ import * as _opensearch_project_opensearch_lib_Helpers from '@opensearch-project/opensearch/lib/Helpers';
8
+ import * as _opensearch_project_opensearch_lib_Transport from '@opensearch-project/opensearch/lib/Transport';
9
+ import * as _opensearch_project_opensearch from '@opensearch-project/opensearch';
10
+ import { Readable } from 'stream';
8
11
 
9
12
  /**
10
- * Options used to configure the `@elastic/elasticsearch` client and
11
- * are what will be passed as an argument to the
12
- * {@link ElasticSearchSearchEngine.newClient} method
13
+ * Typeguard to differentiate ElasticSearch client options which are compatible
14
+ * with OpenSearch vs. ElasticSearch clients. Useful when calling the
15
+ * {@link ElasticSearchSearchEngine.newClient} method.
16
+ *
17
+ * @public
18
+ */
19
+ declare const isOpenSearchCompatible: (opts: ElasticSearchClientOptions) => opts is OpenSearchElasticSearchClientOptions;
20
+ /**
21
+ * Options used to configure the `@elastic/elasticsearch` client or the
22
+ * `@opensearch-project/opensearch` client, depending on the given config. It
23
+ * will be passed as an argument to the
24
+ * {@link ElasticSearchSearchEngine.newClient} method.
25
+ *
26
+ * @public
27
+ */
28
+ declare type ElasticSearchClientOptions = ElasticSearchElasticSearchClientOptions | OpenSearchElasticSearchClientOptions;
29
+ /**
30
+ * Options used to configure the `@opensearch-project/opensearch` client.
31
+ *
32
+ * They are drawn from the `ClientOptions` class of `@opensearch-project/opensearch`,
33
+ * but are maintained separately so that this interface is not coupled to it.
34
+ *
35
+ * @public
36
+ */
37
+ interface OpenSearchElasticSearchClientOptions extends BaseElasticSearchClientOptions {
38
+ provider?: 'aws';
39
+ auth?: OpenSearchAuth;
40
+ connection?: OpenSearchConnectionConstructor;
41
+ node?: string | string[] | OpenSearchNodeOptions | OpenSearchNodeOptions[];
42
+ nodes?: string | string[] | OpenSearchNodeOptions | OpenSearchNodeOptions[];
43
+ }
44
+ /**
45
+ * Options used to configure the `@elastic/elasticsearch` client.
13
46
  *
14
47
  * They are drawn from the `ClientOptions` class of `@elastic/elasticsearch`,
15
- * but are maintained separately so that this interface is not coupled to
48
+ * but are maintained separately so that this interface is not coupled to it.
16
49
  *
17
50
  * @public
18
51
  */
19
- interface ElasticSearchClientOptions {
20
- provider?: 'aws' | 'elastic';
52
+ interface ElasticSearchElasticSearchClientOptions extends BaseElasticSearchClientOptions {
53
+ provider?: 'elastic';
54
+ auth?: ElasticSearchAuth;
55
+ Connection?: ElasticSearchConnectionConstructor;
21
56
  node?: string | string[] | ElasticSearchNodeOptions | ElasticSearchNodeOptions[];
22
57
  nodes?: string | string[] | ElasticSearchNodeOptions | ElasticSearchNodeOptions[];
58
+ cloud?: {
59
+ id: string;
60
+ username?: string;
61
+ password?: string;
62
+ };
63
+ }
64
+ /**
65
+ * Base client options that are shared across `@opensearch-project/opensearch`
66
+ * and `@elastic/elasticsearch` clients.
67
+ *
68
+ * @public
69
+ */
70
+ interface BaseElasticSearchClientOptions {
23
71
  Transport?: ElasticSearchTransportConstructor;
24
- Connection?: ElasticSearchConnectionConstructor;
25
72
  maxRetries?: number;
26
73
  requestTimeout?: number;
27
74
  pingTimeout?: number;
@@ -39,23 +86,21 @@ interface ElasticSearchClientOptions {
39
86
  headers?: Record<string, any>;
40
87
  opaqueIdPrefix?: string;
41
88
  name?: string | symbol;
42
- auth?: ElasticSearchAuth;
43
89
  proxy?: string | URL;
44
90
  enableMetaHeader?: boolean;
45
- cloud?: {
46
- id: string;
47
- username?: string;
48
- password?: string;
49
- };
50
91
  disablePrototypePoisoningProtection?: boolean | 'proto' | 'constructor';
51
92
  }
52
93
  /**
53
94
  * @public
54
95
  */
55
- declare type ElasticSearchAuth = {
96
+ declare type OpenSearchAuth = {
56
97
  username: string;
57
98
  password: string;
58
- } | {
99
+ };
100
+ /**
101
+ * @public
102
+ */
103
+ declare type ElasticSearchAuth = OpenSearchAuth | {
59
104
  apiKey: string | {
60
105
  id: string;
61
106
  api_key: string;
@@ -77,6 +122,21 @@ interface ElasticSearchNodeOptions {
77
122
  ml: boolean;
78
123
  };
79
124
  }
125
+ /**
126
+ * @public
127
+ */
128
+ interface OpenSearchNodeOptions {
129
+ url: URL;
130
+ id?: string;
131
+ agent?: ElasticSearchAgentOptions;
132
+ ssl?: ConnectionOptions;
133
+ headers?: Record<string, any>;
134
+ roles?: {
135
+ master: boolean;
136
+ data: boolean;
137
+ ingest: boolean;
138
+ };
139
+ }
80
140
  /**
81
141
  * @public
82
142
  */
@@ -102,6 +162,21 @@ interface ElasticSearchConnectionConstructor {
102
162
  ML: string;
103
163
  };
104
164
  }
165
+ /**
166
+ * @public
167
+ */
168
+ interface OpenSearchConnectionConstructor {
169
+ new (opts?: any): any;
170
+ statuses: {
171
+ ALIVE: string;
172
+ DEAD: string;
173
+ };
174
+ roles: {
175
+ MASTER: string;
176
+ DATA: string;
177
+ INGEST: string;
178
+ };
179
+ }
105
180
  /**
106
181
  * @public
107
182
  */
@@ -115,6 +190,116 @@ interface ElasticSearchTransportConstructor {
115
190
  };
116
191
  }
117
192
 
193
+ /**
194
+ * Elasticsearch specific index template
195
+ * @public
196
+ */
197
+ declare type ElasticSearchCustomIndexTemplate = {
198
+ name: string;
199
+ body: ElasticSearchCustomIndexTemplateBody;
200
+ };
201
+ /**
202
+ * Elasticsearch specific index template body
203
+ * @public
204
+ */
205
+ declare type ElasticSearchCustomIndexTemplateBody = {
206
+ /**
207
+ * Array of wildcard (*) expressions used to match the names of data streams and indices during creation.
208
+ */
209
+ index_patterns: string[];
210
+ /**
211
+ * An ordered list of component template names.
212
+ * Component templates are merged in the order specified,
213
+ * meaning that the last component template specified has the highest precedence.
214
+ */
215
+ composed_of?: string[];
216
+ /**
217
+ * See available properties of template
218
+ * https://www.elastic.co/guide/en/elasticsearch/reference/7.15/indices-put-template.html#put-index-template-api-request-body
219
+ */
220
+ template?: Record<string, any>;
221
+ };
222
+
223
+ /**
224
+ * @public
225
+ */
226
+ declare type ElasticSearchAliasAction = {
227
+ remove: {
228
+ index: any;
229
+ alias: any;
230
+ };
231
+ add?: undefined;
232
+ } | {
233
+ add: {
234
+ indices: any;
235
+ alias: any;
236
+ index?: undefined;
237
+ };
238
+ remove?: undefined;
239
+ } | {
240
+ add: {
241
+ index: any;
242
+ alias: any;
243
+ indices?: undefined;
244
+ };
245
+ remove?: undefined;
246
+ } | undefined;
247
+ /**
248
+ * @public
249
+ */
250
+ declare type ElasticSearchIndexAction = {
251
+ index: {
252
+ _index: string;
253
+ [key: string]: any;
254
+ };
255
+ };
256
+ /**
257
+ * A wrapper class that exposes logical methods that are conditionally fired
258
+ * against either a configured Elasticsearch client or a configured Opensearch
259
+ * client.
260
+ *
261
+ * This is necessary because, despite its intention to be API-compatible, the
262
+ * opensearch client does not support API key-based authentication. This is
263
+ * also the sanest way to accomplish this while making typescript happy.
264
+ *
265
+ * In the future, if the differences between implementations become
266
+ * unmaintainably divergent, we should split out the Opensearch and
267
+ * Elasticsearch search engine implementations.
268
+ *
269
+ * @public
270
+ */
271
+ declare class ElasticSearchClientWrapper {
272
+ private readonly elasticSearchClient;
273
+ private readonly openSearchClient;
274
+ private constructor();
275
+ static fromClientOptions(options: ElasticSearchClientOptions): ElasticSearchClientWrapper;
276
+ search({ index, body }: {
277
+ index: string | string[];
278
+ body: Object;
279
+ }): _opensearch_project_opensearch_lib_Transport.TransportRequestPromise<_opensearch_project_opensearch.ApiResponse<Record<string, any>, unknown>>;
280
+ bulk(bulkOptions: {
281
+ datasource: Readable;
282
+ onDocument: () => ElasticSearchIndexAction;
283
+ refreshOnCompletion?: string | boolean;
284
+ }): _opensearch_project_opensearch_lib_Helpers.BulkHelper<_opensearch_project_opensearch_lib_Helpers.BulkStats>;
285
+ putIndexTemplate(template: ElasticSearchCustomIndexTemplate): _opensearch_project_opensearch_lib_Transport.TransportRequestPromise<_opensearch_project_opensearch.ApiResponse<Record<string, any>, unknown>>;
286
+ indexExists({ index }: {
287
+ index: string | string[];
288
+ }): _opensearch_project_opensearch_lib_Transport.TransportRequestPromise<_opensearch_project_opensearch.ApiResponse<boolean, unknown>>;
289
+ deleteIndex({ index }: {
290
+ index: string | string[];
291
+ }): _opensearch_project_opensearch_lib_Transport.TransportRequestPromise<_opensearch_project_opensearch.ApiResponse<Record<string, any>, unknown>>;
292
+ createIndex({ index }: {
293
+ index: string;
294
+ }): _opensearch_project_opensearch_lib_Transport.TransportRequestPromise<_opensearch_project_opensearch.ApiResponse<Record<string, any>, unknown>>;
295
+ getAliases({ aliases }: {
296
+ aliases: string[];
297
+ }): _opensearch_project_opensearch_lib_Transport.TransportRequestPromise<_opensearch_project_opensearch.ApiResponse<Record<string, any>, unknown>>;
298
+ updateAliases({ actions }: {
299
+ actions: ElasticSearchAliasAction[];
300
+ }): _opensearch_project_opensearch_lib_Transport.TransportRequestPromise<_opensearch_project_opensearch.ApiResponse<Record<string, any>, unknown>>;
301
+ }
302
+
118
303
  /**
119
304
  * Options for instansiate ElasticSearchSearchEngineIndexer
120
305
  * @public
@@ -125,7 +310,7 @@ declare type ElasticSearchSearchEngineIndexerOptions = {
125
310
  indexSeparator: string;
126
311
  alias: string;
127
312
  logger: Logger;
128
- elasticSearchClient: Client;
313
+ elasticSearchClientWrapper: ElasticSearchClientWrapper;
129
314
  };
130
315
  /**
131
316
  * Elasticsearch specific search engine indexer.
@@ -144,7 +329,7 @@ declare class ElasticSearchSearchEngineIndexer extends BatchSearchEngineIndexer
144
329
  private readonly removableAlias;
145
330
  private readonly logger;
146
331
  private readonly sourceStream;
147
- private readonly elasticSearchClient;
332
+ private readonly elasticSearchClientWrapper;
148
333
  private bulkResult;
149
334
  constructor(options: ElasticSearchSearchEngineIndexerOptions);
150
335
  initialize(): Promise<void>;
@@ -159,35 +344,6 @@ declare class ElasticSearchSearchEngineIndexer extends BatchSearchEngineIndexer
159
344
  private constructIndexName;
160
345
  }
161
346
 
162
- /**
163
- * Elasticsearch specific index template
164
- * @public
165
- */
166
- declare type ElasticSearchCustomIndexTemplate = {
167
- name: string;
168
- body: ElasticSearchCustomIndexTemplateBody;
169
- };
170
- /**
171
- * Elasticsearch specific index template body
172
- * @public
173
- */
174
- declare type ElasticSearchCustomIndexTemplateBody = {
175
- /**
176
- * Array of wildcard (*) expressions used to match the names of data streams and indices during creation.
177
- */
178
- index_patterns: string[];
179
- /**
180
- * An ordered list of component template names.
181
- * Component templates are merged in the order specified,
182
- * meaning that the last component template specified has the highest precedence.
183
- */
184
- composed_of?: string[];
185
- /**
186
- * See available properties of template
187
- * https://www.elastic.co/guide/en/elasticsearch/reference/7.15/indices-put-template.html#put-index-template-api-request-body
188
- */
189
- template?: Record<string, any>;
190
- };
191
347
  /**
192
348
  * Search query that the elasticsearch engine understands.
193
349
  * @public
@@ -245,7 +401,7 @@ declare class ElasticSearchSearchEngine implements SearchEngine {
245
401
  private readonly aliasPostfix;
246
402
  private readonly indexPrefix;
247
403
  private readonly logger;
248
- private readonly elasticSearchClient;
404
+ private readonly elasticSearchClientWrapper;
249
405
  private readonly highlightOptions;
250
406
  constructor(elasticSearchClientOptions: ElasticSearchClientOptions, aliasPostfix: string, indexPrefix: string, logger: Logger, highlightOptions?: ElasticSearchHighlightOptions);
251
407
  static fromConfig({ logger, config, aliasPostfix, indexPrefix, }: ElasticSearchOptions): Promise<ElasticSearchSearchEngine>;
@@ -265,4 +421,4 @@ declare class ElasticSearchSearchEngine implements SearchEngine {
265
421
  private constructSearchAlias;
266
422
  }
267
423
 
268
- export { ElasticSearchAgentOptions, ElasticSearchAuth, ElasticSearchClientOptions, ElasticSearchConcreteQuery, ElasticSearchConnectionConstructor, ElasticSearchCustomIndexTemplate, ElasticSearchCustomIndexTemplateBody, ElasticSearchHighlightConfig, ElasticSearchHighlightOptions, ElasticSearchNodeOptions, ElasticSearchOptions, ElasticSearchQueryTranslator, ElasticSearchQueryTranslatorOptions, ElasticSearchSearchEngine, ElasticSearchSearchEngineIndexer, ElasticSearchSearchEngineIndexerOptions, ElasticSearchTransportConstructor };
424
+ export { BaseElasticSearchClientOptions, ElasticSearchAgentOptions, ElasticSearchAliasAction, ElasticSearchAuth, ElasticSearchClientOptions, ElasticSearchClientWrapper, ElasticSearchConcreteQuery, ElasticSearchConnectionConstructor, ElasticSearchCustomIndexTemplate, ElasticSearchCustomIndexTemplateBody, ElasticSearchElasticSearchClientOptions, ElasticSearchHighlightConfig, ElasticSearchHighlightOptions, ElasticSearchIndexAction, ElasticSearchNodeOptions, ElasticSearchOptions, ElasticSearchQueryTranslator, ElasticSearchQueryTranslatorOptions, ElasticSearchSearchEngine, ElasticSearchSearchEngineIndexer, ElasticSearchSearchEngineIndexerOptions, ElasticSearchTransportConstructor, OpenSearchAuth, OpenSearchConnectionConstructor, OpenSearchElasticSearchClientOptions, OpenSearchNodeOptions, isOpenSearchCompatible };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@backstage/plugin-search-backend-module-elasticsearch",
3
3
  "description": "A module for the search backend that implements search using ElasticSearch",
4
- "version": "0.1.6-next.0",
4
+ "version": "0.2.0-next.1",
5
5
  "main": "dist/index.cjs.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "license": "Apache-2.0",
@@ -23,11 +23,12 @@
23
23
  "clean": "backstage-cli package clean"
24
24
  },
25
25
  "dependencies": {
26
- "@acuris/aws-es-connection": "^2.2.0",
27
26
  "@backstage/config": "^1.0.1",
28
- "@backstage/plugin-search-backend-node": "^0.6.3-next.0",
29
- "@backstage/plugin-search-common": "^0.3.5",
30
- "@elastic/elasticsearch": "7.13.0",
27
+ "@backstage/plugin-search-backend-node": "^0.6.3-next.1",
28
+ "@backstage/plugin-search-common": "^0.3.6-next.0",
29
+ "@elastic/elasticsearch": "^7.13.0",
30
+ "@opensearch-project/opensearch": "^1.1.0",
31
+ "aws-os-connection": "^0.1.0",
31
32
  "aws-sdk": "^2.948.0",
32
33
  "elastic-builder": "^2.16.0",
33
34
  "lodash": "^4.17.21",
@@ -35,14 +36,15 @@
35
36
  "winston": "^3.2.1"
36
37
  },
37
38
  "devDependencies": {
38
- "@backstage/backend-common": "^0.14.1-next.0",
39
- "@backstage/cli": "^0.17.3-next.0",
40
- "@elastic/elasticsearch-mock": "^1.0.0"
39
+ "@backstage/backend-common": "^0.14.1-next.1",
40
+ "@backstage/cli": "^0.18.0-next.1",
41
+ "@elastic/elasticsearch-mock": "^1.0.0",
42
+ "@short.io/opensearch-mock": "^0.3.1"
41
43
  },
42
44
  "files": [
43
45
  "dist",
44
46
  "config.d.ts"
45
47
  ],
46
48
  "configSchema": "config.d.ts",
47
- "gitHead": "6d8bf5b9cf494815c0916e18daa226fb8186ba5f"
49
+ "gitHead": "e0a993834c31487a97a1ae6878eaf3685f03fc1a"
48
50
  }