@backstage/plugin-search-backend-node 0.5.2 → 0.6.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 +188 -0
- package/dist/index.cjs.js +65 -78
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +43 -35
- package/package.json +7 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,193 @@
|
|
|
1
1
|
# @backstage/plugin-search-backend-node
|
|
2
2
|
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 0a63e99a26: **BREAKING**: `IndexBuilder.addCollator()` now requires a `schedule` parameter (replacing `defaultRefreshIntervalSeconds`) which is expected to be a `TaskRunner` that is configured with the desired search indexing schedule for the given collator.
|
|
8
|
+
|
|
9
|
+
`Scheduler.addToSchedule()` now takes a new parameter object (`ScheduleTaskParameters`) with two new options `id` and `scheduledRunner` in addition to the migrated `task` argument.
|
|
10
|
+
|
|
11
|
+
NOTE: The search backend plugin now creates a dedicated database for coordinating indexing tasks.
|
|
12
|
+
|
|
13
|
+
To make this change to an existing app, make the following changes to `packages/backend/src/plugins/search.ts`:
|
|
14
|
+
|
|
15
|
+
```diff
|
|
16
|
+
+import { Duration } from 'luxon';
|
|
17
|
+
|
|
18
|
+
/* ... */
|
|
19
|
+
|
|
20
|
+
+ const schedule = env.scheduler.createScheduledTaskRunner({
|
|
21
|
+
+ frequency: Duration.fromObject({ minutes: 10 }),
|
|
22
|
+
+ timeout: Duration.fromObject({ minutes: 15 }),
|
|
23
|
+
+ initialDelay: Duration.fromObject({ seconds: 3 }),
|
|
24
|
+
+ });
|
|
25
|
+
|
|
26
|
+
indexBuilder.addCollator({
|
|
27
|
+
- defaultRefreshIntervalSeconds: 600,
|
|
28
|
+
+ schedule,
|
|
29
|
+
factory: DefaultCatalogCollatorFactory.fromConfig(env.config, {
|
|
30
|
+
discovery: env.discovery,
|
|
31
|
+
tokenManager: env.tokenManager,
|
|
32
|
+
}),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
indexBuilder.addCollator({
|
|
36
|
+
- defaultRefreshIntervalSeconds: 600,
|
|
37
|
+
+ schedule,
|
|
38
|
+
factory: DefaultTechDocsCollatorFactory.fromConfig(env.config, {
|
|
39
|
+
discovery: env.discovery,
|
|
40
|
+
tokenManager: env.tokenManager,
|
|
41
|
+
}),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const { scheduler } = await indexBuilder.build();
|
|
45
|
+
- setTimeout(() => scheduler.start(), 3000);
|
|
46
|
+
+ scheduler.start();
|
|
47
|
+
/* ... */
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
NOTE: For scenarios where the `lunr` search engine is used in a multi-node configuration, a non-distributed `TaskRunner` like the following should be implemented to ensure consistency across nodes (alternatively, you can configure
|
|
51
|
+
the search plugin to use a non-distributed DB such as [SQLite](https://backstage.io/docs/tutorials/configuring-plugin-databases#postgresql-and-sqlite-3)):
|
|
52
|
+
|
|
53
|
+
```diff
|
|
54
|
+
+import { TaskInvocationDefinition, TaskRunner } from '@backstage/backend-tasks';
|
|
55
|
+
|
|
56
|
+
/* ... */
|
|
57
|
+
|
|
58
|
+
+ const schedule: TaskRunner = {
|
|
59
|
+
+ run: async (task: TaskInvocationDefinition) => {
|
|
60
|
+
+ const startRefresh = async () => {
|
|
61
|
+
+ while (!task.signal?.aborted) {
|
|
62
|
+
+ try {
|
|
63
|
+
+ await task.fn(task.signal);
|
|
64
|
+
+ } catch {
|
|
65
|
+
+ // ignore intentionally
|
|
66
|
+
+ }
|
|
67
|
+
+
|
|
68
|
+
+ await new Promise(resolve => setTimeout(resolve, 600 * 1000));
|
|
69
|
+
+ }
|
|
70
|
+
+ };
|
|
71
|
+
+ startRefresh();
|
|
72
|
+
+ },
|
|
73
|
+
+ };
|
|
74
|
+
|
|
75
|
+
indexBuilder.addCollator({
|
|
76
|
+
- defaultRefreshIntervalSeconds: 600,
|
|
77
|
+
+ schedule,
|
|
78
|
+
factory: DefaultCatalogCollatorFactory.fromConfig(env.config, {
|
|
79
|
+
discovery: env.discovery,
|
|
80
|
+
tokenManager: env.tokenManager,
|
|
81
|
+
}),
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
/* ... */
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Patch Changes
|
|
88
|
+
|
|
89
|
+
- 62ee65422c: Use new `IndexableResultSet` type as return type of query method in `SearchEngine` implementation.
|
|
90
|
+
- 230ad0826f: Bump to using `@types/node` v16
|
|
91
|
+
- Updated dependencies
|
|
92
|
+
- @backstage/backend-tasks@0.3.0
|
|
93
|
+
- @backstage/plugin-search-common@0.3.3
|
|
94
|
+
|
|
95
|
+
## 0.6.0-next.1
|
|
96
|
+
|
|
97
|
+
### Minor Changes
|
|
98
|
+
|
|
99
|
+
- 0a63e99a26: **BREAKING**: `IndexBuilder.addCollator()` now requires a `schedule` parameter (replacing `defaultRefreshIntervalSeconds`) which is expected to be a `TaskRunner` that is configured with the desired search indexing schedule for the given collator.
|
|
100
|
+
|
|
101
|
+
`Scheduler.addToSchedule()` now takes a new parameter object (`ScheduleTaskParameters`) with two new options `id` and `scheduledRunner` in addition to the migrated `task` argument.
|
|
102
|
+
|
|
103
|
+
NOTE: The search backend plugin now creates a dedicated database for coordinating indexing tasks.
|
|
104
|
+
|
|
105
|
+
To make this change to an existing app, make the following changes to `packages/backend/src/plugins/search.ts`:
|
|
106
|
+
|
|
107
|
+
```diff
|
|
108
|
+
+import { Duration } from 'luxon';
|
|
109
|
+
|
|
110
|
+
/* ... */
|
|
111
|
+
|
|
112
|
+
+ const schedule = env.scheduler.createScheduledTaskRunner({
|
|
113
|
+
+ frequency: Duration.fromObject({ minutes: 10 }),
|
|
114
|
+
+ timeout: Duration.fromObject({ minutes: 15 }),
|
|
115
|
+
+ initialDelay: Duration.fromObject({ seconds: 3 }),
|
|
116
|
+
+ });
|
|
117
|
+
|
|
118
|
+
indexBuilder.addCollator({
|
|
119
|
+
- defaultRefreshIntervalSeconds: 600,
|
|
120
|
+
+ schedule,
|
|
121
|
+
factory: DefaultCatalogCollatorFactory.fromConfig(env.config, {
|
|
122
|
+
discovery: env.discovery,
|
|
123
|
+
tokenManager: env.tokenManager,
|
|
124
|
+
}),
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
indexBuilder.addCollator({
|
|
128
|
+
- defaultRefreshIntervalSeconds: 600,
|
|
129
|
+
+ schedule,
|
|
130
|
+
factory: DefaultTechDocsCollatorFactory.fromConfig(env.config, {
|
|
131
|
+
discovery: env.discovery,
|
|
132
|
+
tokenManager: env.tokenManager,
|
|
133
|
+
}),
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const { scheduler } = await indexBuilder.build();
|
|
137
|
+
- setTimeout(() => scheduler.start(), 3000);
|
|
138
|
+
+ scheduler.start();
|
|
139
|
+
/* ... */
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
NOTE: For scenarios where the `lunr` search engine is used in a multi-node configuration, a non-distributed `TaskRunner` like the following should be implemented to ensure consistency across nodes (alternatively, you can configure
|
|
143
|
+
the search plugin to use a non-distributed DB such as [SQLite](https://backstage.io/docs/tutorials/configuring-plugin-databases#postgresql-and-sqlite-3)):
|
|
144
|
+
|
|
145
|
+
```diff
|
|
146
|
+
+import { TaskInvocationDefinition, TaskRunner } from '@backstage/backend-tasks';
|
|
147
|
+
|
|
148
|
+
/* ... */
|
|
149
|
+
|
|
150
|
+
+ const schedule: TaskRunner = {
|
|
151
|
+
+ run: async (task: TaskInvocationDefinition) => {
|
|
152
|
+
+ const startRefresh = async () => {
|
|
153
|
+
+ while (!task.signal?.aborted) {
|
|
154
|
+
+ try {
|
|
155
|
+
+ await task.fn(task.signal);
|
|
156
|
+
+ } catch {
|
|
157
|
+
+ // ignore intentionally
|
|
158
|
+
+ }
|
|
159
|
+
+
|
|
160
|
+
+ await new Promise(resolve => setTimeout(resolve, 600 * 1000));
|
|
161
|
+
+ }
|
|
162
|
+
+ };
|
|
163
|
+
+ startRefresh();
|
|
164
|
+
+ },
|
|
165
|
+
+ };
|
|
166
|
+
|
|
167
|
+
indexBuilder.addCollator({
|
|
168
|
+
- defaultRefreshIntervalSeconds: 600,
|
|
169
|
+
+ schedule,
|
|
170
|
+
factory: DefaultCatalogCollatorFactory.fromConfig(env.config, {
|
|
171
|
+
discovery: env.discovery,
|
|
172
|
+
tokenManager: env.tokenManager,
|
|
173
|
+
}),
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
/* ... */
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Patch Changes
|
|
180
|
+
|
|
181
|
+
- 230ad0826f: Bump to using `@types/node` v16
|
|
182
|
+
|
|
183
|
+
## 0.5.3-next.0
|
|
184
|
+
|
|
185
|
+
### Patch Changes
|
|
186
|
+
|
|
187
|
+
- 62ee65422c: Use new `IndexableResultSet` type as return type of query method in `SearchEngine` implementation.
|
|
188
|
+
- Updated dependencies
|
|
189
|
+
- @backstage/plugin-search-common@0.3.3-next.0
|
|
190
|
+
|
|
3
191
|
## 0.5.2
|
|
4
192
|
|
|
5
193
|
### Patch Changes
|
package/dist/index.cjs.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var stream = require('stream');
|
|
6
|
+
var nodeAbortController = require('node-abort-controller');
|
|
6
7
|
var lunr = require('lunr');
|
|
7
8
|
var errors = require('@backstage/errors');
|
|
8
9
|
|
|
@@ -10,6 +11,41 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
|
|
|
10
11
|
|
|
11
12
|
var lunr__default = /*#__PURE__*/_interopDefaultLegacy(lunr);
|
|
12
13
|
|
|
14
|
+
class Scheduler {
|
|
15
|
+
constructor({ logger }) {
|
|
16
|
+
this.logger = logger;
|
|
17
|
+
this.schedule = {};
|
|
18
|
+
this.abortController = new nodeAbortController.AbortController();
|
|
19
|
+
this.isRunning = false;
|
|
20
|
+
}
|
|
21
|
+
addToSchedule({ id, task, scheduledRunner }) {
|
|
22
|
+
if (this.isRunning) {
|
|
23
|
+
throw new Error("Cannot add task to schedule that has already been started.");
|
|
24
|
+
}
|
|
25
|
+
if (this.schedule[id]) {
|
|
26
|
+
throw new Error(`Task with id ${id} already exists.`);
|
|
27
|
+
}
|
|
28
|
+
this.schedule[id] = { task, scheduledRunner };
|
|
29
|
+
}
|
|
30
|
+
start() {
|
|
31
|
+
this.logger.info("Starting all scheduled search tasks.");
|
|
32
|
+
this.isRunning = true;
|
|
33
|
+
Object.keys(this.schedule).forEach((id) => {
|
|
34
|
+
const { task, scheduledRunner } = this.schedule[id];
|
|
35
|
+
scheduledRunner.run({
|
|
36
|
+
id,
|
|
37
|
+
fn: task,
|
|
38
|
+
signal: this.abortController.signal
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
stop() {
|
|
43
|
+
this.logger.info("Stopping all scheduled search tasks.");
|
|
44
|
+
this.abortController.abort();
|
|
45
|
+
this.isRunning = false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
13
49
|
class IndexBuilder {
|
|
14
50
|
constructor({ logger, searchEngine }) {
|
|
15
51
|
this.collators = {};
|
|
@@ -24,14 +60,11 @@ class IndexBuilder {
|
|
|
24
60
|
getDocumentTypes() {
|
|
25
61
|
return this.documentTypes;
|
|
26
62
|
}
|
|
27
|
-
addCollator({
|
|
28
|
-
factory,
|
|
29
|
-
defaultRefreshIntervalSeconds
|
|
30
|
-
}) {
|
|
63
|
+
addCollator({ factory, schedule }) {
|
|
31
64
|
this.logger.info(`Added ${factory.constructor.name} collator factory for type ${factory.type}`);
|
|
32
65
|
this.collators[factory.type] = {
|
|
33
|
-
|
|
34
|
-
|
|
66
|
+
factory,
|
|
67
|
+
schedule
|
|
35
68
|
};
|
|
36
69
|
this.documentTypes[factory.type] = {
|
|
37
70
|
visibilityPermission: factory.visibilityPermission
|
|
@@ -49,28 +82,34 @@ class IndexBuilder {
|
|
|
49
82
|
});
|
|
50
83
|
}
|
|
51
84
|
async build() {
|
|
52
|
-
const scheduler = new Scheduler({
|
|
85
|
+
const scheduler = new Scheduler({
|
|
86
|
+
logger: this.logger
|
|
87
|
+
});
|
|
53
88
|
Object.keys(this.collators).forEach((type) => {
|
|
54
|
-
scheduler.addToSchedule(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
this.logger.info(`
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
89
|
+
scheduler.addToSchedule({
|
|
90
|
+
id: `search_index_${type.replace("-", "_").toLocaleLowerCase("en-US")}`,
|
|
91
|
+
scheduledRunner: this.collators[type].schedule,
|
|
92
|
+
task: async () => {
|
|
93
|
+
const collator = await this.collators[type].factory.getCollator();
|
|
94
|
+
this.logger.info(`Collating documents for ${type} via ${this.collators[type].factory.constructor.name}`);
|
|
95
|
+
const decorators = await Promise.all((this.decorators["*"] || []).concat(this.decorators[type] || []).map(async (factory) => {
|
|
96
|
+
const decorator = await factory.getDecorator();
|
|
97
|
+
this.logger.info(`Attached decorator via ${factory.constructor.name} to ${type} index pipeline.`);
|
|
98
|
+
return decorator;
|
|
99
|
+
}));
|
|
100
|
+
const indexer = await this.searchEngine.getIndexer(type);
|
|
101
|
+
return new Promise((done) => {
|
|
102
|
+
stream.pipeline([collator, ...decorators, indexer], (error) => {
|
|
103
|
+
if (error) {
|
|
104
|
+
this.logger.error(`Collating documents for ${type} failed: ${error}`);
|
|
105
|
+
} else {
|
|
106
|
+
this.logger.info(`Collating documents for ${type} succeeded`);
|
|
107
|
+
}
|
|
108
|
+
done();
|
|
109
|
+
});
|
|
71
110
|
});
|
|
72
|
-
}
|
|
73
|
-
}
|
|
111
|
+
}
|
|
112
|
+
});
|
|
74
113
|
});
|
|
75
114
|
return {
|
|
76
115
|
scheduler
|
|
@@ -78,58 +117,6 @@ class IndexBuilder {
|
|
|
78
117
|
}
|
|
79
118
|
}
|
|
80
119
|
|
|
81
|
-
function runPeriodically(fn, delayMs) {
|
|
82
|
-
let cancel;
|
|
83
|
-
let cancelled = false;
|
|
84
|
-
const cancellationPromise = new Promise((resolve) => {
|
|
85
|
-
cancel = () => {
|
|
86
|
-
resolve();
|
|
87
|
-
cancelled = true;
|
|
88
|
-
};
|
|
89
|
-
});
|
|
90
|
-
const startRefresh = async () => {
|
|
91
|
-
while (!cancelled) {
|
|
92
|
-
try {
|
|
93
|
-
await fn();
|
|
94
|
-
} catch {
|
|
95
|
-
}
|
|
96
|
-
await Promise.race([
|
|
97
|
-
new Promise((resolve) => setTimeout(resolve, delayMs)),
|
|
98
|
-
cancellationPromise
|
|
99
|
-
]);
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
startRefresh();
|
|
103
|
-
return cancel;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
class Scheduler {
|
|
107
|
-
constructor({ logger }) {
|
|
108
|
-
this.runningTasks = [];
|
|
109
|
-
this.logger = logger;
|
|
110
|
-
this.schedule = [];
|
|
111
|
-
}
|
|
112
|
-
addToSchedule(task, interval) {
|
|
113
|
-
if (this.runningTasks.length) {
|
|
114
|
-
throw new Error("Cannot add task to schedule that has already been started.");
|
|
115
|
-
}
|
|
116
|
-
this.schedule.push({ task, interval });
|
|
117
|
-
}
|
|
118
|
-
start() {
|
|
119
|
-
this.logger.info("Starting all scheduled search tasks.");
|
|
120
|
-
this.schedule.forEach(({ task, interval }) => {
|
|
121
|
-
this.runningTasks.push(runPeriodically(() => task(), interval));
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
stop() {
|
|
125
|
-
this.logger.info("Stopping all scheduled search tasks.");
|
|
126
|
-
this.runningTasks.forEach((cancel) => {
|
|
127
|
-
cancel();
|
|
128
|
-
});
|
|
129
|
-
this.runningTasks = [];
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
120
|
class BatchSearchEngineIndexer extends stream.Writable {
|
|
134
121
|
constructor(options) {
|
|
135
122
|
super({ objectMode: true });
|
package/dist/index.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../src/IndexBuilder.ts","../src/runPeriodically.ts","../src/Scheduler.ts","../src/indexing/BatchSearchEngineIndexer.ts","../src/indexing/DecoratorBase.ts","../src/engines/LunrSearchEngineIndexer.ts","../src/engines/LunrSearchEngine.ts","../src/test-utils/TestPipeline.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 DocumentCollatorFactory,\n DocumentDecoratorFactory,\n DocumentTypeInfo,\n SearchEngine,\n} from '@backstage/plugin-search-common';\nimport { Transform, pipeline } from 'stream';\nimport { Logger } from 'winston';\nimport { Scheduler } from './index';\nimport {\n IndexBuilderOptions,\n RegisterCollatorParameters,\n RegisterDecoratorParameters,\n} from './types';\n\ninterface CollatorEnvelope {\n factory: DocumentCollatorFactory;\n refreshInterval: number;\n}\n\n/**\n * @beta\n */\nexport class IndexBuilder {\n private collators: Record<string, CollatorEnvelope>;\n private decorators: Record<string, DocumentDecoratorFactory[]>;\n private documentTypes: Record<string, DocumentTypeInfo>;\n private searchEngine: SearchEngine;\n private logger: Logger;\n\n constructor({ logger, searchEngine }: IndexBuilderOptions) {\n this.collators = {};\n this.decorators = {};\n this.documentTypes = {};\n this.logger = logger;\n this.searchEngine = searchEngine;\n }\n\n getSearchEngine(): SearchEngine {\n return this.searchEngine;\n }\n\n getDocumentTypes(): Record<string, DocumentTypeInfo> {\n return this.documentTypes;\n }\n\n /**\n * Makes the index builder aware of a collator that should be executed at the\n * given refresh interval.\n */\n addCollator({\n factory,\n defaultRefreshIntervalSeconds,\n }: RegisterCollatorParameters): void {\n this.logger.info(\n `Added ${factory.constructor.name} collator factory for type ${factory.type}`,\n );\n this.collators[factory.type] = {\n refreshInterval: defaultRefreshIntervalSeconds,\n factory,\n };\n this.documentTypes[factory.type] = {\n visibilityPermission: factory.visibilityPermission,\n };\n }\n\n /**\n * Makes the index builder aware of a decorator. If no types are provided on\n * the decorator, it will be applied to documents from all known collators,\n * otherwise it will only be applied to documents of the given types.\n */\n addDecorator({ factory }: RegisterDecoratorParameters): void {\n const types = factory.types || ['*'];\n this.logger.info(\n `Added decorator ${factory.constructor.name} to types ${types.join(\n ', ',\n )}`,\n );\n types.forEach(type => {\n if (this.decorators.hasOwnProperty(type)) {\n this.decorators[type].push(factory);\n } else {\n this.decorators[type] = [factory];\n }\n });\n }\n\n /**\n * Compiles collators and decorators into tasks, which are added to a\n * scheduler returned to the caller.\n */\n async build(): Promise<{ scheduler: Scheduler }> {\n const scheduler = new Scheduler({ logger: this.logger });\n\n Object.keys(this.collators).forEach(type => {\n scheduler.addToSchedule(async () => {\n // Instantiate the collator.\n const collator = await this.collators[type].factory.getCollator();\n this.logger.info(\n `Collating documents for ${type} via ${this.collators[type].factory.constructor.name}`,\n );\n\n // Instantiate all relevant decorators.\n const decorators: Transform[] = await Promise.all(\n (this.decorators['*'] || [])\n .concat(this.decorators[type] || [])\n .map(async factory => {\n const decorator = await factory.getDecorator();\n this.logger.info(\n `Attached decorator via ${factory.constructor.name} to ${type} index pipeline.`,\n );\n return decorator;\n }),\n );\n\n // Instantiate the indexer.\n const indexer = await this.searchEngine.getIndexer(type);\n\n // Compose collator/decorators/indexer into a pipeline\n return new Promise<void>(done => {\n pipeline([collator, ...decorators, indexer], error => {\n if (error) {\n this.logger.error(\n `Collating documents for ${type} failed: ${error}`,\n );\n } else {\n this.logger.info(`Collating documents for ${type} succeeded`);\n }\n\n // Signal index pipeline completion!\n done();\n });\n });\n }, this.collators[type].refreshInterval * 1000);\n });\n\n return {\n scheduler,\n };\n }\n}\n","/*\n * Copyright 2020 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\n/**\n * Runs a function repeatedly, with a fixed wait between invocations.\n *\n * Supports async functions, and silently ignores exceptions and rejections.\n *\n * @param fn - The function to run. May return a Promise.\n * @param delayMs - The delay between a completed function invocation and the\n * next.\n * @returns A function that, when called, stops the invocation loop.\n */\nexport function runPeriodically(fn: () => any, delayMs: number): () => void {\n let cancel: () => void;\n let cancelled = false;\n const cancellationPromise = new Promise<void>(resolve => {\n cancel = () => {\n resolve();\n cancelled = true;\n };\n });\n\n const startRefresh = async () => {\n while (!cancelled) {\n try {\n await fn();\n } catch {\n // ignore intentionally\n }\n\n await Promise.race([\n new Promise(resolve => setTimeout(resolve, delayMs)),\n cancellationPromise,\n ]);\n }\n };\n startRefresh();\n\n return cancel!;\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 { Logger } from 'winston';\nimport { runPeriodically } from './runPeriodically';\n\ntype TaskEnvelope = {\n task: Function;\n interval: number;\n};\n\n/**\n * TODO: coordination, error handling\n */\n\n/**\n * @beta\n */\nexport class Scheduler {\n private logger: Logger;\n private schedule: TaskEnvelope[];\n private runningTasks: Function[] = [];\n\n constructor({ logger }: { logger: Logger }) {\n this.logger = logger;\n this.schedule = [];\n }\n\n /**\n * Adds each task and interval to the schedule.\n * When running the tasks, the scheduler waits at least for the time specified\n * in the interval once the task was completed, before running it again.\n */\n addToSchedule(task: Function, interval: number) {\n if (this.runningTasks.length) {\n throw new Error(\n 'Cannot add task to schedule that has already been started.',\n );\n }\n this.schedule.push({ task, interval });\n }\n\n /**\n * Starts the scheduling process for each task\n */\n start() {\n this.logger.info('Starting all scheduled search tasks.');\n this.schedule.forEach(({ task, interval }) => {\n this.runningTasks.push(runPeriodically(() => task(), interval));\n });\n }\n\n /**\n * Stop all scheduled tasks.\n */\n stop() {\n this.logger.info('Stopping all scheduled search tasks.');\n this.runningTasks.forEach(cancel => {\n cancel();\n });\n this.runningTasks = [];\n }\n}\n","/*\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 { assertError } from '@backstage/errors';\nimport { IndexableDocument } from '@backstage/plugin-search-common';\nimport { Writable } from 'stream';\n\n/**\n * @beta\n */\nexport type BatchSearchEngineOptions = {\n batchSize: number;\n};\n\n/**\n * Base class encapsulating batch-based stream processing. Useful as a base\n * class for search engine indexers.\n * @beta\n */\nexport abstract class BatchSearchEngineIndexer extends Writable {\n private batchSize: number;\n private currentBatch: IndexableDocument[] = [];\n private initialized: Promise<undefined | Error>;\n\n constructor(options: BatchSearchEngineOptions) {\n super({ objectMode: true });\n this.batchSize = options.batchSize;\n\n // @todo Once node v15 is minimum, convert to _construct implementation.\n this.initialized = new Promise(done => {\n // Necessary to allow concrete implementation classes to construct\n // themselves before calling their initialize() methods.\n setImmediate(async () => {\n try {\n await this.initialize();\n done(undefined);\n } catch (e) {\n assertError(e);\n done(e);\n }\n });\n });\n }\n\n /**\n * Receives an array of indexable documents (of size this.batchSize) which\n * should be written to the search engine. This method won't be called again\n * at least until it resolves.\n */\n public abstract index(documents: IndexableDocument[]): Promise<void>;\n\n /**\n * Any asynchronous setup tasks can be performed here.\n */\n public abstract initialize(): Promise<void>;\n\n /**\n * Any asynchronous teardown tasks can be performed here.\n */\n public abstract finalize(): Promise<void>;\n\n /**\n * Encapsulates batch stream write logic.\n * @internal\n */\n async _write(\n doc: IndexableDocument,\n _e: any,\n done: (error?: Error | null) => void,\n ) {\n // Wait for init before proceeding. Throw error if initialization failed.\n const maybeError = await this.initialized;\n if (maybeError) {\n done(maybeError);\n return;\n }\n\n this.currentBatch.push(doc);\n if (this.currentBatch.length < this.batchSize) {\n done();\n return;\n }\n\n try {\n await this.index(this.currentBatch);\n this.currentBatch = [];\n done();\n } catch (e) {\n assertError(e);\n done(e);\n }\n }\n\n /**\n * Encapsulates finalization and final error handling logic.\n * @internal\n */\n async _final(done: (error?: Error | null) => void) {\n try {\n // Index any remaining documents.\n if (this.currentBatch.length) {\n await this.index(this.currentBatch);\n this.currentBatch = [];\n }\n await this.finalize();\n done();\n } catch (e) {\n assertError(e);\n done(e);\n }\n }\n}\n","/*\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 { assertError } from '@backstage/errors';\nimport { IndexableDocument } from '@backstage/plugin-search-common';\nimport { Transform } from 'stream';\n\n/**\n * Base class encapsulating simple async transformations. Useful as a base\n * class for Backstage search decorators.\n * @beta\n */\nexport abstract class DecoratorBase extends Transform {\n private initialized: Promise<undefined | Error>;\n\n constructor() {\n super({ objectMode: true });\n\n // @todo Once node v15 is minimum, convert to _construct implementation.\n this.initialized = new Promise(done => {\n // Necessary to allow concrete implementation classes to construct\n // themselves before calling their initialize() methods.\n setImmediate(async () => {\n try {\n await this.initialize();\n done(undefined);\n } catch (e) {\n assertError(e);\n done(e);\n }\n });\n });\n }\n\n /**\n * Any asynchronous setup tasks can be performed here.\n */\n public abstract initialize(): Promise<void>;\n\n /**\n * Receives a single indexable document. In your decorate method, you can:\n *\n * - Resolve `undefined` to indicate the record should be omitted.\n * - Resolve a single modified document, which could contain new fields,\n * edited fields, or removed fields.\n * - Resolve an array of indexable documents, if the purpose if the decorator\n * is to convert one document into multiple derivative documents.\n */\n public abstract decorate(\n document: IndexableDocument,\n ): Promise<IndexableDocument | IndexableDocument[] | undefined>;\n\n /**\n * Any asynchronous teardown tasks can be performed here.\n */\n public abstract finalize(): Promise<void>;\n\n /**\n * Encapsulates simple transform stream logic.\n * @internal\n */\n async _transform(\n document: IndexableDocument,\n _: any,\n done: (error?: Error | null) => void,\n ) {\n // Wait for init before proceeding. Throw error if initialization failed.\n const maybeError = await this.initialized;\n if (maybeError) {\n done(maybeError);\n return;\n }\n\n try {\n const decorated = await this.decorate(document);\n\n // If undefined was returned, omit the record and move on.\n if (decorated === undefined) {\n done();\n return;\n }\n\n // If an array of documents was given, push them all.\n if (Array.isArray(decorated)) {\n decorated.forEach(doc => {\n this.push(doc);\n });\n done();\n return;\n }\n\n // Otherwise, just push the decorated document.\n this.push(decorated);\n done();\n } catch (e) {\n assertError(e);\n done(e);\n }\n }\n\n /**\n * Encapsulates finalization and final error handling logic.\n * @internal\n */\n async _final(done: (error?: Error | null) => void) {\n try {\n await this.finalize();\n done();\n } catch (e) {\n assertError(e);\n done(e);\n }\n }\n}\n","/*\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 { IndexableDocument } from '@backstage/plugin-search-common';\nimport lunr from 'lunr';\nimport { BatchSearchEngineIndexer } from '../indexing';\n\n/**\n * @beta\n */\nexport class LunrSearchEngineIndexer extends BatchSearchEngineIndexer {\n private schemaInitialized = false;\n private builder: lunr.Builder;\n private docStore: Record<string, IndexableDocument> = {};\n\n constructor() {\n super({ batchSize: 100 });\n\n this.builder = new lunr.Builder();\n this.builder.pipeline.add(lunr.trimmer, lunr.stopWordFilter, lunr.stemmer);\n this.builder.searchPipeline.add(lunr.stemmer);\n }\n\n // No async initialization required.\n async initialize(): Promise<void> {}\n async finalize(): Promise<void> {}\n\n async index(documents: IndexableDocument[]): Promise<void> {\n if (!this.schemaInitialized) {\n // Make this lunr index aware of all relevant fields.\n Object.keys(documents[0]).forEach(field => {\n this.builder.field(field);\n });\n\n // Set \"location\" field as reference field\n this.builder.ref('location');\n\n this.schemaInitialized = true;\n }\n\n documents.forEach(document => {\n // Add document to Lunar index\n this.builder.add(document);\n\n // Store documents in memory to be able to look up document using the ref during query time\n // This is not how you should implement your SearchEngine implementation! Do not copy!\n this.docStore[document.location] = document;\n });\n }\n\n buildIndex() {\n return this.builder.build();\n }\n\n getDocumentStore() {\n return this.docStore;\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 IndexableDocument,\n SearchQuery,\n SearchResultSet,\n QueryTranslator,\n SearchEngine,\n} from '@backstage/plugin-search-common';\nimport lunr from 'lunr';\nimport { Logger } from 'winston';\nimport { LunrSearchEngineIndexer } from './LunrSearchEngineIndexer';\n\n/**\n * @beta\n */\nexport type ConcreteLunrQuery = {\n lunrQueryBuilder: lunr.Index.QueryBuilder;\n documentTypes?: string[];\n pageSize: number;\n};\n\ntype LunrResultEnvelope = {\n result: lunr.Index.Result;\n type: string;\n};\n\n/**\n * @beta\n */\nexport type LunrQueryTranslator = (query: SearchQuery) => ConcreteLunrQuery;\n\n/**\n * @beta\n */\nexport class LunrSearchEngine implements SearchEngine {\n protected lunrIndices: Record<string, lunr.Index> = {};\n protected docStore: Record<string, IndexableDocument>;\n protected logger: Logger;\n\n constructor({ logger }: { logger: Logger }) {\n this.logger = logger;\n this.docStore = {};\n }\n\n protected translator: QueryTranslator = ({\n term,\n filters,\n types,\n }: SearchQuery): ConcreteLunrQuery => {\n const pageSize = 25;\n\n return {\n lunrQueryBuilder: q => {\n const termToken = lunr.tokenizer(term);\n\n // Support for typeahead search is based on https://github.com/olivernn/lunr.js/issues/256#issuecomment-295407852\n // look for an exact match and apply a large positive boost\n q.term(termToken, {\n usePipeline: true,\n boost: 100,\n });\n // look for terms that match the beginning of this term and apply a\n // medium boost\n q.term(termToken, {\n usePipeline: false,\n boost: 10,\n wildcard: lunr.Query.wildcard.TRAILING,\n });\n // look for terms that match with an edit distance of 2 and apply a\n // small boost\n q.term(termToken, {\n usePipeline: false,\n editDistance: 2,\n boost: 1,\n });\n\n if (filters) {\n Object.entries(filters).forEach(([field, fieldValue]) => {\n if (!q.allFields.includes(field)) {\n // Throw for unknown field, as this will be a non match\n throw new Error(`unrecognised field ${field}`);\n }\n // Arrays are poorly supported, but we can make it better for single-item arrays,\n // which should be a common case\n const value =\n Array.isArray(fieldValue) && fieldValue.length === 1\n ? fieldValue[0]\n : fieldValue;\n\n // Require that the given field has the given value\n if (['string', 'number', 'boolean'].includes(typeof value)) {\n q.term(lunr.tokenizer(value?.toString()), {\n presence: lunr.Query.presence.REQUIRED,\n fields: [field],\n });\n } else if (Array.isArray(value)) {\n // Illustrate how multi-value filters could work.\n // But warn that Lurn supports this poorly.\n this.logger.warn(\n `Non-scalar filter value used for field ${field}. Consider using a different Search Engine for better results.`,\n );\n q.term(lunr.tokenizer(value), {\n presence: lunr.Query.presence.OPTIONAL,\n fields: [field],\n });\n } else {\n // Log a warning or something about unknown filter value\n this.logger.warn(`Unknown filter type used on field ${field}`);\n }\n });\n }\n },\n documentTypes: types,\n pageSize,\n };\n };\n\n setTranslator(translator: LunrQueryTranslator) {\n this.translator = translator;\n }\n\n async getIndexer(type: string) {\n const indexer = new LunrSearchEngineIndexer();\n\n indexer.on('close', () => {\n // Once the stream is closed, build the index and store the documents in\n // memory for later retrieval.\n this.lunrIndices[type] = indexer.buildIndex();\n this.docStore = { ...this.docStore, ...indexer.getDocumentStore() };\n });\n\n return indexer;\n }\n\n async query(query: SearchQuery): Promise<SearchResultSet> {\n const { lunrQueryBuilder, documentTypes, pageSize } = this.translator(\n query,\n ) as ConcreteLunrQuery;\n\n const results: LunrResultEnvelope[] = [];\n\n // Iterate over the filtered list of this.lunrIndex keys.\n Object.keys(this.lunrIndices)\n .filter(type => !documentTypes || documentTypes.includes(type))\n .forEach(type => {\n try {\n results.push(\n ...this.lunrIndices[type].query(lunrQueryBuilder).map(result => {\n return {\n result: result,\n type: type,\n };\n }),\n );\n } catch (err) {\n // if a field does not exist on a index, we can see that as a no-match\n if (\n err instanceof Error &&\n err.message.startsWith('unrecognised field')\n ) {\n return;\n }\n throw err;\n }\n });\n\n // Sort results.\n results.sort((doc1, doc2) => {\n return doc2.result.score - doc1.result.score;\n });\n\n // Perform paging\n const { page } = decodePageCursor(query.pageCursor);\n const offset = page * pageSize;\n const hasPreviousPage = page > 0;\n const hasNextPage = results.length > offset + pageSize;\n const nextPageCursor = hasNextPage\n ? encodePageCursor({ page: page + 1 })\n : undefined;\n const previousPageCursor = hasPreviousPage\n ? encodePageCursor({ page: page - 1 })\n : undefined;\n\n // Translate results into SearchResultSet\n const realResultSet: SearchResultSet = {\n results: results.slice(offset, offset + pageSize).map(d => {\n return { type: d.type, document: this.docStore[d.result.ref] };\n }),\n nextPageCursor,\n previousPageCursor,\n };\n\n return realResultSet;\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","/*\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 { IndexableDocument } from '@backstage/plugin-search-common';\nimport { pipeline, Readable, Transform, Writable } from 'stream';\n\n/**\n * Object resolved after a test pipeline is executed.\n * @beta\n */\nexport type TestPipelineResult = {\n /**\n * If an error was emitted by the pipeline, it will be set here.\n */\n error: unknown;\n\n /**\n * A list of documents collected at the end of the pipeline. If the subject\n * under test is an indexer, this will be an empty array (because your\n * indexer should have received the documents instead).\n */\n documents: IndexableDocument[];\n};\n\n/**\n * Test utility for Backstage Search collators, decorators, and indexers.\n * @beta\n */\nexport class TestPipeline {\n private collator?: Readable;\n private decorator?: Transform;\n private indexer?: Writable;\n\n private constructor({\n collator,\n decorator,\n indexer,\n }: {\n collator?: Readable;\n decorator?: Transform;\n indexer?: Writable;\n }) {\n this.collator = collator;\n this.decorator = decorator;\n this.indexer = indexer;\n }\n\n /**\n * Provide the collator, decorator, or indexer to be tested.\n */\n static withSubject(subject: Readable | Transform | Writable) {\n if (subject instanceof Transform) {\n return new TestPipeline({ decorator: subject });\n }\n\n if (subject instanceof Readable) {\n return new TestPipeline({ collator: subject });\n }\n\n if (subject instanceof Writable) {\n return new TestPipeline({ indexer: subject });\n }\n\n throw new Error(\n 'Unknown test subject: are you passing a readable, writable, or transform stream?',\n );\n }\n\n /**\n * Provide documents for testing decorators and indexers.\n */\n withDocuments(documents: IndexableDocument[]): TestPipeline {\n if (this.collator) {\n throw new Error('Cannot provide documents when testing a collator.');\n }\n\n // Set a naive readable stream that just pushes all given documents.\n this.collator = new Readable({ objectMode: true });\n this.collator._read = () => {};\n process.nextTick(() => {\n documents.forEach(document => {\n this.collator!.push(document);\n });\n this.collator!.push(null);\n });\n\n return this;\n }\n\n /**\n * Execute the test pipeline so that you can make assertions about the result\n * or behavior of the given test subject.\n */\n async execute(): Promise<TestPipelineResult> {\n const documents: IndexableDocument[] = [];\n if (!this.collator) {\n throw new Error(\n 'Cannot execute pipeline without a collator or documents',\n );\n }\n\n // If we are here and there is no indexer, we are testing a collator or a\n // decorator. Set up a naive writable that captures documents in memory.\n if (!this.indexer) {\n this.indexer = new Writable({ objectMode: true });\n this.indexer._write = (document: IndexableDocument, _, done) => {\n documents.push(document);\n done();\n };\n }\n\n return new Promise<TestPipelineResult>(done => {\n const pipes: (Readable | Transform | Writable)[] = [this.collator!];\n if (this.decorator) {\n pipes.push(this.decorator);\n }\n pipes.push(this.indexer!);\n\n pipeline(pipes, error => {\n done({\n error,\n documents,\n });\n });\n });\n }\n}\n"],"names":["pipeline","Writable","assertError","Transform","lunr","Readable"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../src/Scheduler.ts","../src/IndexBuilder.ts","../src/indexing/BatchSearchEngineIndexer.ts","../src/indexing/DecoratorBase.ts","../src/engines/LunrSearchEngineIndexer.ts","../src/engines/LunrSearchEngine.ts","../src/test-utils/TestPipeline.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 { AbortController } from 'node-abort-controller';\nimport { Logger } from 'winston';\nimport { TaskFunction, TaskRunner } from '@backstage/backend-tasks';\n\ntype TaskEnvelope = {\n task: TaskFunction;\n scheduledRunner: TaskRunner;\n};\n\n/**\n * @public ScheduleTaskParameters\n */\nexport interface ScheduleTaskParameters {\n id: string;\n task: TaskFunction;\n scheduledRunner: TaskRunner;\n}\n\n/**\n * @beta\n */\nexport class Scheduler {\n private logger: Logger;\n private schedule: { [id: string]: TaskEnvelope };\n private abortController: AbortController;\n private isRunning: boolean;\n\n constructor({ logger }: { logger: Logger }) {\n this.logger = logger;\n this.schedule = {};\n this.abortController = new AbortController();\n this.isRunning = false;\n }\n\n /**\n * Adds each task and interval to the schedule.\n * When running the tasks, the scheduler waits at least for the time specified\n * in the interval once the task was completed, before running it again.\n */\n addToSchedule({ id, task, scheduledRunner }: ScheduleTaskParameters) {\n if (this.isRunning) {\n throw new Error(\n 'Cannot add task to schedule that has already been started.',\n );\n }\n\n if (this.schedule[id]) {\n throw new Error(`Task with id ${id} already exists.`);\n }\n\n this.schedule[id] = { task, scheduledRunner };\n }\n\n /**\n * Starts the scheduling process for each task\n */\n start() {\n this.logger.info('Starting all scheduled search tasks.');\n this.isRunning = true;\n Object.keys(this.schedule).forEach(id => {\n const { task, scheduledRunner } = this.schedule[id];\n scheduledRunner.run({\n id,\n fn: task,\n signal: this.abortController.signal,\n });\n });\n }\n\n /**\n * Stop all scheduled tasks.\n */\n stop() {\n this.logger.info('Stopping all scheduled search tasks.');\n this.abortController.abort();\n this.isRunning = false;\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 {\n DocumentDecoratorFactory,\n DocumentTypeInfo,\n SearchEngine,\n} from '@backstage/plugin-search-common';\nimport { Transform, pipeline } from 'stream';\nimport { Logger } from 'winston';\nimport { Scheduler } from './Scheduler';\nimport {\n IndexBuilderOptions,\n RegisterCollatorParameters,\n RegisterDecoratorParameters,\n} from './types';\n\n/**\n * @beta\n */\nexport class IndexBuilder {\n private collators: Record<string, RegisterCollatorParameters>;\n private decorators: Record<string, DocumentDecoratorFactory[]>;\n private documentTypes: Record<string, DocumentTypeInfo>;\n private searchEngine: SearchEngine;\n private logger: Logger;\n\n constructor({ logger, searchEngine }: IndexBuilderOptions) {\n this.collators = {};\n this.decorators = {};\n this.documentTypes = {};\n this.logger = logger;\n this.searchEngine = searchEngine;\n }\n\n getSearchEngine(): SearchEngine {\n return this.searchEngine;\n }\n\n getDocumentTypes(): Record<string, DocumentTypeInfo> {\n return this.documentTypes;\n }\n\n /**\n * Makes the index builder aware of a collator that should be executed at the\n * given refresh interval.\n */\n addCollator({ factory, schedule }: RegisterCollatorParameters): void {\n this.logger.info(\n `Added ${factory.constructor.name} collator factory for type ${factory.type}`,\n );\n this.collators[factory.type] = {\n factory,\n schedule,\n };\n this.documentTypes[factory.type] = {\n visibilityPermission: factory.visibilityPermission,\n };\n }\n\n /**\n * Makes the index builder aware of a decorator. If no types are provided on\n * the decorator, it will be applied to documents from all known collators,\n * otherwise it will only be applied to documents of the given types.\n */\n addDecorator({ factory }: RegisterDecoratorParameters): void {\n const types = factory.types || ['*'];\n this.logger.info(\n `Added decorator ${factory.constructor.name} to types ${types.join(\n ', ',\n )}`,\n );\n types.forEach(type => {\n if (this.decorators.hasOwnProperty(type)) {\n this.decorators[type].push(factory);\n } else {\n this.decorators[type] = [factory];\n }\n });\n }\n\n /**\n * Compiles collators and decorators into tasks, which are added to a\n * scheduler returned to the caller.\n */\n async build(): Promise<{ scheduler: Scheduler }> {\n const scheduler = new Scheduler({\n logger: this.logger,\n });\n\n Object.keys(this.collators).forEach(type => {\n scheduler.addToSchedule({\n id: `search_index_${type.replace('-', '_').toLocaleLowerCase('en-US')}`,\n scheduledRunner: this.collators[type].schedule,\n task: async () => {\n // Instantiate the collator.\n const collator = await this.collators[type].factory.getCollator();\n this.logger.info(\n `Collating documents for ${type} via ${this.collators[type].factory.constructor.name}`,\n );\n\n // Instantiate all relevant decorators.\n const decorators: Transform[] = await Promise.all(\n (this.decorators['*'] || [])\n .concat(this.decorators[type] || [])\n .map(async factory => {\n const decorator = await factory.getDecorator();\n this.logger.info(\n `Attached decorator via ${factory.constructor.name} to ${type} index pipeline.`,\n );\n return decorator;\n }),\n );\n\n // Instantiate the indexer.\n const indexer = await this.searchEngine.getIndexer(type);\n\n // Compose collator/decorators/indexer into a pipeline\n return new Promise<void>(done => {\n pipeline(\n [collator, ...decorators, indexer],\n (error: NodeJS.ErrnoException | null) => {\n if (error) {\n this.logger.error(\n `Collating documents for ${type} failed: ${error}`,\n );\n } else {\n this.logger.info(`Collating documents for ${type} succeeded`);\n }\n\n // Signal index pipeline completion!\n done();\n },\n );\n });\n },\n });\n });\n\n return {\n scheduler,\n };\n }\n}\n","/*\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 { assertError } from '@backstage/errors';\nimport { IndexableDocument } from '@backstage/plugin-search-common';\nimport { Writable } from 'stream';\n\n/**\n * @beta\n */\nexport type BatchSearchEngineOptions = {\n batchSize: number;\n};\n\n/**\n * Base class encapsulating batch-based stream processing. Useful as a base\n * class for search engine indexers.\n * @beta\n */\nexport abstract class BatchSearchEngineIndexer extends Writable {\n private batchSize: number;\n private currentBatch: IndexableDocument[] = [];\n private initialized: Promise<undefined | Error>;\n\n constructor(options: BatchSearchEngineOptions) {\n super({ objectMode: true });\n this.batchSize = options.batchSize;\n\n // @todo Once node v15 is minimum, convert to _construct implementation.\n this.initialized = new Promise(done => {\n // Necessary to allow concrete implementation classes to construct\n // themselves before calling their initialize() methods.\n setImmediate(async () => {\n try {\n await this.initialize();\n done(undefined);\n } catch (e) {\n assertError(e);\n done(e);\n }\n });\n });\n }\n\n /**\n * Receives an array of indexable documents (of size this.batchSize) which\n * should be written to the search engine. This method won't be called again\n * at least until it resolves.\n */\n public abstract index(documents: IndexableDocument[]): Promise<void>;\n\n /**\n * Any asynchronous setup tasks can be performed here.\n */\n public abstract initialize(): Promise<void>;\n\n /**\n * Any asynchronous teardown tasks can be performed here.\n */\n public abstract finalize(): Promise<void>;\n\n /**\n * Encapsulates batch stream write logic.\n * @internal\n */\n async _write(\n doc: IndexableDocument,\n _e: any,\n done: (error?: Error | null) => void,\n ) {\n // Wait for init before proceeding. Throw error if initialization failed.\n const maybeError = await this.initialized;\n if (maybeError) {\n done(maybeError);\n return;\n }\n\n this.currentBatch.push(doc);\n if (this.currentBatch.length < this.batchSize) {\n done();\n return;\n }\n\n try {\n await this.index(this.currentBatch);\n this.currentBatch = [];\n done();\n } catch (e) {\n assertError(e);\n done(e);\n }\n }\n\n /**\n * Encapsulates finalization and final error handling logic.\n * @internal\n */\n async _final(done: (error?: Error | null) => void) {\n try {\n // Index any remaining documents.\n if (this.currentBatch.length) {\n await this.index(this.currentBatch);\n this.currentBatch = [];\n }\n await this.finalize();\n done();\n } catch (e) {\n assertError(e);\n done(e);\n }\n }\n}\n","/*\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 { assertError } from '@backstage/errors';\nimport { IndexableDocument } from '@backstage/plugin-search-common';\nimport { Transform } from 'stream';\n\n/**\n * Base class encapsulating simple async transformations. Useful as a base\n * class for Backstage search decorators.\n * @beta\n */\nexport abstract class DecoratorBase extends Transform {\n private initialized: Promise<undefined | Error>;\n\n constructor() {\n super({ objectMode: true });\n\n // @todo Once node v15 is minimum, convert to _construct implementation.\n this.initialized = new Promise(done => {\n // Necessary to allow concrete implementation classes to construct\n // themselves before calling their initialize() methods.\n setImmediate(async () => {\n try {\n await this.initialize();\n done(undefined);\n } catch (e) {\n assertError(e);\n done(e);\n }\n });\n });\n }\n\n /**\n * Any asynchronous setup tasks can be performed here.\n */\n public abstract initialize(): Promise<void>;\n\n /**\n * Receives a single indexable document. In your decorate method, you can:\n *\n * - Resolve `undefined` to indicate the record should be omitted.\n * - Resolve a single modified document, which could contain new fields,\n * edited fields, or removed fields.\n * - Resolve an array of indexable documents, if the purpose if the decorator\n * is to convert one document into multiple derivative documents.\n */\n public abstract decorate(\n document: IndexableDocument,\n ): Promise<IndexableDocument | IndexableDocument[] | undefined>;\n\n /**\n * Any asynchronous teardown tasks can be performed here.\n */\n public abstract finalize(): Promise<void>;\n\n /**\n * Encapsulates simple transform stream logic.\n * @internal\n */\n async _transform(\n document: IndexableDocument,\n _: any,\n done: (error?: Error | null) => void,\n ) {\n // Wait for init before proceeding. Throw error if initialization failed.\n const maybeError = await this.initialized;\n if (maybeError) {\n done(maybeError);\n return;\n }\n\n try {\n const decorated = await this.decorate(document);\n\n // If undefined was returned, omit the record and move on.\n if (decorated === undefined) {\n done();\n return;\n }\n\n // If an array of documents was given, push them all.\n if (Array.isArray(decorated)) {\n decorated.forEach(doc => {\n this.push(doc);\n });\n done();\n return;\n }\n\n // Otherwise, just push the decorated document.\n this.push(decorated);\n done();\n } catch (e) {\n assertError(e);\n done(e);\n }\n }\n\n /**\n * Encapsulates finalization and final error handling logic.\n * @internal\n */\n async _final(done: (error?: Error | null) => void) {\n try {\n await this.finalize();\n done();\n } catch (e) {\n assertError(e);\n done(e);\n }\n }\n}\n","/*\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 { IndexableDocument } from '@backstage/plugin-search-common';\nimport lunr from 'lunr';\nimport { BatchSearchEngineIndexer } from '../indexing';\n\n/**\n * @beta\n */\nexport class LunrSearchEngineIndexer extends BatchSearchEngineIndexer {\n private schemaInitialized = false;\n private builder: lunr.Builder;\n private docStore: Record<string, IndexableDocument> = {};\n\n constructor() {\n super({ batchSize: 100 });\n\n this.builder = new lunr.Builder();\n this.builder.pipeline.add(lunr.trimmer, lunr.stopWordFilter, lunr.stemmer);\n this.builder.searchPipeline.add(lunr.stemmer);\n }\n\n // No async initialization required.\n async initialize(): Promise<void> {}\n async finalize(): Promise<void> {}\n\n async index(documents: IndexableDocument[]): Promise<void> {\n if (!this.schemaInitialized) {\n // Make this lunr index aware of all relevant fields.\n Object.keys(documents[0]).forEach(field => {\n this.builder.field(field);\n });\n\n // Set \"location\" field as reference field\n this.builder.ref('location');\n\n this.schemaInitialized = true;\n }\n\n documents.forEach(document => {\n // Add document to Lunar index\n this.builder.add(document);\n\n // Store documents in memory to be able to look up document using the ref during query time\n // This is not how you should implement your SearchEngine implementation! Do not copy!\n this.docStore[document.location] = document;\n });\n }\n\n buildIndex() {\n return this.builder.build();\n }\n\n getDocumentStore() {\n return this.docStore;\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 IndexableDocument,\n IndexableResultSet,\n SearchQuery,\n QueryTranslator,\n SearchEngine,\n} from '@backstage/plugin-search-common';\nimport lunr from 'lunr';\nimport { Logger } from 'winston';\nimport { LunrSearchEngineIndexer } from './LunrSearchEngineIndexer';\n\n/**\n * @beta\n */\nexport type ConcreteLunrQuery = {\n lunrQueryBuilder: lunr.Index.QueryBuilder;\n documentTypes?: string[];\n pageSize: number;\n};\n\ntype LunrResultEnvelope = {\n result: lunr.Index.Result;\n type: string;\n};\n\n/**\n * @beta\n */\nexport type LunrQueryTranslator = (query: SearchQuery) => ConcreteLunrQuery;\n\n/**\n * @beta\n */\nexport class LunrSearchEngine implements SearchEngine {\n protected lunrIndices: Record<string, lunr.Index> = {};\n protected docStore: Record<string, IndexableDocument>;\n protected logger: Logger;\n\n constructor({ logger }: { logger: Logger }) {\n this.logger = logger;\n this.docStore = {};\n }\n\n protected translator: QueryTranslator = ({\n term,\n filters,\n types,\n }: SearchQuery): ConcreteLunrQuery => {\n const pageSize = 25;\n\n return {\n lunrQueryBuilder: q => {\n const termToken = lunr.tokenizer(term);\n\n // Support for typeahead search is based on https://github.com/olivernn/lunr.js/issues/256#issuecomment-295407852\n // look for an exact match and apply a large positive boost\n q.term(termToken, {\n usePipeline: true,\n boost: 100,\n });\n // look for terms that match the beginning of this term and apply a\n // medium boost\n q.term(termToken, {\n usePipeline: false,\n boost: 10,\n wildcard: lunr.Query.wildcard.TRAILING,\n });\n // look for terms that match with an edit distance of 2 and apply a\n // small boost\n q.term(termToken, {\n usePipeline: false,\n editDistance: 2,\n boost: 1,\n });\n\n if (filters) {\n Object.entries(filters).forEach(([field, fieldValue]) => {\n if (!q.allFields.includes(field)) {\n // Throw for unknown field, as this will be a non match\n throw new Error(`unrecognised field ${field}`);\n }\n // Arrays are poorly supported, but we can make it better for single-item arrays,\n // which should be a common case\n const value =\n Array.isArray(fieldValue) && fieldValue.length === 1\n ? fieldValue[0]\n : fieldValue;\n\n // Require that the given field has the given value\n if (['string', 'number', 'boolean'].includes(typeof value)) {\n q.term(lunr.tokenizer(value?.toString()), {\n presence: lunr.Query.presence.REQUIRED,\n fields: [field],\n });\n } else if (Array.isArray(value)) {\n // Illustrate how multi-value filters could work.\n // But warn that Lurn supports this poorly.\n this.logger.warn(\n `Non-scalar filter value used for field ${field}. Consider using a different Search Engine for better results.`,\n );\n q.term(lunr.tokenizer(value), {\n presence: lunr.Query.presence.OPTIONAL,\n fields: [field],\n });\n } else {\n // Log a warning or something about unknown filter value\n this.logger.warn(`Unknown filter type used on field ${field}`);\n }\n });\n }\n },\n documentTypes: types,\n pageSize,\n };\n };\n\n setTranslator(translator: LunrQueryTranslator) {\n this.translator = translator;\n }\n\n async getIndexer(type: string) {\n const indexer = new LunrSearchEngineIndexer();\n\n indexer.on('close', () => {\n // Once the stream is closed, build the index and store the documents in\n // memory for later retrieval.\n this.lunrIndices[type] = indexer.buildIndex();\n this.docStore = { ...this.docStore, ...indexer.getDocumentStore() };\n });\n\n return indexer;\n }\n\n async query(query: SearchQuery): Promise<IndexableResultSet> {\n const { lunrQueryBuilder, documentTypes, pageSize } = this.translator(\n query,\n ) as ConcreteLunrQuery;\n\n const results: LunrResultEnvelope[] = [];\n\n // Iterate over the filtered list of this.lunrIndex keys.\n Object.keys(this.lunrIndices)\n .filter(type => !documentTypes || documentTypes.includes(type))\n .forEach(type => {\n try {\n results.push(\n ...this.lunrIndices[type].query(lunrQueryBuilder).map(result => {\n return {\n result: result,\n type: type,\n };\n }),\n );\n } catch (err) {\n // if a field does not exist on a index, we can see that as a no-match\n if (\n err instanceof Error &&\n err.message.startsWith('unrecognised field')\n ) {\n return;\n }\n throw err;\n }\n });\n\n // Sort results.\n results.sort((doc1, doc2) => {\n return doc2.result.score - doc1.result.score;\n });\n\n // Perform paging\n const { page } = decodePageCursor(query.pageCursor);\n const offset = page * pageSize;\n const hasPreviousPage = page > 0;\n const hasNextPage = results.length > offset + pageSize;\n const nextPageCursor = hasNextPage\n ? encodePageCursor({ page: page + 1 })\n : undefined;\n const previousPageCursor = hasPreviousPage\n ? encodePageCursor({ page: page - 1 })\n : undefined;\n\n // Translate results into IndexableResultSet\n const realResultSet: IndexableResultSet = {\n results: results.slice(offset, offset + pageSize).map(d => {\n return { type: d.type, document: this.docStore[d.result.ref] };\n }),\n nextPageCursor,\n previousPageCursor,\n };\n\n return realResultSet;\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","/*\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 { IndexableDocument } from '@backstage/plugin-search-common';\nimport { pipeline, Readable, Transform, Writable } from 'stream';\n\n/**\n * Object resolved after a test pipeline is executed.\n * @beta\n */\nexport type TestPipelineResult = {\n /**\n * If an error was emitted by the pipeline, it will be set here.\n */\n error: unknown;\n\n /**\n * A list of documents collected at the end of the pipeline. If the subject\n * under test is an indexer, this will be an empty array (because your\n * indexer should have received the documents instead).\n */\n documents: IndexableDocument[];\n};\n\n/**\n * Test utility for Backstage Search collators, decorators, and indexers.\n * @beta\n */\nexport class TestPipeline {\n private collator?: Readable;\n private decorator?: Transform;\n private indexer?: Writable;\n\n private constructor({\n collator,\n decorator,\n indexer,\n }: {\n collator?: Readable;\n decorator?: Transform;\n indexer?: Writable;\n }) {\n this.collator = collator;\n this.decorator = decorator;\n this.indexer = indexer;\n }\n\n /**\n * Provide the collator, decorator, or indexer to be tested.\n */\n static withSubject(subject: Readable | Transform | Writable) {\n if (subject instanceof Transform) {\n return new TestPipeline({ decorator: subject });\n }\n\n if (subject instanceof Readable) {\n return new TestPipeline({ collator: subject });\n }\n\n if (subject instanceof Writable) {\n return new TestPipeline({ indexer: subject });\n }\n\n throw new Error(\n 'Unknown test subject: are you passing a readable, writable, or transform stream?',\n );\n }\n\n /**\n * Provide documents for testing decorators and indexers.\n */\n withDocuments(documents: IndexableDocument[]): TestPipeline {\n if (this.collator) {\n throw new Error('Cannot provide documents when testing a collator.');\n }\n\n // Set a naive readable stream that just pushes all given documents.\n this.collator = new Readable({ objectMode: true });\n this.collator._read = () => {};\n process.nextTick(() => {\n documents.forEach(document => {\n this.collator!.push(document);\n });\n this.collator!.push(null);\n });\n\n return this;\n }\n\n /**\n * Execute the test pipeline so that you can make assertions about the result\n * or behavior of the given test subject.\n */\n async execute(): Promise<TestPipelineResult> {\n const documents: IndexableDocument[] = [];\n if (!this.collator) {\n throw new Error(\n 'Cannot execute pipeline without a collator or documents',\n );\n }\n\n // If we are here and there is no indexer, we are testing a collator or a\n // decorator. Set up a naive writable that captures documents in memory.\n if (!this.indexer) {\n this.indexer = new Writable({ objectMode: true });\n this.indexer._write = (document: IndexableDocument, _, done) => {\n documents.push(document);\n done();\n };\n }\n\n return new Promise<TestPipelineResult>(done => {\n const pipes: (Readable | Transform | Writable)[] = [this.collator!];\n if (this.decorator) {\n pipes.push(this.decorator);\n }\n pipes.push(this.indexer!);\n\n pipeline(pipes, (error: NodeJS.ErrnoException | null) => {\n done({\n error,\n documents,\n });\n });\n });\n }\n}\n"],"names":["AbortController","pipeline","Writable","assertError","Transform","lunr","Readable"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,46 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import { SearchEngine, DocumentCollatorFactory, DocumentDecoratorFactory, DocumentTypeInfo, IndexableDocument, SearchQuery, QueryTranslator,
|
|
2
|
+
import { SearchEngine, DocumentCollatorFactory, DocumentDecoratorFactory, DocumentTypeInfo, IndexableDocument, SearchQuery, QueryTranslator, IndexableResultSet } from '@backstage/plugin-search-common';
|
|
3
3
|
export { SearchEngine } from '@backstage/plugin-search-common';
|
|
4
4
|
import { Logger } from 'winston';
|
|
5
|
+
import { TaskFunction, TaskRunner } from '@backstage/backend-tasks';
|
|
5
6
|
import lunr from 'lunr';
|
|
6
7
|
import { Writable, Transform, Readable } from 'stream';
|
|
7
8
|
|
|
9
|
+
/**
|
|
10
|
+
* @public ScheduleTaskParameters
|
|
11
|
+
*/
|
|
12
|
+
interface ScheduleTaskParameters {
|
|
13
|
+
id: string;
|
|
14
|
+
task: TaskFunction;
|
|
15
|
+
scheduledRunner: TaskRunner;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* @beta
|
|
19
|
+
*/
|
|
20
|
+
declare class Scheduler {
|
|
21
|
+
private logger;
|
|
22
|
+
private schedule;
|
|
23
|
+
private abortController;
|
|
24
|
+
private isRunning;
|
|
25
|
+
constructor({ logger }: {
|
|
26
|
+
logger: Logger;
|
|
27
|
+
});
|
|
28
|
+
/**
|
|
29
|
+
* Adds each task and interval to the schedule.
|
|
30
|
+
* When running the tasks, the scheduler waits at least for the time specified
|
|
31
|
+
* in the interval once the task was completed, before running it again.
|
|
32
|
+
*/
|
|
33
|
+
addToSchedule({ id, task, scheduledRunner }: ScheduleTaskParameters): void;
|
|
34
|
+
/**
|
|
35
|
+
* Starts the scheduling process for each task
|
|
36
|
+
*/
|
|
37
|
+
start(): void;
|
|
38
|
+
/**
|
|
39
|
+
* Stop all scheduled tasks.
|
|
40
|
+
*/
|
|
41
|
+
stop(): void;
|
|
42
|
+
}
|
|
43
|
+
|
|
8
44
|
/**
|
|
9
45
|
* @beta
|
|
10
46
|
*/
|
|
@@ -18,9 +54,10 @@ declare type IndexBuilderOptions = {
|
|
|
18
54
|
*/
|
|
19
55
|
interface RegisterCollatorParameters {
|
|
20
56
|
/**
|
|
21
|
-
* The
|
|
57
|
+
* The schedule for which the provided collator will be called, commonly the result of
|
|
58
|
+
* {@link @backstage/backend-tasks#PluginTaskScheduler.createScheduledTaskRunner}
|
|
22
59
|
*/
|
|
23
|
-
|
|
60
|
+
schedule: TaskRunner;
|
|
24
61
|
/**
|
|
25
62
|
* The class responsible for returning the document collator of the given type.
|
|
26
63
|
*/
|
|
@@ -53,7 +90,7 @@ declare class IndexBuilder {
|
|
|
53
90
|
* Makes the index builder aware of a collator that should be executed at the
|
|
54
91
|
* given refresh interval.
|
|
55
92
|
*/
|
|
56
|
-
addCollator({ factory,
|
|
93
|
+
addCollator({ factory, schedule }: RegisterCollatorParameters): void;
|
|
57
94
|
/**
|
|
58
95
|
* Makes the index builder aware of a decorator. If no types are provided on
|
|
59
96
|
* the decorator, it will be applied to documents from all known collators,
|
|
@@ -69,35 +106,6 @@ declare class IndexBuilder {
|
|
|
69
106
|
}>;
|
|
70
107
|
}
|
|
71
108
|
|
|
72
|
-
/**
|
|
73
|
-
* TODO: coordination, error handling
|
|
74
|
-
*/
|
|
75
|
-
/**
|
|
76
|
-
* @beta
|
|
77
|
-
*/
|
|
78
|
-
declare class Scheduler {
|
|
79
|
-
private logger;
|
|
80
|
-
private schedule;
|
|
81
|
-
private runningTasks;
|
|
82
|
-
constructor({ logger }: {
|
|
83
|
-
logger: Logger;
|
|
84
|
-
});
|
|
85
|
-
/**
|
|
86
|
-
* Adds each task and interval to the schedule.
|
|
87
|
-
* When running the tasks, the scheduler waits at least for the time specified
|
|
88
|
-
* in the interval once the task was completed, before running it again.
|
|
89
|
-
*/
|
|
90
|
-
addToSchedule(task: Function, interval: number): void;
|
|
91
|
-
/**
|
|
92
|
-
* Starts the scheduling process for each task
|
|
93
|
-
*/
|
|
94
|
-
start(): void;
|
|
95
|
-
/**
|
|
96
|
-
* Stop all scheduled tasks.
|
|
97
|
-
*/
|
|
98
|
-
stop(): void;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
109
|
/**
|
|
102
110
|
* @beta
|
|
103
111
|
*/
|
|
@@ -198,7 +206,7 @@ declare class LunrSearchEngine implements SearchEngine {
|
|
|
198
206
|
protected translator: QueryTranslator;
|
|
199
207
|
setTranslator(translator: LunrQueryTranslator): void;
|
|
200
208
|
getIndexer(type: string): Promise<LunrSearchEngineIndexer>;
|
|
201
|
-
query(query: SearchQuery): Promise<
|
|
209
|
+
query(query: SearchQuery): Promise<IndexableResultSet>;
|
|
202
210
|
}
|
|
203
211
|
|
|
204
212
|
/**
|
|
@@ -241,4 +249,4 @@ declare class TestPipeline {
|
|
|
241
249
|
execute(): Promise<TestPipelineResult>;
|
|
242
250
|
}
|
|
243
251
|
|
|
244
|
-
export { BatchSearchEngineIndexer, BatchSearchEngineOptions, ConcreteLunrQuery, DecoratorBase, IndexBuilder, IndexBuilderOptions, LunrQueryTranslator, LunrSearchEngine, LunrSearchEngineIndexer, RegisterCollatorParameters, RegisterDecoratorParameters, Scheduler, TestPipeline, TestPipelineResult };
|
|
252
|
+
export { BatchSearchEngineIndexer, BatchSearchEngineOptions, ConcreteLunrQuery, DecoratorBase, IndexBuilder, IndexBuilderOptions, LunrQueryTranslator, LunrSearchEngine, LunrSearchEngineIndexer, RegisterCollatorParameters, RegisterDecoratorParameters, ScheduleTaskParameters, Scheduler, TestPipeline, TestPipelineResult };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/plugin-search-backend-node",
|
|
3
3
|
"description": "A library for Backstage backend plugins that want to interact with the search backend plugin",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.6.0",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"license": "Apache-2.0",
|
|
@@ -23,19 +23,21 @@
|
|
|
23
23
|
"clean": "backstage-cli package clean"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
+
"@backstage/backend-tasks": "^0.3.0",
|
|
26
27
|
"@backstage/errors": "^1.0.0",
|
|
27
|
-
"@backstage/plugin-search-common": "^0.3.
|
|
28
|
+
"@backstage/plugin-search-common": "^0.3.3",
|
|
28
29
|
"@types/lunr": "^2.3.3",
|
|
29
30
|
"lodash": "^4.17.21",
|
|
30
31
|
"lunr": "^2.3.9",
|
|
32
|
+
"node-abort-controller": "^3.0.1",
|
|
31
33
|
"winston": "^3.2.1"
|
|
32
34
|
},
|
|
33
35
|
"devDependencies": {
|
|
34
|
-
"@backstage/backend-common": "^0.13.
|
|
35
|
-
"@backstage/cli": "^0.
|
|
36
|
+
"@backstage/backend-common": "^0.13.2",
|
|
37
|
+
"@backstage/cli": "^0.17.0"
|
|
36
38
|
},
|
|
37
39
|
"files": [
|
|
38
40
|
"dist"
|
|
39
41
|
],
|
|
40
|
-
"gitHead": "
|
|
42
|
+
"gitHead": "e0e44c433319711c2fb8b175db411a621f7aaec2"
|
|
41
43
|
}
|