Skip to content

Commit 2ee995b

Browse files
Duckierstone42UZ9
andauthored
Analytics Config CRUD Methods (#215)
Co-authored-by: Yerti <36551149+UZ9@users.noreply.github.com>
1 parent 6b4a616 commit 2ee995b

34 files changed

Lines changed: 1510 additions & 139 deletions

‎.github/workflows/e2e-tests.yml‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,12 @@ jobs:
8383
- uses: pnpm/action-setup@v4
8484
- name: Test
8585
run: pnpm test:e2e:logging-service
86+
analytics-service:
87+
name: Analytics Service E2E Tests
88+
runs-on: ubuntu-latest
89+
steps:
90+
- name: Checkout
91+
uses: actions/checkout@v3
92+
- uses: pnpm/action-setup@v4
93+
- name: Test
94+
run: pnpm test:e2e:analytics-service

‎docker-compose-dev.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,14 @@ services:
9696
- 5000
9797
entrypoint: pnpm --filter analytics-service ${ANALYTICS_COMMAND-start:dev}
9898
environment:
99+
NODE_OPTIONS: --experimental-vm-modules
99100
AUTH_SERVICE_ADDR: auth-service:5000
100101
DB_SERVICE_ADDR: db-service:5000
101102
EMAIL_SERVICE_ADDR: email-service:5000
102103
ANALYTICS_SERVICE_ADDR: analytics-service:5000
103104
LOGGING_SERVICE_ADDR: logging-service:5000
104105
FILE_SERVICE_ADDR: file-service:5000
105106
NODE_ENV: test
106-
SENDGRID_API_KEY: test-key
107107
SENTRY_DSN: 'https://01e85b2d3c5222b4a54d0796db847b55@o4506876382609408.ingest.us.sentry.io/4510019961749504'
108108
networks:
109109
- bog-api-net

‎packages/analytics-service/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"test:watch": "jest --watch",
1818
"test:cov": "jest --coverage",
1919
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
20-
"test:e2e": "jest --config ./test/jest-e2e.json --runInBand",
20+
"test:e2e": "NODE_OPTIONS=--experimental-vm-modules jest --config ./test/jest-e2e.json --runInBand",
2121
"test:e2e:watch": "jest --config ./test/jest-e2e.json --runInBand --watchAll",
2222
"tsc": "tsc",
2323
"sentry:sourcemaps": "sentry-cli sourcemaps inject --org bits-of-good-development --project juno-analytics-service ./dist && sentry-cli sourcemaps upload --org bits-of-good-development --project juno-analytics-service ./dist"

‎packages/analytics-service/src/app.module.ts‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,15 @@ import { AppService } from './app.service';
44
import { SentryModule } from '@sentry/nestjs/setup';
55
import { HealthModule } from './modules/health/health.module';
66
import { AnalyticsModule } from './modules/analytics/analytics.module';
7+
import { AnalyticsConfigModule } from './modules/analytics_config/analytics_config.module';
78

89
@Module({
9-
imports: [SentryModule.forRoot(), HealthModule, AnalyticsModule],
10+
imports: [
11+
SentryModule.forRoot(),
12+
HealthModule,
13+
AnalyticsModule,
14+
AnalyticsConfigModule,
15+
],
1016
controllers: [AppController],
1117
providers: [AppService],
1218
})

‎packages/analytics-service/src/main.ts‎

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@ import { MicroserviceOptions, Transport } from '@nestjs/microservices';
55
import { join } from 'path';
66
import { ConfigModule } from '@nestjs/config';
77
import { SentryFilter } from './sentry.filter';
8-
import { HealthProto, HealthProtoFile } from 'juno-proto';
8+
import {
9+
AnalyticsProto,
10+
HealthProto,
11+
HealthProtoFile,
12+
AnalyticsConfigProto,
13+
AnalyticsConfigProtoFile,
14+
AnalyticsProtoFile,
15+
} from 'juno-proto';
916

1017
async function bootstrap() {
1118
ConfigModule.forRoot({
@@ -16,8 +23,16 @@ async function bootstrap() {
1623
{
1724
transport: Transport.GRPC,
1825
options: {
19-
package: [HealthProto.GRPC_HEALTH_V1_PACKAGE_NAME],
20-
protoPath: [HealthProtoFile],
26+
package: [
27+
HealthProto.GRPC_HEALTH_V1_PACKAGE_NAME,
28+
AnalyticsProto.JUNO_ANALYTICS_SERVICE_ANALYTICS_PACKAGE_NAME,
29+
AnalyticsConfigProto.JUNO_ANALYTICS_SERVICE_ANALYTICS_CONFIG_PACKAGE_NAME,
30+
],
31+
protoPath: [
32+
HealthProtoFile,
33+
AnalyticsProtoFile,
34+
AnalyticsConfigProtoFile,
35+
],
2136
url: process.env.ANALYTICS_SERVICE_ADDR,
2237
},
2338
},

‎packages/analytics-service/src/modules/analytics/analytics.module.ts‎

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import { Module } from '@nestjs/common';
22
import { ClientsModule, Transport } from '@nestjs/microservices';
33
import { AnalyticsProto, AnalyticsProtoFile } from 'juno-proto';
4-
import { BogAnalyticsService } from 'src/bog-analytics.service';
54
import { AnalyticsController } from './analytics.controller';
65
import { AnalyticsService } from './analytics.service';
7-
import { EventEnvironment } from 'bog-analytics';
6+
import { AnalyticsConfigModule } from '../analytics_config/analytics_config.module';
87

98
const {
109
ANALYTICS_SERVICE_NAME,
@@ -23,15 +22,22 @@ const {
2322
},
2423
},
2524
]),
25+
AnalyticsConfigModule,
2626
],
2727
controllers: [AnalyticsController],
2828
providers: [
2929
AnalyticsService,
3030
{
31-
provide: BogAnalyticsService,
32-
useFactory: () => {
33-
return new BogAnalyticsService({
34-
environment: EventEnvironment.DEVELOPMENT, // TODO: we'll have to switch this later
31+
provide: 'BOG_ANALYTICS',
32+
useFactory: async () => {
33+
// bog-analytics is absolutely cursed and outdated, have to do some shenanigans
34+
// to get around ESM import
35+
const loadModule = eval('(specifier) => import(specifier)');
36+
const { AnalyticsLogger, EventEnvironment } =
37+
await loadModule('bog-analytics');
38+
39+
return new AnalyticsLogger({
40+
environment: EventEnvironment.DEVELOPMENT,
3541
});
3642
},
3743
},

‎packages/analytics-service/src/modules/analytics/analytics.service.ts‎

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,38 @@
11
import { status } from '@grpc/grpc-js';
2-
import { Injectable } from '@nestjs/common';
2+
import { Inject, Injectable } from '@nestjs/common';
33
import { RpcException } from '@nestjs/microservices';
44
import { AnalyticsProto } from 'juno-proto';
5-
import { BogAnalyticsService } from 'src/bog-analytics.service';
5+
import { AnalyticsConfigService } from '../analytics_config/analytics_config.service';
66

77
@Injectable()
88
export class AnalyticsService {
9-
constructor(private readonly bogAnalytics: BogAnalyticsService) {}
9+
constructor(
10+
@Inject('BOG_ANALYTICS') private readonly bogAnalytics: any,
11+
private readonly analyticsConfigService: AnalyticsConfigService,
12+
) {}
1013

11-
authenticateAnalytics(key: string) {
14+
async authenticateAnalytics(projectId: number, environment: string) {
1215
try {
16+
const key = await this.analyticsConfigService.getAnalyticsKey(
17+
projectId,
18+
environment,
19+
);
1320
this.bogAnalytics.authenticate(key);
1421
} catch (e) {
1522
throw new RpcException({
1623
code: status.UNAUTHENTICATED,
17-
message: 'Invalid API key',
24+
message: 'Invalid API key or analytics config not found',
1825
});
1926
}
2027
}
2128

2229
async logClickEvent(
2330
event: AnalyticsProto.ClickEventRequest,
2431
): Promise<AnalyticsProto.ClickEventResponse> {
25-
this.authenticateAnalytics(event.apiKey);
32+
await this.authenticateAnalytics(
33+
Number(event.projectId),
34+
event.environment,
35+
);
2636

2737
if (
2838
!event ||
@@ -62,7 +72,10 @@ export class AnalyticsService {
6272
async logInputEvent(
6373
event: AnalyticsProto.InputEventRequest,
6474
): Promise<AnalyticsProto.InputEventResponse> {
65-
this.authenticateAnalytics(event.apiKey);
75+
await this.authenticateAnalytics(
76+
Number(event.projectId),
77+
event.environment,
78+
);
6679

6780
if (
6881
!event ||
@@ -106,7 +119,10 @@ export class AnalyticsService {
106119
async logVisitEvent(
107120
event: AnalyticsProto.VisitEventRequest,
108121
): Promise<AnalyticsProto.VisitEventResponse> {
109-
this.authenticateAnalytics(event.apiKey);
122+
await this.authenticateAnalytics(
123+
Number(event.projectId),
124+
event.environment,
125+
);
110126

111127
if (
112128
!event ||
@@ -146,7 +162,10 @@ export class AnalyticsService {
146162
async logCustomEvent(
147163
event: AnalyticsProto.CustomEventRequest,
148164
): Promise<AnalyticsProto.CustomEventResponse> {
149-
this.authenticateAnalytics(event.apiKey);
165+
await this.authenticateAnalytics(
166+
Number(event.projectId),
167+
event.environment,
168+
);
150169

151170
if (
152171
!event ||
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Module } from '@nestjs/common';
2+
import { ClientsModule, Transport } from '@nestjs/microservices';
3+
import { AnalyticsConfigProto, AnalyticsConfigProtoFile } from 'juno-proto';
4+
import { AnalyticsConfigService } from './analytics_config.service';
5+
6+
const {
7+
ANALYTICS_CONFIG_DB_SERVICE_NAME,
8+
JUNO_ANALYTICS_SERVICE_ANALYTICS_CONFIG_PACKAGE_NAME,
9+
} = AnalyticsConfigProto;
10+
11+
@Module({
12+
imports: [
13+
ClientsModule.register([
14+
{
15+
name: ANALYTICS_CONFIG_DB_SERVICE_NAME,
16+
transport: Transport.GRPC,
17+
options: {
18+
url: process.env.DB_SERVICE_ADDR,
19+
package: JUNO_ANALYTICS_SERVICE_ANALYTICS_CONFIG_PACKAGE_NAME,
20+
protoPath: AnalyticsConfigProtoFile,
21+
},
22+
},
23+
]),
24+
],
25+
providers: [AnalyticsConfigService],
26+
exports: [AnalyticsConfigService],
27+
})
28+
export class AnalyticsConfigModule {}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { Injectable, Inject, OnModuleInit } from '@nestjs/common';
2+
import { ClientGrpc } from '@nestjs/microservices';
3+
import { AnalyticsConfigProto } from 'juno-proto';
4+
import { lastValueFrom } from 'rxjs';
5+
6+
const { ANALYTICS_CONFIG_DB_SERVICE_NAME } = AnalyticsConfigProto;
7+
8+
@Injectable()
9+
export class AnalyticsConfigService implements OnModuleInit {
10+
private analyticsConfigDbService: AnalyticsConfigProto.AnalyticsConfigDbServiceClient;
11+
12+
constructor(
13+
@Inject(ANALYTICS_CONFIG_DB_SERVICE_NAME)
14+
private analyticsConfigDbClient: ClientGrpc,
15+
) {}
16+
17+
onModuleInit() {
18+
this.analyticsConfigDbService =
19+
this.analyticsConfigDbClient.getService<AnalyticsConfigProto.AnalyticsConfigDbServiceClient>(
20+
ANALYTICS_CONFIG_DB_SERVICE_NAME,
21+
);
22+
}
23+
24+
async getAnalyticsKey(
25+
projectId: number,
26+
environment: string,
27+
): Promise<string> {
28+
const config = await lastValueFrom(
29+
this.analyticsConfigDbService.readAnalyticsConfig({
30+
id: projectId,
31+
environment,
32+
}),
33+
);
34+
return config.analyticsKey;
35+
}
36+
}

0 commit comments

Comments
 (0)