@stackoverflow/backstage-stack-overflow-teams-collator 1.5.0 → 1.6.3

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/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
- # Stack Overflow for Teams Search Backend Module
2
+ # Stack Internal Search Backend Module
3
3
 
4
- This module for the search plugin is an enhanced version of the original [Stack Overflow collator](https://github.com/backstage/backstage/tree/master/plugins/search-backend-module-stack-overflow-collator). It provides additional information while coded to work specifically with Stack Overflow for Teams API Version 3.
4
+ This module for the search plugin is an enhanced version of the original [Stack Overflow Internal collator](https://github.com/backstage/backstage/tree/master/plugins/search-backend-module-stack-overflow-collator). It provides additional information while coded to work specifically with Stack Internal API Version 3.
5
5
 
6
6
  ## Getting started
7
7
 
@@ -13,24 +13,24 @@ To use any of the functionality this plugin provides, you need to start by confi
13
13
 
14
14
  ```yaml
15
15
  stackoverflow:
16
- baseUrl: https://api.stackoverflowteams.com # alternative: your Stack Overflow Enterprise site
16
+ baseUrl: https://api.stackoverflowteams.com # alternative: your Stack Internal Enterprise site
17
17
  teamName: $STACK_OVERFLOW_TEAM_NAME # optional if you are on Enterprise
18
18
  apiAccessToken: $STACK_OVERFLOW_API_ACCESS_TOKEN
19
19
  ```
20
20
 
21
- ### Stack Overflow for Teams
21
+ ### Stack Internal
22
22
 
23
- If you have a private Stack Overflow instance and/or a private Stack Overflow Team you will need to supply a Personal Access Token. You can read more about how to set this up by going to [Stack Overflow's Help Page](https://stackoverflowteams.help/en/articles/7913768-stack-overflow-for-teams-api-v3).
23
+ If you have a private Stack Overflow Internal instance and/or a private Stack Overflow Internal Team you will need to supply a Personal Access Token. You can read more about how to set this up by going to [Stack Overflow Internal's Help Page](https://stackoverflowteams.help/en/articles/7913768-stack-overflow-for-teams-api-v3).
24
24
 
25
25
  ## Areas of Responsibility
26
26
 
27
- This stack overflow backend plugin is primarily responsible for the following:
27
+ This Stack Overflow Internal backend plugin is primarily responsible for the following:
28
28
 
29
- - Provides a `StackOverflowQuestionsCollatorFactory`, which can be used in the search backend to index stack overflow questions to your Backstage Search.
29
+ - Provides a `StackOverflowQuestionsCollatorFactory`, which can be used in the search backend to index Stack Overflow Internal questions to your Backstage Search.
30
30
 
31
- ### Index Stack Overflow Questions to search
31
+ ### Index Stack Overflow Internal Questions to search
32
32
 
33
- Before you are able to start index stack overflow questions to search, you need to go through the [search getting started guide](https://backstage.io/docs/features/search/getting-started).
33
+ Before you are able to start index Stack Overflow Internal questions to search, you need to go through the [search getting started guide](https://backstage.io/docs/features/search/getting-started).
34
34
 
35
35
  When you have your `packages/backend/src/plugins/search.ts` file ready to make modifications, add the following code snippet to add the `StackOverflowQuestionsCollatorFactory`. Note that you can optionally modify the `requestParams`, otherwise it will defaults to `{ order: 'desc', sort: 'activity' }`.
36
36
 
@@ -49,7 +49,7 @@ indexBuilder.addCollator({
49
49
 
50
50
  ## New Backend System
51
51
 
52
- This package exports a module that extends the search backend to also indexing the questions exposed by the [`Stack Overflow for Teams API version 3`](https://stackoverflowteams.help/en/articles/7913768-stack-overflow-for-teams-api-v3).
52
+ This package exports a module that extends the search backend to also indexing the questions exposed by the [`Stack Internal API version 3`](https://stackoverflowteams.help/en/articles/7913768-stack-overflow-for-teams-api-v3).
53
53
 
54
54
  ### Installation
55
55
 
package/config.d.ts CHANGED
@@ -16,22 +16,22 @@
16
16
 
17
17
  export interface Config {
18
18
  /**
19
- * Configuration options for the stack overflow plugin
19
+ * Configuration options for the Stack Overflow Internal plugin
20
20
  */
21
21
  stackoverflow?: {
22
22
  /**
23
- * The base url of the Stack Overflow API used for the plugin, if no BaseUrl is provided it will default to https://api.stackoverflowteams.com
23
+ * The base url of the Stack Overflow Internal API used for the plugin, if no BaseUrl is provided it will default to https://api.stackoverflowteams.com
24
24
  */
25
25
  baseUrl?: string;
26
26
 
27
27
  /**
28
- * The API Access Token to authenticate to Stack Overflow API Version 3
28
+ * The API Access Token to authenticate to Stack Overflow Internal API Version 3
29
29
  * @visibility secret
30
30
  */
31
31
  apiAccessToken: string;
32
32
 
33
33
  /**
34
- * The name of the team for a Stack Overflow for Teams account. When teamName is provided baseUrl will always be https://api.stackoverflowteams.com
34
+ * The name of the team for a Stack Internal account. When teamName is provided baseUrl will always be https://api.stackoverflowteams.com
35
35
  */
36
36
  teamName?: string;
37
37
 
@@ -0,0 +1,118 @@
1
+ 'use strict';
2
+
3
+ var stream = require('stream');
4
+ var qs = require('qs');
5
+
6
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
7
+
8
+ var qs__default = /*#__PURE__*/_interopDefaultCompat(qs);
9
+
10
+ class StackOverflowQuestionsCollatorFactory {
11
+ requestParams;
12
+ baseUrl;
13
+ apiAccessToken;
14
+ teamName;
15
+ logger;
16
+ referrer = "Backstage_Plugin";
17
+ type = "stack-overflow";
18
+ stackOverflowTeamsAPI = "https://api.stackoverflowteams.com";
19
+ forceOriginUrl = (baseUrl) => `${new URL(baseUrl).origin}`;
20
+ constructor(options) {
21
+ this.baseUrl = this.forceOriginUrl(options.baseUrl || this.stackOverflowTeamsAPI);
22
+ this.apiAccessToken = options.apiAccessToken;
23
+ this.teamName = options.teamName;
24
+ this.logger = options.logger.child({ documentType: this.type });
25
+ this.requestParams = {
26
+ order: "desc",
27
+ sort: "creation",
28
+ ...options.requestParams ?? {}
29
+ };
30
+ }
31
+ static fromConfig(config, options) {
32
+ const apiAccessToken = config.getString("stackoverflow.apiAccessToken");
33
+ const teamName = config.getOptionalString("stackoverflow.teamName");
34
+ const baseUrl = config.getOptionalString("stackoverflow.baseUrl");
35
+ const requestParams = config.getOptionalConfig("stackoverflow.requestParams")?.get();
36
+ return new StackOverflowQuestionsCollatorFactory({
37
+ baseUrl,
38
+ apiAccessToken,
39
+ teamName,
40
+ requestParams,
41
+ ...options
42
+ });
43
+ }
44
+ async getCollator() {
45
+ return stream.Readable.from(this.execute());
46
+ }
47
+ // Error logging and debugging
48
+ async *execute() {
49
+ this.logger.info(`Retrieving data using Stack Overflow Internal API Version 3`);
50
+ if (!this.baseUrl && this.teamName) {
51
+ this.logger.info(
52
+ `Connecting to the Teams API at https://api.stackoverflowteams.com`
53
+ );
54
+ }
55
+ if (!this.baseUrl && !this.teamName) {
56
+ this.logger.error(
57
+ `No stackoverflow.teamName has been provided while trying to connect to the Teams API.`
58
+ );
59
+ }
60
+ const params = qs__default.default.stringify(this.requestParams, {
61
+ arrayFormat: "comma",
62
+ addQueryPrefix: true
63
+ });
64
+ let requestUrl;
65
+ if (this.teamName) {
66
+ requestUrl = `${this.stackOverflowTeamsAPI}/v3/teams/${this.teamName}/questions${params}`;
67
+ } else {
68
+ requestUrl = `${this.baseUrl}/api/v3/questions${params}`;
69
+ }
70
+ let page = 1;
71
+ let totalPages = 1;
72
+ const pageSize = this.requestParams.pageSize || 50;
73
+ this.logger.warn(
74
+ "Starting collating Stack Internal questions, wait for the success message"
75
+ );
76
+ while (page <= totalPages) {
77
+ const res = await fetch(
78
+ `${requestUrl}&page=${page}&pageSize=${pageSize}`,
79
+ {
80
+ headers: {
81
+ Authorization: `Bearer ${this.apiAccessToken}`
82
+ }
83
+ }
84
+ );
85
+ const data = await res.json();
86
+ totalPages = data.totalPages;
87
+ for (const question of data.items ?? []) {
88
+ const tags = question.tags?.map((tag) => ({
89
+ id: tag.id,
90
+ description: tag.description,
91
+ location: tag.webUrl,
92
+ name: tag.name
93
+ })) || [];
94
+ yield {
95
+ title: question.title,
96
+ location: `${question.webUrl}?r=${this.referrer}`,
97
+ text: question.owner?.name || "Deleted user",
98
+ userReputation: question.owner?.reputation,
99
+ avatar: question.owner?.avatarUrl,
100
+ userProfile: question.owner?.webUrl,
101
+ userRole: question.owner?.role,
102
+ tags,
103
+ answers: question.answerCount,
104
+ score: question.score,
105
+ viewCount: question.viewCount,
106
+ isAnswered: question.isAnswered,
107
+ bounty: question.bounty,
108
+ creationDate: question.creationDate,
109
+ lastActivityDate: question.lastActivityDate
110
+ };
111
+ }
112
+ page++;
113
+ }
114
+ }
115
+ }
116
+
117
+ exports.StackOverflowQuestionsCollatorFactory = StackOverflowQuestionsCollatorFactory;
118
+ //# sourceMappingURL=StackOverflowQuestionsCollatorFactory.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StackOverflowQuestionsCollatorFactory.cjs.js","sources":["../../src/collators/StackOverflowQuestionsCollatorFactory.ts"],"sourcesContent":["/*\n * This component is a modified version of https://github.com/backstage/backstage/blob/master/plugins/search-backend-module-stack-overflow-collator/src/collators/StackOverflowQuestionsCollatorFactory.ts \n *\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n DocumentCollatorFactory,\n IndexableDocument,\n} from '@backstage/plugin-search-common';\nimport { Config } from '@backstage/config';\nimport { Readable } from 'stream';\n\nimport qs from 'qs';\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { Tag } from '../types';\n\n/**\n * Extended IndexableDocument with stack overflow specific properties\n *\n * @public\n */\nexport interface StackOverflowDocument extends IndexableDocument {\n answers: number;\n userReputation: number;\n avatar: string;\n userProfile: string;\n userRole: string;\n tags: object[];\n score: number;\n viewCount: number;\n isAnswered: boolean;\n bounty: {} | null;\n creationDate: string;\n lastActivityDate: string;\n}\n\n/**\n * Type representing the request parameters accepted by the {@link StackOverflowQuestionsCollatorFactory}\n *\n * @public\n */\nexport type StackOverflowQuestionsRequestParams = {\n [key: string]: string | string[] | number;\n};\n\n/**\n * Options for {@link StackOverflowQuestionsCollatorFactory}\n *\n * @public\n */\nexport type StackOverflowQuestionsCollatorFactoryOptions = {\n apiAccessToken?: string;\n teamName?: string;\n requestParams?: StackOverflowQuestionsRequestParams;\n logger: LoggerService;\n};\n\n/**\n * Search collator responsible for collecting stack overflow questions to index.\n *\n * @public\n */\nexport class StackOverflowQuestionsCollatorFactory\n implements DocumentCollatorFactory\n{\n protected requestParams: StackOverflowQuestionsRequestParams;\n private readonly baseUrl: string;\n private readonly apiAccessToken: string | undefined;\n private readonly teamName: string | undefined;\n private readonly logger: LoggerService;\n private readonly referrer: string = 'Backstage_Plugin';\n public readonly type: string = 'stack-overflow';\n public readonly stackOverflowTeamsAPI: string =\n 'https://api.stackoverflowteams.com';\n\n private forceOriginUrl = (baseUrl: string): string =>\n `${new URL(baseUrl).origin}`;\n\n private constructor(options: StackOverflowQuestionsCollatorFactoryOptions & { baseUrl?: string }) {\n this.baseUrl = this.forceOriginUrl(options.baseUrl || this.stackOverflowTeamsAPI);\n this.apiAccessToken = options.apiAccessToken;\n this.teamName = options.teamName;\n this.logger = options.logger.child({ documentType: this.type });\n\n // Sets the same default request parameters as the official API documentation\n // See https://api.stackexchange.com/docs/questions\n this.requestParams = {\n order: 'desc',\n sort: 'creation',\n ...(options.requestParams ?? {}),\n };\n }\n\n static fromConfig(\n config: Config,\n options: StackOverflowQuestionsCollatorFactoryOptions,\n ) {\n const apiAccessToken = config.getString('stackoverflow.apiAccessToken');\n const teamName = config.getOptionalString('stackoverflow.teamName');\n const baseUrl = config.getOptionalString('stackoverflow.baseUrl');\n const requestParams = config\n .getOptionalConfig('stackoverflow.requestParams')\n ?.get<StackOverflowQuestionsRequestParams>();\n\n return new StackOverflowQuestionsCollatorFactory({\n baseUrl,\n apiAccessToken,\n teamName,\n requestParams,\n ...options,\n });\n }\n\n async getCollator() {\n return Readable.from(this.execute());\n }\n\n // Error logging and debugging\n\n async *execute(): AsyncGenerator<StackOverflowDocument> {\n this.logger.info(`Retrieving data using Stack Overflow Internal API Version 3`);\n\n if (!this.baseUrl && this.teamName) {\n this.logger.info(\n `Connecting to the Teams API at https://api.stackoverflowteams.com`,\n );\n }\n\n if (!this.baseUrl && !this.teamName) {\n this.logger.error(\n `No stackoverflow.teamName has been provided while trying to connect to the Teams API.`\n )\n }\n\n const params = qs.stringify(this.requestParams, {\n arrayFormat: 'comma',\n addQueryPrefix: true,\n });\n\n let requestUrl;\n\n if (this.teamName) {\n requestUrl = `${this.stackOverflowTeamsAPI}/v3/teams/${this.teamName}/questions${params}`;\n } else {\n requestUrl = `${this.baseUrl}/api/v3/questions${params}`;\n }\n\n // The code below has been commented, it has potential compatiblity with Enterprise Private Teams but I haven't tested it and since Private Teams is not widely used I've decided to change the logic to prioritise the support for the Basic and Business Teams.\n\n // if (this.teamName) {\n // const basePath =\n // this.baseUrl === this.stackOverflowTeamsAPI ? '/v3' : '/api/v3';\n // requestUrl = `${this.baseUrl}${basePath}/teams/${this.teamName}/questions${params}`;\n // } else {\n // requestUrl = `${this.baseUrl}/api/v3/questions${params}`;\n // }\n\n let page = 1;\n let totalPages = 1;\n const pageSize = this.requestParams.pageSize || 50;\n this.logger.warn(\n 'Starting collating Stack Internal questions, wait for the success message',\n );\n while (page <= totalPages) {\n const res = await fetch(\n `${requestUrl}&page=${page}&pageSize=${pageSize}`,\n {\n headers: {\n Authorization: `Bearer ${this.apiAccessToken}`,\n },\n },\n );\n\n const data = await res.json();\n totalPages = data.totalPages;\n\n for (const question of data.items ?? []) {\n const tags =\n question.tags?.map((tag: Tag) => ({\n id: tag.id,\n description: tag.description,\n location: tag.webUrl,\n name: tag.name,\n })) || [];\n\n yield {\n title: question.title,\n location: `${question.webUrl}?r=${this.referrer}`,\n text: question.owner?.name || 'Deleted user',\n userReputation: question.owner?.reputation,\n avatar: question.owner?.avatarUrl,\n userProfile: question.owner?.webUrl,\n userRole: question.owner?.role,\n tags: tags,\n answers: question.answerCount,\n score: question.score,\n viewCount: question.viewCount,\n isAnswered: question.isAnswered,\n bounty: question.bounty,\n creationDate: question.creationDate,\n lastActivityDate: question.lastActivityDate,\n };\n }\n page++;\n }\n }\n}\n"],"names":["Readable","qs"],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var StackOverflowQuestionsCollatorFactory = require('./collators/StackOverflowQuestionsCollatorFactory.cjs.js');
6
+ var SearchStackOverflowCollatorModule = require('./module/SearchStackOverflowCollatorModule.cjs.js');
7
+
8
+
9
+
10
+ exports.StackOverflowQuestionsCollatorFactory = StackOverflowQuestionsCollatorFactory.StackOverflowQuestionsCollatorFactory;
11
+ exports.default = SearchStackOverflowCollatorModule.searchStackOverflowCollatorModule;
12
+ //# sourceMappingURL=index.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;"}
@@ -0,0 +1,73 @@
1
+ import { IndexableDocument, DocumentCollatorFactory } from '@backstage/plugin-search-common';
2
+ import { Config } from '@backstage/config';
3
+ import { Readable } from 'stream';
4
+ import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
5
+ import { LoggerService } from '@backstage/backend-plugin-api';
6
+
7
+ /**
8
+ * Extended IndexableDocument with stack overflow specific properties
9
+ *
10
+ * @public
11
+ */
12
+ interface StackOverflowDocument extends IndexableDocument {
13
+ answers: number;
14
+ userReputation: number;
15
+ avatar: string;
16
+ userProfile: string;
17
+ userRole: string;
18
+ tags: object[];
19
+ score: number;
20
+ viewCount: number;
21
+ isAnswered: boolean;
22
+ bounty: {} | null;
23
+ creationDate: string;
24
+ lastActivityDate: string;
25
+ }
26
+ /**
27
+ * Type representing the request parameters accepted by the {@link StackOverflowQuestionsCollatorFactory}
28
+ *
29
+ * @public
30
+ */
31
+ type StackOverflowQuestionsRequestParams = {
32
+ [key: string]: string | string[] | number;
33
+ };
34
+ /**
35
+ * Options for {@link StackOverflowQuestionsCollatorFactory}
36
+ *
37
+ * @public
38
+ */
39
+ type StackOverflowQuestionsCollatorFactoryOptions = {
40
+ apiAccessToken?: string;
41
+ teamName?: string;
42
+ requestParams?: StackOverflowQuestionsRequestParams;
43
+ logger: LoggerService;
44
+ };
45
+ /**
46
+ * Search collator responsible for collecting stack overflow questions to index.
47
+ *
48
+ * @public
49
+ */
50
+ declare class StackOverflowQuestionsCollatorFactory implements DocumentCollatorFactory {
51
+ protected requestParams: StackOverflowQuestionsRequestParams;
52
+ private readonly baseUrl;
53
+ private readonly apiAccessToken;
54
+ private readonly teamName;
55
+ private readonly logger;
56
+ private readonly referrer;
57
+ readonly type: string;
58
+ readonly stackOverflowTeamsAPI: string;
59
+ private forceOriginUrl;
60
+ private constructor();
61
+ static fromConfig(config: Config, options: StackOverflowQuestionsCollatorFactoryOptions): StackOverflowQuestionsCollatorFactory;
62
+ getCollator(): Promise<Readable>;
63
+ execute(): AsyncGenerator<StackOverflowDocument>;
64
+ }
65
+
66
+ /**
67
+ * @public
68
+ * Search backend module for the Stack Overflow Internal index.
69
+ */
70
+ declare const searchStackOverflowCollatorModule: _backstage_backend_plugin_api.BackendFeature;
71
+
72
+ export { StackOverflowQuestionsCollatorFactory, searchStackOverflowCollatorModule as default };
73
+ export type { StackOverflowDocument, StackOverflowQuestionsCollatorFactoryOptions, StackOverflowQuestionsRequestParams };
@@ -0,0 +1,40 @@
1
+ 'use strict';
2
+
3
+ var backendPluginApi = require('@backstage/backend-plugin-api');
4
+ var alpha = require('@backstage/plugin-search-backend-node/alpha');
5
+ var StackOverflowQuestionsCollatorFactory = require('../collators/StackOverflowQuestionsCollatorFactory.cjs.js');
6
+
7
+ const searchStackOverflowCollatorModule = backendPluginApi.createBackendModule({
8
+ pluginId: "search",
9
+ moduleId: "stack-overflow-collator",
10
+ register(env) {
11
+ env.registerInit({
12
+ deps: {
13
+ config: backendPluginApi.coreServices.rootConfig,
14
+ logger: backendPluginApi.coreServices.logger,
15
+ discovery: backendPluginApi.coreServices.discovery,
16
+ scheduler: backendPluginApi.coreServices.scheduler,
17
+ indexRegistry: alpha.searchIndexRegistryExtensionPoint
18
+ },
19
+ async init({ config, logger, scheduler, indexRegistry }) {
20
+ const defaultSchedule = {
21
+ frequency: { minutes: 10 },
22
+ timeout: { minutes: 15 },
23
+ initialDelay: { seconds: 3 }
24
+ };
25
+ const schedule = config.has("stackoverflow.schedule") ? backendPluginApi.readSchedulerServiceTaskScheduleDefinitionFromConfig(
26
+ config.getConfig("stackoverflow.schedule")
27
+ ) : defaultSchedule;
28
+ indexRegistry.addCollator({
29
+ schedule: scheduler.createScheduledTaskRunner(schedule),
30
+ factory: StackOverflowQuestionsCollatorFactory.StackOverflowQuestionsCollatorFactory.fromConfig(config, {
31
+ logger
32
+ })
33
+ });
34
+ }
35
+ });
36
+ }
37
+ });
38
+
39
+ exports.searchStackOverflowCollatorModule = searchStackOverflowCollatorModule;
40
+ //# sourceMappingURL=SearchStackOverflowCollatorModule.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SearchStackOverflowCollatorModule.cjs.js","sources":["../../src/module/SearchStackOverflowCollatorModule.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { readSchedulerServiceTaskScheduleDefinitionFromConfig } from '@backstage/backend-plugin-api';\nimport {\n coreServices,\n createBackendModule,\n} from '@backstage/backend-plugin-api';\nimport { searchIndexRegistryExtensionPoint } from '@backstage/plugin-search-backend-node/alpha';\nimport { StackOverflowQuestionsCollatorFactory } from '../collators';\n\n/**\n * @public\n * Search backend module for the Stack Overflow Internal index.\n */\nexport const searchStackOverflowCollatorModule = createBackendModule({\n pluginId: 'search',\n moduleId: 'stack-overflow-collator',\n register(env) {\n env.registerInit({\n deps: {\n config: coreServices.rootConfig,\n logger: coreServices.logger,\n discovery: coreServices.discovery,\n scheduler: coreServices.scheduler,\n indexRegistry: searchIndexRegistryExtensionPoint,\n },\n async init({ config, logger, scheduler, indexRegistry }) {\n const defaultSchedule = {\n frequency: { minutes: 10 },\n timeout: { minutes: 15 },\n initialDelay: { seconds: 3 },\n };\n\n const schedule = config.has('stackoverflow.schedule')\n ? readSchedulerServiceTaskScheduleDefinitionFromConfig(\n config.getConfig('stackoverflow.schedule'),\n )\n : defaultSchedule;\n\n indexRegistry.addCollator({\n schedule: scheduler.createScheduledTaskRunner(schedule),\n factory: StackOverflowQuestionsCollatorFactory.fromConfig(config, {\n logger,\n }),\n });\n },\n });\n },\n});\n"],"names":["createBackendModule","coreServices","searchIndexRegistryExtensionPoint","readSchedulerServiceTaskScheduleDefinitionFromConfig","StackOverflowQuestionsCollatorFactory"],"mappings":";;;;;;AA4BO,MAAM,oCAAoCA,oCAAA,CAAoB;AAAA,EACnE,QAAA,EAAU,QAAA;AAAA,EACV,QAAA,EAAU,yBAAA;AAAA,EACV,SAAS,GAAA,EAAK;AACZ,IAAA,GAAA,CAAI,YAAA,CAAa;AAAA,MACf,IAAA,EAAM;AAAA,QACJ,QAAQC,6BAAA,CAAa,UAAA;AAAA,QACrB,QAAQA,6BAAA,CAAa,MAAA;AAAA,QACrB,WAAWA,6BAAA,CAAa,SAAA;AAAA,QACxB,WAAWA,6BAAA,CAAa,SAAA;AAAA,QACxB,aAAA,EAAeC;AAAA,OACjB;AAAA,MACA,MAAM,IAAA,CAAK,EAAE,QAAQ,MAAA,EAAQ,SAAA,EAAW,eAAc,EAAG;AACvD,QAAA,MAAM,eAAA,GAAkB;AAAA,UACtB,SAAA,EAAW,EAAE,OAAA,EAAS,EAAA,EAAG;AAAA,UACzB,OAAA,EAAS,EAAE,OAAA,EAAS,EAAA,EAAG;AAAA,UACvB,YAAA,EAAc,EAAE,OAAA,EAAS,CAAA;AAAE,SAC7B;AAEA,QAAA,MAAM,QAAA,GAAW,MAAA,CAAO,GAAA,CAAI,wBAAwB,CAAA,GAChDC,qEAAA;AAAA,UACE,MAAA,CAAO,UAAU,wBAAwB;AAAA,SAC3C,GACA,eAAA;AAEJ,QAAA,aAAA,CAAc,WAAA,CAAY;AAAA,UACxB,QAAA,EAAU,SAAA,CAAU,yBAAA,CAA0B,QAAQ,CAAA;AAAA,UACtD,OAAA,EAASC,2EAAA,CAAsC,UAAA,CAAW,MAAA,EAAQ;AAAA,YAChE;AAAA,WACD;AAAA,SACF,CAAA;AAAA,MACH;AAAA,KACD,CAAA;AAAA,EACH;AACF,CAAC;;;;"}
package/package.json CHANGED
@@ -1,18 +1,19 @@
1
1
  {
2
2
  "name": "@stackoverflow/backstage-stack-overflow-teams-collator",
3
- "version": "1.5.0",
4
- "description": "A module for the search backend that exports stack overflow for teams modules",
3
+ "version": "1.6.3",
4
+ "description": "A module for the search backend that exports Stack Internal modules",
5
5
  "backstage": {
6
6
  "role": "backend-plugin-module",
7
7
  "pluginId": "search",
8
- "pluginPackage": "@backstage/plugin-search-backend"
8
+ "pluginPackage": "@backstage/plugin-search-backend",
9
+ "features": {
10
+ ".": "@backstage/BackendFeature"
11
+ }
9
12
  },
10
- "main": "dist/index.esm.js",
11
- "types": "dist/index.d.ts",
13
+ "main": "./dist/index.cjs.js",
14
+ "types": "./dist/index.d.ts",
12
15
  "publishConfig": {
13
- "access": "public",
14
- "main": "dist/index.esm.js",
15
- "types": "dist/index.d.ts"
16
+ "access": "public"
16
17
  },
17
18
  "repository": {
18
19
  "type": "git",
@@ -47,5 +48,12 @@
47
48
  "@backstage/cli": "^0.34.4",
48
49
  "msw": "^1.2.1",
49
50
  "typescript": "^5.8.2"
51
+ },
52
+ "typesVersions": {
53
+ "*": {
54
+ "package.json": [
55
+ "package.json"
56
+ ]
57
+ }
50
58
  }
51
59
  }