@red-hat-developer-hub/backstage-plugin-adoption-insights-backend 0.0.2
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 +9 -0
- package/README.md +180 -0
- package/config.d.ts +47 -0
- package/dist/controllers/EventApiController.cjs.js +122 -0
- package/dist/controllers/EventApiController.cjs.js.map +1 -0
- package/dist/database/DatabaseFactory.cjs.js +21 -0
- package/dist/database/DatabaseFactory.cjs.js.map +1 -0
- package/dist/database/adapters/BaseAdapter.cjs.js +276 -0
- package/dist/database/adapters/BaseAdapter.cjs.js.map +1 -0
- package/dist/database/adapters/PostgresAdapter.cjs.js +92 -0
- package/dist/database/adapters/PostgresAdapter.cjs.js.map +1 -0
- package/dist/database/adapters/SqliteAdapter.cjs.js +61 -0
- package/dist/database/adapters/SqliteAdapter.cjs.js.map +1 -0
- package/dist/database/migration.cjs.js +19 -0
- package/dist/database/migration.cjs.js.map +1 -0
- package/dist/database/partition.cjs.js +40 -0
- package/dist/database/partition.cjs.js.map +1 -0
- package/dist/domain/EventBatchProcessor.cjs.js +87 -0
- package/dist/domain/EventBatchProcessor.cjs.js.map +1 -0
- package/dist/index.cjs.js +10 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/models/Event.cjs.js +41 -0
- package/dist/models/Event.cjs.js.map +1 -0
- package/dist/plugin.cjs.js +64 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/router.cjs.js +63 -0
- package/dist/router.cjs.js.map +1 -0
- package/dist/types/event-request.cjs.js +14 -0
- package/dist/types/event-request.cjs.js.map +1 -0
- package/dist/utils/config.cjs.js +19 -0
- package/dist/utils/config.cjs.js.map +1 -0
- package/dist/utils/date.cjs.js +46 -0
- package/dist/utils/date.cjs.js.map +1 -0
- package/dist/validation/ValidationError.cjs.js +13 -0
- package/dist/validation/ValidationError.cjs.js.map +1 -0
- package/dist/validation/event-request.cjs.js +53 -0
- package/dist/validation/event-request.cjs.js.map +1 -0
- package/dist/validation/event.cjs.js +19 -0
- package/dist/validation/event.cjs.js.map +1 -0
- package/migrations/20250227120154_init.js +71 -0
- package/migrations/20250301104959_failed_events.js +39 -0
- package/package.json +71 -0
package/CHANGELOG.md
ADDED
package/README.md
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
# adoption-insights
|
2
|
+
|
3
|
+
This plugin builds the backend for Adoption Insights Plugin. It helps store analytics events emitted by analytics API into the DB and provides an API layer for the frontend.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
This plugin is installed via the `@red-hat-developer-hub/backstage-plugin-adoption-insights-backend` package. To install it to your backend package, run the following command:
|
8
|
+
|
9
|
+
```bash
|
10
|
+
# From your root directory
|
11
|
+
yarn --cwd packages/backend add @red-hat-developer-hub/backstage-plugin-adoption-insights-backend
|
12
|
+
```
|
13
|
+
|
14
|
+
Then add the plugin to your backend in `packages/backend/src/index.ts`:
|
15
|
+
|
16
|
+
```ts
|
17
|
+
const backend = createBackend();
|
18
|
+
// ...
|
19
|
+
backend.add(
|
20
|
+
import('@red-hat-developer-hub/backstage-plugin-adoption-insights-backend'),
|
21
|
+
);
|
22
|
+
```
|
23
|
+
|
24
|
+
## Configuration
|
25
|
+
|
26
|
+
```yaml
|
27
|
+
app:
|
28
|
+
analytics:
|
29
|
+
adoptionInsights:
|
30
|
+
maxBufferSize: 25
|
31
|
+
flushInterval: 6000
|
32
|
+
debug: false # enable this to debug
|
33
|
+
licensedUsers: 100 # Administrators can set this value to see the user adoption metrics.
|
34
|
+
```
|
35
|
+
|
36
|
+
#### Permission Framework Support
|
37
|
+
|
38
|
+
The Adoption Insights Backend plugin has support for the permission framework.
|
39
|
+
|
40
|
+
- When [RBAC permission](https://github.com/backstage/community-plugins/tree/main/workspaces/rbac/plugins/rbac-backend#installation) framework is enabled, for non-admin users to access Adoption Insights backend API, the role associated with your user should have the following permission policies associated with it. Add the following in your permission policies configuration file named `rbac-policy.csv`:
|
41
|
+
|
42
|
+
```CSV
|
43
|
+
p, role:default/team_a, adoption-insights.events.read, read, allow
|
44
|
+
|
45
|
+
g, user:default/<your-user-name>, role:default/team_a
|
46
|
+
|
47
|
+
```
|
48
|
+
|
49
|
+
You can specify the path to this configuration file in your application configuration:
|
50
|
+
|
51
|
+
```yaml
|
52
|
+
permission:
|
53
|
+
enabled: true
|
54
|
+
rbac:
|
55
|
+
policies-csv-file: /some/path/rbac-policy.csv
|
56
|
+
policyFileReload: true
|
57
|
+
```
|
58
|
+
|
59
|
+
- When using the [permission policy](https://backstage.io/docs/permissions/writing-a-policy/) framework. To test the permission policy, we have created a AdoptionInsightsTestPermissionPolicy and a permissionsPolicyExtension.
|
60
|
+
|
61
|
+
1. add the policy extension module in the `workspaces/adoption-insights/packages/backend/src/index.ts` and comment the Allow all Permission policy module as shown below.
|
62
|
+
|
63
|
+
```diff
|
64
|
+
backend.add(import('@backstage/plugin-permission-backend'));
|
65
|
+
// See https://backstage.io/docs/permissions/getting-started for how to create your own permission policy
|
66
|
+
-backend.add(
|
67
|
+
- import('@backstage/plugin-permission-backend-module-allow-all-policy'),
|
68
|
+
-);
|
69
|
+
|
70
|
+
+backend.add(import('./extensions/PermissionPolicyExtension'));
|
71
|
+
|
72
|
+
// search plugin
|
73
|
+
backend.add(import('@backstage/plugin-search-backend'));
|
74
|
+
```
|
75
|
+
|
76
|
+
2. Make a simple change to our [AdoptionInsightsTestPermissionPolicy](https://github.com/redhat-developer/rhdh-plugins/blob/main/workspaces/adoption-insights/packages/backend/src/extensions/PermissionPolicyExtension.ts) to confirm that policy is indeed wired up correctly. With the below change all the event API requests will fail with `Unauthorized` error.
|
77
|
+
|
78
|
+
```diff
|
79
|
+
class AdoptionInsightsTestPermissionPolicy implements PermissionPolicy {
|
80
|
+
isPermission(request.permission, adoptionInsightsEventsReadPermission)
|
81
|
+
) {
|
82
|
+
return {
|
83
|
+
- result: AuthorizeResult.ALLOW,
|
84
|
+
+ result: AuthorizeResult.DENY,
|
85
|
+
};
|
86
|
+
}
|
87
|
+
```
|
88
|
+
|
89
|
+
3. start the application by running `yarn dev` from `workspaces/adoption-insights` directory.
|
90
|
+
|
91
|
+
## Development
|
92
|
+
|
93
|
+
This plugin backend can be started in a standalone mode from directly in this
|
94
|
+
package with `yarn start`. It is a limited setup that is most convenient when
|
95
|
+
developing the plugin backend itself.
|
96
|
+
|
97
|
+
If you want to run the entire project, including the frontend, run `yarn dev` from the root directory.
|
98
|
+
|
99
|
+
# Events API
|
100
|
+
|
101
|
+
## Endpoint
|
102
|
+
|
103
|
+
`GET /api/adoption-insights/events`
|
104
|
+
|
105
|
+
## Query Parameters
|
106
|
+
|
107
|
+
| Parameter | Type | Required | Description |
|
108
|
+
| ------------ | ------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
109
|
+
| `type` | string | Yes | Filter events by type (e.g., `total_users`, `active_users`,`top_plugins`,`top_templates`,`top_techdocs`,`top_searches`,`top_catalog_entities`). |
|
110
|
+
| `start_date` | string (YYYY-MM-DD) | Yes | Fetch events starting from this date. |
|
111
|
+
| `end_date` | string (YYYY-MM-DD) | Yes | Fetch events up to this date. |
|
112
|
+
| `limit` | integer | No | Limit the number of events returned (default: `3`). |
|
113
|
+
| `kind` | string | No | Filter the entities by kind. |
|
114
|
+
| `grouping` | string | No | Group API endpoint `(active_users,top_plugins and top_searches)` response by `hourly`, `daily`, `weekly`, and `monthly`. |
|
115
|
+
| `format` | string | No | Response format, either `json` (default) or `csv`. |
|
116
|
+
|
117
|
+
## Example Request
|
118
|
+
|
119
|
+
```http
|
120
|
+
GET /api/adoption-insights/events?type=top_plugins&start_date=2025-03-01&end_date=2025-03-02&limit=3
|
121
|
+
```
|
122
|
+
|
123
|
+
## Example Response
|
124
|
+
|
125
|
+
<details> <summary>Click to expand</summary>
|
126
|
+
|
127
|
+
```json
|
128
|
+
{
|
129
|
+
"grouping": "daily",
|
130
|
+
"data": [
|
131
|
+
{
|
132
|
+
"plugin_id": "catalog",
|
133
|
+
"visit_count": "27",
|
134
|
+
"trend": [
|
135
|
+
{
|
136
|
+
"date": "2025-03-01",
|
137
|
+
"count": 10
|
138
|
+
},
|
139
|
+
{
|
140
|
+
"date": "2025-03-02",
|
141
|
+
"count": 17
|
142
|
+
}
|
143
|
+
],
|
144
|
+
"trend_percentage": "70.00"
|
145
|
+
},
|
146
|
+
{
|
147
|
+
"plugin_id": "root",
|
148
|
+
"visit_count": "15",
|
149
|
+
"trend": [
|
150
|
+
{
|
151
|
+
"date": "2025-03-01",
|
152
|
+
"count": 9
|
153
|
+
},
|
154
|
+
{
|
155
|
+
"date": "2025-03-02",
|
156
|
+
"count": 6
|
157
|
+
}
|
158
|
+
],
|
159
|
+
"trend_percentage": "-33.33"
|
160
|
+
},
|
161
|
+
{
|
162
|
+
"plugin_id": "kubernetes",
|
163
|
+
"visit_count": "9",
|
164
|
+
"trend": [
|
165
|
+
{
|
166
|
+
"date": "2025-03-01",
|
167
|
+
"count": 4
|
168
|
+
},
|
169
|
+
{
|
170
|
+
"date": "2025-03-02",
|
171
|
+
"count": 5
|
172
|
+
}
|
173
|
+
],
|
174
|
+
"trend_percentage": "25.00"
|
175
|
+
}
|
176
|
+
]
|
177
|
+
}
|
178
|
+
```
|
179
|
+
|
180
|
+
</details>
|
package/config.d.ts
ADDED
@@ -0,0 +1,47 @@
|
|
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
|
+
}
|
@@ -0,0 +1,122 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
4
|
+
|
5
|
+
var json2Csv = require('json-2-csv');
|
6
|
+
var Event = require('../models/Event.cjs.js');
|
7
|
+
var date = require('../utils/date.cjs.js');
|
8
|
+
var event = require('../validation/event.cjs.js');
|
9
|
+
var eventRequest = require('../validation/event-request.cjs.js');
|
10
|
+
var ValidationError = require('../validation/ValidationError.cjs.js');
|
11
|
+
var config = require('../utils/config.cjs.js');
|
12
|
+
|
13
|
+
class EventApiController {
|
14
|
+
database;
|
15
|
+
config;
|
16
|
+
processor;
|
17
|
+
constructor(eventDatabase, processor, config) {
|
18
|
+
this.database = eventDatabase;
|
19
|
+
this.processor = processor;
|
20
|
+
this.config = config;
|
21
|
+
}
|
22
|
+
async getBaseUrl(pluginId) {
|
23
|
+
return `${this.config.getString("backend.baseUrl")}/api/${pluginId}`;
|
24
|
+
}
|
25
|
+
processIncomingEvents(events) {
|
26
|
+
const proccessedEvents = events.filter((e) => !!e.context?.userId).map((event) => new Event.Event(event, this.database.isJsonSupported()));
|
27
|
+
proccessedEvents.forEach((event$1) => {
|
28
|
+
const result = event.EventSchema.safeParse(event$1);
|
29
|
+
if (!result.success) {
|
30
|
+
throw new ValidationError.ValidationError("Invalid event data", result.error.flatten());
|
31
|
+
}
|
32
|
+
return this.processor.addEvent(event$1);
|
33
|
+
});
|
34
|
+
}
|
35
|
+
trackEvents(req, res) {
|
36
|
+
const events = req.body;
|
37
|
+
try {
|
38
|
+
this.processIncomingEvents(events);
|
39
|
+
res.status(200).json({ success: true, message: "Event received" });
|
40
|
+
} catch (error) {
|
41
|
+
res.status(400).json({ message: error.message, errors: error.details.fieldErrors });
|
42
|
+
}
|
43
|
+
}
|
44
|
+
// Get insights based on the type of data requested
|
45
|
+
async getInsights(req, res) {
|
46
|
+
const parsed = eventRequest.EventRequestSchema.safeParse(req.query);
|
47
|
+
if (!parsed.success) {
|
48
|
+
res.status(400).json({
|
49
|
+
message: "Invalid query",
|
50
|
+
errors: parsed.error.flatten().fieldErrors
|
51
|
+
});
|
52
|
+
return;
|
53
|
+
}
|
54
|
+
const { type, format, ...params } = parsed.data;
|
55
|
+
const licensedUsers = config.getLicensedUsersCount(this.config);
|
56
|
+
const filters = {
|
57
|
+
...params,
|
58
|
+
end_date: date.toEndOfDayUTC(params.end_date),
|
59
|
+
start_date: date.toStartOfDayUTC(params.start_date)
|
60
|
+
};
|
61
|
+
const db = this.database;
|
62
|
+
db.setFilters(filters);
|
63
|
+
db.setConfig({ licensedUsers });
|
64
|
+
const queryHandlers = {
|
65
|
+
total_users: () => db.getUsers(),
|
66
|
+
active_users: () => db.getDailyUsers(),
|
67
|
+
top_searches: () => db.getTopSearches(),
|
68
|
+
top_plugins: () => db.getTopPluginViews(),
|
69
|
+
top_techdocs: () => db.getTopTechDocsViews(),
|
70
|
+
top_templates: () => db.getTopTemplateViews(),
|
71
|
+
top_catalog_entities: () => db.getTopCatalogEntitiesViews()
|
72
|
+
};
|
73
|
+
try {
|
74
|
+
const result = await queryHandlers[type]();
|
75
|
+
if (type === "top_techdocs") {
|
76
|
+
await this.getTechdocsMetadata(req, result);
|
77
|
+
}
|
78
|
+
if (format === "csv" && result.data) {
|
79
|
+
const csv = json2Csv.json2csv(result.data);
|
80
|
+
res.header("Content-Type", "text/csv");
|
81
|
+
res.attachment(`adoption_insights_${type}.csv`);
|
82
|
+
res.send(csv);
|
83
|
+
return;
|
84
|
+
}
|
85
|
+
res.json(result);
|
86
|
+
} catch (error) {
|
87
|
+
res.status(500).json({ error: "Internal Server Error" });
|
88
|
+
return;
|
89
|
+
}
|
90
|
+
}
|
91
|
+
async getTechdocsMetadata(req, result) {
|
92
|
+
const promises = [];
|
93
|
+
const baseUrl = await this.getBaseUrl("techdocs");
|
94
|
+
result.data.forEach((row) => {
|
95
|
+
if (!row.namespace || !row.kind || !row.name) {
|
96
|
+
row.site_name = "";
|
97
|
+
} else {
|
98
|
+
promises.push(
|
99
|
+
fetch(
|
100
|
+
`${baseUrl}/metadata/techdocs/${row.namespace}/${row.kind}/${row.name}`,
|
101
|
+
{
|
102
|
+
headers: {
|
103
|
+
Accept: "application/json",
|
104
|
+
Authorization: req.headers.authorization
|
105
|
+
}
|
106
|
+
}
|
107
|
+
).then(async (response) => {
|
108
|
+
const data = await response.json();
|
109
|
+
row.site_name = data.site_name ?? row.name;
|
110
|
+
}).catch((e) => {
|
111
|
+
console.warn(e);
|
112
|
+
row.site_name = row.name;
|
113
|
+
})
|
114
|
+
);
|
115
|
+
}
|
116
|
+
});
|
117
|
+
await Promise.all(promises);
|
118
|
+
}
|
119
|
+
}
|
120
|
+
|
121
|
+
exports.default = EventApiController;
|
122
|
+
//# sourceMappingURL=EventApiController.cjs.js.map
|
@@ -0,0 +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 { RootConfigService } 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\n constructor(\n eventDatabase: EventDatabase,\n processor: EventBatchProcessor,\n config: RootConfigService,\n ) {\n this.database = eventDatabase;\n this.processor = processor;\n this.config = config;\n }\n\n async getBaseUrl(pluginId: string): Promise<string> {\n return `${this.config.getString('backend.baseUrl')}/api/${pluginId}`;\n }\n\n private processIncomingEvents(events: AnalyticsEvent[]): 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 throw new ValidationError('Invalid event data', result.error.flatten());\n }\n\n return this.processor.addEvent(event);\n });\n }\n\n trackEvents(req: Request<{}, {}, AnalyticsEvent[]>, res: Response): void {\n const events = req.body;\n\n try {\n this.processIncomingEvents(events);\n res.status(200).json({ success: true, message: 'Event received' });\n } catch (error) {\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 parsed = EventRequestSchema.safeParse(req.query);\n if (!parsed.success) {\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) as string,\n start_date: toStartOfDayUTC(params.start_date) 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 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 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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"}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
var PostgresAdapter = require('./adapters/PostgresAdapter.cjs.js');
|
4
|
+
var SqliteAdapter = require('./adapters/SqliteAdapter.cjs.js');
|
5
|
+
|
6
|
+
class DatabaseFactory {
|
7
|
+
static getDatabase(db, logger) {
|
8
|
+
const dbType = db.client.config.client;
|
9
|
+
switch (dbType) {
|
10
|
+
case "pg":
|
11
|
+
return new PostgresAdapter.PostgresAdapter(db, logger);
|
12
|
+
case "better-sqlite3":
|
13
|
+
return new SqliteAdapter.SqliteAdapter(db, logger);
|
14
|
+
default:
|
15
|
+
throw new Error(`Unsupported database type: ${dbType}`);
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
exports.DatabaseFactory = DatabaseFactory;
|
21
|
+
//# sourceMappingURL=DatabaseFactory.cjs.js.map
|
@@ -0,0 +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,eAAgB,CAAA;AAAA,EAC3B,OAAO,WAAY,CAAA,EAAA,EAAU,MAAsC,EAAA;AACjE,IAAM,MAAA,MAAA,GAAS,EAAG,CAAA,MAAA,CAAO,MAAO,CAAA,MAAA;AAEhC,IAAA,QAAQ,MAAQ;AAAA,MACd,KAAK,IAAA;AACH,QAAO,OAAA,IAAIA,+BAAgB,CAAA,EAAA,EAAI,MAAM,CAAA;AAAA,MACvC,KAAK,gBAAA;AACH,QAAO,OAAA,IAAIC,2BAAc,CAAA,EAAA,EAAI,MAAM,CAAA;AAAA,MACrC;AACE,QAAA,MAAM,IAAI,KAAA,CAAM,CAA8B,2BAAA,EAAA,MAAM,CAAE,CAAA,CAAA;AAAA;AAC1D;AAEJ;;;;"}
|