@red-hat-developer-hub/backstage-plugin-adoption-insights-backend 0.8.3 → 0.9.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 +18 -0
- package/config.schema.json +41 -0
- package/dist/controllers/EventApiController.cjs.js.map +1 -1
- package/dist/database/DatabaseFactory.cjs.js.map +1 -1
- package/dist/database/adapters/BaseAdapter.cjs.js.map +1 -1
- package/dist/database/adapters/PostgresAdapter.cjs.js.map +1 -1
- package/dist/database/adapters/SqliteAdapter.cjs.js.map +1 -1
- package/dist/database/migration.cjs.js.map +1 -1
- package/dist/database/partition.cjs.js.map +1 -1
- package/dist/domain/EventBatchProcessor.cjs.js.map +1 -1
- package/dist/models/Event.cjs.js.map +1 -1
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/router.cjs.js.map +1 -1
- package/dist/types/event-request.cjs.js.map +1 -1
- package/dist/utils/config.cjs.js.map +1 -1
- package/dist/utils/date.cjs.js.map +1 -1
- package/dist/utils/partition.cjs.js.map +1 -1
- package/dist/validation/ValidationError.cjs.js.map +1 -1
- package/dist/validation/event-request.cjs.js.map +1 -1
- package/dist/validation/event.cjs.js.map +1 -1
- package/package.json +13 -13
- package/config.d.ts +0 -47
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @red-hat-developer-hub/backstage-plugin-adoption-insights-backend
|
|
2
2
|
|
|
3
|
+
## 0.9.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 8af2af2: Backstage version bump to v1.52.0
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- 0acb261: Updated dependency `json-2-csv` to `^5.5.11`.
|
|
12
|
+
- Updated dependencies [8af2af2]
|
|
13
|
+
- @red-hat-developer-hub/backstage-plugin-adoption-insights-common@0.9.0
|
|
14
|
+
|
|
15
|
+
## 0.8.4
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- @red-hat-developer-hub/backstage-plugin-adoption-insights-common@0.8.4
|
|
20
|
+
|
|
3
21
|
## 0.8.3
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"app": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"analytics": {
|
|
9
|
+
"type": "object",
|
|
10
|
+
"properties": {
|
|
11
|
+
"adoptionInsights": {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"properties": {
|
|
14
|
+
"maxBufferSize": {
|
|
15
|
+
"type": "number",
|
|
16
|
+
"description": "Maximum buffer size for event batching. default 20",
|
|
17
|
+
"visibility": "frontend"
|
|
18
|
+
},
|
|
19
|
+
"flushInterval": {
|
|
20
|
+
"type": "number",
|
|
21
|
+
"description": "Flush interval in millisecond for event batching. All events will be flushed after this interval. default 5000ms",
|
|
22
|
+
"visibility": "frontend"
|
|
23
|
+
},
|
|
24
|
+
"debug": {
|
|
25
|
+
"type": "boolean",
|
|
26
|
+
"description": "Flag to enable Debug mode which logs every event in the browser console. default false",
|
|
27
|
+
"visibility": "frontend"
|
|
28
|
+
},
|
|
29
|
+
"licensedUsers": {
|
|
30
|
+
"type": "number",
|
|
31
|
+
"description": "Total number of licensed users.",
|
|
32
|
+
"visibility": "backend"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EventApiController.cjs.js","sources":["../../src/controllers/EventApiController.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { Request, Response } from 'express';\nimport { json2csv as Parser } from 'json-2-csv';\nimport { Knex } from 'knex';\nimport { AnalyticsEvent } from '@backstage/core-plugin-api';\nimport { EventDatabase, Filters } from '../database/event-database';\nimport { EventBatchProcessor } from '../domain/EventBatchProcessor';\nimport { Event } from '../models/Event';\nimport { QueryParams, QueryType } from '../types/event-request';\nimport { toEndOfDayUTC, toStartOfDayUTC } from '../utils/date';\nimport { EventSchema } from '../validation/event';\nimport { EventRequestSchema } from '../validation/event-request';\nimport { ValidationError } from '../validation/ValidationError';\nimport {\n AuditorService,\n AuditorServiceEvent,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { getLicensedUsersCount } from '../utils/config';\nimport { TechDocsCount, TopTechDocsCount } from '../types/event';\n\nclass EventApiController {\n private readonly database: EventDatabase;\n private readonly config: RootConfigService;\n private readonly processor: EventBatchProcessor;\n private readonly auditor: AuditorService;\n\n constructor(\n eventDatabase: EventDatabase,\n processor: EventBatchProcessor,\n config: RootConfigService,\n auditor: AuditorService,\n ) {\n this.database = eventDatabase;\n this.processor = processor;\n this.config = config;\n this.auditor = auditor;\n }\n\n async getBaseUrl(pluginId: string): Promise<string> {\n return `${this.config.getString('backend.baseUrl')}/api/${pluginId}`;\n }\n\n private processIncomingEvents(\n events: AnalyticsEvent[],\n auditEvent: AuditorServiceEvent,\n ): void {\n const proccessedEvents = events\n .filter(e => !!e.context?.userId)\n .map(event => new Event(event, this.database.isJsonSupported()));\n\n proccessedEvents.forEach(event => {\n const result = EventSchema.safeParse(event);\n\n if (!result.success) {\n auditEvent.fail({\n error: new Error(JSON.stringify(result.error.flatten())),\n });\n throw new ValidationError('Invalid event data', result.error.flatten());\n }\n auditEvent.success({ meta: { eventId: event.id } });\n return this.processor.addEvent(event);\n });\n }\n\n async trackEvents(\n req: Request<{}, {}, AnalyticsEvent[]>,\n res: Response,\n ): Promise<void> {\n const events = req.body;\n\n const auditEvent = await this.auditor.createEvent({\n eventId: 'capture-event',\n request: req,\n });\n\n try {\n this.processIncomingEvents(events, auditEvent);\n res.status(200).json({ success: true, message: 'Event received' });\n } catch (error) {\n auditEvent.fail({\n error: error instanceof Error ? error : new Error(String(error)),\n });\n res\n .status(400)\n .json({ message: error.message, errors: error.details.fieldErrors });\n }\n }\n\n // Get insights based on the type of data requested\n async getInsights(\n req: Request<{}, {}, {}, QueryParams>,\n res: Response,\n ): Promise<void> {\n const auditEvent = await this.auditor.createEvent({\n eventId: 'get-insights',\n request: req,\n });\n\n const parsed = EventRequestSchema.safeParse(req.query);\n if (!parsed.success) {\n auditEvent.fail({\n error: new Error(\n JSON.stringify({\n message: 'Invalid query',\n errors: parsed.error.flatten().fieldErrors,\n }),\n ),\n });\n res.status(400).json({\n message: 'Invalid query',\n errors: parsed.error.flatten().fieldErrors,\n });\n return;\n }\n const { type, format, ...params } = parsed.data;\n const licensedUsers = getLicensedUsersCount(this.config);\n const filters: Filters = {\n ...params,\n end_date: toEndOfDayUTC(params.end_date, params.timezone) as string,\n start_date: toStartOfDayUTC(params.start_date, params.timezone) as string,\n };\n const db = this.database;\n\n db.setFilters(filters);\n db.setConfig({ licensedUsers });\n const queryHandlers: Record<QueryType, () => Promise<Knex.QueryBuilder>> = {\n total_users: () => db.getUsers(),\n active_users: () => db.getDailyUsers(),\n top_searches: () => db.getTopSearches(),\n top_plugins: () => db.getTopPluginViews(),\n top_techdocs: () => db.getTopTechDocsViews(),\n top_templates: () => db.getTopTemplateViews(),\n top_catalog_entities: () => db.getTopCatalogEntitiesViews(),\n };\n\n try {\n const result = await queryHandlers[type as QueryType]();\n\n if (type === 'top_techdocs') {\n await this.getTechdocsMetadata(req, result);\n }\n\n auditEvent.success({\n meta: {\n queryType: type,\n format: format,\n resultsCount: result.data?.length || 0,\n },\n });\n\n if (format === 'csv' && result.data) {\n const csv = Parser(result.data);\n res.header('Content-Type', 'text/csv');\n res.attachment(`adoption_insights_${type}.csv`);\n res.send(csv);\n return;\n }\n res.json(result);\n } catch (error) {\n auditEvent.fail({\n error: error instanceof Error ? error : new Error(String(error)),\n });\n res.status(500).json({ error: 'Internal Server Error' });\n return;\n }\n }\n\n async getTechdocsMetadata(\n req: Request<{}, {}, {}, QueryParams>,\n result: TopTechDocsCount,\n ) {\n const promises: Promise<void>[] = [];\n const baseUrl = await this.getBaseUrl('techdocs');\n\n result.data.forEach((row: TechDocsCount) => {\n if (!row.namespace || !row.kind || !row.name) {\n row.site_name = '';\n } else {\n promises.push(\n fetch(\n `${baseUrl}/metadata/techdocs/${row.namespace}/${row.kind}/${row.name}`,\n {\n headers: {\n Accept: 'application/json',\n Authorization: req.headers.authorization as string,\n },\n },\n )\n .then(async response => {\n const data = await response.json();\n row.site_name = data.site_name ?? row.name;\n })\n .catch(e => {\n console.warn(e);\n row.site_name = row.name;\n }),\n );\n }\n });\n\n await Promise.all(promises);\n }\n}\n\nexport default EventApiController;\n"],"names":["Event","event","EventSchema","ValidationError","EventRequestSchema","getLicensedUsersCount","toEndOfDayUTC","toStartOfDayUTC","Parser"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EventApiController.cjs.js","sources":["../../src/controllers/EventApiController.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { Request, Response } from 'express';\nimport { json2csv as Parser } from 'json-2-csv';\nimport { Knex } from 'knex';\nimport { AnalyticsEvent } from '@backstage/core-plugin-api';\nimport { EventDatabase, Filters } from '../database/event-database';\nimport { EventBatchProcessor } from '../domain/EventBatchProcessor';\nimport { Event } from '../models/Event';\nimport { QueryParams, QueryType } from '../types/event-request';\nimport { toEndOfDayUTC, toStartOfDayUTC } from '../utils/date';\nimport { EventSchema } from '../validation/event';\nimport { EventRequestSchema } from '../validation/event-request';\nimport { ValidationError } from '../validation/ValidationError';\nimport {\n AuditorService,\n AuditorServiceEvent,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { getLicensedUsersCount } from '../utils/config';\nimport { TechDocsCount, TopTechDocsCount } from '../types/event';\n\nclass EventApiController {\n private readonly database: EventDatabase;\n private readonly config: RootConfigService;\n private readonly processor: EventBatchProcessor;\n private readonly auditor: AuditorService;\n\n constructor(\n eventDatabase: EventDatabase,\n processor: EventBatchProcessor,\n config: RootConfigService,\n auditor: AuditorService,\n ) {\n this.database = eventDatabase;\n this.processor = processor;\n this.config = config;\n this.auditor = auditor;\n }\n\n async getBaseUrl(pluginId: string): Promise<string> {\n return `${this.config.getString('backend.baseUrl')}/api/${pluginId}`;\n }\n\n private processIncomingEvents(\n events: AnalyticsEvent[],\n auditEvent: AuditorServiceEvent,\n ): void {\n const proccessedEvents = events\n .filter(e => !!e.context?.userId)\n .map(event => new Event(event, this.database.isJsonSupported()));\n\n proccessedEvents.forEach(event => {\n const result = EventSchema.safeParse(event);\n\n if (!result.success) {\n auditEvent.fail({\n error: new Error(JSON.stringify(result.error.flatten())),\n });\n throw new ValidationError('Invalid event data', result.error.flatten());\n }\n auditEvent.success({ meta: { eventId: event.id } });\n return this.processor.addEvent(event);\n });\n }\n\n async trackEvents(\n req: Request<{}, {}, AnalyticsEvent[]>,\n res: Response,\n ): Promise<void> {\n const events = req.body;\n\n const auditEvent = await this.auditor.createEvent({\n eventId: 'capture-event',\n request: req,\n });\n\n try {\n this.processIncomingEvents(events, auditEvent);\n res.status(200).json({ success: true, message: 'Event received' });\n } catch (error) {\n auditEvent.fail({\n error: error instanceof Error ? error : new Error(String(error)),\n });\n res\n .status(400)\n .json({ message: error.message, errors: error.details.fieldErrors });\n }\n }\n\n // Get insights based on the type of data requested\n async getInsights(\n req: Request<{}, {}, {}, QueryParams>,\n res: Response,\n ): Promise<void> {\n const auditEvent = await this.auditor.createEvent({\n eventId: 'get-insights',\n request: req,\n });\n\n const parsed = EventRequestSchema.safeParse(req.query);\n if (!parsed.success) {\n auditEvent.fail({\n error: new Error(\n JSON.stringify({\n message: 'Invalid query',\n errors: parsed.error.flatten().fieldErrors,\n }),\n ),\n });\n res.status(400).json({\n message: 'Invalid query',\n errors: parsed.error.flatten().fieldErrors,\n });\n return;\n }\n const { type, format, ...params } = parsed.data;\n const licensedUsers = getLicensedUsersCount(this.config);\n const filters: Filters = {\n ...params,\n end_date: toEndOfDayUTC(params.end_date, params.timezone) as string,\n start_date: toStartOfDayUTC(params.start_date, params.timezone) as string,\n };\n const db = this.database;\n\n db.setFilters(filters);\n db.setConfig({ licensedUsers });\n const queryHandlers: Record<QueryType, () => Promise<Knex.QueryBuilder>> = {\n total_users: () => db.getUsers(),\n active_users: () => db.getDailyUsers(),\n top_searches: () => db.getTopSearches(),\n top_plugins: () => db.getTopPluginViews(),\n top_techdocs: () => db.getTopTechDocsViews(),\n top_templates: () => db.getTopTemplateViews(),\n top_catalog_entities: () => db.getTopCatalogEntitiesViews(),\n };\n\n try {\n const result = await queryHandlers[type as QueryType]();\n\n if (type === 'top_techdocs') {\n await this.getTechdocsMetadata(req, result);\n }\n\n auditEvent.success({\n meta: {\n queryType: type,\n format: format,\n resultsCount: result.data?.length || 0,\n },\n });\n\n if (format === 'csv' && result.data) {\n const csv = Parser(result.data);\n res.header('Content-Type', 'text/csv');\n res.attachment(`adoption_insights_${type}.csv`);\n res.send(csv);\n return;\n }\n res.json(result);\n } catch (error) {\n auditEvent.fail({\n error: error instanceof Error ? error : new Error(String(error)),\n });\n res.status(500).json({ error: 'Internal Server Error' });\n return;\n }\n }\n\n async getTechdocsMetadata(\n req: Request<{}, {}, {}, QueryParams>,\n result: TopTechDocsCount,\n ) {\n const promises: Promise<void>[] = [];\n const baseUrl = await this.getBaseUrl('techdocs');\n\n result.data.forEach((row: TechDocsCount) => {\n if (!row.namespace || !row.kind || !row.name) {\n row.site_name = '';\n } else {\n promises.push(\n fetch(\n `${baseUrl}/metadata/techdocs/${row.namespace}/${row.kind}/${row.name}`,\n {\n headers: {\n Accept: 'application/json',\n Authorization: req.headers.authorization as string,\n },\n },\n )\n .then(async response => {\n const data = await response.json();\n row.site_name = data.site_name ?? row.name;\n })\n .catch(e => {\n console.warn(e);\n row.site_name = row.name;\n }),\n );\n }\n });\n\n await Promise.all(promises);\n }\n}\n\nexport default EventApiController;\n"],"names":["Event","event","EventSchema","ValidationError","EventRequestSchema","getLicensedUsersCount","toEndOfDayUTC","toStartOfDayUTC","Parser"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DatabaseFactory.cjs.js","sources":["../../src/database/DatabaseFactory.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { Knex } from 'knex';\nimport { PostgresAdapter } from './adapters/PostgresAdapter';\nimport { EventDatabase } from './event-database';\nimport { SqliteAdapter } from './adapters/SqliteAdapter';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\nexport class DatabaseFactory {\n static getDatabase(db: Knex, logger: LoggerService): EventDatabase {\n const dbType = db.client.config.client;\n\n switch (dbType) {\n case 'pg':\n return new PostgresAdapter(db, logger);\n case 'better-sqlite3':\n return new SqliteAdapter(db, logger);\n default:\n throw new Error(`Unsupported database type: ${dbType}`);\n }\n }\n}\n"],"names":["PostgresAdapter","SqliteAdapter"],"mappings":";;;;;AAqBO,MAAM,
|
|
1
|
+
{"version":3,"file":"DatabaseFactory.cjs.js","sources":["../../src/database/DatabaseFactory.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { Knex } from 'knex';\nimport { PostgresAdapter } from './adapters/PostgresAdapter';\nimport { EventDatabase } from './event-database';\nimport { SqliteAdapter } from './adapters/SqliteAdapter';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\nexport class DatabaseFactory {\n static getDatabase(db: Knex, logger: LoggerService): EventDatabase {\n const dbType = db.client.config.client;\n\n switch (dbType) {\n case 'pg':\n return new PostgresAdapter(db, logger);\n case 'better-sqlite3':\n return new SqliteAdapter(db, logger);\n default:\n throw new Error(`Unsupported database type: ${dbType}`);\n }\n }\n}\n"],"names":["PostgresAdapter","SqliteAdapter"],"mappings":";;;;;AAqBO,MAAM,eAAA,CAAgB;AAAA,EAC3B,OAAO,WAAA,CAAY,EAAA,EAAU,MAAA,EAAsC;AACjE,IAAA,MAAM,MAAA,GAAS,EAAA,CAAG,MAAA,CAAO,MAAA,CAAO,MAAA;AAEhC,IAAA,QAAQ,MAAA;AAAQ,MACd,KAAK,IAAA;AACH,QAAA,OAAO,IAAIA,+BAAA,CAAgB,EAAA,EAAI,MAAM,CAAA;AAAA,MACvC,KAAK,gBAAA;AACH,QAAA,OAAO,IAAIC,2BAAA,CAAc,EAAA,EAAI,MAAM,CAAA;AAAA,MACrC;AACE,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,2BAAA,EAA8B,MAAM,CAAA,CAAE,CAAA;AAAA;AAC1D,EACF;AACF;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseAdapter.cjs.js","sources":["../../../src/database/adapters/BaseAdapter.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { Knex } from 'knex';\nimport { Filters, EventDatabase, UserConfig } from '../event-database';\nimport { Event } from '../../models/Event';\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport {\n DailyUser,\n Grouping,\n ResponseData,\n ResponseWithGrouping,\n} from '../../types/event';\nimport { convertToTargetTimezone } from '../../utils/date';\n\nexport abstract class BaseDatabaseAdapter implements EventDatabase {\n protected db: Knex;\n private readonly logger: LoggerService;\n protected filters?: Filters;\n protected config?: UserConfig;\n\n constructor(db: Knex, logger: LoggerService) {\n this.db = db;\n this.logger = logger;\n }\n\n setFilters(filters: Filters) {\n this.filters = filters;\n return this;\n }\n setConfig(config: UserConfig) {\n this.config = config;\n return this;\n }\n\n async insertFailedEvent(\n event: string,\n errorMessage: string,\n retryAttempts = 0,\n ) {\n const db = this.db;\n try {\n await db('failed_events').insert({\n event_data: event,\n error_message: errorMessage,\n retry_attempts: retryAttempts,\n created_at: new Date().toISOString(),\n });\n\n this.logger.info(`[DB] Failed event logged: ${event}`);\n } catch (error) {\n this.logger.error(`[DB] Error inserting failed event:`, error);\n }\n }\n\n async insertEvents(events: Event[]): Promise<void> {\n try {\n await this.db.transaction(async trx => {\n const evts = events.map(event => event.toJSON());\n await trx('events').insert(evts); // Bulk insert into events table ('events')\n });\n this.logger.info(\n `[DB] Successfully inserted ${events.length} events in bulk`,\n );\n } catch (error) {\n this.logger.error(`[DB] Error inserting batch:`, error);\n throw error; // Re-throw the error for retry handling\n }\n }\n\n protected selectFromEvents(\n queryBuilder: Knex.QueryBuilder,\n columns: (string | Knex.Raw<any>)[],\n dateRange?: [string, string],\n ): Knex.QueryBuilder {\n const query = queryBuilder\n .select(columns)\n .from('events')\n .andWhere('action', 'navigate');\n\n if (dateRange) {\n query.whereBetween('created_at', dateRange);\n }\n\n return query;\n }\n\n async getDailyUsers(): Promise<Knex.QueryBuilder> {\n this.ensureFiltersSet();\n const { start_date, end_date } = this.filters!;\n const db = this.db;\n\n const groupedEventsQuery = db('events')\n .select(\n db.raw(this.getDynamicDateGrouping({ useTimestamp: true })),\n 'user_ref',\n )\n .whereBetween('created_at', [start_date, end_date])\n .groupByRaw('date, user_ref');\n\n const firstSeenQuery = db('events')\n .select('user_ref')\n .min('created_at as first_seen')\n .groupBy('user_ref');\n\n const query = db\n .from(db.raw('(?) as ge', [groupedEventsQuery]))\n .leftJoin(\n db.raw('(?) as fs', [firstSeenQuery]),\n 'ge.user_ref',\n 'fs.user_ref',\n )\n .select(\n 'ge.date',\n db.raw('CAST(COUNT(*) as INTEGER) as total_users'),\n db.raw(\n `CAST(SUM(CASE WHEN fs.first_seen >= ${this.getFormatedDate(\n 'ge.date',\n )} THEN 1 ELSE 0 END) as INTEGER) as new_users`,\n ),\n db.raw(\n `CAST(SUM(CASE WHEN fs.first_seen < ${this.getFormatedDate(\n 'ge.date',\n )} THEN 1 ELSE 0 END) as INTEGER) as returning_users`,\n ),\n )\n .groupBy('ge.date')\n .orderBy('ge.date');\n\n return query.then(data => this.getResponseWithGrouping<DailyUser[]>(data));\n }\n\n async getUsers(): Promise<Knex.QueryBuilder> {\n this.ensureFiltersSet();\n const { start_date, end_date } = this.filters!;\n const db = this.db;\n const query = db('events')\n .select(db.raw('CAST(COUNT(*) as INTEGER) as logged_in_users'))\n .from(\n db('events')\n .select('user_ref')\n .whereBetween('created_at', [start_date, end_date])\n .groupBy('user_ref')\n .as('sub'),\n );\n\n return query.then(result => {\n const { licensedUsers } = this.config!;\n result[0] = { ...result[0], licensed_users: licensedUsers } as any;\n return this.getResponseData(result);\n });\n }\n\n async getTopTemplateViews(): Promise<Knex.QueryBuilder> {\n this.ensureFiltersSet();\n const { start_date, end_date, limit } = this.filters!;\n const db = this.db;\n const query = db('events')\n .select(\n db.raw(`context->>'entityRef' AS entityref`),\n db.raw('CAST(COUNT(*) as INTEGER) AS count'),\n db.raw(this.getLastUsedDate()),\n )\n .where({\n action: 'click',\n subject: 'Create',\n plugin_id: 'scaffolder',\n })\n .whereBetween('created_at', [start_date, end_date])\n .groupByRaw('entityref')\n .orderBy('count', 'desc')\n .limit(Number(limit) || 3);\n\n return query.then(data => this.getResponseData(data, 'last_used'));\n }\n\n async getTopSearches(): Promise<Knex.QueryBuilder> {\n const db = this.db;\n this.ensureFiltersSet();\n const { start_date, end_date, limit } = this.filters!;\n const query = db('events')\n .select(\n db.raw(this.getDynamicDateGrouping({ useTimestamp: true })),\n db.raw('CAST(COUNT(*) as INTEGER) AS count'),\n )\n .whereBetween('created_at', [start_date, end_date])\n .andWhere('action', 'search')\n .groupByRaw('date')\n .orderBy('date', 'asc')\n .limit(Number(limit) || 3);\n\n return query.then(data => this.getResponseWithGrouping(data));\n }\n\n async getTopTechDocsViews(): Promise<Knex.QueryBuilder> {\n this.ensureFiltersSet();\n const { start_date, end_date, limit } = this.filters!;\n const db = this.db;\n const query = db('events')\n .select(\n db.raw('CAST(COUNT(*) as INTEGER) AS count'),\n db.raw(this.getLastUsedDate()),\n db.raw(`COALESCE(LOWER(attributes->>'kind'), '') AS kind`),\n db.raw(`COALESCE(attributes->>'name', '') AS name`),\n db.raw(`COALESCE(attributes->>'namespace', '') AS namespace`),\n )\n .where({\n action: 'navigate',\n plugin_id: 'techdocs',\n })\n .whereBetween('created_at', [start_date, end_date])\n .groupByRaw(`name, kind, namespace`)\n .orderBy('count', 'desc')\n .limit(Number(limit) || 3);\n\n return query.then(data => this.getResponseData(data, 'last_used'));\n }\n\n async getTopCatalogEntitiesViews(): Promise<Knex.QueryBuilder> {\n this.ensureFiltersSet();\n const { start_date, end_date, limit, kind } = this.filters!;\n const db = this.db;\n const query = db('events')\n .select(\n 'plugin_id',\n db.raw(`LOWER(attributes->>'kind') AS kind`),\n db.raw(`attributes->>'name' AS name`),\n db.raw(`attributes->>'namespace' AS namespace`),\n db.raw(this.getLastUsedDate()),\n db.raw('CAST(COUNT(*) as INTEGER) AS count'),\n )\n .whereBetween('created_at', [start_date, end_date])\n .andWhere(db.raw(`attributes->>'kind' IS NOT NULL`))\n .andWhere('action', 'navigate')\n .andWhere('plugin_id', 'catalog')\n .groupByRaw('plugin_id, kind, name, namespace')\n .orderBy('count', 'desc')\n .limit(Number(limit) || 3);\n\n if (kind) {\n query.andWhere(db.raw(`LOWER(attributes->>'kind') = ?`, [kind]));\n }\n return query.then(data => this.getResponseData(data, 'last_used'));\n }\n\n async getTopPluginViews(): Promise<Knex.QueryBuilder> {\n this.ensureFiltersSet();\n const { start_date, end_date, limit = 3 } = this.filters!;\n const dateRange: [string, string] = [start_date, end_date];\n const db = this.db;\n\n // return grouped plugin counts by date.\n const getTrendDataQuery = (qb: Knex.QueryBuilder) => {\n const trend_data_columns = [\n 'plugin_id',\n db.raw(this.getDynamicDateGrouping({ useTimestamp: false })),\n db.raw('CAST(COUNT(*) as INTEGER) AS count'),\n ];\n\n return this.selectFromEvents(qb, trend_data_columns, dateRange).groupBy(\n 'plugin_id',\n 'date',\n );\n };\n\n // return grouped plugin counts\n const getPluginCountsQuery = (qb: Knex.QueryBuilder) => {\n const plugin_counts_columns = [\n 'plugin_id',\n db.raw('CAST(COUNT(*) as INTEGER) AS visit_count'),\n ];\n return this.selectFromEvents(\n qb,\n plugin_counts_columns,\n dateRange,\n ).groupBy('plugin_id');\n };\n\n // return aggregated trends JSON object and first_count, last_count to calculate the trend percentage\n const getAggregatedTrendsQuery = (qb: Knex.QueryBuilder) => {\n return qb\n .select([\n 'plugin_id',\n db.raw(`\n json(${this.getJsonAggregationQuery('date', 'count')}) AS trend,\n COALESCE((SELECT count FROM trend_data td WHERE td.plugin_id = t.plugin_id ORDER BY date LIMIT 1),0) AS first_count,\n COALESCE((SELECT count FROM trend_data td WHERE td.plugin_id = t.plugin_id ORDER BY date DESC LIMIT 1),0) AS last_count\n `),\n ])\n .from('trend_data AS t')\n .groupBy('plugin_id');\n };\n\n // Main query to get top plugins counts, trends for the given date range.\n const query = db\n .with('trend_data', qb => getTrendDataQuery(qb))\n .with('plugin_counts', qb => getPluginCountsQuery(qb))\n .with('aggregated_trends', qb => getAggregatedTrendsQuery(qb))\n .select([\n 'p.plugin_id',\n 'p.visit_count',\n 't.trend',\n db.raw(`\n CASE\n WHEN t.first_count = 0 THEN NULL\n ELSE ROUND(((t.last_count - t.first_count) * 100.0) / NULLIF(t.first_count, 0), 2)\n END AS trend_percentage\n `),\n ])\n .from('plugin_counts AS p')\n .leftJoin('aggregated_trends AS t', 'p.plugin_id', 't.plugin_id')\n .orderBy('p.visit_count', 'desc')\n .limit(limit);\n\n return query.then(data => {\n return this.getResponseWithGrouping(\n this.transformJson(data, 'trend'),\n 'trend',\n );\n });\n }\n\n abstract getDate(): string;\n abstract getLastUsedDate(): string;\n abstract isJsonSupported(): boolean;\n abstract isTimezoneSupported(): boolean;\n abstract isPartitionSupported(): boolean;\n abstract getDateBetweenQuery(): string;\n abstract getDynamicDateGrouping(options?: {\n onlyText?: boolean;\n useTimestamp?: boolean;\n }): Grouping | string;\n abstract getFormatedDate(column: string): string;\n abstract getJsonAggregationQuery(...args: any[]): string;\n\n // Helper methods\n transformDateRange(dateRange: [string, string]): [string, string] {\n return dateRange;\n }\n\n transformJson(data: any[], jsonField: string): any {\n return data.map((row: any) => ({\n ...row,\n [jsonField]: row[jsonField]\n ? JSON.parse(JSON.stringify(row[jsonField]))\n : null,\n }));\n }\n\n modifyDateInObject<T extends any>(\n obj: T & { [key: string]: string | number },\n datePath: string = 'date',\n ) {\n if (obj[datePath]) {\n return {\n ...obj,\n ...(this.isTimezoneSupported()\n ? {\n [datePath]: convertToTargetTimezone(\n obj[datePath] as string,\n this.filters?.timezone,\n ),\n }\n : {}),\n };\n }\n return obj;\n }\n\n getResponseData<T extends any[]>(\n data: T,\n datePath: string = 'date',\n ): ResponseData<T> {\n return {\n data: data.map(d => {\n if (Array.isArray(d[datePath])) {\n return {\n ...d,\n [datePath]: d[datePath].map((dp: any) =>\n this.modifyDateInObject(dp),\n ),\n };\n }\n return this.modifyDateInObject(d, datePath);\n }) as T,\n };\n }\n\n getResponseWithGrouping = <T extends any[]>(\n data: T,\n datePath: string = 'date',\n ): ResponseWithGrouping<T> => {\n const grouping = this.getDynamicDateGrouping({\n onlyText: true,\n }) as Grouping;\n\n if (grouping === 'hourly') {\n return {\n grouping,\n data: data.map(d => {\n if (Array.isArray(d[datePath])) {\n return {\n ...d,\n [datePath]: d[datePath].map((dp: any) =>\n this.modifyDateInObject(dp),\n ),\n };\n }\n return this.modifyDateInObject(d, datePath);\n }) as T,\n };\n }\n\n return {\n grouping,\n data,\n };\n };\n\n ensureFiltersSet() {\n if (!this.filters) {\n throw new Error(\n 'Filters must be set using setFilters() before calling methods.',\n );\n }\n }\n}\n"],"names":["convertToTargetTimezone"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BaseAdapter.cjs.js","sources":["../../../src/database/adapters/BaseAdapter.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { Knex } from 'knex';\nimport { Filters, EventDatabase, UserConfig } from '../event-database';\nimport { Event } from '../../models/Event';\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport {\n DailyUser,\n Grouping,\n ResponseData,\n ResponseWithGrouping,\n} from '../../types/event';\nimport { convertToTargetTimezone } from '../../utils/date';\n\nexport abstract class BaseDatabaseAdapter implements EventDatabase {\n protected db: Knex;\n private readonly logger: LoggerService;\n protected filters?: Filters;\n protected config?: UserConfig;\n\n constructor(db: Knex, logger: LoggerService) {\n this.db = db;\n this.logger = logger;\n }\n\n setFilters(filters: Filters) {\n this.filters = filters;\n return this;\n }\n setConfig(config: UserConfig) {\n this.config = config;\n return this;\n }\n\n async insertFailedEvent(\n event: string,\n errorMessage: string,\n retryAttempts = 0,\n ) {\n const db = this.db;\n try {\n await db('failed_events').insert({\n event_data: event,\n error_message: errorMessage,\n retry_attempts: retryAttempts,\n created_at: new Date().toISOString(),\n });\n\n this.logger.info(`[DB] Failed event logged: ${event}`);\n } catch (error) {\n this.logger.error(`[DB] Error inserting failed event:`, error);\n }\n }\n\n async insertEvents(events: Event[]): Promise<void> {\n try {\n await this.db.transaction(async trx => {\n const evts = events.map(event => event.toJSON());\n await trx('events').insert(evts); // Bulk insert into events table ('events')\n });\n this.logger.info(\n `[DB] Successfully inserted ${events.length} events in bulk`,\n );\n } catch (error) {\n this.logger.error(`[DB] Error inserting batch:`, error);\n throw error; // Re-throw the error for retry handling\n }\n }\n\n protected selectFromEvents(\n queryBuilder: Knex.QueryBuilder,\n columns: (string | Knex.Raw<any>)[],\n dateRange?: [string, string],\n ): Knex.QueryBuilder {\n const query = queryBuilder\n .select(columns)\n .from('events')\n .andWhere('action', 'navigate');\n\n if (dateRange) {\n query.whereBetween('created_at', dateRange);\n }\n\n return query;\n }\n\n async getDailyUsers(): Promise<Knex.QueryBuilder> {\n this.ensureFiltersSet();\n const { start_date, end_date } = this.filters!;\n const db = this.db;\n\n const groupedEventsQuery = db('events')\n .select(\n db.raw(this.getDynamicDateGrouping({ useTimestamp: true })),\n 'user_ref',\n )\n .whereBetween('created_at', [start_date, end_date])\n .groupByRaw('date, user_ref');\n\n const firstSeenQuery = db('events')\n .select('user_ref')\n .min('created_at as first_seen')\n .groupBy('user_ref');\n\n const query = db\n .from(db.raw('(?) as ge', [groupedEventsQuery]))\n .leftJoin(\n db.raw('(?) as fs', [firstSeenQuery]),\n 'ge.user_ref',\n 'fs.user_ref',\n )\n .select(\n 'ge.date',\n db.raw('CAST(COUNT(*) as INTEGER) as total_users'),\n db.raw(\n `CAST(SUM(CASE WHEN fs.first_seen >= ${this.getFormatedDate(\n 'ge.date',\n )} THEN 1 ELSE 0 END) as INTEGER) as new_users`,\n ),\n db.raw(\n `CAST(SUM(CASE WHEN fs.first_seen < ${this.getFormatedDate(\n 'ge.date',\n )} THEN 1 ELSE 0 END) as INTEGER) as returning_users`,\n ),\n )\n .groupBy('ge.date')\n .orderBy('ge.date');\n\n return query.then(data => this.getResponseWithGrouping<DailyUser[]>(data));\n }\n\n async getUsers(): Promise<Knex.QueryBuilder> {\n this.ensureFiltersSet();\n const { start_date, end_date } = this.filters!;\n const db = this.db;\n const query = db('events')\n .select(db.raw('CAST(COUNT(*) as INTEGER) as logged_in_users'))\n .from(\n db('events')\n .select('user_ref')\n .whereBetween('created_at', [start_date, end_date])\n .groupBy('user_ref')\n .as('sub'),\n );\n\n return query.then(result => {\n const { licensedUsers } = this.config!;\n result[0] = { ...result[0], licensed_users: licensedUsers } as any;\n return this.getResponseData(result);\n });\n }\n\n async getTopTemplateViews(): Promise<Knex.QueryBuilder> {\n this.ensureFiltersSet();\n const { start_date, end_date, limit } = this.filters!;\n const db = this.db;\n const query = db('events')\n .select(\n db.raw(`context->>'entityRef' AS entityref`),\n db.raw('CAST(COUNT(*) as INTEGER) AS count'),\n db.raw(this.getLastUsedDate()),\n )\n .where({\n action: 'click',\n subject: 'Create',\n plugin_id: 'scaffolder',\n })\n .whereBetween('created_at', [start_date, end_date])\n .groupByRaw('entityref')\n .orderBy('count', 'desc')\n .limit(Number(limit) || 3);\n\n return query.then(data => this.getResponseData(data, 'last_used'));\n }\n\n async getTopSearches(): Promise<Knex.QueryBuilder> {\n const db = this.db;\n this.ensureFiltersSet();\n const { start_date, end_date, limit } = this.filters!;\n const query = db('events')\n .select(\n db.raw(this.getDynamicDateGrouping({ useTimestamp: true })),\n db.raw('CAST(COUNT(*) as INTEGER) AS count'),\n )\n .whereBetween('created_at', [start_date, end_date])\n .andWhere('action', 'search')\n .groupByRaw('date')\n .orderBy('date', 'asc')\n .limit(Number(limit) || 3);\n\n return query.then(data => this.getResponseWithGrouping(data));\n }\n\n async getTopTechDocsViews(): Promise<Knex.QueryBuilder> {\n this.ensureFiltersSet();\n const { start_date, end_date, limit } = this.filters!;\n const db = this.db;\n const query = db('events')\n .select(\n db.raw('CAST(COUNT(*) as INTEGER) AS count'),\n db.raw(this.getLastUsedDate()),\n db.raw(`COALESCE(LOWER(attributes->>'kind'), '') AS kind`),\n db.raw(`COALESCE(attributes->>'name', '') AS name`),\n db.raw(`COALESCE(attributes->>'namespace', '') AS namespace`),\n )\n .where({\n action: 'navigate',\n plugin_id: 'techdocs',\n })\n .whereBetween('created_at', [start_date, end_date])\n .groupByRaw(`name, kind, namespace`)\n .orderBy('count', 'desc')\n .limit(Number(limit) || 3);\n\n return query.then(data => this.getResponseData(data, 'last_used'));\n }\n\n async getTopCatalogEntitiesViews(): Promise<Knex.QueryBuilder> {\n this.ensureFiltersSet();\n const { start_date, end_date, limit, kind } = this.filters!;\n const db = this.db;\n const query = db('events')\n .select(\n 'plugin_id',\n db.raw(`LOWER(attributes->>'kind') AS kind`),\n db.raw(`attributes->>'name' AS name`),\n db.raw(`attributes->>'namespace' AS namespace`),\n db.raw(this.getLastUsedDate()),\n db.raw('CAST(COUNT(*) as INTEGER) AS count'),\n )\n .whereBetween('created_at', [start_date, end_date])\n .andWhere(db.raw(`attributes->>'kind' IS NOT NULL`))\n .andWhere('action', 'navigate')\n .andWhere('plugin_id', 'catalog')\n .groupByRaw('plugin_id, kind, name, namespace')\n .orderBy('count', 'desc')\n .limit(Number(limit) || 3);\n\n if (kind) {\n query.andWhere(db.raw(`LOWER(attributes->>'kind') = ?`, [kind]));\n }\n return query.then(data => this.getResponseData(data, 'last_used'));\n }\n\n async getTopPluginViews(): Promise<Knex.QueryBuilder> {\n this.ensureFiltersSet();\n const { start_date, end_date, limit = 3 } = this.filters!;\n const dateRange: [string, string] = [start_date, end_date];\n const db = this.db;\n\n // return grouped plugin counts by date.\n const getTrendDataQuery = (qb: Knex.QueryBuilder) => {\n const trend_data_columns = [\n 'plugin_id',\n db.raw(this.getDynamicDateGrouping({ useTimestamp: false })),\n db.raw('CAST(COUNT(*) as INTEGER) AS count'),\n ];\n\n return this.selectFromEvents(qb, trend_data_columns, dateRange).groupBy(\n 'plugin_id',\n 'date',\n );\n };\n\n // return grouped plugin counts\n const getPluginCountsQuery = (qb: Knex.QueryBuilder) => {\n const plugin_counts_columns = [\n 'plugin_id',\n db.raw('CAST(COUNT(*) as INTEGER) AS visit_count'),\n ];\n return this.selectFromEvents(\n qb,\n plugin_counts_columns,\n dateRange,\n ).groupBy('plugin_id');\n };\n\n // return aggregated trends JSON object and first_count, last_count to calculate the trend percentage\n const getAggregatedTrendsQuery = (qb: Knex.QueryBuilder) => {\n return qb\n .select([\n 'plugin_id',\n db.raw(`\n json(${this.getJsonAggregationQuery('date', 'count')}) AS trend,\n COALESCE((SELECT count FROM trend_data td WHERE td.plugin_id = t.plugin_id ORDER BY date LIMIT 1),0) AS first_count,\n COALESCE((SELECT count FROM trend_data td WHERE td.plugin_id = t.plugin_id ORDER BY date DESC LIMIT 1),0) AS last_count\n `),\n ])\n .from('trend_data AS t')\n .groupBy('plugin_id');\n };\n\n // Main query to get top plugins counts, trends for the given date range.\n const query = db\n .with('trend_data', qb => getTrendDataQuery(qb))\n .with('plugin_counts', qb => getPluginCountsQuery(qb))\n .with('aggregated_trends', qb => getAggregatedTrendsQuery(qb))\n .select([\n 'p.plugin_id',\n 'p.visit_count',\n 't.trend',\n db.raw(`\n CASE\n WHEN t.first_count = 0 THEN NULL\n ELSE ROUND(((t.last_count - t.first_count) * 100.0) / NULLIF(t.first_count, 0), 2)\n END AS trend_percentage\n `),\n ])\n .from('plugin_counts AS p')\n .leftJoin('aggregated_trends AS t', 'p.plugin_id', 't.plugin_id')\n .orderBy('p.visit_count', 'desc')\n .limit(limit);\n\n return query.then(data => {\n return this.getResponseWithGrouping(\n this.transformJson(data, 'trend'),\n 'trend',\n );\n });\n }\n\n abstract getDate(): string;\n abstract getLastUsedDate(): string;\n abstract isJsonSupported(): boolean;\n abstract isTimezoneSupported(): boolean;\n abstract isPartitionSupported(): boolean;\n abstract getDateBetweenQuery(): string;\n abstract getDynamicDateGrouping(options?: {\n onlyText?: boolean;\n useTimestamp?: boolean;\n }): Grouping | string;\n abstract getFormatedDate(column: string): string;\n abstract getJsonAggregationQuery(...args: any[]): string;\n\n // Helper methods\n transformDateRange(dateRange: [string, string]): [string, string] {\n return dateRange;\n }\n\n transformJson(data: any[], jsonField: string): any {\n return data.map((row: any) => ({\n ...row,\n [jsonField]: row[jsonField]\n ? JSON.parse(JSON.stringify(row[jsonField]))\n : null,\n }));\n }\n\n modifyDateInObject<T extends any>(\n obj: T & { [key: string]: string | number },\n datePath: string = 'date',\n ) {\n if (obj[datePath]) {\n return {\n ...obj,\n ...(this.isTimezoneSupported()\n ? {\n [datePath]: convertToTargetTimezone(\n obj[datePath] as string,\n this.filters?.timezone,\n ),\n }\n : {}),\n };\n }\n return obj;\n }\n\n getResponseData<T extends any[]>(\n data: T,\n datePath: string = 'date',\n ): ResponseData<T> {\n return {\n data: data.map(d => {\n if (Array.isArray(d[datePath])) {\n return {\n ...d,\n [datePath]: d[datePath].map((dp: any) =>\n this.modifyDateInObject(dp),\n ),\n };\n }\n return this.modifyDateInObject(d, datePath);\n }) as T,\n };\n }\n\n getResponseWithGrouping = <T extends any[]>(\n data: T,\n datePath: string = 'date',\n ): ResponseWithGrouping<T> => {\n const grouping = this.getDynamicDateGrouping({\n onlyText: true,\n }) as Grouping;\n\n if (grouping === 'hourly') {\n return {\n grouping,\n data: data.map(d => {\n if (Array.isArray(d[datePath])) {\n return {\n ...d,\n [datePath]: d[datePath].map((dp: any) =>\n this.modifyDateInObject(dp),\n ),\n };\n }\n return this.modifyDateInObject(d, datePath);\n }) as T,\n };\n }\n\n return {\n grouping,\n data,\n };\n };\n\n ensureFiltersSet() {\n if (!this.filters) {\n throw new Error(\n 'Filters must be set using setFilters() before calling methods.',\n );\n }\n }\n}\n"],"names":["convertToTargetTimezone"],"mappings":";;;;AA2BO,MAAe,mBAAA,CAA6C;AAAA,EACvD,EAAA;AAAA,EACO,MAAA;AAAA,EACP,OAAA;AAAA,EACA,MAAA;AAAA,EAEV,WAAA,CAAY,IAAU,MAAA,EAAuB;AAC3C,IAAA,IAAA,CAAK,EAAA,GAAK,EAAA;AACV,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAAA,EAChB;AAAA,EAEA,WAAW,OAAA,EAAkB;AAC3B,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA;AACf,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EACA,UAAU,MAAA,EAAoB;AAC5B,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEA,MAAM,iBAAA,CACJ,KAAA,EACA,YAAA,EACA,gBAAgB,CAAA,EAChB;AACA,IAAA,MAAM,KAAK,IAAA,CAAK,EAAA;AAChB,IAAA,IAAI;AACF,MAAA,MAAM,EAAA,CAAG,eAAe,CAAA,CAAE,MAAA,CAAO;AAAA,QAC/B,UAAA,EAAY,KAAA;AAAA,QACZ,aAAA,EAAe,YAAA;AAAA,QACf,cAAA,EAAgB,aAAA;AAAA,QAChB,UAAA,EAAA,iBAAY,IAAI,IAAA,EAAK,EAAE,WAAA;AAAY,OACpC,CAAA;AAED,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,CAAA,0BAAA,EAA6B,KAAK,CAAA,CAAE,CAAA;AAAA,IACvD,SAAS,KAAA,EAAO;AACd,MAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,CAAA,kCAAA,CAAA,EAAsC,KAAK,CAAA;AAAA,IAC/D;AAAA,EACF;AAAA,EAEA,MAAM,aAAa,MAAA,EAAgC;AACjD,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,CAAK,EAAA,CAAG,WAAA,CAAY,OAAM,GAAA,KAAO;AACrC,QAAA,MAAM,OAAO,MAAA,CAAO,GAAA,CAAI,CAAA,KAAA,KAAS,KAAA,CAAM,QAAQ,CAAA;AAC/C,QAAA,MAAM,GAAA,CAAI,QAAQ,CAAA,CAAE,MAAA,CAAO,IAAI,CAAA;AAAA,MACjC,CAAC,CAAA;AACD,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,QACV,CAAA,2BAAA,EAA8B,OAAO,MAAM,CAAA,eAAA;AAAA,OAC7C;AAAA,IACF,SAAS,KAAA,EAAO;AACd,MAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,CAAA,2BAAA,CAAA,EAA+B,KAAK,CAAA;AACtD,MAAA,MAAM,KAAA;AAAA,IACR;AAAA,EACF;AAAA,EAEU,gBAAA,CACR,YAAA,EACA,OAAA,EACA,SAAA,EACmB;AACnB,IAAA,MAAM,KAAA,GAAQ,YAAA,CACX,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,QAAQ,CAAA,CACb,QAAA,CAAS,QAAA,EAAU,UAAU,CAAA;AAEhC,IAAA,IAAI,SAAA,EAAW;AACb,MAAA,KAAA,CAAM,YAAA,CAAa,cAAc,SAAS,CAAA;AAAA,IAC5C;AAEA,IAAA,OAAO,KAAA;AAAA,EACT;AAAA,EAEA,MAAM,aAAA,GAA4C;AAChD,IAAA,IAAA,CAAK,gBAAA,EAAiB;AACtB,IAAA,MAAM,EAAE,UAAA,EAAY,QAAA,EAAS,GAAI,IAAA,CAAK,OAAA;AACtC,IAAA,MAAM,KAAK,IAAA,CAAK,EAAA;AAEhB,IAAA,MAAM,kBAAA,GAAqB,EAAA,CAAG,QAAQ,CAAA,CACnC,MAAA;AAAA,MACC,EAAA,CAAG,IAAI,IAAA,CAAK,sBAAA,CAAuB,EAAE,YAAA,EAAc,IAAA,EAAM,CAAC,CAAA;AAAA,MAC1D;AAAA,KACF,CACC,aAAa,YAAA,EAAc,CAAC,YAAY,QAAQ,CAAC,CAAA,CACjD,UAAA,CAAW,gBAAgB,CAAA;AAE9B,IAAA,MAAM,cAAA,GAAiB,EAAA,CAAG,QAAQ,CAAA,CAC/B,MAAA,CAAO,UAAU,CAAA,CACjB,GAAA,CAAI,0BAA0B,CAAA,CAC9B,OAAA,CAAQ,UAAU,CAAA;AAErB,IAAA,MAAM,KAAA,GAAQ,EAAA,CACX,IAAA,CAAK,EAAA,CAAG,GAAA,CAAI,aAAa,CAAC,kBAAkB,CAAC,CAAC,CAAA,CAC9C,QAAA;AAAA,MACC,EAAA,CAAG,GAAA,CAAI,WAAA,EAAa,CAAC,cAAc,CAAC,CAAA;AAAA,MACpC,aAAA;AAAA,MACA;AAAA,KACF,CACC,MAAA;AAAA,MACC,SAAA;AAAA,MACA,EAAA,CAAG,IAAI,0CAA0C,CAAA;AAAA,MACjD,EAAA,CAAG,GAAA;AAAA,QACD,uCAAuC,IAAA,CAAK,eAAA;AAAA,UAC1C;AAAA,SACD,CAAA,4CAAA;AAAA,OACH;AAAA,MACA,EAAA,CAAG,GAAA;AAAA,QACD,sCAAsC,IAAA,CAAK,eAAA;AAAA,UACzC;AAAA,SACD,CAAA,kDAAA;AAAA;AACH,KACF,CACC,OAAA,CAAQ,SAAS,CAAA,CACjB,QAAQ,SAAS,CAAA;AAEpB,IAAA,OAAO,MAAM,IAAA,CAAK,CAAA,IAAA,KAAQ,IAAA,CAAK,uBAAA,CAAqC,IAAI,CAAC,CAAA;AAAA,EAC3E;AAAA,EAEA,MAAM,QAAA,GAAuC;AAC3C,IAAA,IAAA,CAAK,gBAAA,EAAiB;AACtB,IAAA,MAAM,EAAE,UAAA,EAAY,QAAA,EAAS,GAAI,IAAA,CAAK,OAAA;AACtC,IAAA,MAAM,KAAK,IAAA,CAAK,EAAA;AAChB,IAAA,MAAM,KAAA,GAAQ,GAAG,QAAQ,CAAA,CACtB,OAAO,EAAA,CAAG,GAAA,CAAI,8CAA8C,CAAC,CAAA,CAC7D,IAAA;AAAA,MACC,GAAG,QAAQ,CAAA,CACR,MAAA,CAAO,UAAU,EACjB,YAAA,CAAa,YAAA,EAAc,CAAC,UAAA,EAAY,QAAQ,CAAC,CAAA,CACjD,QAAQ,UAAU,CAAA,CAClB,GAAG,KAAK;AAAA,KACb;AAEF,IAAA,OAAO,KAAA,CAAM,KAAK,CAAA,MAAA,KAAU;AAC1B,MAAA,MAAM,EAAE,aAAA,EAAc,GAAI,IAAA,CAAK,MAAA;AAC/B,MAAA,MAAA,CAAO,CAAC,IAAI,EAAE,GAAG,OAAO,CAAC,CAAA,EAAG,gBAAgB,aAAA,EAAc;AAC1D,MAAA,OAAO,IAAA,CAAK,gBAAgB,MAAM,CAAA;AAAA,IACpC,CAAC,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,mBAAA,GAAkD;AACtD,IAAA,IAAA,CAAK,gBAAA,EAAiB;AACtB,IAAA,MAAM,EAAE,UAAA,EAAY,QAAA,EAAU,KAAA,KAAU,IAAA,CAAK,OAAA;AAC7C,IAAA,MAAM,KAAK,IAAA,CAAK,EAAA;AAChB,IAAA,MAAM,KAAA,GAAQ,EAAA,CAAG,QAAQ,CAAA,CACtB,MAAA;AAAA,MACC,EAAA,CAAG,IAAI,CAAA,kCAAA,CAAoC,CAAA;AAAA,MAC3C,EAAA,CAAG,IAAI,oCAAoC,CAAA;AAAA,MAC3C,EAAA,CAAG,GAAA,CAAI,IAAA,CAAK,eAAA,EAAiB;AAAA,MAE9B,KAAA,CAAM;AAAA,MACL,MAAA,EAAQ,OAAA;AAAA,MACR,OAAA,EAAS,QAAA;AAAA,MACT,SAAA,EAAW;AAAA,KACZ,EACA,YAAA,CAAa,YAAA,EAAc,CAAC,UAAA,EAAY,QAAQ,CAAC,CAAA,CACjD,UAAA,CAAW,WAAW,CAAA,CACtB,OAAA,CAAQ,SAAS,MAAM,CAAA,CACvB,MAAM,MAAA,CAAO,KAAK,KAAK,CAAC,CAAA;AAE3B,IAAA,OAAO,MAAM,IAAA,CAAK,CAAA,IAAA,KAAQ,KAAK,eAAA,CAAgB,IAAA,EAAM,WAAW,CAAC,CAAA;AAAA,EACnE;AAAA,EAEA,MAAM,cAAA,GAA6C;AACjD,IAAA,MAAM,KAAK,IAAA,CAAK,EAAA;AAChB,IAAA,IAAA,CAAK,gBAAA,EAAiB;AACtB,IAAA,MAAM,EAAE,UAAA,EAAY,QAAA,EAAU,KAAA,KAAU,IAAA,CAAK,OAAA;AAC7C,IAAA,MAAM,KAAA,GAAQ,EAAA,CAAG,QAAQ,CAAA,CACtB,MAAA;AAAA,MACC,EAAA,CAAG,IAAI,IAAA,CAAK,sBAAA,CAAuB,EAAE,YAAA,EAAc,IAAA,EAAM,CAAC,CAAA;AAAA,MAC1D,EAAA,CAAG,IAAI,oCAAoC;AAAA,KAC7C,CACC,aAAa,YAAA,EAAc,CAAC,YAAY,QAAQ,CAAC,CAAA,CACjD,QAAA,CAAS,QAAA,EAAU,QAAQ,EAC3B,UAAA,CAAW,MAAM,CAAA,CACjB,OAAA,CAAQ,MAAA,EAAQ,KAAK,EACrB,KAAA,CAAM,MAAA,CAAO,KAAK,CAAA,IAAK,CAAC,CAAA;AAE3B,IAAA,OAAO,MAAM,IAAA,CAAK,CAAA,IAAA,KAAQ,IAAA,CAAK,uBAAA,CAAwB,IAAI,CAAC,CAAA;AAAA,EAC9D;AAAA,EAEA,MAAM,mBAAA,GAAkD;AACtD,IAAA,IAAA,CAAK,gBAAA,EAAiB;AACtB,IAAA,MAAM,EAAE,UAAA,EAAY,QAAA,EAAU,KAAA,KAAU,IAAA,CAAK,OAAA;AAC7C,IAAA,MAAM,KAAK,IAAA,CAAK,EAAA;AAChB,IAAA,MAAM,KAAA,GAAQ,EAAA,CAAG,QAAQ,CAAA,CACtB,MAAA;AAAA,MACC,EAAA,CAAG,IAAI,oCAAoC,CAAA;AAAA,MAC3C,EAAA,CAAG,GAAA,CAAI,IAAA,CAAK,eAAA,EAAiB,CAAA;AAAA,MAC7B,EAAA,CAAG,IAAI,CAAA,gDAAA,CAAkD,CAAA;AAAA,MACzD,EAAA,CAAG,IAAI,CAAA,yCAAA,CAA2C,CAAA;AAAA,MAClD,EAAA,CAAG,IAAI,CAAA,mDAAA,CAAqD;AAAA,MAE7D,KAAA,CAAM;AAAA,MACL,MAAA,EAAQ,UAAA;AAAA,MACR,SAAA,EAAW;AAAA,KACZ,EACA,YAAA,CAAa,YAAA,EAAc,CAAC,UAAA,EAAY,QAAQ,CAAC,CAAA,CACjD,UAAA,CAAW,uBAAuB,CAAA,CAClC,OAAA,CAAQ,SAAS,MAAM,CAAA,CACvB,MAAM,MAAA,CAAO,KAAK,KAAK,CAAC,CAAA;AAE3B,IAAA,OAAO,MAAM,IAAA,CAAK,CAAA,IAAA,KAAQ,KAAK,eAAA,CAAgB,IAAA,EAAM,WAAW,CAAC,CAAA;AAAA,EACnE;AAAA,EAEA,MAAM,0BAAA,GAAyD;AAC7D,IAAA,IAAA,CAAK,gBAAA,EAAiB;AACtB,IAAA,MAAM,EAAE,UAAA,EAAY,QAAA,EAAU,KAAA,EAAO,IAAA,KAAS,IAAA,CAAK,OAAA;AACnD,IAAA,MAAM,KAAK,IAAA,CAAK,EAAA;AAChB,IAAA,MAAM,KAAA,GAAQ,EAAA,CAAG,QAAQ,CAAA,CACtB,MAAA;AAAA,MACC,WAAA;AAAA,MACA,EAAA,CAAG,IAAI,CAAA,kCAAA,CAAoC,CAAA;AAAA,MAC3C,EAAA,CAAG,IAAI,CAAA,2BAAA,CAA6B,CAAA;AAAA,MACpC,EAAA,CAAG,IAAI,CAAA,qCAAA,CAAuC,CAAA;AAAA,MAC9C,EAAA,CAAG,GAAA,CAAI,IAAA,CAAK,eAAA,EAAiB,CAAA;AAAA,MAC7B,EAAA,CAAG,IAAI,oCAAoC;AAAA,KAC7C,CACC,YAAA,CAAa,YAAA,EAAc,CAAC,YAAY,QAAQ,CAAC,CAAA,CACjD,QAAA,CAAS,EAAA,CAAG,GAAA,CAAI,CAAA,+BAAA,CAAiC,CAAC,EAClD,QAAA,CAAS,QAAA,EAAU,UAAU,CAAA,CAC7B,QAAA,CAAS,WAAA,EAAa,SAAS,CAAA,CAC/B,WAAW,kCAAkC,CAAA,CAC7C,OAAA,CAAQ,OAAA,EAAS,MAAM,CAAA,CACvB,KAAA,CAAM,MAAA,CAAO,KAAK,KAAK,CAAC,CAAA;AAE3B,IAAA,IAAI,IAAA,EAAM;AACR,MAAA,KAAA,CAAM,SAAS,EAAA,CAAG,GAAA,CAAI,kCAAkC,CAAC,IAAI,CAAC,CAAC,CAAA;AAAA,IACjE;AACA,IAAA,OAAO,MAAM,IAAA,CAAK,CAAA,IAAA,KAAQ,KAAK,eAAA,CAAgB,IAAA,EAAM,WAAW,CAAC,CAAA;AAAA,EACnE;AAAA,EAEA,MAAM,iBAAA,GAAgD;AACpD,IAAA,IAAA,CAAK,gBAAA,EAAiB;AACtB,IAAA,MAAM,EAAE,UAAA,EAAY,QAAA,EAAU,KAAA,GAAQ,CAAA,KAAM,IAAA,CAAK,OAAA;AACjD,IAAA,MAAM,SAAA,GAA8B,CAAC,UAAA,EAAY,QAAQ,CAAA;AACzD,IAAA,MAAM,KAAK,IAAA,CAAK,EAAA;AAGhB,IAAA,MAAM,iBAAA,GAAoB,CAAC,EAAA,KAA0B;AACnD,MAAA,MAAM,kBAAA,GAAqB;AAAA,QACzB,WAAA;AAAA,QACA,EAAA,CAAG,IAAI,IAAA,CAAK,sBAAA,CAAuB,EAAE,YAAA,EAAc,KAAA,EAAO,CAAC,CAAA;AAAA,QAC3D,EAAA,CAAG,IAAI,oCAAoC;AAAA,OAC7C;AAEA,MAAA,OAAO,IAAA,CAAK,gBAAA,CAAiB,EAAA,EAAI,kBAAA,EAAoB,SAAS,CAAA,CAAE,OAAA;AAAA,QAC9D,WAAA;AAAA,QACA;AAAA,OACF;AAAA,IACF,CAAA;AAGA,IAAA,MAAM,oBAAA,GAAuB,CAAC,EAAA,KAA0B;AACtD,MAAA,MAAM,qBAAA,GAAwB;AAAA,QAC5B,WAAA;AAAA,QACA,EAAA,CAAG,IAAI,0CAA0C;AAAA,OACnD;AACA,MAAA,OAAO,IAAA,CAAK,gBAAA;AAAA,QACV,EAAA;AAAA,QACA,qBAAA;AAAA,QACA;AAAA,OACF,CAAE,QAAQ,WAAW,CAAA;AAAA,IACvB,CAAA;AAGA,IAAA,MAAM,wBAAA,GAA2B,CAAC,EAAA,KAA0B;AAC1D,MAAA,OAAO,GACJ,MAAA,CAAO;AAAA,QACN,WAAA;AAAA,QACA,GAAG,GAAA,CAAI;AAAA,qBAAA,EACM,IAAA,CAAK,uBAAA,CAAwB,MAAA,EAAQ,OAAO,CAAC,CAAA;AAAA;AAAA;AAAA,gBAAA,CAGnD;AAAA,OACR,CAAA,CACA,IAAA,CAAK,iBAAiB,CAAA,CACtB,QAAQ,WAAW,CAAA;AAAA,IACxB,CAAA;AAGA,IAAA,MAAM,KAAA,GAAQ,GACX,IAAA,CAAK,YAAA,EAAc,QAAM,iBAAA,CAAkB,EAAE,CAAC,CAAA,CAC9C,IAAA,CAAK,eAAA,EAAiB,QAAM,oBAAA,CAAqB,EAAE,CAAC,CAAA,CACpD,IAAA,CAAK,mBAAA,EAAqB,QAAM,wBAAA,CAAyB,EAAE,CAAC,CAAA,CAC5D,MAAA,CAAO;AAAA,MACN,aAAA;AAAA,MACA,eAAA;AAAA,MACA,SAAA;AAAA,MACA,GAAG,GAAA,CAAI;AAAA;AAAA;AAAA;AAAA;AAAA,QAAA,CAKN;AAAA,KACF,CAAA,CACA,IAAA,CAAK,oBAAoB,EACzB,QAAA,CAAS,wBAAA,EAA0B,aAAA,EAAe,aAAa,EAC/D,OAAA,CAAQ,eAAA,EAAiB,MAAM,CAAA,CAC/B,MAAM,KAAK,CAAA;AAEd,IAAA,OAAO,KAAA,CAAM,KAAK,CAAA,IAAA,KAAQ;AACxB,MAAA,OAAO,IAAA,CAAK,uBAAA;AAAA,QACV,IAAA,CAAK,aAAA,CAAc,IAAA,EAAM,OAAO,CAAA;AAAA,QAChC;AAAA,OACF;AAAA,IACF,CAAC,CAAA;AAAA,EACH;AAAA;AAAA,EAgBA,mBAAmB,SAAA,EAA+C;AAChE,IAAA,OAAO,SAAA;AAAA,EACT;AAAA,EAEA,aAAA,CAAc,MAAa,SAAA,EAAwB;AACjD,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,MAAc;AAAA,MAC7B,GAAG,GAAA;AAAA,MACH,CAAC,SAAS,GAAG,GAAA,CAAI,SAAS,CAAA,GACtB,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI,SAAS,CAAC,CAAC,CAAA,GACzC;AAAA,KACN,CAAE,CAAA;AAAA,EACJ;AAAA,EAEA,kBAAA,CACE,GAAA,EACA,QAAA,GAAmB,MAAA,EACnB;AACA,IAAA,IAAI,GAAA,CAAI,QAAQ,CAAA,EAAG;AACjB,MAAA,OAAO;AAAA,QACL,GAAG,GAAA;AAAA,QACH,GAAI,IAAA,CAAK,mBAAA,EAAoB,GACzB;AAAA,UACE,CAAC,QAAQ,GAAGA,4BAAA;AAAA,YACV,IAAI,QAAQ,CAAA;AAAA,YACZ,KAAK,OAAA,EAAS;AAAA;AAChB,YAEF;AAAC,OACP;AAAA,IACF;AACA,IAAA,OAAO,GAAA;AAAA,EACT;AAAA,EAEA,eAAA,CACE,IAAA,EACA,QAAA,GAAmB,MAAA,EACF;AACjB,IAAA,OAAO;AAAA,MACL,IAAA,EAAM,IAAA,CAAK,GAAA,CAAI,CAAA,CAAA,KAAK;AAClB,QAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,CAAA,CAAE,QAAQ,CAAC,CAAA,EAAG;AAC9B,UAAA,OAAO;AAAA,YACL,GAAG,CAAA;AAAA,YACH,CAAC,QAAQ,GAAG,CAAA,CAAE,QAAQ,CAAA,CAAE,GAAA;AAAA,cAAI,CAAC,EAAA,KAC3B,IAAA,CAAK,kBAAA,CAAmB,EAAE;AAAA;AAC5B,WACF;AAAA,QACF;AACA,QAAA,OAAO,IAAA,CAAK,kBAAA,CAAmB,CAAA,EAAG,QAAQ,CAAA;AAAA,MAC5C,CAAC;AAAA,KACH;AAAA,EACF;AAAA,EAEA,uBAAA,GAA0B,CACxB,IAAA,EACA,QAAA,GAAmB,MAAA,KACS;AAC5B,IAAA,MAAM,QAAA,GAAW,KAAK,sBAAA,CAAuB;AAAA,MAC3C,QAAA,EAAU;AAAA,KACX,CAAA;AAED,IAAA,IAAI,aAAa,QAAA,EAAU;AACzB,MAAA,OAAO;AAAA,QACL,QAAA;AAAA,QACA,IAAA,EAAM,IAAA,CAAK,GAAA,CAAI,CAAA,CAAA,KAAK;AAClB,UAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,CAAA,CAAE,QAAQ,CAAC,CAAA,EAAG;AAC9B,YAAA,OAAO;AAAA,cACL,GAAG,CAAA;AAAA,cACH,CAAC,QAAQ,GAAG,CAAA,CAAE,QAAQ,CAAA,CAAE,GAAA;AAAA,gBAAI,CAAC,EAAA,KAC3B,IAAA,CAAK,kBAAA,CAAmB,EAAE;AAAA;AAC5B,aACF;AAAA,UACF;AACA,UAAA,OAAO,IAAA,CAAK,kBAAA,CAAmB,CAAA,EAAG,QAAQ,CAAA;AAAA,QAC5C,CAAC;AAAA,OACH;AAAA,IACF;AAEA,IAAA,OAAO;AAAA,MACL,QAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF,CAAA;AAAA,EAEA,gBAAA,GAAmB;AACjB,IAAA,IAAI,CAAC,KAAK,OAAA,EAAS;AACjB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AAAA,EACF;AACF;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PostgresAdapter.cjs.js","sources":["../../../src/database/adapters/PostgresAdapter.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { BaseDatabaseAdapter } from './BaseAdapter';\nimport { calculateDateRange, getDateGroupingType } from '../../utils/date';\n\nexport class PostgresAdapter extends BaseDatabaseAdapter {\n isJsonSupported(): boolean {\n return true;\n }\n\n isPartitionSupported(): boolean {\n return true;\n }\n\n isTimezoneSupported(): boolean {\n return true;\n }\n\n getFormatedDate(column: string): string {\n return `${column}::timestamp`;\n }\n\n getDate(): string {\n const timeZone =\n this.filters?.timezone ||\n new Intl.DateTimeFormat().resolvedOptions().timeZone;\n return this.db\n .raw(\n `to_char(created_at AT AT TIME ZONE ?,'YYYY-MM-DD\"T\"HH24:MI:SS.MSZ')`,\n [timeZone],\n )\n .toQuery();\n }\n\n getLastUsedDate(): string {\n const timeZone =\n this.filters?.timezone ||\n new Intl.DateTimeFormat().resolvedOptions().timeZone;\n return this.db\n .raw(\n `to_char(MAX(created_at) AT TIME ZONE ?,'YYYY-MM-DD\"T\"HH24:MI:SS.FF3') || 'Z' AS last_used`,\n [timeZone],\n )\n .toQuery();\n }\n\n getDateBetweenQuery() {\n return 'created_at';\n }\n getJsonAggregationQuery(...args: any[]): string {\n const { grouping } = this.filters!;\n const fieldMappings = args\n .map(field => {\n if (field === 'date' && grouping === 'hourly') {\n return `'${field}', to_char(date, 'YYYY-MM-DD HH24:MI:SS')`;\n }\n return `'${field}', ${field}`;\n })\n .join(', ');\n\n return `jsonb_agg(jsonb_build_object(${fieldMappings}) ORDER BY date)`;\n }\n\n transformJson(data: any[]): any {\n return data;\n }\n\n getDynamicDateGrouping({ onlyText = false, useTimestamp = false }): string {\n this.ensureFiltersSet();\n const { start_date, end_date, grouping: groupingStrategy } = this.filters!;\n const dateDiff = calculateDateRange(start_date, end_date);\n const grouping = groupingStrategy || getDateGroupingType(dateDiff);\n\n if (onlyText) {\n return grouping;\n }\n\n return `${this.getDateGroupingQuery(grouping, useTimestamp)} as date`;\n }\n\n private getDateGroupingQuery(\n grouping: string,\n useTimeStamp: boolean = false,\n ): string {\n const timeZone =\n this.filters?.timezone ||\n new Intl.DateTimeFormat().resolvedOptions().timeZone;\n\n const rawQuery = (query: any, bindings: any) =>\n this.db.raw(query, bindings).toQuery();\n switch (grouping) {\n case 'hourly':\n return useTimeStamp\n ? rawQuery(\n `to_char(date_trunc('hour', created_at AT TIME ZONE ?) AT TIME ZONE ?, 'YYYY-MM-DD\"T\"HH24:MI:SSOF')`,\n [timeZone, timeZone],\n )\n : rawQuery(\n `date_trunc('hour', created_at AT TIME ZONE ?) AT TIME ZONE ?`,\n [timeZone, timeZone],\n );\n case 'daily':\n return rawQuery(`to_char(created_at AT TIME ZONE ?, 'YYYY-MM-DD')`, [\n timeZone,\n ]);\n case 'weekly':\n return rawQuery(\n `to_char(date_trunc('week', created_at AT TIME ZONE ?) AT TIME ZONE ?, 'YYYY-MM-DD\"T\"HH24:MI:SSOF')`,\n [timeZone, timeZone],\n );\n case 'monthly':\n return rawQuery(\n `to_char(\n LEAST (\n (date_trunc('month', created_at AT TIME ZONE ?) AT TIME ZONE ?\n + interval '1 month' - interval '1 day'), \n ?::date\n ),\n 'YYYY-MM-DD\"T\"HH24:MI:SSOF'\n )`,\n [timeZone, timeZone, this.filters?.end_date],\n );\n default:\n throw new Error('Invalid date grouping');\n }\n }\n}\n"],"names":["BaseDatabaseAdapter","calculateDateRange","getDateGroupingType"],"mappings":";;;;;AAkBO,MAAM,wBAAwBA,+
|
|
1
|
+
{"version":3,"file":"PostgresAdapter.cjs.js","sources":["../../../src/database/adapters/PostgresAdapter.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { BaseDatabaseAdapter } from './BaseAdapter';\nimport { calculateDateRange, getDateGroupingType } from '../../utils/date';\n\nexport class PostgresAdapter extends BaseDatabaseAdapter {\n isJsonSupported(): boolean {\n return true;\n }\n\n isPartitionSupported(): boolean {\n return true;\n }\n\n isTimezoneSupported(): boolean {\n return true;\n }\n\n getFormatedDate(column: string): string {\n return `${column}::timestamp`;\n }\n\n getDate(): string {\n const timeZone =\n this.filters?.timezone ||\n new Intl.DateTimeFormat().resolvedOptions().timeZone;\n return this.db\n .raw(\n `to_char(created_at AT AT TIME ZONE ?,'YYYY-MM-DD\"T\"HH24:MI:SS.MSZ')`,\n [timeZone],\n )\n .toQuery();\n }\n\n getLastUsedDate(): string {\n const timeZone =\n this.filters?.timezone ||\n new Intl.DateTimeFormat().resolvedOptions().timeZone;\n return this.db\n .raw(\n `to_char(MAX(created_at) AT TIME ZONE ?,'YYYY-MM-DD\"T\"HH24:MI:SS.FF3') || 'Z' AS last_used`,\n [timeZone],\n )\n .toQuery();\n }\n\n getDateBetweenQuery() {\n return 'created_at';\n }\n getJsonAggregationQuery(...args: any[]): string {\n const { grouping } = this.filters!;\n const fieldMappings = args\n .map(field => {\n if (field === 'date' && grouping === 'hourly') {\n return `'${field}', to_char(date, 'YYYY-MM-DD HH24:MI:SS')`;\n }\n return `'${field}', ${field}`;\n })\n .join(', ');\n\n return `jsonb_agg(jsonb_build_object(${fieldMappings}) ORDER BY date)`;\n }\n\n transformJson(data: any[]): any {\n return data;\n }\n\n getDynamicDateGrouping({ onlyText = false, useTimestamp = false }): string {\n this.ensureFiltersSet();\n const { start_date, end_date, grouping: groupingStrategy } = this.filters!;\n const dateDiff = calculateDateRange(start_date, end_date);\n const grouping = groupingStrategy || getDateGroupingType(dateDiff);\n\n if (onlyText) {\n return grouping;\n }\n\n return `${this.getDateGroupingQuery(grouping, useTimestamp)} as date`;\n }\n\n private getDateGroupingQuery(\n grouping: string,\n useTimeStamp: boolean = false,\n ): string {\n const timeZone =\n this.filters?.timezone ||\n new Intl.DateTimeFormat().resolvedOptions().timeZone;\n\n const rawQuery = (query: any, bindings: any) =>\n this.db.raw(query, bindings).toQuery();\n switch (grouping) {\n case 'hourly':\n return useTimeStamp\n ? rawQuery(\n `to_char(date_trunc('hour', created_at AT TIME ZONE ?) AT TIME ZONE ?, 'YYYY-MM-DD\"T\"HH24:MI:SSOF')`,\n [timeZone, timeZone],\n )\n : rawQuery(\n `date_trunc('hour', created_at AT TIME ZONE ?) AT TIME ZONE ?`,\n [timeZone, timeZone],\n );\n case 'daily':\n return rawQuery(`to_char(created_at AT TIME ZONE ?, 'YYYY-MM-DD')`, [\n timeZone,\n ]);\n case 'weekly':\n return rawQuery(\n `to_char(date_trunc('week', created_at AT TIME ZONE ?) AT TIME ZONE ?, 'YYYY-MM-DD\"T\"HH24:MI:SSOF')`,\n [timeZone, timeZone],\n );\n case 'monthly':\n return rawQuery(\n `to_char(\n LEAST (\n (date_trunc('month', created_at AT TIME ZONE ?) AT TIME ZONE ?\n + interval '1 month' - interval '1 day'), \n ?::date\n ),\n 'YYYY-MM-DD\"T\"HH24:MI:SSOF'\n )`,\n [timeZone, timeZone, this.filters?.end_date],\n );\n default:\n throw new Error('Invalid date grouping');\n }\n }\n}\n"],"names":["BaseDatabaseAdapter","calculateDateRange","getDateGroupingType"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SqliteAdapter.cjs.js","sources":["../../../src/database/adapters/SqliteAdapter.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { BaseDatabaseAdapter } from './BaseAdapter';\nimport {\n calculateDateRange,\n getDateGroupingType,\n getTimeZoneOffsetString,\n} from '../../utils/date';\n\nexport class SqliteAdapter extends BaseDatabaseAdapter {\n isJsonSupported(): boolean {\n return false;\n }\n\n isPartitionSupported(): boolean {\n return false;\n }\n\n isTimezoneSupported(): boolean {\n return false;\n }\n\n getDate(): string {\n return `strftime('%Y-%m-%d', datetime(created_at, '${getTimeZoneOffsetString(\n this.filters?.timezone,\n )}')) AS date`;\n }\n\n getLastUsedDate(): string {\n return `strftime('%Y-%m-%dT%H:%M:%SZ', MAX(datetime(created_at, '${getTimeZoneOffsetString(\n this.filters?.timezone,\n )}'))) AS last_used`;\n }\n\n getFormatedDate(column: string): string {\n return `datetime(${column})`;\n }\n\n getDateBetweenQuery(): string {\n return 'created_at BETWEEN datetime(?) AND datetime(?)';\n }\n\n getJsonAggregationQuery(...args: any[]): string {\n const fieldMappings = args.map(field => `'${field}', ${field}`).join(', ');\n return `json_group_array(json_object(${fieldMappings}))`;\n }\n\n transformJson(data: any[], jsonField: string): any {\n return data.map((row: any) => ({\n ...row,\n [jsonField]: row[jsonField] ? JSON.parse(row[jsonField]) : null,\n }));\n }\n\n getDynamicDateGrouping({ onlyText = false }): string {\n const { start_date, end_date, grouping: groupingStrategy } = this.filters!;\n const dateDiff = calculateDateRange(start_date, end_date);\n\n const grouping = groupingStrategy || getDateGroupingType(dateDiff);\n\n if (onlyText) {\n return grouping;\n }\n\n return this.db\n .raw(`${this.getDateGroupingQuery(grouping)} as date`)\n .toQuery();\n }\n\n private getDateGroupingQuery(grouping: string): string {\n const offsetStr = getTimeZoneOffsetString(this.filters?.timezone);\n switch (grouping) {\n case 'hourly':\n return `strftime('%Y-%m-%d %H:00:00', datetime(created_at, '${offsetStr}'))`;\n case 'daily':\n return `strftime('%Y-%m-%d', datetime(created_at, '${offsetStr}'))`;\n case 'weekly':\n return `strftime('%Y-%m-%d', datetime(created_at, '${offsetStr}', 'weekday 0', '-6 days'))`;\n case 'monthly':\n return `strftime('%Y-%m-%d', datetime(created_at, '${offsetStr}'))`;\n default:\n throw new Error('Invalid date grouping');\n }\n }\n}\n"],"names":["BaseDatabaseAdapter","getTimeZoneOffsetString","calculateDateRange","getDateGroupingType"],"mappings":";;;;;AAsBO,MAAM,sBAAsBA,+
|
|
1
|
+
{"version":3,"file":"SqliteAdapter.cjs.js","sources":["../../../src/database/adapters/SqliteAdapter.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { BaseDatabaseAdapter } from './BaseAdapter';\nimport {\n calculateDateRange,\n getDateGroupingType,\n getTimeZoneOffsetString,\n} from '../../utils/date';\n\nexport class SqliteAdapter extends BaseDatabaseAdapter {\n isJsonSupported(): boolean {\n return false;\n }\n\n isPartitionSupported(): boolean {\n return false;\n }\n\n isTimezoneSupported(): boolean {\n return false;\n }\n\n getDate(): string {\n return `strftime('%Y-%m-%d', datetime(created_at, '${getTimeZoneOffsetString(\n this.filters?.timezone,\n )}')) AS date`;\n }\n\n getLastUsedDate(): string {\n return `strftime('%Y-%m-%dT%H:%M:%SZ', MAX(datetime(created_at, '${getTimeZoneOffsetString(\n this.filters?.timezone,\n )}'))) AS last_used`;\n }\n\n getFormatedDate(column: string): string {\n return `datetime(${column})`;\n }\n\n getDateBetweenQuery(): string {\n return 'created_at BETWEEN datetime(?) AND datetime(?)';\n }\n\n getJsonAggregationQuery(...args: any[]): string {\n const fieldMappings = args.map(field => `'${field}', ${field}`).join(', ');\n return `json_group_array(json_object(${fieldMappings}))`;\n }\n\n transformJson(data: any[], jsonField: string): any {\n return data.map((row: any) => ({\n ...row,\n [jsonField]: row[jsonField] ? JSON.parse(row[jsonField]) : null,\n }));\n }\n\n getDynamicDateGrouping({ onlyText = false }): string {\n const { start_date, end_date, grouping: groupingStrategy } = this.filters!;\n const dateDiff = calculateDateRange(start_date, end_date);\n\n const grouping = groupingStrategy || getDateGroupingType(dateDiff);\n\n if (onlyText) {\n return grouping;\n }\n\n return this.db\n .raw(`${this.getDateGroupingQuery(grouping)} as date`)\n .toQuery();\n }\n\n private getDateGroupingQuery(grouping: string): string {\n const offsetStr = getTimeZoneOffsetString(this.filters?.timezone);\n switch (grouping) {\n case 'hourly':\n return `strftime('%Y-%m-%d %H:00:00', datetime(created_at, '${offsetStr}'))`;\n case 'daily':\n return `strftime('%Y-%m-%d', datetime(created_at, '${offsetStr}'))`;\n case 'weekly':\n return `strftime('%Y-%m-%d', datetime(created_at, '${offsetStr}', 'weekday 0', '-6 days'))`;\n case 'monthly':\n return `strftime('%Y-%m-%d', datetime(created_at, '${offsetStr}'))`;\n default:\n throw new Error('Invalid date grouping');\n }\n }\n}\n"],"names":["BaseDatabaseAdapter","getTimeZoneOffsetString","calculateDateRange","getDateGroupingType"],"mappings":";;;;;AAsBO,MAAM,sBAAsBA,+BAAA,CAAoB;AAAA,EACrD,eAAA,GAA2B;AACzB,IAAA,OAAO,KAAA;AAAA,EACT;AAAA,EAEA,oBAAA,GAAgC;AAC9B,IAAA,OAAO,KAAA;AAAA,EACT;AAAA,EAEA,mBAAA,GAA+B;AAC7B,IAAA,OAAO,KAAA;AAAA,EACT;AAAA,EAEA,OAAA,GAAkB;AAChB,IAAA,OAAO,CAAA,2CAAA,EAA8CC,4BAAA;AAAA,MACnD,KAAK,OAAA,EAAS;AAAA,KACf,CAAA,WAAA,CAAA;AAAA,EACH;AAAA,EAEA,eAAA,GAA0B;AACxB,IAAA,OAAO,CAAA,yDAAA,EAA4DA,4BAAA;AAAA,MACjE,KAAK,OAAA,EAAS;AAAA,KACf,CAAA,iBAAA,CAAA;AAAA,EACH;AAAA,EAEA,gBAAgB,MAAA,EAAwB;AACtC,IAAA,OAAO,YAAY,MAAM,CAAA,CAAA,CAAA;AAAA,EAC3B;AAAA,EAEA,mBAAA,GAA8B;AAC5B,IAAA,OAAO,gDAAA;AAAA,EACT;AAAA,EAEA,2BAA2B,IAAA,EAAqB;AAC9C,IAAA,MAAM,aAAA,GAAgB,IAAA,CAAK,GAAA,CAAI,CAAA,KAAA,KAAS,CAAA,CAAA,EAAI,KAAK,CAAA,GAAA,EAAM,KAAK,CAAA,CAAE,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA;AACzE,IAAA,OAAO,gCAAgC,aAAa,CAAA,EAAA,CAAA;AAAA,EACtD;AAAA,EAEA,aAAA,CAAc,MAAa,SAAA,EAAwB;AACjD,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,MAAc;AAAA,MAC7B,GAAG,GAAA;AAAA,MACH,CAAC,SAAS,GAAG,GAAA,CAAI,SAAS,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,SAAS,CAAC,CAAA,GAAI;AAAA,KAC7D,CAAE,CAAA;AAAA,EACJ;AAAA,EAEA,sBAAA,CAAuB,EAAE,QAAA,GAAW,KAAA,EAAM,EAAW;AACnD,IAAA,MAAM,EAAE,UAAA,EAAY,QAAA,EAAU,QAAA,EAAU,gBAAA,KAAqB,IAAA,CAAK,OAAA;AAClE,IAAA,MAAM,QAAA,GAAWC,uBAAA,CAAmB,UAAA,EAAY,QAAQ,CAAA;AAExD,IAAA,MAAM,QAAA,GAAW,gBAAA,IAAoBC,wBAAA,CAAoB,QAAQ,CAAA;AAEjE,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,OAAO,QAAA;AAAA,IACT;AAEA,IAAA,OAAO,IAAA,CAAK,EAAA,CACT,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,qBAAqB,QAAQ,CAAC,CAAA,QAAA,CAAU,CAAA,CACpD,OAAA,EAAQ;AAAA,EACb;AAAA,EAEQ,qBAAqB,QAAA,EAA0B;AACrD,IAAA,MAAM,SAAA,GAAYF,4BAAA,CAAwB,IAAA,CAAK,OAAA,EAAS,QAAQ,CAAA;AAChE,IAAA,QAAQ,QAAA;AAAU,MAChB,KAAK,QAAA;AACH,QAAA,OAAO,uDAAuD,SAAS,CAAA,GAAA,CAAA;AAAA,MACzE,KAAK,OAAA;AACH,QAAA,OAAO,8CAA8C,SAAS,CAAA,GAAA,CAAA;AAAA,MAChE,KAAK,QAAA;AACH,QAAA,OAAO,8CAA8C,SAAS,CAAA,2BAAA,CAAA;AAAA,MAChE,KAAK,SAAA;AACH,QAAA,OAAO,8CAA8C,SAAS,CAAA,GAAA,CAAA;AAAA,MAChE;AACE,QAAA,MAAM,IAAI,MAAM,uBAAuB,CAAA;AAAA;AAC3C,EACF;AACF;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migration.cjs.js","sources":["../../src/database/migration.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 DatabaseService,\n resolvePackagePath,\n} from '@backstage/backend-plugin-api';\n\nconst migrationsDir = resolvePackagePath(\n '@red-hat-developer-hub/backstage-plugin-adoption-insights-backend',\n 'migrations',\n);\n\nexport async function migrate(databaseManager: DatabaseService) {\n const knex = await databaseManager.getClient();\n\n if (!databaseManager.migrations?.skip) {\n await knex.migrate.latest({\n directory: migrationsDir,\n });\n }\n}\n"],"names":["resolvePackagePath"],"mappings":";;;;AAoBA,MAAM,
|
|
1
|
+
{"version":3,"file":"migration.cjs.js","sources":["../../src/database/migration.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 DatabaseService,\n resolvePackagePath,\n} from '@backstage/backend-plugin-api';\n\nconst migrationsDir = resolvePackagePath(\n '@red-hat-developer-hub/backstage-plugin-adoption-insights-backend',\n 'migrations',\n);\n\nexport async function migrate(databaseManager: DatabaseService) {\n const knex = await databaseManager.getClient();\n\n if (!databaseManager.migrations?.skip) {\n await knex.migrate.latest({\n directory: migrationsDir,\n });\n }\n}\n"],"names":["resolvePackagePath"],"mappings":";;;;AAoBA,MAAM,aAAA,GAAgBA,mCAAA;AAAA,EACpB,mEAAA;AAAA,EACA;AACF,CAAA;AAEA,eAAsB,QAAQ,eAAA,EAAkC;AAC9D,EAAA,MAAM,IAAA,GAAO,MAAM,eAAA,CAAgB,SAAA,EAAU;AAE7C,EAAA,IAAI,CAAC,eAAA,CAAgB,UAAA,EAAY,IAAA,EAAM;AACrC,IAAA,MAAM,IAAA,CAAK,QAAQ,MAAA,CAAO;AAAA,MACxB,SAAA,EAAW;AAAA,KACZ,CAAA;AAAA,EACH;AACF;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"partition.cjs.js","sources":["../../src/database/partition.ts"],"sourcesContent":["import { Knex } from 'knex';\n/*\n * Copyright Red Hat, Inc.\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 { LoggerService, SchedulerService } from '@backstage/backend-plugin-api';\nimport {\n extractOverlappingPartition,\n isPartitionOverlapError,\n parsePartitionDate,\n} from '../utils/partition';\nimport { DateTime } from 'luxon';\n\ntype AttemptTracker = Map<string, number>;\n\nexport const createPartition = async (\n knex: Knex,\n year: number,\n month: number,\n attempts: AttemptTracker = new Map(),\n maxRetries = 1,\n) => {\n const startObject = DateTime.fromObject(\n { year, month, day: 1 },\n { zone: 'UTC' },\n );\n const endObject = DateTime.fromObject(\n { year, month: month, day: 1 },\n { zone: 'UTC' },\n ).plus({ month: 1 });\n if (!endObject.isValid || !startObject.isValid) {\n throw new Error(\n `The combination of year ${year} and month ${month} is invalid.`,\n );\n }\n\n const startDate = startObject.toISODate();\n const endDate = endObject.toISODate();\n\n const partitionName = `events_${year}_${month.toString().padStart(2, '0')}`;\n const key = `${year}_${month}`;\n\n // track max attempts\n const currentAttempt = attempts.get(key) ?? 0;\n if (currentAttempt > maxRetries) {\n throw new Error(`Exceeded max retries for partition ${key}`);\n }\n attempts.set(key, currentAttempt + 1);\n\n try {\n await knex.schema.raw(`\n CREATE TABLE IF NOT EXISTS ${partitionName} \n PARTITION OF events\n FOR VALUES FROM ('${startDate}') TO ('${endDate}');\n `);\n } catch (error) {\n if (isPartitionOverlapError(error)) {\n const overlappingPartition = extractOverlappingPartition(error.message);\n const { year: y, month: m } = parsePartitionDate(overlappingPartition);\n\n await knex.schema.raw(\n `DROP TABLE IF EXISTS ${overlappingPartition} CASCADE`,\n );\n\n // Recreate the dropped overlapping partition\n await createPartition(knex, y, m, attempts, maxRetries);\n\n // Retry the current one\n await createPartition(knex, year, month, attempts, maxRetries);\n } else {\n throw error;\n }\n }\n};\n\nexport const schedulePartition = async (\n client: Knex,\n services: {\n logger: LoggerService;\n scheduler: SchedulerService;\n },\n) => {\n const { logger, scheduler } = services;\n const partitionEventsTable = async () => {\n const year = new Date().getFullYear();\n const month = new Date().getMonth() + 1;\n logger.info(\n `Creating partition for ${year}_${month.toString().padStart(2, '0')}`,\n );\n await createPartition(client, year, month);\n };\n\n const runner = scheduler.createScheduledTaskRunner({\n frequency: { cron: '0 0 1 * *' }, // Runs at midnight on the 1st of every month\n timeout: { seconds: 30 },\n });\n\n runner.run({\n id: 'create-partition',\n fn: partitionEventsTable,\n });\n\n logger.info('[TASK] Running initial execution on startup...');\n await partitionEventsTable();\n};\n"],"names":["DateTime","isPartitionOverlapError","extractOverlappingPartition","parsePartitionDate"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"partition.cjs.js","sources":["../../src/database/partition.ts"],"sourcesContent":["import { Knex } from 'knex';\n/*\n * Copyright Red Hat, Inc.\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 { LoggerService, SchedulerService } from '@backstage/backend-plugin-api';\nimport {\n extractOverlappingPartition,\n isPartitionOverlapError,\n parsePartitionDate,\n} from '../utils/partition';\nimport { DateTime } from 'luxon';\n\ntype AttemptTracker = Map<string, number>;\n\nexport const createPartition = async (\n knex: Knex,\n year: number,\n month: number,\n attempts: AttemptTracker = new Map(),\n maxRetries = 1,\n) => {\n const startObject = DateTime.fromObject(\n { year, month, day: 1 },\n { zone: 'UTC' },\n );\n const endObject = DateTime.fromObject(\n { year, month: month, day: 1 },\n { zone: 'UTC' },\n ).plus({ month: 1 });\n if (!endObject.isValid || !startObject.isValid) {\n throw new Error(\n `The combination of year ${year} and month ${month} is invalid.`,\n );\n }\n\n const startDate = startObject.toISODate();\n const endDate = endObject.toISODate();\n\n const partitionName = `events_${year}_${month.toString().padStart(2, '0')}`;\n const key = `${year}_${month}`;\n\n // track max attempts\n const currentAttempt = attempts.get(key) ?? 0;\n if (currentAttempt > maxRetries) {\n throw new Error(`Exceeded max retries for partition ${key}`);\n }\n attempts.set(key, currentAttempt + 1);\n\n try {\n await knex.schema.raw(`\n CREATE TABLE IF NOT EXISTS ${partitionName} \n PARTITION OF events\n FOR VALUES FROM ('${startDate}') TO ('${endDate}');\n `);\n } catch (error) {\n if (isPartitionOverlapError(error)) {\n const overlappingPartition = extractOverlappingPartition(error.message);\n const { year: y, month: m } = parsePartitionDate(overlappingPartition);\n\n await knex.schema.raw(\n `DROP TABLE IF EXISTS ${overlappingPartition} CASCADE`,\n );\n\n // Recreate the dropped overlapping partition\n await createPartition(knex, y, m, attempts, maxRetries);\n\n // Retry the current one\n await createPartition(knex, year, month, attempts, maxRetries);\n } else {\n throw error;\n }\n }\n};\n\nexport const schedulePartition = async (\n client: Knex,\n services: {\n logger: LoggerService;\n scheduler: SchedulerService;\n },\n) => {\n const { logger, scheduler } = services;\n const partitionEventsTable = async () => {\n const year = new Date().getFullYear();\n const month = new Date().getMonth() + 1;\n logger.info(\n `Creating partition for ${year}_${month.toString().padStart(2, '0')}`,\n );\n await createPartition(client, year, month);\n };\n\n const runner = scheduler.createScheduledTaskRunner({\n frequency: { cron: '0 0 1 * *' }, // Runs at midnight on the 1st of every month\n timeout: { seconds: 30 },\n });\n\n runner.run({\n id: 'create-partition',\n fn: partitionEventsTable,\n });\n\n logger.info('[TASK] Running initial execution on startup...');\n await partitionEventsTable();\n};\n"],"names":["DateTime","isPartitionOverlapError","extractOverlappingPartition","parsePartitionDate"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EventBatchProcessor.cjs.js","sources":["../../src/domain/EventBatchProcessor.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { LoggerService } from '@backstage/backend-plugin-api';\nimport { EventDatabase } from '../database/event-database';\nimport { Event } from '../models/Event';\n\nexport type ProcessorConfigOptions = {\n debug?: boolean;\n batchSize?: number;\n maxRetries?: number;\n batchInterval?: number;\n};\nexport class EventBatchProcessor {\n private readonly queue: Event[];\n private processing: boolean;\n private readonly batchSize: number;\n private readonly batchInterval: number;\n private readonly maxRetries: number;\n private readonly failedEvents: any;\n private readonly database: EventDatabase;\n private readonly logger: LoggerService;\n private readonly debug: boolean;\n\n constructor(\n database: EventDatabase,\n logger: LoggerService,\n options: ProcessorConfigOptions,\n ) {\n this.queue = [];\n this.logger = logger;\n this.processing = false;\n this.database = database;\n this.failedEvents = new Map();\n this.debug = options.debug || false;\n\n const { batchSize = 5, maxRetries = 3, batchInterval = 2000 } = options;\n\n this.batchSize = batchSize;\n this.maxRetries = maxRetries;\n this.batchInterval = batchInterval;\n\n setInterval(() => this.processEvents(), this.batchInterval);\n setInterval(() => this.logQueueStats(), 5000);\n }\n\n addEvent(event: Event) {\n if (\n !this.queue.some((existingEvent: Event) => existingEvent.id === event.id)\n ) {\n this.logger.info(`[QUEUE] Event added: ${JSON.stringify(event)}`);\n this.queue.push({ ...event, toJSON: event.toJSON });\n }\n }\n\n async processEvents() {\n if (this.processing || this.queue.length === 0) return;\n this.processing = true;\n\n const batch = this.queue.splice(0, this.batchSize);\n try {\n await this.database.insertEvents(batch);\n this.logger.info(`[SUCCESS] Inserted ${batch.length} events`);\n batch.forEach((event: Event) => this.failedEvents.delete(event.id));\n } catch (error) {\n this.logger.error(`[ERROR] Batch insert failed:`, error);\n batch.forEach(event =>\n this.retryOrStoreFailedEvent(event, error.message),\n );\n }\n\n this.processing = false;\n }\n\n async retryOrStoreFailedEvent(event: Event, errorMessage: string) {\n const retries = this.failedEvents.get(event.id) || 0;\n if (retries >= this.maxRetries) {\n this.logger.error(\n `[DROPPED] Event permanently failed, storing in DB: ${JSON.stringify(\n event.toJSON(),\n )}`,\n );\n await this.storeFailedEvent(event, errorMessage);\n return;\n }\n\n this.failedEvents.set(event.id, retries + 1);\n this.queue.push(event);\n this.logger.warn(\n `[RETRY] Event re-added to queue (Attempt ${\n retries + 1\n }): ${JSON.stringify(event.toJSON())})`,\n );\n }\n\n async storeFailedEvent(event: Event, errorMessage: string) {\n try {\n await this.database.insertFailedEvent(\n JSON.stringify(event),\n errorMessage,\n this.maxRetries,\n );\n this.logger.warn(`[FAILED-LOGGED] Event stored in DB:`, event.toJSON());\n } catch (err) {\n this.logger.error(`[DB-ERROR] Failed to store event in DB:`, err);\n }\n }\n\n logQueueStats() {\n if (this.debug)\n this.logger.info(\n `[STATS] Queue Size: ${this.queue.length}, Failed Events: ${this.failedEvents.size}`,\n );\n }\n}\n"],"names":[],"mappings":";;AAyBO,MAAM,
|
|
1
|
+
{"version":3,"file":"EventBatchProcessor.cjs.js","sources":["../../src/domain/EventBatchProcessor.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { LoggerService } from '@backstage/backend-plugin-api';\nimport { EventDatabase } from '../database/event-database';\nimport { Event } from '../models/Event';\n\nexport type ProcessorConfigOptions = {\n debug?: boolean;\n batchSize?: number;\n maxRetries?: number;\n batchInterval?: number;\n};\nexport class EventBatchProcessor {\n private readonly queue: Event[];\n private processing: boolean;\n private readonly batchSize: number;\n private readonly batchInterval: number;\n private readonly maxRetries: number;\n private readonly failedEvents: any;\n private readonly database: EventDatabase;\n private readonly logger: LoggerService;\n private readonly debug: boolean;\n\n constructor(\n database: EventDatabase,\n logger: LoggerService,\n options: ProcessorConfigOptions,\n ) {\n this.queue = [];\n this.logger = logger;\n this.processing = false;\n this.database = database;\n this.failedEvents = new Map();\n this.debug = options.debug || false;\n\n const { batchSize = 5, maxRetries = 3, batchInterval = 2000 } = options;\n\n this.batchSize = batchSize;\n this.maxRetries = maxRetries;\n this.batchInterval = batchInterval;\n\n setInterval(() => this.processEvents(), this.batchInterval);\n setInterval(() => this.logQueueStats(), 5000);\n }\n\n addEvent(event: Event) {\n if (\n !this.queue.some((existingEvent: Event) => existingEvent.id === event.id)\n ) {\n this.logger.info(`[QUEUE] Event added: ${JSON.stringify(event)}`);\n this.queue.push({ ...event, toJSON: event.toJSON });\n }\n }\n\n async processEvents() {\n if (this.processing || this.queue.length === 0) return;\n this.processing = true;\n\n const batch = this.queue.splice(0, this.batchSize);\n try {\n await this.database.insertEvents(batch);\n this.logger.info(`[SUCCESS] Inserted ${batch.length} events`);\n batch.forEach((event: Event) => this.failedEvents.delete(event.id));\n } catch (error) {\n this.logger.error(`[ERROR] Batch insert failed:`, error);\n batch.forEach(event =>\n this.retryOrStoreFailedEvent(event, error.message),\n );\n }\n\n this.processing = false;\n }\n\n async retryOrStoreFailedEvent(event: Event, errorMessage: string) {\n const retries = this.failedEvents.get(event.id) || 0;\n if (retries >= this.maxRetries) {\n this.logger.error(\n `[DROPPED] Event permanently failed, storing in DB: ${JSON.stringify(\n event.toJSON(),\n )}`,\n );\n await this.storeFailedEvent(event, errorMessage);\n return;\n }\n\n this.failedEvents.set(event.id, retries + 1);\n this.queue.push(event);\n this.logger.warn(\n `[RETRY] Event re-added to queue (Attempt ${\n retries + 1\n }): ${JSON.stringify(event.toJSON())})`,\n );\n }\n\n async storeFailedEvent(event: Event, errorMessage: string) {\n try {\n await this.database.insertFailedEvent(\n JSON.stringify(event),\n errorMessage,\n this.maxRetries,\n );\n this.logger.warn(`[FAILED-LOGGED] Event stored in DB:`, event.toJSON());\n } catch (err) {\n this.logger.error(`[DB-ERROR] Failed to store event in DB:`, err);\n }\n }\n\n logQueueStats() {\n if (this.debug)\n this.logger.info(\n `[STATS] Queue Size: ${this.queue.length}, Failed Events: ${this.failedEvents.size}`,\n );\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Event.cjs.js","sources":["../../src/models/Event.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { v4 as uuidv4 } from 'uuid';\nimport {\n AnalyticsContextValue,\n AnalyticsEvent,\n AnalyticsEventAttributes,\n} from '@backstage/core-plugin-api';\nimport { normalizeToUTCISO } from '../utils/date';\n\nexport type EventType = {\n user_ref: string;\n plugin_id: string;\n action: string;\n context: AnalyticsContextValue | string;\n subject: string;\n value: number | undefined;\n attributes: AnalyticsEventAttributes | string;\n created_at: string;\n};\n\nexport class Event {\n public readonly id: string;\n public readonly user_ref?: string;\n public readonly plugin_id?: string;\n public readonly action: string;\n public readonly context: AnalyticsContextValue | string;\n public readonly subject: string;\n public readonly value: number | undefined;\n public readonly attributes: AnalyticsEventAttributes | string;\n public readonly created_at: string;\n\n constructor(event: AnalyticsEvent, isJson: boolean = true) {\n this.id = uuidv4();\n this.user_ref = event.context?.userName as string;\n this.plugin_id = event.context?.pluginId;\n this.action = event.action;\n this.subject = event.subject;\n this.value = event.value;\n const rawTimestamp =\n (event.context?.timestamp as string) || new Date().toISOString();\n this.created_at = normalizeToUTCISO(\n typeof rawTimestamp === 'string' ? rawTimestamp : new Date(),\n );\n\n // Handle type-based conversion\n this.context = isJson ? event.context : JSON.stringify(event.context ?? {});\n this.attributes = isJson\n ? event.attributes ?? {}\n : JSON.stringify(event.attributes ?? {});\n }\n\n toJSON() {\n return {\n user_ref: this.user_ref,\n plugin_id: this.plugin_id,\n action: this.action,\n context: this.context,\n subject: this.subject,\n attributes: this.attributes,\n created_at: this.created_at,\n value: this.value,\n };\n }\n}\n"],"names":["uuidv4","normalizeToUTCISO"],"mappings":";;;;;AAkCO,MAAM,
|
|
1
|
+
{"version":3,"file":"Event.cjs.js","sources":["../../src/models/Event.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { v4 as uuidv4 } from 'uuid';\nimport {\n AnalyticsContextValue,\n AnalyticsEvent,\n AnalyticsEventAttributes,\n} from '@backstage/core-plugin-api';\nimport { normalizeToUTCISO } from '../utils/date';\n\nexport type EventType = {\n user_ref: string;\n plugin_id: string;\n action: string;\n context: AnalyticsContextValue | string;\n subject: string;\n value: number | undefined;\n attributes: AnalyticsEventAttributes | string;\n created_at: string;\n};\n\nexport class Event {\n public readonly id: string;\n public readonly user_ref?: string;\n public readonly plugin_id?: string;\n public readonly action: string;\n public readonly context: AnalyticsContextValue | string;\n public readonly subject: string;\n public readonly value: number | undefined;\n public readonly attributes: AnalyticsEventAttributes | string;\n public readonly created_at: string;\n\n constructor(event: AnalyticsEvent, isJson: boolean = true) {\n this.id = uuidv4();\n this.user_ref = event.context?.userName as string;\n this.plugin_id = event.context?.pluginId;\n this.action = event.action;\n this.subject = event.subject;\n this.value = event.value;\n const rawTimestamp =\n (event.context?.timestamp as string) || new Date().toISOString();\n this.created_at = normalizeToUTCISO(\n typeof rawTimestamp === 'string' ? rawTimestamp : new Date(),\n );\n\n // Handle type-based conversion\n this.context = isJson ? event.context : JSON.stringify(event.context ?? {});\n this.attributes = isJson\n ? event.attributes ?? {}\n : JSON.stringify(event.attributes ?? {});\n }\n\n toJSON() {\n return {\n user_ref: this.user_ref,\n plugin_id: this.plugin_id,\n action: this.action,\n context: this.context,\n subject: this.subject,\n attributes: this.attributes,\n created_at: this.created_at,\n value: this.value,\n };\n }\n}\n"],"names":["uuidv4","normalizeToUTCISO"],"mappings":";;;;;AAkCO,MAAM,KAAA,CAAM;AAAA,EACD,EAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,KAAA;AAAA,EACA,UAAA;AAAA,EACA,UAAA;AAAA,EAEhB,WAAA,CAAY,KAAA,EAAuB,MAAA,GAAkB,IAAA,EAAM;AACzD,IAAA,IAAA,CAAK,KAAKA,OAAA,EAAO;AACjB,IAAA,IAAA,CAAK,QAAA,GAAW,MAAM,OAAA,EAAS,QAAA;AAC/B,IAAA,IAAA,CAAK,SAAA,GAAY,MAAM,OAAA,EAAS,QAAA;AAChC,IAAA,IAAA,CAAK,SAAS,KAAA,CAAM,MAAA;AACpB,IAAA,IAAA,CAAK,UAAU,KAAA,CAAM,OAAA;AACrB,IAAA,IAAA,CAAK,QAAQ,KAAA,CAAM,KAAA;AACnB,IAAA,MAAM,eACH,KAAA,CAAM,OAAA,EAAS,8BAAwB,IAAI,IAAA,IAAO,WAAA,EAAY;AACjE,IAAA,IAAA,CAAK,UAAA,GAAaC,sBAAA;AAAA,MAChB,OAAO,YAAA,KAAiB,QAAA,GAAW,YAAA,uBAAmB,IAAA;AAAK,KAC7D;AAGA,IAAA,IAAA,CAAK,OAAA,GAAU,SAAS,KAAA,CAAM,OAAA,GAAU,KAAK,SAAA,CAAU,KAAA,CAAM,OAAA,IAAW,EAAE,CAAA;AAC1E,IAAA,IAAA,CAAK,UAAA,GAAa,MAAA,GACd,KAAA,CAAM,UAAA,IAAc,EAAC,GACrB,IAAA,CAAK,SAAA,CAAU,KAAA,CAAM,UAAA,IAAc,EAAE,CAAA;AAAA,EAC3C;AAAA,EAEA,MAAA,GAAS;AACP,IAAA,OAAO;AAAA,MACL,UAAU,IAAA,CAAK,QAAA;AAAA,MACf,WAAW,IAAA,CAAK,SAAA;AAAA,MAChB,QAAQ,IAAA,CAAK,MAAA;AAAA,MACb,SAAS,IAAA,CAAK,OAAA;AAAA,MACd,SAAS,IAAA,CAAK,OAAA;AAAA,MACd,YAAY,IAAA,CAAK,UAAA;AAAA,MACjB,YAAY,IAAA,CAAK,UAAA;AAAA,MACjB,OAAO,IAAA,CAAK;AAAA,KACd;AAAA,EACF;AACF;;;;"}
|
package/dist/plugin.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { adoptionInsightsEventsReadPermission } from '@red-hat-developer-hub/backstage-plugin-adoption-insights-common';\nimport { createRouter } from './router';\nimport { migrate } from './database/migration';\nimport { DatabaseFactory } from './database/DatabaseFactory';\nimport { EventBatchProcessor } from './domain/EventBatchProcessor';\nimport EventApiController from './controllers/EventApiController';\nimport { schedulePartition } from './database/partition';\nimport { getConfigurationOptions } from './utils/config';\n\n/**\n * adoptionInsightsPlugin backend plugin\n *\n * @public\n */\nexport const adoptionInsightsPlugin = createBackendPlugin({\n pluginId: 'adoption-insights',\n register(env) {\n env.registerInit({\n deps: {\n auditor: coreServices.auditor,\n config: coreServices.rootConfig,\n logger: coreServices.logger,\n httpAuth: coreServices.httpAuth,\n httpRouter: coreServices.httpRouter,\n database: coreServices.database,\n scheduler: coreServices.scheduler,\n permissions: coreServices.permissions,\n permissionsRegistry: coreServices.permissionsRegistry,\n },\n async init({\n auditor,\n config,\n logger,\n httpAuth,\n httpRouter,\n database,\n scheduler,\n permissions,\n permissionsRegistry,\n }) {\n // Register plugin permission\n permissionsRegistry.addPermissions([\n adoptionInsightsEventsReadPermission,\n ]);\n\n // Queue configuration\n const options = getConfigurationOptions(config);\n const client = await database.getClient();\n const db = DatabaseFactory.getDatabase(client, logger);\n const processor = new EventBatchProcessor(db, logger, options);\n const eventApiController = new EventApiController(\n db,\n processor,\n config,\n auditor,\n );\n\n // Migrate database\n await migrate(database);\n\n // Schedule partition creation\n if (db.isPartitionSupported()) {\n schedulePartition(client, { logger, scheduler });\n }\n\n httpRouter.use(\n await createRouter({\n httpAuth,\n permissions,\n eventApiController,\n }),\n );\n\n httpRouter.addAuthPolicy({\n path: '/health',\n allow: 'unauthenticated',\n });\n },\n });\n },\n});\n"],"names":["createBackendPlugin","coreServices","config","adoptionInsightsEventsReadPermission","getConfigurationOptions","DatabaseFactory","EventBatchProcessor","EventApiController","migrate","schedulePartition","createRouter"],"mappings":";;;;;;;;;;;;AAiCO,MAAM,yBAAyBA,
|
|
1
|
+
{"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { adoptionInsightsEventsReadPermission } from '@red-hat-developer-hub/backstage-plugin-adoption-insights-common';\nimport { createRouter } from './router';\nimport { migrate } from './database/migration';\nimport { DatabaseFactory } from './database/DatabaseFactory';\nimport { EventBatchProcessor } from './domain/EventBatchProcessor';\nimport EventApiController from './controllers/EventApiController';\nimport { schedulePartition } from './database/partition';\nimport { getConfigurationOptions } from './utils/config';\n\n/**\n * adoptionInsightsPlugin backend plugin\n *\n * @public\n */\nexport const adoptionInsightsPlugin = createBackendPlugin({\n pluginId: 'adoption-insights',\n register(env) {\n env.registerInit({\n deps: {\n auditor: coreServices.auditor,\n config: coreServices.rootConfig,\n logger: coreServices.logger,\n httpAuth: coreServices.httpAuth,\n httpRouter: coreServices.httpRouter,\n database: coreServices.database,\n scheduler: coreServices.scheduler,\n permissions: coreServices.permissions,\n permissionsRegistry: coreServices.permissionsRegistry,\n },\n async init({\n auditor,\n config,\n logger,\n httpAuth,\n httpRouter,\n database,\n scheduler,\n permissions,\n permissionsRegistry,\n }) {\n // Register plugin permission\n permissionsRegistry.addPermissions([\n adoptionInsightsEventsReadPermission,\n ]);\n\n // Queue configuration\n const options = getConfigurationOptions(config);\n const client = await database.getClient();\n const db = DatabaseFactory.getDatabase(client, logger);\n const processor = new EventBatchProcessor(db, logger, options);\n const eventApiController = new EventApiController(\n db,\n processor,\n config,\n auditor,\n );\n\n // Migrate database\n await migrate(database);\n\n // Schedule partition creation\n if (db.isPartitionSupported()) {\n schedulePartition(client, { logger, scheduler });\n }\n\n httpRouter.use(\n await createRouter({\n httpAuth,\n permissions,\n eventApiController,\n }),\n );\n\n httpRouter.addAuthPolicy({\n path: '/health',\n allow: 'unauthenticated',\n });\n },\n });\n },\n});\n"],"names":["createBackendPlugin","coreServices","config","adoptionInsightsEventsReadPermission","getConfigurationOptions","DatabaseFactory","EventBatchProcessor","EventApiController","migrate","schedulePartition","createRouter"],"mappings":";;;;;;;;;;;;AAiCO,MAAM,yBAAyBA,oCAAA,CAAoB;AAAA,EACxD,QAAA,EAAU,mBAAA;AAAA,EACV,SAAS,GAAA,EAAK;AACZ,IAAA,GAAA,CAAI,YAAA,CAAa;AAAA,MACf,IAAA,EAAM;AAAA,QACJ,SAASC,6BAAA,CAAa,OAAA;AAAA,QACtB,QAAQA,6BAAA,CAAa,UAAA;AAAA,QACrB,QAAQA,6BAAA,CAAa,MAAA;AAAA,QACrB,UAAUA,6BAAA,CAAa,QAAA;AAAA,QACvB,YAAYA,6BAAA,CAAa,UAAA;AAAA,QACzB,UAAUA,6BAAA,CAAa,QAAA;AAAA,QACvB,WAAWA,6BAAA,CAAa,SAAA;AAAA,QACxB,aAAaA,6BAAA,CAAa,WAAA;AAAA,QAC1B,qBAAqBA,6BAAA,CAAa;AAAA,OACpC;AAAA,MACA,MAAM,IAAA,CAAK;AAAA,QACT,OAAA;AAAA,gBACAC,QAAA;AAAA,QACA,MAAA;AAAA,QACA,QAAA;AAAA,QACA,UAAA;AAAA,QACA,QAAA;AAAA,QACA,SAAA;AAAA,QACA,WAAA;AAAA,QACA;AAAA,OACF,EAAG;AAED,QAAA,mBAAA,CAAoB,cAAA,CAAe;AAAA,UACjCC;AAAA,SACD,CAAA;AAGD,QAAA,MAAM,OAAA,GAAUC,+BAAwBF,QAAM,CAAA;AAC9C,QAAA,MAAM,MAAA,GAAS,MAAM,QAAA,CAAS,SAAA,EAAU;AACxC,QAAA,MAAM,EAAA,GAAKG,+BAAA,CAAgB,WAAA,CAAY,MAAA,EAAQ,MAAM,CAAA;AACrD,QAAA,MAAM,SAAA,GAAY,IAAIC,uCAAA,CAAoB,EAAA,EAAI,QAAQ,OAAO,CAAA;AAC7D,QAAA,MAAM,qBAAqB,IAAIC,0BAAA;AAAA,UAC7B,EAAA;AAAA,UACA,SAAA;AAAA,UACAL,QAAA;AAAA,UACA;AAAA,SACF;AAGA,QAAA,MAAMM,kBAAQ,QAAQ,CAAA;AAGtB,QAAA,IAAI,EAAA,CAAG,sBAAqB,EAAG;AAC7B,UAAAC,2BAAA,CAAkB,MAAA,EAAQ,EAAE,MAAA,EAAQ,SAAA,EAAW,CAAA;AAAA,QACjD;AAEA,QAAA,UAAA,CAAW,GAAA;AAAA,UACT,MAAMC,mBAAA,CAAa;AAAA,YACjB,QAAA;AAAA,YACA,WAAA;AAAA,YACA;AAAA,WACD;AAAA,SACH;AAEA,QAAA,UAAA,CAAW,aAAA,CAAc;AAAA,UACvB,IAAA,EAAM,SAAA;AAAA,UACN,KAAA,EAAO;AAAA,SACR,CAAA;AAAA,MACH;AAAA,KACD,CAAA;AAAA,EACH;AACF,CAAC;;;;"}
|
package/dist/router.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.cjs.js","sources":["../src/router.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 HttpAuthService,\n PermissionsService,\n} from '@backstage/backend-plugin-api';\nimport { NotAllowedError } from '@backstage/errors';\nimport { AuthorizeResult } from '@backstage/plugin-permission-common';\nimport { adoptionInsightsEventsReadPermission } from '@red-hat-developer-hub/backstage-plugin-adoption-insights-common';\nimport express, { Request, Response } from 'express';\nimport Router from 'express-promise-router';\n\nimport EventApiController from './controllers/EventApiController';\nimport { QueryParams } from './types/event-request';\n\nexport async function createRouter({\n httpAuth,\n permissions,\n eventApiController,\n}: {\n httpAuth: HttpAuthService;\n permissions: PermissionsService;\n eventApiController: EventApiController;\n}): Promise<express.Router> {\n const router = Router();\n\n router.use(express.json());\n\n const authorizeUser = async (\n req: Request<{}, {}, {}, QueryParams>,\n ): Promise<void> => {\n const credentials = await httpAuth.credentials(req, { allow: ['user'] });\n const decision = (\n await permissions.authorize(\n [{ permission: adoptionInsightsEventsReadPermission }],\n { credentials },\n )\n )[0];\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError('Unauthorized');\n }\n };\n const getUserEntityRef = async (req: Request): Promise<string> => {\n const credentials = await httpAuth.credentials(req, {\n allow: ['user'],\n });\n return credentials.principal.userEntityRef;\n };\n router.use(async (req, _, next) => {\n await getUserEntityRef(req);\n next();\n });\n\n router.get(\n '/events',\n async (req: Request<{}, {}, {}, QueryParams>, res: Response) => {\n await authorizeUser(req);\n return eventApiController.getInsights(req, res);\n },\n );\n\n router.post('/events', async (req, res) => {\n return eventApiController.trackEvents(req, res);\n });\n\n router.get('/health', (_, response) => {\n response.json({ status: 'ok' });\n });\n\n return router;\n}\n"],"names":["Router","express","adoptionInsightsEventsReadPermission","AuthorizeResult","NotAllowedError"],"mappings":";;;;;;;;;;;;;AA4BA,eAAsB,
|
|
1
|
+
{"version":3,"file":"router.cjs.js","sources":["../src/router.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 HttpAuthService,\n PermissionsService,\n} from '@backstage/backend-plugin-api';\nimport { NotAllowedError } from '@backstage/errors';\nimport { AuthorizeResult } from '@backstage/plugin-permission-common';\nimport { adoptionInsightsEventsReadPermission } from '@red-hat-developer-hub/backstage-plugin-adoption-insights-common';\nimport express, { Request, Response } from 'express';\nimport Router from 'express-promise-router';\n\nimport EventApiController from './controllers/EventApiController';\nimport { QueryParams } from './types/event-request';\n\nexport async function createRouter({\n httpAuth,\n permissions,\n eventApiController,\n}: {\n httpAuth: HttpAuthService;\n permissions: PermissionsService;\n eventApiController: EventApiController;\n}): Promise<express.Router> {\n const router = Router();\n\n router.use(express.json());\n\n const authorizeUser = async (\n req: Request<{}, {}, {}, QueryParams>,\n ): Promise<void> => {\n const credentials = await httpAuth.credentials(req, { allow: ['user'] });\n const decision = (\n await permissions.authorize(\n [{ permission: adoptionInsightsEventsReadPermission }],\n { credentials },\n )\n )[0];\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError('Unauthorized');\n }\n };\n const getUserEntityRef = async (req: Request): Promise<string> => {\n const credentials = await httpAuth.credentials(req, {\n allow: ['user'],\n });\n return credentials.principal.userEntityRef;\n };\n router.use(async (req, _, next) => {\n await getUserEntityRef(req);\n next();\n });\n\n router.get(\n '/events',\n async (req: Request<{}, {}, {}, QueryParams>, res: Response) => {\n await authorizeUser(req);\n return eventApiController.getInsights(req, res);\n },\n );\n\n router.post('/events', async (req, res) => {\n return eventApiController.trackEvents(req, res);\n });\n\n router.get('/health', (_, response) => {\n response.json({ status: 'ok' });\n });\n\n return router;\n}\n"],"names":["Router","express","adoptionInsightsEventsReadPermission","AuthorizeResult","NotAllowedError"],"mappings":";;;;;;;;;;;;;AA4BA,eAAsB,YAAA,CAAa;AAAA,EACjC,QAAA;AAAA,EACA,WAAA;AAAA,EACA;AACF,CAAA,EAI4B;AAC1B,EAAA,MAAM,SAASA,uBAAA,EAAO;AAEtB,EAAA,MAAA,CAAO,GAAA,CAAIC,wBAAA,CAAQ,IAAA,EAAM,CAAA;AAEzB,EAAA,MAAM,aAAA,GAAgB,OACpB,GAAA,KACkB;AAClB,IAAA,MAAM,WAAA,GAAc,MAAM,QAAA,CAAS,WAAA,CAAY,GAAA,EAAK,EAAE,KAAA,EAAO,CAAC,MAAM,CAAA,EAAG,CAAA;AACvE,IAAA,MAAM,QAAA,GAAA,CACJ,MAAM,WAAA,CAAY,SAAA;AAAA,MAChB,CAAC,EAAE,UAAA,EAAYC,0EAAA,EAAsC,CAAA;AAAA,MACrD,EAAE,WAAA;AAAY,OAEhB,CAAC,CAAA;AAEH,IAAA,IAAI,QAAA,CAAS,MAAA,KAAWC,sCAAA,CAAgB,IAAA,EAAM;AAC5C,MAAA,MAAM,IAAIC,uBAAgB,cAAc,CAAA;AAAA,IAC1C;AAAA,EACF,CAAA;AACA,EAAA,MAAM,gBAAA,GAAmB,OAAO,GAAA,KAAkC;AAChE,IAAA,MAAM,WAAA,GAAc,MAAM,QAAA,CAAS,WAAA,CAAY,GAAA,EAAK;AAAA,MAClD,KAAA,EAAO,CAAC,MAAM;AAAA,KACf,CAAA;AACD,IAAA,OAAO,YAAY,SAAA,CAAU,aAAA;AAAA,EAC/B,CAAA;AACA,EAAA,MAAA,CAAO,GAAA,CAAI,OAAO,GAAA,EAAK,CAAA,EAAG,IAAA,KAAS;AACjC,IAAA,MAAM,iBAAiB,GAAG,CAAA;AAC1B,IAAA,IAAA,EAAK;AAAA,EACP,CAAC,CAAA;AAED,EAAA,MAAA,CAAO,GAAA;AAAA,IACL,SAAA;AAAA,IACA,OAAO,KAAuC,GAAA,KAAkB;AAC9D,MAAA,MAAM,cAAc,GAAG,CAAA;AACvB,MAAA,OAAO,kBAAA,CAAmB,WAAA,CAAY,GAAA,EAAK,GAAG,CAAA;AAAA,IAChD;AAAA,GACF;AAEA,EAAA,MAAA,CAAO,IAAA,CAAK,SAAA,EAAW,OAAO,GAAA,EAAK,GAAA,KAAQ;AACzC,IAAA,OAAO,kBAAA,CAAmB,WAAA,CAAY,GAAA,EAAK,GAAG,CAAA;AAAA,EAChD,CAAC,CAAA;AAED,EAAA,MAAA,CAAO,GAAA,CAAI,SAAA,EAAW,CAAC,CAAA,EAAG,QAAA,KAAa;AACrC,IAAA,QAAA,CAAS,IAAA,CAAK,EAAE,MAAA,EAAQ,IAAA,EAAM,CAAA;AAAA,EAChC,CAAC,CAAA;AAED,EAAA,OAAO,MAAA;AACT;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-request.cjs.js","sources":["../../src/types/event-request.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 */\nexport const QUERY_TYPES = [\n 'total_users',\n 'active_users',\n 'top_plugins',\n 'top_templates',\n 'top_techdocs',\n 'top_searches',\n 'top_catalog_entities',\n] as const;\n\nexport type QueryType = (typeof QUERY_TYPES)[number];\n\nexport interface QueryParams {\n type: QueryType;\n start_date: string;\n end_date: string;\n limit?: string;\n kind?: string;\n format?: string;\n licensedUsers?: number;\n}\n"],"names":[],"mappings":";;AAeO,MAAM,
|
|
1
|
+
{"version":3,"file":"event-request.cjs.js","sources":["../../src/types/event-request.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 */\nexport const QUERY_TYPES = [\n 'total_users',\n 'active_users',\n 'top_plugins',\n 'top_templates',\n 'top_techdocs',\n 'top_searches',\n 'top_catalog_entities',\n] as const;\n\nexport type QueryType = (typeof QUERY_TYPES)[number];\n\nexport interface QueryParams {\n type: QueryType;\n start_date: string;\n end_date: string;\n limit?: string;\n kind?: string;\n format?: string;\n licensedUsers?: number;\n}\n"],"names":[],"mappings":";;AAeO,MAAM,WAAA,GAAc;AAAA,EACzB,aAAA;AAAA,EACA,cAAA;AAAA,EACA,aAAA;AAAA,EACA,eAAA;AAAA,EACA,cAAA;AAAA,EACA,cAAA;AAAA,EACA;AACF;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.cjs.js","sources":["../../src/utils/config.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { RootConfigService } from '@backstage/backend-plugin-api';\nimport { ProcessorConfigOptions } from '../domain/EventBatchProcessor';\n\nexport const getConfigurationOptions = (\n config: RootConfigService,\n): ProcessorConfigOptions => {\n const batchSize =\n config.getOptionalNumber('app.analytics.adoptionInsights.maxBufferSize') ||\n 5;\n\n const batchInterval =\n config.getOptionalNumber('app.analytics.adoptionInsights.flushInterval') ||\n 2000;\n\n const debug =\n config.getOptionalBoolean('app.analytics.adoptionInsights.debug') || false;\n\n return {\n debug,\n batchSize,\n batchInterval,\n };\n};\nexport const getLicensedUsersCount = (config: RootConfigService) => {\n return (\n config.getOptionalNumber('app.analytics.adoptionInsights.licensedUsers') ||\n 100\n );\n};\n"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"config.cjs.js","sources":["../../src/utils/config.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { RootConfigService } from '@backstage/backend-plugin-api';\nimport { ProcessorConfigOptions } from '../domain/EventBatchProcessor';\n\nexport const getConfigurationOptions = (\n config: RootConfigService,\n): ProcessorConfigOptions => {\n const batchSize =\n config.getOptionalNumber('app.analytics.adoptionInsights.maxBufferSize') ||\n 5;\n\n const batchInterval =\n config.getOptionalNumber('app.analytics.adoptionInsights.flushInterval') ||\n 2000;\n\n const debug =\n config.getOptionalBoolean('app.analytics.adoptionInsights.debug') || false;\n\n return {\n debug,\n batchSize,\n batchInterval,\n };\n};\nexport const getLicensedUsersCount = (config: RootConfigService) => {\n return (\n config.getOptionalNumber('app.analytics.adoptionInsights.licensedUsers') ||\n 100\n );\n};\n"],"names":[],"mappings":";;AAkBO,MAAM,uBAAA,GAA0B,CACrC,MAAA,KAC2B;AAC3B,EAAA,MAAM,SAAA,GACJ,MAAA,CAAO,iBAAA,CAAkB,8CAA8C,CAAA,IACvE,CAAA;AAEF,EAAA,MAAM,aAAA,GACJ,MAAA,CAAO,iBAAA,CAAkB,8CAA8C,CAAA,IACvE,GAAA;AAEF,EAAA,MAAM,KAAA,GACJ,MAAA,CAAO,kBAAA,CAAmB,sCAAsC,CAAA,IAAK,KAAA;AAEvE,EAAA,OAAO;AAAA,IACL,KAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GACF;AACF;AACO,MAAM,qBAAA,GAAwB,CAAC,MAAA,KAA8B;AAClE,EAAA,OACE,MAAA,CAAO,iBAAA,CAAkB,8CAA8C,CAAA,IACvE,GAAA;AAEJ;;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"date.cjs.js","sources":["../../src/utils/date.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { DateTime } from 'luxon';\nimport { Grouping } from '../types/event';\n\nexport const toStartOfDayUTC = (dateString: string, timezone = 'UTC') =>\n DateTime.fromFormat(dateString, 'yyyy-MM-dd', { zone: timezone })\n .startOf('day')\n .toUTC()\n .toISO();\n\nexport const toEndOfDayUTC = (dateString: string, timezone = 'UTC') =>\n DateTime.fromFormat(dateString, 'yyyy-MM-dd', { zone: timezone })\n .endOf('day')\n .toUTC()\n .toISO();\n\nexport const calculateDateRange = (\n start_date: string,\n end_date: string,\n): number => {\n const start = DateTime.fromISO(start_date, { zone: 'UTC' });\n const end = DateTime.fromISO(end_date, { zone: 'UTC' });\n\n return Math.floor(end.diff(start, 'days').days);\n};\n\nexport const isSameMonth = (start_date: string, end_date: string): boolean => {\n const start = DateTime.fromISO(start_date, { zone: 'UTC' });\n const end = DateTime.fromISO(end_date, { zone: 'UTC' });\n\n return start.hasSame(end, 'month');\n};\n\nexport const getDateGroupingType = (dateDiff: number): Grouping => {\n if (dateDiff === 0) return 'hourly';\n if (dateDiff <= 7) return 'daily';\n if (dateDiff <= 30) return 'weekly';\n return 'monthly';\n};\n\nexport const hasZFormat = (dateStr: string): boolean => {\n return dateStr.includes('Z') || dateStr.includes('T');\n};\n\n/**\n * Normalize any date string or Date to UTC ISO string (e.g. 2026-03-11T16:17:46.540Z).\n * Use this before storing timestamps in the database so they are consistent.\n */\nexport const normalizeToUTCISO = (date: string | Date): string => {\n if (date instanceof Date) {\n return date.toISOString();\n }\n const s = String(date).trim();\n // Luxon fromISO handles ISO with Z or offset (e.g. -05:00)\n let dt = DateTime.fromISO(s, { setZone: true });\n if (dt.isValid) {\n return dt.toUTC().toISO() ?? s;\n }\n // Try \"yyyy-MM-dd HH:mm:ss.SSS -0500\" or \"yyyy-MM-dd HH:mm:ss -0500\" style\n dt = DateTime.fromFormat(s, 'yyyy-MM-dd HH:mm:ss.SSS ZZZ', { setZone: true });\n if (dt.isValid) {\n return dt.toUTC().toISO() ?? s;\n }\n dt = DateTime.fromFormat(s, 'yyyy-MM-dd HH:mm:ss ZZZ', { setZone: true });\n if (dt.isValid) {\n return dt.toUTC().toISO() ?? s;\n }\n dt = DateTime.fromFormat(s, 'yyyy-MM-dd HH:mm:ss', { zone: 'UTC' });\n if (dt.isValid) {\n return dt.toISO() ?? s;\n }\n // Return original when unparseable: do not substitute current time, which would\n // silently corrupt created_at and misplace events in partitions/date-range queries.\n return s;\n};\n\nexport const convertToTargetTimezone = (\n date: string | Date,\n timeZone: string = new Intl.DateTimeFormat().resolvedOptions().timeZone,\n) => {\n const dateString = date instanceof Date ? date.toISOString() : date;\n\n const isoParsed = DateTime.fromISO(dateString, { setZone: true });\n\n if (isoParsed.isValid) {\n return isoParsed.setZone(timeZone).toISO();\n }\n\n // If not valid ISO, try parsing as 'yyyy-MM-dd HH:mm:ss' in UTC\n const fallback = DateTime.fromFormat(dateString, 'yyyy-MM-dd HH:mm:ss', {\n zone: 'UTC',\n });\n\n if (fallback.isValid) {\n return fallback.setZone(timeZone).toISO();\n }\n\n // Last resort: return the original date\n console.warn('Unable to parse date:', date);\n return date;\n};\n\nexport const getTimeZoneOffsetString = (\n timeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone,\n) => {\n const now = DateTime.now().setZone(timeZone);\n return now.toFormat('ZZ');\n};\n"],"names":["DateTime"],"mappings":";;;;AAkBO,MAAM,kBAAkB,CAAC,UAAA,EAAoB,WAAW,
|
|
1
|
+
{"version":3,"file":"date.cjs.js","sources":["../../src/utils/date.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { DateTime } from 'luxon';\nimport { Grouping } from '../types/event';\n\nexport const toStartOfDayUTC = (dateString: string, timezone = 'UTC') =>\n DateTime.fromFormat(dateString, 'yyyy-MM-dd', { zone: timezone })\n .startOf('day')\n .toUTC()\n .toISO();\n\nexport const toEndOfDayUTC = (dateString: string, timezone = 'UTC') =>\n DateTime.fromFormat(dateString, 'yyyy-MM-dd', { zone: timezone })\n .endOf('day')\n .toUTC()\n .toISO();\n\nexport const calculateDateRange = (\n start_date: string,\n end_date: string,\n): number => {\n const start = DateTime.fromISO(start_date, { zone: 'UTC' });\n const end = DateTime.fromISO(end_date, { zone: 'UTC' });\n\n return Math.floor(end.diff(start, 'days').days);\n};\n\nexport const isSameMonth = (start_date: string, end_date: string): boolean => {\n const start = DateTime.fromISO(start_date, { zone: 'UTC' });\n const end = DateTime.fromISO(end_date, { zone: 'UTC' });\n\n return start.hasSame(end, 'month');\n};\n\nexport const getDateGroupingType = (dateDiff: number): Grouping => {\n if (dateDiff === 0) return 'hourly';\n if (dateDiff <= 7) return 'daily';\n if (dateDiff <= 30) return 'weekly';\n return 'monthly';\n};\n\nexport const hasZFormat = (dateStr: string): boolean => {\n return dateStr.includes('Z') || dateStr.includes('T');\n};\n\n/**\n * Normalize any date string or Date to UTC ISO string (e.g. 2026-03-11T16:17:46.540Z).\n * Use this before storing timestamps in the database so they are consistent.\n */\nexport const normalizeToUTCISO = (date: string | Date): string => {\n if (date instanceof Date) {\n return date.toISOString();\n }\n const s = String(date).trim();\n // Luxon fromISO handles ISO with Z or offset (e.g. -05:00)\n let dt = DateTime.fromISO(s, { setZone: true });\n if (dt.isValid) {\n return dt.toUTC().toISO() ?? s;\n }\n // Try \"yyyy-MM-dd HH:mm:ss.SSS -0500\" or \"yyyy-MM-dd HH:mm:ss -0500\" style\n dt = DateTime.fromFormat(s, 'yyyy-MM-dd HH:mm:ss.SSS ZZZ', { setZone: true });\n if (dt.isValid) {\n return dt.toUTC().toISO() ?? s;\n }\n dt = DateTime.fromFormat(s, 'yyyy-MM-dd HH:mm:ss ZZZ', { setZone: true });\n if (dt.isValid) {\n return dt.toUTC().toISO() ?? s;\n }\n dt = DateTime.fromFormat(s, 'yyyy-MM-dd HH:mm:ss', { zone: 'UTC' });\n if (dt.isValid) {\n return dt.toISO() ?? s;\n }\n // Return original when unparseable: do not substitute current time, which would\n // silently corrupt created_at and misplace events in partitions/date-range queries.\n return s;\n};\n\nexport const convertToTargetTimezone = (\n date: string | Date,\n timeZone: string = new Intl.DateTimeFormat().resolvedOptions().timeZone,\n) => {\n const dateString = date instanceof Date ? date.toISOString() : date;\n\n const isoParsed = DateTime.fromISO(dateString, { setZone: true });\n\n if (isoParsed.isValid) {\n return isoParsed.setZone(timeZone).toISO();\n }\n\n // If not valid ISO, try parsing as 'yyyy-MM-dd HH:mm:ss' in UTC\n const fallback = DateTime.fromFormat(dateString, 'yyyy-MM-dd HH:mm:ss', {\n zone: 'UTC',\n });\n\n if (fallback.isValid) {\n return fallback.setZone(timeZone).toISO();\n }\n\n // Last resort: return the original date\n console.warn('Unable to parse date:', date);\n return date;\n};\n\nexport const getTimeZoneOffsetString = (\n timeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone,\n) => {\n const now = DateTime.now().setZone(timeZone);\n return now.toFormat('ZZ');\n};\n"],"names":["DateTime"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"partition.cjs.js","sources":["../../src/utils/partition.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 */\nexport const extractOverlappingPartition = (message: string): string => {\n const match = message.match(/would overlap partition \"(.*?)\"$/);\n return match?.[1] || '';\n};\n\nexport const parsePartitionDate = (\n name: string,\n): { year: number; month: number } => {\n const match = name.match(/events_(\\d{4})_(\\d{1,2})/);\n if (!match) throw new Error(`Cannot parse partition name: ${name}`);\n return { year: parseInt(match[1], 10), month: parseInt(match[2], 10) };\n};\n\nexport const isPartitionOverlapError = (error: unknown): boolean => {\n return (\n error !== null &&\n typeof (error as any).message === 'string' &&\n (error as any).message.includes('would overlap partition')\n );\n};\n"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"partition.cjs.js","sources":["../../src/utils/partition.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 */\nexport const extractOverlappingPartition = (message: string): string => {\n const match = message.match(/would overlap partition \"(.*?)\"$/);\n return match?.[1] || '';\n};\n\nexport const parsePartitionDate = (\n name: string,\n): { year: number; month: number } => {\n const match = name.match(/events_(\\d{4})_(\\d{1,2})/);\n if (!match) throw new Error(`Cannot parse partition name: ${name}`);\n return { year: parseInt(match[1], 10), month: parseInt(match[2], 10) };\n};\n\nexport const isPartitionOverlapError = (error: unknown): boolean => {\n return (\n error !== null &&\n typeof (error as any).message === 'string' &&\n (error as any).message.includes('would overlap partition')\n );\n};\n"],"names":[],"mappings":";;AAeO,MAAM,2BAAA,GAA8B,CAAC,OAAA,KAA4B;AACtE,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,KAAA,CAAM,kCAAkC,CAAA;AAC9D,EAAA,OAAO,KAAA,GAAQ,CAAC,CAAA,IAAK,EAAA;AACvB;AAEO,MAAM,kBAAA,GAAqB,CAChC,IAAA,KACoC;AACpC,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,0BAA0B,CAAA;AACnD,EAAA,IAAI,CAAC,KAAA,EAAO,MAAM,IAAI,KAAA,CAAM,CAAA,6BAAA,EAAgC,IAAI,CAAA,CAAE,CAAA;AAClE,EAAA,OAAO,EAAE,IAAA,EAAM,QAAA,CAAS,KAAA,CAAM,CAAC,CAAA,EAAG,EAAE,CAAA,EAAG,KAAA,EAAO,QAAA,CAAS,KAAA,CAAM,CAAC,CAAA,EAAG,EAAE,CAAA,EAAE;AACvE;AAEO,MAAM,uBAAA,GAA0B,CAAC,KAAA,KAA4B;AAClE,EAAA,OACE,KAAA,KAAU,QACV,OAAQ,KAAA,CAAc,YAAY,QAAA,IACjC,KAAA,CAAc,OAAA,CAAQ,QAAA,CAAS,yBAAyB,CAAA;AAE7D;;;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ValidationError.cjs.js","sources":["../../src/validation/ValidationError.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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\nexport class ValidationError extends Error {\n public details: any;\n\n constructor(message: string, details: any) {\n super(message);\n this.name = 'ValidationError';\n this.details = details;\n }\n}\n"],"names":[],"mappings":";;AAgBO,MAAM,wBAAwB,
|
|
1
|
+
{"version":3,"file":"ValidationError.cjs.js","sources":["../../src/validation/ValidationError.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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\nexport class ValidationError extends Error {\n public details: any;\n\n constructor(message: string, details: any) {\n super(message);\n this.name = 'ValidationError';\n this.details = details;\n }\n}\n"],"names":[],"mappings":";;AAgBO,MAAM,wBAAwB,KAAA,CAAM;AAAA,EAClC,OAAA;AAAA,EAEP,WAAA,CAAY,SAAiB,OAAA,EAAc;AACzC,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AACZ,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA;AAAA,EACjB;AACF;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-request.cjs.js","sources":["../../src/validation/event-request.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { z } from 'zod';\nimport { DateTime } from 'luxon';\nimport { QUERY_TYPES } from '../types/event-request';\n\nconst dateRequiredSchema = (fieldName: string) =>\n z.string({\n required_error: `${fieldName} is required. Use YYYY-MM-DD (e.g., 2025-03-02)`,\n });\n\nexport const EventRequestSchema = z\n .object({\n grouping: z.enum(['hourly', 'weekly', 'daily', 'monthly']).optional(),\n start_date: dateRequiredSchema('start_date'),\n end_date: dateRequiredSchema('end_date'),\n timezone: z.string(),\n limit: z.string().regex(/^\\d+$/).transform(Number).optional(),\n kind: z.string().optional(),\n type: z.enum(QUERY_TYPES, {\n errorMap: () => ({\n message: `Invalid type. Allowed values: ${QUERY_TYPES}`,\n }),\n }),\n format: z\n .enum(['csv', 'json'], {\n errorMap: () => ({\n message: 'Invalid format. Allowed values: json, csv',\n }),\n })\n .optional(),\n })\n .superRefine((data, ctx) => {\n const startDate = DateTime.fromISO(data.start_date);\n const endDate = DateTime.fromISO(data.end_date);\n\n if (!startDate.isValid) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n 'Invalid date format for start_date. Expected YYYY-MM-DD (e.g., 2025-03-02)',\n path: ['start_date'],\n });\n }\n\n if (!endDate.isValid) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n 'Invalid date format for end_date. Expected YYYY-MM-DD (e.g., 2025-03-02)',\n path: ['end_date'],\n });\n }\n\n if (startDate.isValid && endDate.isValid && startDate > endDate) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: 'start_date should not be greater than end_date',\n path: ['end_date'],\n });\n }\n });\n"],"names":["z","QUERY_TYPES","DateTime"],"mappings":";;;;;;AAmBA,MAAM,
|
|
1
|
+
{"version":3,"file":"event-request.cjs.js","sources":["../../src/validation/event-request.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { z } from 'zod';\nimport { DateTime } from 'luxon';\nimport { QUERY_TYPES } from '../types/event-request';\n\nconst dateRequiredSchema = (fieldName: string) =>\n z.string({\n required_error: `${fieldName} is required. Use YYYY-MM-DD (e.g., 2025-03-02)`,\n });\n\nexport const EventRequestSchema = z\n .object({\n grouping: z.enum(['hourly', 'weekly', 'daily', 'monthly']).optional(),\n start_date: dateRequiredSchema('start_date'),\n end_date: dateRequiredSchema('end_date'),\n timezone: z.string(),\n limit: z.string().regex(/^\\d+$/).transform(Number).optional(),\n kind: z.string().optional(),\n type: z.enum(QUERY_TYPES, {\n errorMap: () => ({\n message: `Invalid type. Allowed values: ${QUERY_TYPES}`,\n }),\n }),\n format: z\n .enum(['csv', 'json'], {\n errorMap: () => ({\n message: 'Invalid format. Allowed values: json, csv',\n }),\n })\n .optional(),\n })\n .superRefine((data, ctx) => {\n const startDate = DateTime.fromISO(data.start_date);\n const endDate = DateTime.fromISO(data.end_date);\n\n if (!startDate.isValid) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n 'Invalid date format for start_date. Expected YYYY-MM-DD (e.g., 2025-03-02)',\n path: ['start_date'],\n });\n }\n\n if (!endDate.isValid) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n 'Invalid date format for end_date. Expected YYYY-MM-DD (e.g., 2025-03-02)',\n path: ['end_date'],\n });\n }\n\n if (startDate.isValid && endDate.isValid && startDate > endDate) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: 'start_date should not be greater than end_date',\n path: ['end_date'],\n });\n }\n });\n"],"names":["z","QUERY_TYPES","DateTime"],"mappings":";;;;;;AAmBA,MAAM,kBAAA,GAAqB,CAAC,SAAA,KAC1BA,KAAA,CAAE,MAAA,CAAO;AAAA,EACP,cAAA,EAAgB,GAAG,SAAS,CAAA,+CAAA;AAC9B,CAAC,CAAA;AAEI,MAAM,kBAAA,GAAqBA,MAC/B,MAAA,CAAO;AAAA,EACN,QAAA,EAAUA,KAAA,CAAE,IAAA,CAAK,CAAC,QAAA,EAAU,UAAU,OAAA,EAAS,SAAS,CAAC,CAAA,CAAE,QAAA,EAAS;AAAA,EACpE,UAAA,EAAY,mBAAmB,YAAY,CAAA;AAAA,EAC3C,QAAA,EAAU,mBAAmB,UAAU,CAAA;AAAA,EACvC,QAAA,EAAUA,MAAE,MAAA,EAAO;AAAA,EACnB,KAAA,EAAOA,KAAA,CAAE,MAAA,EAAO,CAAE,KAAA,CAAM,OAAO,CAAA,CAAE,SAAA,CAAU,MAAM,CAAA,CAAE,QAAA,EAAS;AAAA,EAC5D,IAAA,EAAMA,KAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAC1B,IAAA,EAAMA,KAAA,CAAE,IAAA,CAAKC,wBAAA,EAAa;AAAA,IACxB,UAAU,OAAO;AAAA,MACf,OAAA,EAAS,iCAAiCA,wBAAW,CAAA;AAAA,KACvD;AAAA,GACD,CAAA;AAAA,EACD,QAAQD,KAAA,CACL,IAAA,CAAK,CAAC,KAAA,EAAO,MAAM,CAAA,EAAG;AAAA,IACrB,UAAU,OAAO;AAAA,MACf,OAAA,EAAS;AAAA,KACX;AAAA,GACD,EACA,QAAA;AACL,CAAC,CAAA,CACA,WAAA,CAAY,CAAC,IAAA,EAAM,GAAA,KAAQ;AAC1B,EAAA,MAAM,SAAA,GAAYE,cAAA,CAAS,OAAA,CAAQ,IAAA,CAAK,UAAU,CAAA;AAClD,EAAA,MAAM,OAAA,GAAUA,cAAA,CAAS,OAAA,CAAQ,IAAA,CAAK,QAAQ,CAAA;AAE9C,EAAA,IAAI,CAAC,UAAU,OAAA,EAAS;AACtB,IAAA,GAAA,CAAI,QAAA,CAAS;AAAA,MACX,IAAA,EAAMF,MAAE,YAAA,CAAa,MAAA;AAAA,MACrB,OAAA,EACE,4EAAA;AAAA,MACF,IAAA,EAAM,CAAC,YAAY;AAAA,KACpB,CAAA;AAAA,EACH;AAEA,EAAA,IAAI,CAAC,QAAQ,OAAA,EAAS;AACpB,IAAA,GAAA,CAAI,QAAA,CAAS;AAAA,MACX,IAAA,EAAMA,MAAE,YAAA,CAAa,MAAA;AAAA,MACrB,OAAA,EACE,0EAAA;AAAA,MACF,IAAA,EAAM,CAAC,UAAU;AAAA,KAClB,CAAA;AAAA,EACH;AAEA,EAAA,IAAI,SAAA,CAAU,OAAA,IAAW,OAAA,CAAQ,OAAA,IAAW,YAAY,OAAA,EAAS;AAC/D,IAAA,GAAA,CAAI,QAAA,CAAS;AAAA,MACX,IAAA,EAAMA,MAAE,YAAA,CAAa,MAAA;AAAA,MACrB,OAAA,EAAS,gDAAA;AAAA,MACT,IAAA,EAAM,CAAC,UAAU;AAAA,KAClB,CAAA;AAAA,EACH;AACF,CAAC;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event.cjs.js","sources":["../../src/validation/event.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { z } from 'zod';\n\nconst JsonObjectSchema = z.record(\n z.union([z.string(), z.boolean(), z.number(), z.undefined()]),\n);\nexport const EventSchema = z.object({\n user_ref: z.string({ required_error: 'User Id is required' }).min(1),\n plugin_id: z.string({ required_error: 'Plugin ID is required' }).min(1),\n action: z.string({ required_error: 'Action is required' }).min(1),\n context: z.union([JsonObjectSchema, z.string()]),\n subject: z.string().optional(),\n attributes: z.union([JsonObjectSchema, z.string()]).optional(),\n value: z.number().optional(),\n});\n\nexport const validateEvent = (event: any) => EventSchema.parse(event);\n"],"names":["z"],"mappings":";;;;AAiBA,MAAM,mBAAmBA,
|
|
1
|
+
{"version":3,"file":"event.cjs.js","sources":["../../src/validation/event.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 { z } from 'zod';\n\nconst JsonObjectSchema = z.record(\n z.union([z.string(), z.boolean(), z.number(), z.undefined()]),\n);\nexport const EventSchema = z.object({\n user_ref: z.string({ required_error: 'User Id is required' }).min(1),\n plugin_id: z.string({ required_error: 'Plugin ID is required' }).min(1),\n action: z.string({ required_error: 'Action is required' }).min(1),\n context: z.union([JsonObjectSchema, z.string()]),\n subject: z.string().optional(),\n attributes: z.union([JsonObjectSchema, z.string()]).optional(),\n value: z.number().optional(),\n});\n\nexport const validateEvent = (event: any) => EventSchema.parse(event);\n"],"names":["z"],"mappings":";;;;AAiBA,MAAM,mBAAmBA,KAAA,CAAE,MAAA;AAAA,EACzBA,KAAA,CAAE,KAAA,CAAM,CAACA,KAAA,CAAE,QAAO,EAAGA,KAAA,CAAE,OAAA,EAAQ,EAAGA,MAAE,MAAA,EAAO,EAAGA,KAAA,CAAE,SAAA,EAAW,CAAC;AAC9D,CAAA;AACO,MAAM,WAAA,GAAcA,MAAE,MAAA,CAAO;AAAA,EAClC,QAAA,EAAUA,MAAE,MAAA,CAAO,EAAE,gBAAgB,qBAAA,EAAuB,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA;AAAA,EACnE,SAAA,EAAWA,MAAE,MAAA,CAAO,EAAE,gBAAgB,uBAAA,EAAyB,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA;AAAA,EACtE,MAAA,EAAQA,MAAE,MAAA,CAAO,EAAE,gBAAgB,oBAAA,EAAsB,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA;AAAA,EAChE,OAAA,EAASA,MAAE,KAAA,CAAM,CAAC,kBAAkBA,KAAA,CAAE,MAAA,EAAQ,CAAC,CAAA;AAAA,EAC/C,OAAA,EAASA,KAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAC7B,UAAA,EAAYA,KAAA,CAAE,KAAA,CAAM,CAAC,gBAAA,EAAkBA,MAAE,MAAA,EAAQ,CAAC,CAAA,CAAE,QAAA,EAAS;AAAA,EAC7D,KAAA,EAAOA,KAAA,CAAE,MAAA,EAAO,CAAE,QAAA;AACpB,CAAC;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@red-hat-developer-hub/backstage-plugin-adoption-insights-backend",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -36,24 +36,24 @@
|
|
|
36
36
|
"postpack": "backstage-cli package postpack"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@backstage/backend-defaults": "^0.
|
|
40
|
-
"@backstage/backend-plugin-api": "^1.
|
|
41
|
-
"@backstage/core-plugin-api": "^1.12.
|
|
42
|
-
"@backstage/errors": "^1.
|
|
43
|
-
"@backstage/plugin-catalog-node": "^2.
|
|
44
|
-
"@backstage/plugin-permission-common": "^0.9.
|
|
45
|
-
"@red-hat-developer-hub/backstage-plugin-adoption-insights-common": "^0.
|
|
39
|
+
"@backstage/backend-defaults": "^0.17.3",
|
|
40
|
+
"@backstage/backend-plugin-api": "^1.9.2",
|
|
41
|
+
"@backstage/core-plugin-api": "^1.12.7",
|
|
42
|
+
"@backstage/errors": "^1.3.1",
|
|
43
|
+
"@backstage/plugin-catalog-node": "^2.2.2",
|
|
44
|
+
"@backstage/plugin-permission-common": "^0.9.9",
|
|
45
|
+
"@red-hat-developer-hub/backstage-plugin-adoption-insights-common": "^0.9.0",
|
|
46
46
|
"express": "^4.17.1",
|
|
47
47
|
"express-promise-router": "^4.1.0",
|
|
48
|
-
"json-2-csv": "^5.5.
|
|
48
|
+
"json-2-csv": "^5.5.11",
|
|
49
49
|
"knex": "^3.1.0",
|
|
50
50
|
"luxon": "^3.5.0",
|
|
51
51
|
"uuid": "^11.1.0",
|
|
52
52
|
"zod": "^3.22.4"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"@backstage/backend-test-utils": "^1.11.
|
|
56
|
-
"@backstage/cli": "^0.36.
|
|
55
|
+
"@backstage/backend-test-utils": "^1.11.4",
|
|
56
|
+
"@backstage/cli": "^0.36.3",
|
|
57
57
|
"@types/express": "^4.17.6",
|
|
58
58
|
"@types/luxon": "^3.5.0",
|
|
59
59
|
"@types/supertest": "^2.0.12",
|
|
@@ -61,10 +61,10 @@
|
|
|
61
61
|
},
|
|
62
62
|
"files": [
|
|
63
63
|
"dist",
|
|
64
|
-
"config.
|
|
64
|
+
"config.schema.json",
|
|
65
65
|
"migrations"
|
|
66
66
|
],
|
|
67
|
-
"configSchema": "config.
|
|
67
|
+
"configSchema": "config.schema.json",
|
|
68
68
|
"typesVersions": {
|
|
69
69
|
"*": {
|
|
70
70
|
"package.json": [
|
package/config.d.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright Red Hat, Inc.
|
|
3
|
-
*
|
|
4
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
* you may not use this file except in compliance with the License.
|
|
6
|
-
* You may obtain a copy of the License at
|
|
7
|
-
*
|
|
8
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
*
|
|
10
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
* See the License for the specific language governing permissions and
|
|
14
|
-
* limitations under the License.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
export interface Config {
|
|
18
|
-
app?: {
|
|
19
|
-
analytics?: {
|
|
20
|
-
adoptionInsights?: {
|
|
21
|
-
/**
|
|
22
|
-
* Maximum buffer size for event batching.
|
|
23
|
-
* default 20
|
|
24
|
-
* @visibility frontend
|
|
25
|
-
*/
|
|
26
|
-
maxBufferSize?: number;
|
|
27
|
-
/**
|
|
28
|
-
* Flush interval in millisecond for event batching. All events will be flushed after this interval.
|
|
29
|
-
* default 5000ms
|
|
30
|
-
* @visibility frontend
|
|
31
|
-
*/
|
|
32
|
-
flushInterval?: number;
|
|
33
|
-
/**
|
|
34
|
-
* Flag to enable Debug mode which logs every event in the browser console.
|
|
35
|
-
* default false
|
|
36
|
-
* @visibility frontend
|
|
37
|
-
*/
|
|
38
|
-
debug?: boolean;
|
|
39
|
-
/**
|
|
40
|
-
* Total number of licensed users.
|
|
41
|
-
* @visibility backend
|
|
42
|
-
*/
|
|
43
|
-
licensedUsers?: number;
|
|
44
|
-
};
|
|
45
|
-
};
|
|
46
|
-
};
|
|
47
|
-
}
|