@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
@@ -0,0 +1,87 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
class EventBatchProcessor {
|
4
|
+
queue;
|
5
|
+
processing;
|
6
|
+
batchSize;
|
7
|
+
batchInterval;
|
8
|
+
maxRetries;
|
9
|
+
failedEvents;
|
10
|
+
database;
|
11
|
+
logger;
|
12
|
+
debug;
|
13
|
+
constructor(database, logger, options) {
|
14
|
+
this.queue = [];
|
15
|
+
this.logger = logger;
|
16
|
+
this.processing = false;
|
17
|
+
this.database = database;
|
18
|
+
this.failedEvents = /* @__PURE__ */ new Map();
|
19
|
+
this.debug = options.debug || false;
|
20
|
+
const { batchSize = 5, maxRetries = 3, batchInterval = 2e3 } = options;
|
21
|
+
this.batchSize = batchSize;
|
22
|
+
this.maxRetries = maxRetries;
|
23
|
+
this.batchInterval = batchInterval;
|
24
|
+
setInterval(() => this.processEvents(), this.batchInterval);
|
25
|
+
setInterval(() => this.logQueueStats(), 5e3);
|
26
|
+
}
|
27
|
+
addEvent(event) {
|
28
|
+
if (!this.queue.some((existingEvent) => existingEvent.id === event.id)) {
|
29
|
+
this.logger.info(`[QUEUE] Event added: ${JSON.stringify(event)}`);
|
30
|
+
this.queue.push({ ...event, toJSON: event.toJSON });
|
31
|
+
}
|
32
|
+
}
|
33
|
+
async processEvents() {
|
34
|
+
if (this.processing || this.queue.length === 0) return;
|
35
|
+
this.processing = true;
|
36
|
+
const batch = this.queue.splice(0, this.batchSize);
|
37
|
+
try {
|
38
|
+
await this.database.insertEvents(batch);
|
39
|
+
this.logger.info(`[SUCCESS] Inserted ${batch.length} events`);
|
40
|
+
batch.forEach((event) => this.failedEvents.delete(event.id));
|
41
|
+
} catch (error) {
|
42
|
+
this.logger.error(`[ERROR] Batch insert failed:`, error);
|
43
|
+
batch.forEach(
|
44
|
+
(event) => this.retryOrStoreFailedEvent(event, error.message)
|
45
|
+
);
|
46
|
+
}
|
47
|
+
this.processing = false;
|
48
|
+
}
|
49
|
+
async retryOrStoreFailedEvent(event, errorMessage) {
|
50
|
+
const retries = this.failedEvents.get(event.id) || 0;
|
51
|
+
if (retries >= this.maxRetries) {
|
52
|
+
this.logger.error(
|
53
|
+
`[DROPPED] Event permanently failed, storing in DB: ${JSON.stringify(
|
54
|
+
event.toJSON()
|
55
|
+
)}`
|
56
|
+
);
|
57
|
+
await this.storeFailedEvent(event, errorMessage);
|
58
|
+
return;
|
59
|
+
}
|
60
|
+
this.failedEvents.set(event.id, retries + 1);
|
61
|
+
this.queue.push(event);
|
62
|
+
this.logger.warn(
|
63
|
+
`[RETRY] Event re-added to queue (Attempt ${retries + 1}): ${JSON.stringify(event.toJSON())})`
|
64
|
+
);
|
65
|
+
}
|
66
|
+
async storeFailedEvent(event, errorMessage) {
|
67
|
+
try {
|
68
|
+
await this.database.insertFailedEvent(
|
69
|
+
JSON.stringify(event),
|
70
|
+
errorMessage,
|
71
|
+
this.maxRetries
|
72
|
+
);
|
73
|
+
this.logger.warn(`[FAILED-LOGGED] Event stored in DB:`, event.toJSON());
|
74
|
+
} catch (err) {
|
75
|
+
this.logger.error(`[DB-ERROR] Failed to store event in DB:`, err);
|
76
|
+
}
|
77
|
+
}
|
78
|
+
logQueueStats() {
|
79
|
+
if (this.debug)
|
80
|
+
this.logger.info(
|
81
|
+
`[STATS] Queue Size: ${this.queue.length}, Failed Events: ${this.failedEvents.size}`
|
82
|
+
);
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
exports.EventBatchProcessor = EventBatchProcessor;
|
87
|
+
//# sourceMappingURL=EventBatchProcessor.cjs.js.map
|
@@ -0,0 +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/index';\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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"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
|
package/dist/index.d.ts
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* adoptionInsightsPlugin backend plugin
|
5
|
+
*
|
6
|
+
* @public
|
7
|
+
*/
|
8
|
+
declare const adoptionInsightsPlugin: _backstage_backend_plugin_api.BackendFeature;
|
9
|
+
|
10
|
+
export { adoptionInsightsPlugin as default };
|
@@ -0,0 +1,41 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
var uuid = require('uuid');
|
4
|
+
|
5
|
+
class Event {
|
6
|
+
id;
|
7
|
+
user_ref;
|
8
|
+
plugin_id;
|
9
|
+
action;
|
10
|
+
context;
|
11
|
+
subject;
|
12
|
+
value;
|
13
|
+
attributes;
|
14
|
+
created_at;
|
15
|
+
constructor(event, isJson = true) {
|
16
|
+
this.id = uuid.v4();
|
17
|
+
this.user_ref = event.context?.userName;
|
18
|
+
this.plugin_id = event.context?.pluginId;
|
19
|
+
this.action = event.action;
|
20
|
+
this.subject = event.subject;
|
21
|
+
this.value = event.value;
|
22
|
+
this.created_at = event.context?.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
23
|
+
this.context = isJson ? event.context : JSON.stringify(event.context ?? {});
|
24
|
+
this.attributes = isJson ? event.attributes ?? {} : JSON.stringify(event.attributes ?? {});
|
25
|
+
}
|
26
|
+
toJSON() {
|
27
|
+
return {
|
28
|
+
user_ref: this.user_ref,
|
29
|
+
plugin_id: this.plugin_id,
|
30
|
+
action: this.action,
|
31
|
+
context: this.context,
|
32
|
+
subject: this.subject,
|
33
|
+
attributes: this.attributes,
|
34
|
+
created_at: this.created_at,
|
35
|
+
value: this.value
|
36
|
+
};
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
exports.Event = Event;
|
41
|
+
//# sourceMappingURL=Event.cjs.js.map
|
@@ -0,0 +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';\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 this.created_at =\n (event.context?.timestamp as string) || new Date().toISOString();\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"],"mappings":";;;;AAiCO,MAAM,KAAM,CAAA;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,KAAuB,EAAA,MAAA,GAAkB,IAAM,EAAA;AACzD,IAAA,IAAA,CAAK,KAAKA,OAAO,EAAA;AACjB,IAAK,IAAA,CAAA,QAAA,GAAW,MAAM,OAAS,EAAA,QAAA;AAC/B,IAAK,IAAA,CAAA,SAAA,GAAY,MAAM,OAAS,EAAA,QAAA;AAChC,IAAA,IAAA,CAAK,SAAS,KAAM,CAAA,MAAA;AACpB,IAAA,IAAA,CAAK,UAAU,KAAM,CAAA,OAAA;AACrB,IAAA,IAAA,CAAK,QAAQ,KAAM,CAAA,KAAA;AACnB,IAAA,IAAA,CAAK,aACF,KAAM,CAAA,OAAA,EAAS,8BAA4B,IAAA,IAAA,IAAO,WAAY,EAAA;AAGjE,IAAK,IAAA,CAAA,OAAA,GAAU,SAAS,KAAM,CAAA,OAAA,GAAU,KAAK,SAAU,CAAA,KAAA,CAAM,OAAW,IAAA,EAAE,CAAA;AAC1E,IAAK,IAAA,CAAA,UAAA,GAAa,MACd,GAAA,KAAA,CAAM,UAAc,IAAA,EACpB,GAAA,IAAA,CAAK,SAAU,CAAA,KAAA,CAAM,UAAc,IAAA,EAAE,CAAA;AAAA;AAC3C,EAEA,MAAS,GAAA;AACP,IAAO,OAAA;AAAA,MACL,UAAU,IAAK,CAAA,QAAA;AAAA,MACf,WAAW,IAAK,CAAA,SAAA;AAAA,MAChB,QAAQ,IAAK,CAAA,MAAA;AAAA,MACb,SAAS,IAAK,CAAA,OAAA;AAAA,MACd,SAAS,IAAK,CAAA,OAAA;AAAA,MACd,YAAY,IAAK,CAAA,UAAA;AAAA,MACjB,YAAY,IAAK,CAAA,UAAA;AAAA,MACjB,OAAO,IAAK,CAAA;AAAA,KACd;AAAA;AAEJ;;;;"}
|
@@ -0,0 +1,64 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
var backendPluginApi = require('@backstage/backend-plugin-api');
|
4
|
+
var router = require('./router.cjs.js');
|
5
|
+
var migration = require('./database/migration.cjs.js');
|
6
|
+
var DatabaseFactory = require('./database/DatabaseFactory.cjs.js');
|
7
|
+
var EventBatchProcessor = require('./domain/EventBatchProcessor.cjs.js');
|
8
|
+
var EventApiController = require('./controllers/EventApiController.cjs.js');
|
9
|
+
var partition = require('./database/partition.cjs.js');
|
10
|
+
var config = require('./utils/config.cjs.js');
|
11
|
+
|
12
|
+
const adoptionInsightsPlugin = backendPluginApi.createBackendPlugin({
|
13
|
+
pluginId: "adoption-insights",
|
14
|
+
register(env) {
|
15
|
+
env.registerInit({
|
16
|
+
deps: {
|
17
|
+
config: backendPluginApi.coreServices.rootConfig,
|
18
|
+
logger: backendPluginApi.coreServices.logger,
|
19
|
+
httpAuth: backendPluginApi.coreServices.httpAuth,
|
20
|
+
httpRouter: backendPluginApi.coreServices.httpRouter,
|
21
|
+
database: backendPluginApi.coreServices.database,
|
22
|
+
scheduler: backendPluginApi.coreServices.scheduler,
|
23
|
+
permissions: backendPluginApi.coreServices.permissions
|
24
|
+
},
|
25
|
+
async init({
|
26
|
+
config: config$1,
|
27
|
+
logger,
|
28
|
+
httpAuth,
|
29
|
+
httpRouter,
|
30
|
+
database,
|
31
|
+
scheduler,
|
32
|
+
permissions
|
33
|
+
}) {
|
34
|
+
const options = config.getConfigurationOptions(config$1);
|
35
|
+
const client = await database.getClient();
|
36
|
+
const db = DatabaseFactory.DatabaseFactory.getDatabase(client, logger);
|
37
|
+
const processor = new EventBatchProcessor.EventBatchProcessor(db, logger, options);
|
38
|
+
const eventApiController = new EventApiController.default(
|
39
|
+
db,
|
40
|
+
processor,
|
41
|
+
config$1
|
42
|
+
);
|
43
|
+
await migration.migrate(database);
|
44
|
+
if (db.isPartitionSupported()) {
|
45
|
+
partition.schedulePartition(client, { logger, scheduler });
|
46
|
+
}
|
47
|
+
httpRouter.use(
|
48
|
+
await router.createRouter({
|
49
|
+
httpAuth,
|
50
|
+
permissions,
|
51
|
+
eventApiController
|
52
|
+
})
|
53
|
+
);
|
54
|
+
httpRouter.addAuthPolicy({
|
55
|
+
path: "/health",
|
56
|
+
allow: "unauthenticated"
|
57
|
+
});
|
58
|
+
}
|
59
|
+
});
|
60
|
+
}
|
61
|
+
});
|
62
|
+
|
63
|
+
exports.adoptionInsightsPlugin = adoptionInsightsPlugin;
|
64
|
+
//# sourceMappingURL=plugin.cjs.js.map
|
@@ -0,0 +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 { 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 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 },\n async init({\n config,\n logger,\n httpAuth,\n httpRouter,\n database,\n scheduler,\n permissions,\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 );\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","getConfigurationOptions","DatabaseFactory","EventBatchProcessor","EventApiController","migrate","schedulePartition","createRouter"],"mappings":";;;;;;;;;;;AAgCO,MAAM,yBAAyBA,oCAAoB,CAAA;AAAA,EACxD,QAAU,EAAA,mBAAA;AAAA,EACV,SAAS,GAAK,EAAA;AACZ,IAAA,GAAA,CAAI,YAAa,CAAA;AAAA,MACf,IAAM,EAAA;AAAA,QACJ,QAAQC,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;AAAA,OAC5B;AAAA,MACA,MAAM,IAAK,CAAA;AAAA,gBACTC,QAAA;AAAA,QACA,MAAA;AAAA,QACA,QAAA;AAAA,QACA,UAAA;AAAA,QACA,QAAA;AAAA,QACA,SAAA;AAAA,QACA;AAAA,OACC,EAAA;AAED,QAAM,MAAA,OAAA,GAAUC,+BAAwBD,QAAM,CAAA;AAC9C,QAAM,MAAA,MAAA,GAAS,MAAM,QAAA,CAAS,SAAU,EAAA;AACxC,QAAA,MAAM,EAAK,GAAAE,+BAAA,CAAgB,WAAY,CAAA,MAAA,EAAQ,MAAM,CAAA;AACrD,QAAA,MAAM,SAAY,GAAA,IAAIC,uCAAoB,CAAA,EAAA,EAAI,QAAQ,OAAO,CAAA;AAC7D,QAAA,MAAM,qBAAqB,IAAIC,0BAAA;AAAA,UAC7B,EAAA;AAAA,UACA,SAAA;AAAA,UACAJ;AAAA,SACF;AAGA,QAAA,MAAMK,kBAAQ,QAAQ,CAAA;AAGtB,QAAI,IAAA,EAAA,CAAG,sBAAwB,EAAA;AAC7B,UAAAC,2BAAA,CAAkB,MAAQ,EAAA,EAAE,MAAQ,EAAA,SAAA,EAAW,CAAA;AAAA;AAGjD,QAAW,UAAA,CAAA,GAAA;AAAA,UACT,MAAMC,mBAAa,CAAA;AAAA,YACjB,QAAA;AAAA,YACA,WAAA;AAAA,YACA;AAAA,WACD;AAAA,SACH;AAEA,QAAA,UAAA,CAAW,aAAc,CAAA;AAAA,UACvB,IAAM,EAAA,SAAA;AAAA,UACN,KAAO,EAAA;AAAA,SACR,CAAA;AAAA;AACH,KACD,CAAA;AAAA;AAEL,CAAC;;;;"}
|
@@ -0,0 +1,63 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
var express = require('express');
|
4
|
+
var Router = require('express-promise-router');
|
5
|
+
var errors = require('@backstage/errors');
|
6
|
+
var pluginPermissionCommon = require('@backstage/plugin-permission-common');
|
7
|
+
var backstagePluginAdoptionInsightsCommon = require('@red-hat-developer-hub/backstage-plugin-adoption-insights-common');
|
8
|
+
var pluginPermissionNode = require('@backstage/plugin-permission-node');
|
9
|
+
|
10
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
11
|
+
|
12
|
+
var express__default = /*#__PURE__*/_interopDefaultCompat(express);
|
13
|
+
var Router__default = /*#__PURE__*/_interopDefaultCompat(Router);
|
14
|
+
|
15
|
+
async function createRouter({
|
16
|
+
httpAuth,
|
17
|
+
permissions,
|
18
|
+
eventApiController
|
19
|
+
}) {
|
20
|
+
const router = Router__default.default();
|
21
|
+
const permissionIntegrationRouter = pluginPermissionNode.createPermissionIntegrationRouter({
|
22
|
+
permissions: [backstagePluginAdoptionInsightsCommon.adoptionInsightsEventsReadPermission]
|
23
|
+
});
|
24
|
+
router.use(permissionIntegrationRouter);
|
25
|
+
router.use(express__default.default.json());
|
26
|
+
const authorizeUser = async (req) => {
|
27
|
+
const credentials = await httpAuth.credentials(req, { allow: ["user"] });
|
28
|
+
const decision = (await permissions.authorize(
|
29
|
+
[{ permission: backstagePluginAdoptionInsightsCommon.adoptionInsightsEventsReadPermission }],
|
30
|
+
{ credentials }
|
31
|
+
))[0];
|
32
|
+
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
33
|
+
throw new errors.NotAllowedError("Unauthorized");
|
34
|
+
}
|
35
|
+
};
|
36
|
+
const getUserEntityRef = async (req) => {
|
37
|
+
const credentials = await httpAuth.credentials(req, {
|
38
|
+
allow: ["user"]
|
39
|
+
});
|
40
|
+
return credentials.principal.userEntityRef;
|
41
|
+
};
|
42
|
+
router.use(async (req, _, next) => {
|
43
|
+
await getUserEntityRef(req);
|
44
|
+
next();
|
45
|
+
});
|
46
|
+
router.get(
|
47
|
+
"/events",
|
48
|
+
async (req, res) => {
|
49
|
+
await authorizeUser(req);
|
50
|
+
return eventApiController.getInsights(req, res);
|
51
|
+
}
|
52
|
+
);
|
53
|
+
router.post("/events", async (req, res) => {
|
54
|
+
return eventApiController.trackEvents(req, res);
|
55
|
+
});
|
56
|
+
router.get("/health", (_, response) => {
|
57
|
+
response.json({ status: "ok" });
|
58
|
+
});
|
59
|
+
return router;
|
60
|
+
}
|
61
|
+
|
62
|
+
exports.createRouter = createRouter;
|
63
|
+
//# sourceMappingURL=router.cjs.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"router.cjs.js","sources":["../src/router.ts"],"sourcesContent":["import express, { Request, Response } from 'express';\nimport Router from 'express-promise-router';\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 {\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 EventApiController from './controllers/EventApiController';\nimport { QueryParams } from './types/event-request';\nimport { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node';\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 const permissionIntegrationRouter = createPermissionIntegrationRouter({\n permissions: [adoptionInsightsEventsReadPermission],\n });\n\n router.use(permissionIntegrationRouter);\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","createPermissionIntegrationRouter","adoptionInsightsEventsReadPermission","express","AuthorizeResult","NotAllowedError"],"mappings":";;;;;;;;;;;;;;AA4BA,eAAsB,YAAa,CAAA;AAAA,EACjC,QAAA;AAAA,EACA,WAAA;AAAA,EACA;AACF,CAI4B,EAAA;AAC1B,EAAA,MAAM,SAASA,uBAAO,EAAA;AAEtB,EAAA,MAAM,8BAA8BC,sDAAkC,CAAA;AAAA,IACpE,WAAA,EAAa,CAACC,0EAAoC;AAAA,GACnD,CAAA;AAED,EAAA,MAAA,CAAO,IAAI,2BAA2B,CAAA;AACtC,EAAO,MAAA,CAAA,GAAA,CAAIC,wBAAQ,CAAA,IAAA,EAAM,CAAA;AAEzB,EAAM,MAAA,aAAA,GAAgB,OACpB,GACkB,KAAA;AAClB,IAAM,MAAA,WAAA,GAAc,MAAM,QAAA,CAAS,WAAY,CAAA,GAAA,EAAK,EAAE,KAAO,EAAA,CAAC,MAAM,CAAA,EAAG,CAAA;AACvE,IAAM,MAAA,QAAA,GAAA,CACJ,MAAM,WAAY,CAAA,SAAA;AAAA,MAChB,CAAC,EAAE,UAAY,EAAAD,0EAAA,EAAsC,CAAA;AAAA,MACrD,EAAE,WAAY;AAAA,OAEhB,CAAC,CAAA;AAEH,IAAI,IAAA,QAAA,CAAS,MAAW,KAAAE,sCAAA,CAAgB,IAAM,EAAA;AAC5C,MAAM,MAAA,IAAIC,uBAAgB,cAAc,CAAA;AAAA;AAC1C,GACF;AACA,EAAM,MAAA,gBAAA,GAAmB,OAAO,GAAkC,KAAA;AAChE,IAAA,MAAM,WAAc,GAAA,MAAM,QAAS,CAAA,WAAA,CAAY,GAAK,EAAA;AAAA,MAClD,KAAA,EAAO,CAAC,MAAM;AAAA,KACf,CAAA;AACD,IAAA,OAAO,YAAY,SAAU,CAAA,aAAA;AAAA,GAC/B;AACA,EAAA,MAAA,CAAO,GAAI,CAAA,OAAO,GAAK,EAAA,CAAA,EAAG,IAAS,KAAA;AACjC,IAAA,MAAM,iBAAiB,GAAG,CAAA;AAC1B,IAAK,IAAA,EAAA;AAAA,GACN,CAAA;AAED,EAAO,MAAA,CAAA,GAAA;AAAA,IACL,SAAA;AAAA,IACA,OAAO,KAAuC,GAAkB,KAAA;AAC9D,MAAA,MAAM,cAAc,GAAG,CAAA;AACvB,MAAO,OAAA,kBAAA,CAAmB,WAAY,CAAA,GAAA,EAAK,GAAG,CAAA;AAAA;AAChD,GACF;AAEA,EAAA,MAAA,CAAO,IAAK,CAAA,SAAA,EAAW,OAAO,GAAA,EAAK,GAAQ,KAAA;AACzC,IAAO,OAAA,kBAAA,CAAmB,WAAY,CAAA,GAAA,EAAK,GAAG,CAAA;AAAA,GAC/C,CAAA;AAED,EAAA,MAAA,CAAO,GAAI,CAAA,SAAA,EAAW,CAAC,CAAA,EAAG,QAAa,KAAA;AACrC,IAAA,QAAA,CAAS,IAAK,CAAA,EAAE,MAAQ,EAAA,IAAA,EAAM,CAAA;AAAA,GAC/B,CAAA;AAED,EAAO,OAAA,MAAA;AACT;;;;"}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
const QUERY_TYPES = [
|
4
|
+
"total_users",
|
5
|
+
"active_users",
|
6
|
+
"top_plugins",
|
7
|
+
"top_templates",
|
8
|
+
"top_techdocs",
|
9
|
+
"top_searches",
|
10
|
+
"top_catalog_entities"
|
11
|
+
];
|
12
|
+
|
13
|
+
exports.QUERY_TYPES = QUERY_TYPES;
|
14
|
+
//# sourceMappingURL=event-request.cjs.js.map
|
@@ -0,0 +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,WAAc,GAAA;AAAA,EACzB,aAAA;AAAA,EACA,cAAA;AAAA,EACA,aAAA;AAAA,EACA,eAAA;AAAA,EACA,cAAA;AAAA,EACA,cAAA;AAAA,EACA;AACF;;;;"}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
const getConfigurationOptions = (config) => {
|
4
|
+
const batchSize = config.getOptionalNumber("app.analytics.adoptionInsights.maxBufferSize") || 5;
|
5
|
+
const batchInterval = config.getOptionalNumber("app.analytics.adoptionInsights.flushInterval") || 2e3;
|
6
|
+
const debug = config.getOptionalBoolean("app.analytics.adoptionInsights.debug") || false;
|
7
|
+
return {
|
8
|
+
debug,
|
9
|
+
batchSize,
|
10
|
+
batchInterval
|
11
|
+
};
|
12
|
+
};
|
13
|
+
const getLicensedUsersCount = (config) => {
|
14
|
+
return config.getOptionalNumber("app.analytics.adoptionInsights.licensedUsers") || 100;
|
15
|
+
};
|
16
|
+
|
17
|
+
exports.getConfigurationOptions = getConfigurationOptions;
|
18
|
+
exports.getLicensedUsersCount = getLicensedUsersCount;
|
19
|
+
//# sourceMappingURL=config.cjs.js.map
|
@@ -0,0 +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/index';\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":";;AAkBa,MAAA,uBAAA,GAA0B,CACrC,MAC2B,KAAA;AAC3B,EAAA,MAAM,SACJ,GAAA,MAAA,CAAO,iBAAkB,CAAA,8CAA8C,CACvE,IAAA,CAAA;AAEF,EAAA,MAAM,aACJ,GAAA,MAAA,CAAO,iBAAkB,CAAA,8CAA8C,CACvE,IAAA,GAAA;AAEF,EAAA,MAAM,KACJ,GAAA,MAAA,CAAO,kBAAmB,CAAA,sCAAsC,CAAK,IAAA,KAAA;AAEvE,EAAO,OAAA;AAAA,IACL,KAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GACF;AACF;AACa,MAAA,qBAAA,GAAwB,CAAC,MAA8B,KAAA;AAClE,EACE,OAAA,MAAA,CAAO,iBAAkB,CAAA,8CAA8C,CACvE,IAAA,GAAA;AAEJ;;;;;"}
|
@@ -0,0 +1,46 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
var luxon = require('luxon');
|
4
|
+
|
5
|
+
const toStartOfDayUTC = (dateString, timezone = "UTC") => luxon.DateTime.fromFormat(dateString, "yyyy-MM-dd", { zone: timezone }).startOf("day").toUTC().toISO();
|
6
|
+
const toEndOfDayUTC = (dateString, timezone = "UTC") => luxon.DateTime.fromFormat(dateString, "yyyy-MM-dd", { zone: timezone }).endOf("day").toUTC().toISO();
|
7
|
+
const calculateDateRange = (start_date, end_date) => {
|
8
|
+
const start = luxon.DateTime.fromISO(start_date, { zone: "UTC" });
|
9
|
+
const end = luxon.DateTime.fromISO(end_date, { zone: "UTC" });
|
10
|
+
return Math.floor(end.diff(start, "days").days);
|
11
|
+
};
|
12
|
+
const isSameMonth = (start_date, end_date) => {
|
13
|
+
const start = luxon.DateTime.fromISO(start_date, { zone: "UTC" });
|
14
|
+
const end = luxon.DateTime.fromISO(end_date, { zone: "UTC" });
|
15
|
+
return start.hasSame(end, "month");
|
16
|
+
};
|
17
|
+
const getDateGroupingType = (dateDiff, start_date, end_date) => {
|
18
|
+
if (dateDiff === 0) return "hourly";
|
19
|
+
if (dateDiff <= 7) return "daily";
|
20
|
+
if (dateDiff <= 30 && isSameMonth(start_date, end_date)) return "weekly";
|
21
|
+
return "monthly";
|
22
|
+
};
|
23
|
+
const hasZFormat = (dateStr) => {
|
24
|
+
return dateStr.includes("Z") || dateStr.includes("T");
|
25
|
+
};
|
26
|
+
const convertToLocalTimezone = (date) => {
|
27
|
+
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
28
|
+
const parsedDate = hasZFormat(date.toString()) ? new Date(date).toISOString() : date;
|
29
|
+
if (luxon.DateTime.fromISO(parsedDate, { zone: timeZone }).isValid) {
|
30
|
+
return luxon.DateTime.fromISO(parsedDate, { zone: timeZone }).toFormat(
|
31
|
+
"yyyy-MM-dd HH:mm:ss ZZZZ"
|
32
|
+
);
|
33
|
+
}
|
34
|
+
return luxon.DateTime.fromFormat(parsedDate, "yyyy-MM-dd HH:mm:ss", {
|
35
|
+
zone: timeZone
|
36
|
+
}).toFormat("yyyy-MM-dd HH:mm:ss ZZZZ");
|
37
|
+
};
|
38
|
+
|
39
|
+
exports.calculateDateRange = calculateDateRange;
|
40
|
+
exports.convertToLocalTimezone = convertToLocalTimezone;
|
41
|
+
exports.getDateGroupingType = getDateGroupingType;
|
42
|
+
exports.hasZFormat = hasZFormat;
|
43
|
+
exports.isSameMonth = isSameMonth;
|
44
|
+
exports.toEndOfDayUTC = toEndOfDayUTC;
|
45
|
+
exports.toStartOfDayUTC = toStartOfDayUTC;
|
46
|
+
//# sourceMappingURL=date.cjs.js.map
|
@@ -0,0 +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 = (\n dateDiff: number,\n start_date: string,\n end_date: string,\n): Grouping => {\n if (dateDiff === 0) return 'hourly';\n if (dateDiff <= 7) return 'daily';\n if (dateDiff <= 30 && isSameMonth(start_date, end_date)) return 'weekly';\n return 'monthly';\n};\n\nexport const hasZFormat = (dateStr: string): boolean => {\n return dateStr.includes('Z') || dateStr.includes('T');\n};\n\nexport const convertToLocalTimezone = (date: string) => {\n const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;\n\n const parsedDate = hasZFormat(date.toString())\n ? new Date(date).toISOString()\n : date;\n\n if (DateTime.fromISO(parsedDate, { zone: timeZone }).isValid) {\n return DateTime.fromISO(parsedDate, { zone: timeZone }).toFormat(\n 'yyyy-MM-dd HH:mm:ss ZZZZ',\n );\n }\n return DateTime.fromFormat(parsedDate, 'yyyy-MM-dd HH:mm:ss', {\n zone: timeZone,\n }).toFormat('yyyy-MM-dd HH:mm:ss ZZZZ');\n};\n"],"names":["DateTime"],"mappings":";;;;AAkBO,MAAM,kBAAkB,CAAC,UAAA,EAAoB,WAAW,KAC7D,KAAAA,cAAA,CAAS,WAAW,UAAY,EAAA,YAAA,EAAc,EAAE,IAAM,EAAA,QAAA,EAAU,CAC7D,CAAA,OAAA,CAAQ,KAAK,CACb,CAAA,KAAA,GACA,KAAM;AAEJ,MAAM,gBAAgB,CAAC,UAAA,EAAoB,WAAW,KAC3D,KAAAA,cAAA,CAAS,WAAW,UAAY,EAAA,YAAA,EAAc,EAAE,IAAM,EAAA,QAAA,EAAU,CAC7D,CAAA,KAAA,CAAM,KAAK,CACX,CAAA,KAAA,GACA,KAAM;AAEE,MAAA,kBAAA,GAAqB,CAChC,UAAA,EACA,QACW,KAAA;AACX,EAAA,MAAM,QAAQA,cAAS,CAAA,OAAA,CAAQ,YAAY,EAAE,IAAA,EAAM,OAAO,CAAA;AAC1D,EAAA,MAAM,MAAMA,cAAS,CAAA,OAAA,CAAQ,UAAU,EAAE,IAAA,EAAM,OAAO,CAAA;AAEtD,EAAA,OAAO,KAAK,KAAM,CAAA,GAAA,CAAI,KAAK,KAAO,EAAA,MAAM,EAAE,IAAI,CAAA;AAChD;AAEa,MAAA,WAAA,GAAc,CAAC,UAAA,EAAoB,QAA8B,KAAA;AAC5E,EAAA,MAAM,QAAQA,cAAS,CAAA,OAAA,CAAQ,YAAY,EAAE,IAAA,EAAM,OAAO,CAAA;AAC1D,EAAA,MAAM,MAAMA,cAAS,CAAA,OAAA,CAAQ,UAAU,EAAE,IAAA,EAAM,OAAO,CAAA;AAEtD,EAAO,OAAA,KAAA,CAAM,OAAQ,CAAA,GAAA,EAAK,OAAO,CAAA;AACnC;AAEO,MAAM,mBAAsB,GAAA,CACjC,QACA,EAAA,UAAA,EACA,QACa,KAAA;AACb,EAAI,IAAA,QAAA,KAAa,GAAU,OAAA,QAAA;AAC3B,EAAI,IAAA,QAAA,IAAY,GAAU,OAAA,OAAA;AAC1B,EAAA,IAAI,YAAY,EAAM,IAAA,WAAA,CAAY,UAAY,EAAA,QAAQ,GAAU,OAAA,QAAA;AAChE,EAAO,OAAA,SAAA;AACT;AAEa,MAAA,UAAA,GAAa,CAAC,OAA6B,KAAA;AACtD,EAAA,OAAO,QAAQ,QAAS,CAAA,GAAG,CAAK,IAAA,OAAA,CAAQ,SAAS,GAAG,CAAA;AACtD;AAEa,MAAA,sBAAA,GAAyB,CAAC,IAAiB,KAAA;AACtD,EAAA,MAAM,QAAW,GAAA,IAAA,CAAK,cAAe,EAAA,CAAE,iBAAkB,CAAA,QAAA;AAEzD,EAAM,MAAA,UAAA,GAAa,UAAW,CAAA,IAAA,CAAK,QAAS,EAAC,CACzC,GAAA,IAAI,IAAK,CAAA,IAAI,CAAE,CAAA,WAAA,EACf,GAAA,IAAA;AAEJ,EAAI,IAAAA,cAAA,CAAS,QAAQ,UAAY,EAAA,EAAE,MAAM,QAAS,EAAC,EAAE,OAAS,EAAA;AAC5D,IAAA,OAAOA,eAAS,OAAQ,CAAA,UAAA,EAAY,EAAE,IAAM,EAAA,QAAA,EAAU,CAAE,CAAA,QAAA;AAAA,MACtD;AAAA,KACF;AAAA;AAEF,EAAO,OAAAA,cAAA,CAAS,UAAW,CAAA,UAAA,EAAY,qBAAuB,EAAA;AAAA,IAC5D,IAAM,EAAA;AAAA,GACP,CAAE,CAAA,QAAA,CAAS,0BAA0B,CAAA;AACxC;;;;;;;;;;"}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
class ValidationError extends Error {
|
4
|
+
details;
|
5
|
+
constructor(message, details) {
|
6
|
+
super(message);
|
7
|
+
this.name = "ValidationError";
|
8
|
+
this.details = details;
|
9
|
+
}
|
10
|
+
}
|
11
|
+
|
12
|
+
exports.ValidationError = ValidationError;
|
13
|
+
//# sourceMappingURL=ValidationError.cjs.js.map
|
@@ -0,0 +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,KAAM,CAAA;AAAA,EAClC,OAAA;AAAA,EAEP,WAAA,CAAY,SAAiB,OAAc,EAAA;AACzC,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAO,GAAA,iBAAA;AACZ,IAAA,IAAA,CAAK,OAAU,GAAA,OAAA;AAAA;AAEnB;;;;"}
|
@@ -0,0 +1,53 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
var zod = require('zod');
|
4
|
+
var luxon = require('luxon');
|
5
|
+
var eventRequest = require('../types/event-request.cjs.js');
|
6
|
+
|
7
|
+
const dateRequiredSchema = (fieldName) => zod.z.string({
|
8
|
+
required_error: `${fieldName} is required. Use YYYY-MM-DD (e.g., 2025-03-02)`
|
9
|
+
});
|
10
|
+
const EventRequestSchema = zod.z.object({
|
11
|
+
grouping: zod.z.enum(["hourly", "weekly", "daily", "monthly"]).optional(),
|
12
|
+
start_date: dateRequiredSchema("start_date"),
|
13
|
+
end_date: dateRequiredSchema("end_date"),
|
14
|
+
limit: zod.z.string().regex(/^\d+$/).transform(Number).optional(),
|
15
|
+
kind: zod.z.string().optional(),
|
16
|
+
type: zod.z.enum(eventRequest.QUERY_TYPES, {
|
17
|
+
errorMap: () => ({
|
18
|
+
message: `Invalid type. Allowed values: ${eventRequest.QUERY_TYPES}`
|
19
|
+
})
|
20
|
+
}),
|
21
|
+
format: zod.z.enum(["csv", "json"], {
|
22
|
+
errorMap: () => ({
|
23
|
+
message: "Invalid format. Allowed values: json, csv"
|
24
|
+
})
|
25
|
+
}).optional()
|
26
|
+
}).superRefine((data, ctx) => {
|
27
|
+
const startDate = luxon.DateTime.fromISO(data.start_date);
|
28
|
+
const endDate = luxon.DateTime.fromISO(data.end_date);
|
29
|
+
if (!startDate.isValid) {
|
30
|
+
ctx.addIssue({
|
31
|
+
code: zod.z.ZodIssueCode.custom,
|
32
|
+
message: "Invalid date format for start_date. Expected YYYY-MM-DD (e.g., 2025-03-02)",
|
33
|
+
path: ["start_date"]
|
34
|
+
});
|
35
|
+
}
|
36
|
+
if (!endDate.isValid) {
|
37
|
+
ctx.addIssue({
|
38
|
+
code: zod.z.ZodIssueCode.custom,
|
39
|
+
message: "Invalid date format for end_date. Expected YYYY-MM-DD (e.g., 2025-03-02)",
|
40
|
+
path: ["end_date"]
|
41
|
+
});
|
42
|
+
}
|
43
|
+
if (startDate.isValid && endDate.isValid && startDate > endDate) {
|
44
|
+
ctx.addIssue({
|
45
|
+
code: zod.z.ZodIssueCode.custom,
|
46
|
+
message: "start_date should not be greater than end_date",
|
47
|
+
path: ["end_date"]
|
48
|
+
});
|
49
|
+
}
|
50
|
+
});
|
51
|
+
|
52
|
+
exports.EventRequestSchema = EventRequestSchema;
|
53
|
+
//# sourceMappingURL=event-request.cjs.js.map
|
@@ -0,0 +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 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,kBAAqB,GAAA,CAAC,SAC1B,KAAAA,KAAA,CAAE,MAAO,CAAA;AAAA,EACP,cAAA,EAAgB,GAAG,SAAS,CAAA,+CAAA;AAC9B,CAAC,CAAA;AAEU,MAAA,kBAAA,GAAqBA,MAC/B,MAAO,CAAA;AAAA,EACN,QAAA,EAAUA,KAAE,CAAA,IAAA,CAAK,CAAC,QAAA,EAAU,UAAU,OAAS,EAAA,SAAS,CAAC,CAAA,CAAE,QAAS,EAAA;AAAA,EACpE,UAAA,EAAY,mBAAmB,YAAY,CAAA;AAAA,EAC3C,QAAA,EAAU,mBAAmB,UAAU,CAAA;AAAA,EACvC,KAAA,EAAOA,KAAE,CAAA,MAAA,EAAS,CAAA,KAAA,CAAM,OAAO,CAAE,CAAA,SAAA,CAAU,MAAM,CAAA,CAAE,QAAS,EAAA;AAAA,EAC5D,IAAM,EAAAA,KAAA,CAAE,MAAO,EAAA,CAAE,QAAS,EAAA;AAAA,EAC1B,IAAA,EAAMA,KAAE,CAAA,IAAA,CAAKC,wBAAa,EAAA;AAAA,IACxB,UAAU,OAAO;AAAA,MACf,OAAA,EAAS,iCAAiCA,wBAAW,CAAA;AAAA,KACvD;AAAA,GACD,CAAA;AAAA,EACD,QAAQD,KACL,CAAA,IAAA,CAAK,CAAC,KAAA,EAAO,MAAM,CAAG,EAAA;AAAA,IACrB,UAAU,OAAO;AAAA,MACf,OAAS,EAAA;AAAA,KACX;AAAA,GACD,EACA,QAAS;AACd,CAAC,CACA,CAAA,WAAA,CAAY,CAAC,IAAA,EAAM,GAAQ,KAAA;AAC1B,EAAA,MAAM,SAAY,GAAAE,cAAA,CAAS,OAAQ,CAAA,IAAA,CAAK,UAAU,CAAA;AAClD,EAAA,MAAM,OAAU,GAAAA,cAAA,CAAS,OAAQ,CAAA,IAAA,CAAK,QAAQ,CAAA;AAE9C,EAAI,IAAA,CAAC,UAAU,OAAS,EAAA;AACtB,IAAA,GAAA,CAAI,QAAS,CAAA;AAAA,MACX,IAAA,EAAMF,MAAE,YAAa,CAAA,MAAA;AAAA,MACrB,OACE,EAAA,4EAAA;AAAA,MACF,IAAA,EAAM,CAAC,YAAY;AAAA,KACpB,CAAA;AAAA;AAGH,EAAI,IAAA,CAAC,QAAQ,OAAS,EAAA;AACpB,IAAA,GAAA,CAAI,QAAS,CAAA;AAAA,MACX,IAAA,EAAMA,MAAE,YAAa,CAAA,MAAA;AAAA,MACrB,OACE,EAAA,0EAAA;AAAA,MACF,IAAA,EAAM,CAAC,UAAU;AAAA,KAClB,CAAA;AAAA;AAGH,EAAA,IAAI,SAAU,CAAA,OAAA,IAAW,OAAQ,CAAA,OAAA,IAAW,YAAY,OAAS,EAAA;AAC/D,IAAA,GAAA,CAAI,QAAS,CAAA;AAAA,MACX,IAAA,EAAMA,MAAE,YAAa,CAAA,MAAA;AAAA,MACrB,OAAS,EAAA,gDAAA;AAAA,MACT,IAAA,EAAM,CAAC,UAAU;AAAA,KAClB,CAAA;AAAA;AAEL,CAAC;;;;"}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
var zod = require('zod');
|
4
|
+
|
5
|
+
const JsonObjectSchema = zod.z.record(
|
6
|
+
zod.z.union([zod.z.string(), zod.z.boolean(), zod.z.number(), zod.z.undefined()])
|
7
|
+
);
|
8
|
+
const EventSchema = zod.z.object({
|
9
|
+
user_ref: zod.z.string({ required_error: "User Id is required" }).min(1),
|
10
|
+
plugin_id: zod.z.string({ required_error: "Plugin ID is required" }).min(1),
|
11
|
+
action: zod.z.string({ required_error: "Action is required" }).min(1),
|
12
|
+
context: zod.z.union([JsonObjectSchema, zod.z.string()]),
|
13
|
+
subject: zod.z.string().optional(),
|
14
|
+
attributes: zod.z.union([JsonObjectSchema, zod.z.string()]).optional(),
|
15
|
+
value: zod.z.number().optional()
|
16
|
+
});
|
17
|
+
|
18
|
+
exports.EventSchema = EventSchema;
|
19
|
+
//# sourceMappingURL=event.cjs.js.map
|
@@ -0,0 +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,KAAE,CAAA,MAAA;AAAA,EACzBA,KAAE,CAAA,KAAA,CAAM,CAACA,KAAA,CAAE,QAAU,EAAAA,KAAA,CAAE,OAAQ,EAAA,EAAGA,MAAE,MAAO,EAAA,EAAGA,KAAE,CAAA,SAAA,EAAW,CAAC;AAC9D,CAAA;AACa,MAAA,WAAA,GAAcA,MAAE,MAAO,CAAA;AAAA,EAClC,QAAA,EAAUA,MAAE,MAAO,CAAA,EAAE,gBAAgB,qBAAsB,EAAC,CAAE,CAAA,GAAA,CAAI,CAAC,CAAA;AAAA,EACnE,SAAA,EAAWA,MAAE,MAAO,CAAA,EAAE,gBAAgB,uBAAwB,EAAC,CAAE,CAAA,GAAA,CAAI,CAAC,CAAA;AAAA,EACtE,MAAA,EAAQA,MAAE,MAAO,CAAA,EAAE,gBAAgB,oBAAqB,EAAC,CAAE,CAAA,GAAA,CAAI,CAAC,CAAA;AAAA,EAChE,OAAA,EAASA,MAAE,KAAM,CAAA,CAAC,kBAAkBA,KAAE,CAAA,MAAA,EAAQ,CAAC,CAAA;AAAA,EAC/C,OAAS,EAAAA,KAAA,CAAE,MAAO,EAAA,CAAE,QAAS,EAAA;AAAA,EAC7B,UAAA,EAAYA,KAAE,CAAA,KAAA,CAAM,CAAC,gBAAA,EAAkBA,MAAE,MAAO,EAAC,CAAC,CAAA,CAAE,QAAS,EAAA;AAAA,EAC7D,KAAO,EAAAA,KAAA,CAAE,MAAO,EAAA,CAAE,QAAS;AAC7B,CAAC;;;;"}
|
@@ -0,0 +1,71 @@
|
|
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
|
+
exports.up = async function (knex) {
|
18
|
+
const client = knex.client.config.client;
|
19
|
+
// Postgres event table schema with partitions, index to boost the performance.
|
20
|
+
if (client === 'pg') {
|
21
|
+
await knex.schema.raw(`
|
22
|
+
CREATE TABLE events (
|
23
|
+
id UUID DEFAULT gen_random_uuid(),
|
24
|
+
action TEXT NOT NULL,
|
25
|
+
subject TEXT NOT NULL,
|
26
|
+
value TEXT,
|
27
|
+
plugin_id TEXT NOT NULL,
|
28
|
+
user_ref TEXT NOT NULL,
|
29
|
+
attributes JSONB NOT NULL,
|
30
|
+
context JSONB NOT NULL,
|
31
|
+
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
32
|
+
PRIMARY KEY (id, created_at)
|
33
|
+
) PARTITION BY RANGE (created_at);
|
34
|
+
`);
|
35
|
+
|
36
|
+
// brin indexing not suppported in better-sqlite3.
|
37
|
+
await knex.schema.raw(`
|
38
|
+
CREATE INDEX idx_events_brin ON events USING brin (created_at);
|
39
|
+
`);
|
40
|
+
} else if (client === 'better-sqlite3') {
|
41
|
+
// better-sqlite3 table does not support partition and jsonb columns.
|
42
|
+
await knex.schema.createTable('events', table => {
|
43
|
+
table.increments('id').primary();
|
44
|
+
table.text('action').notNullable();
|
45
|
+
table.text('subject').notNullable();
|
46
|
+
table.text('value');
|
47
|
+
table.text('plugin_id').notNullable();
|
48
|
+
table.text('user_ref').notNullable();
|
49
|
+
table.text('attributes').notNullable();
|
50
|
+
table.text('context').notNullable();
|
51
|
+
table.timestamp('created_at', { useTz: true }).defaultTo(knex.fn.now());
|
52
|
+
});
|
53
|
+
}
|
54
|
+
|
55
|
+
// Add indexes
|
56
|
+
await knex.schema.alterTable('events', table => {
|
57
|
+
table.index(['created_at', 'user_ref'], 'idx_events_created_at_user_ref');
|
58
|
+
table.index(['created_at', 'plugin_id'], 'idx_events_created_at_plugin');
|
59
|
+
table.index(['action'], 'idx_events_action');
|
60
|
+
table.index(['attributes'], 'idx_events_attributes', { using: 'gin' });
|
61
|
+
table.index(['context'], 'idx_events_context', { using: 'gin' });
|
62
|
+
});
|
63
|
+
};
|
64
|
+
|
65
|
+
/**
|
66
|
+
* @param { import("knex").Knex } knex
|
67
|
+
* @returns { Promise<void> }
|
68
|
+
*/
|
69
|
+
exports.down = async function (knex) {
|
70
|
+
await knex.schema.raw(`DROP TABLE events CASCADE`);
|
71
|
+
};
|