From 1f78730782fd363e1b86345bf4cd8d6137265671 Mon Sep 17 00:00:00 2001 From: Rishabh Jain Date: Mon, 21 Aug 2023 18:55:50 +0530 Subject: [PATCH 1/6] feat : added Custom posthog events --- apps/api/src/app/app.controller.ts | 34 ++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/apps/api/src/app/app.controller.ts b/apps/api/src/app/app.controller.ts index 1490f601..c85f7498 100644 --- a/apps/api/src/app/app.controller.ts +++ b/apps/api/src/app/app.controller.ts @@ -32,6 +32,7 @@ import { link as LinkModel, Prisma, link } from '@prisma/client'; import { AddROToResponseInterceptor } from './interceptors/addROToResponseInterceptor'; import { ApiBody, ApiOperation, ApiResponse } from '@nestjs/swagger'; import { ConfigService } from '@nestjs/config'; +import { TelemetryService } from './telemetry/telemetry.service'; @Controller() @UseInterceptors(AddROToResponseInterceptor) @@ -47,6 +48,7 @@ export class AppController { private prismaIndicator: PrismaHealthIndicator, private readonly configService: ConfigService, private readonly redisService: RedisService, + private readonly telemetryService: TelemetryService, @Inject('CLICK_SERVICE') private clickServiceClient: ClientProxy ) { this.redis = redisService.getClient(configService.get('REDIS_NAME')); @@ -101,6 +103,18 @@ export class AppController { hashid: hashid, }) .subscribe(); + // if we shift this also in service layer then maybe we can capture better data but then it will be a bit slow + // as well a bit more complex + // tight coupling with the service layer + this.telemetryService.sendEvent( + this.configService.get("POSTHOG_DISTINCT_KEY"), + `Redirected Link`, + { + routeName: `/${hashid}}`, + routeParam: hashid, + link: reRouteURL, + } + ); return res.redirect(302, reRouteURL); } else { throw new NotFoundException(); @@ -114,6 +128,16 @@ export class AppController { @ApiResponse({ type: Link, status: 200}) async register(@Body() link: Link): Promise { const response:Promise = this.appService.createLinkInDB(link); + // discuss this stuff , shouuld we send the event here or in the service + // or we make this await and then send the event + this.telemetryService.sendEvent( + this.configService.get("POSTHOG_DISTINCT_KEY"), + `Created Link`, + { + routeName: `/register`, + link: link, + } + ); return response; } @@ -122,6 +146,16 @@ export class AppController { @ApiBody({ type: Link }) @ApiResponse({ type: Link, status: 200}) async update(@Param('id') id: string, @Body() link: link ): Promise { + // make this a separate function in telemetry service + this.telemetryService.sendEvent( + this.configService.get("POSTHOG_DISTINCT_KEY"), + `Updated Link`, + { + routeName: `update/:id`, + routeParam: id, + link: link, + } + ); return this.appService.updateLink(id, link); } From 3fa22cbbe16dd9a632be08b7e5c3135f3642dbfa Mon Sep 17 00:00:00 2001 From: Rishabh Jain Date: Tue, 22 Aug 2023 00:53:04 +0530 Subject: [PATCH 2/6] feat: enhanced data capture for update and create --- apps/api/src/app/app.controller.ts | 55 ++++++++++--------- .../addROToResponseInterceptor.ts | 2 +- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/apps/api/src/app/app.controller.ts b/apps/api/src/app/app.controller.ts index c85f7498..53a2b49d 100644 --- a/apps/api/src/app/app.controller.ts +++ b/apps/api/src/app/app.controller.ts @@ -103,9 +103,9 @@ export class AppController { hashid: hashid, }) .subscribe(); - // if we shift this also in service layer then maybe we can capture better data but then it will be a bit slow - // as well a bit more complex - // tight coupling with the service layer + + // id of link model + // params : query params this.telemetryService.sendEvent( this.configService.get("POSTHOG_DISTINCT_KEY"), `Redirected Link`, @@ -128,17 +128,18 @@ export class AppController { @ApiResponse({ type: Link, status: 200}) async register(@Body() link: Link): Promise { const response:Promise = this.appService.createLinkInDB(link); - // discuss this stuff , shouuld we send the event here or in the service - // or we make this await and then send the event - this.telemetryService.sendEvent( - this.configService.get("POSTHOG_DISTINCT_KEY"), - `Created Link`, - { - routeName: `/register`, - link: link, - } - ); - return response; + return response + .then((createdLink) => { + this.telemetryService.sendEvent( + this.configService.get("POSTHOG_DISTINCT_KEY"), + `Created Link`, + { + routeName: `/register`, + link: createdLink, + } + ); + return createdLink; + }); } @Patch('update/:id') @@ -146,17 +147,21 @@ export class AppController { @ApiBody({ type: Link }) @ApiResponse({ type: Link, status: 200}) async update(@Param('id') id: string, @Body() link: link ): Promise { - // make this a separate function in telemetry service - this.telemetryService.sendEvent( - this.configService.get("POSTHOG_DISTINCT_KEY"), - `Updated Link`, - { - routeName: `update/:id`, - routeParam: id, - link: link, - } - ); - return this.appService.updateLink(id, link); + + return this.appService.updateLink(id, link) + .then((updatedLink) => { + this.telemetryService.sendEvent( + this.configService.get("POSTHOG_DISTINCT_KEY"), + `Updated Link`, + { + linkId:updatedLink.id, + routeName: `update/:id`, + link: updatedLink, + updatedFields: Object.keys(link), + } + ); + return updatedLink; + }) } @MessagePattern('onClick') diff --git a/apps/api/src/app/interceptors/addROToResponseInterceptor.ts b/apps/api/src/app/interceptors/addROToResponseInterceptor.ts index 0b31ce59..10cd34f4 100644 --- a/apps/api/src/app/interceptors/addROToResponseInterceptor.ts +++ b/apps/api/src/app/interceptors/addROToResponseInterceptor.ts @@ -43,7 +43,7 @@ import { URLSearchParams } from 'url'; this.telemetryService.sendEvent( this.configService.get("POSTHOG_DISTINCT_KEY"), - `${url} Execution Time`, + `Endpoint accessed`, { routeName: name, executionTime: `${Date.now() - now}ms`, From 341d6e7ab6f5999213521f7040473e0b36004be0 Mon Sep 17 00:00:00 2001 From: Rishabh Jain Date: Tue, 22 Aug 2023 01:28:00 +0530 Subject: [PATCH 3/6] Feat: enhanched redirected telemetry support --- apps/api/src/app/app.controller.ts | 9 +++++---- apps/api/src/app/app.service.ts | 29 ++++++++++++++++++----------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/apps/api/src/app/app.controller.ts b/apps/api/src/app/app.controller.ts index 53a2b49d..578732f6 100644 --- a/apps/api/src/app/app.controller.ts +++ b/apps/api/src/app/app.controller.ts @@ -94,7 +94,9 @@ export class AppController { @ApiResponse({ status: 301, description: 'will be redirected to the specified link'}) async redirect(@Param('hashid') hashid: string, @Res() res) { - const reRouteURL: string = await this.appService.resolveRedirect(hashid); + const response = await this.appService.resolveRedirect(hashid); + const reRouteURL: string = response?.reRouteurl; + const redirectedLink: LinkModel = response?.redirectedLink; if (reRouteURL !== '') { console.log({reRouteURL}); @@ -104,14 +106,13 @@ export class AppController { }) .subscribe(); - // id of link model - // params : query params this.telemetryService.sendEvent( this.configService.get("POSTHOG_DISTINCT_KEY"), `Redirected Link`, { + linkId:redirectedLink.id, routeName: `/${hashid}}`, - routeParam: hashid, + routeParam: redirectedLink?.params, link: reRouteURL, } ); diff --git a/apps/api/src/app/app.service.ts b/apps/api/src/app/app.service.ts index 9c84d2b1..13e5ff63 100644 --- a/apps/api/src/app/app.service.ts +++ b/apps/api/src/app/app.service.ts @@ -187,7 +187,7 @@ export class AppService { * @param Id * @returns */ - async resolveRedirect(Id: string): Promise { + async resolveRedirect(Id: string): Promise<{reRouteurl:string,redirectedLink:link}> { const validHashIdRegex = /^[0-9]*$/; if(validHashIdRegex.test(Id)){ return this.redirect(Id); @@ -200,7 +200,7 @@ export class AppService { where: { customHashId: Id}, }); - let response = ""; + let response = { reRouteurl : "" , redirectedLink:null }; !(linkData == null) ? response = await this.redirect(linkData.hashid.toString()):0; return response; } @@ -216,7 +216,7 @@ export class AppService { * @param hashid * @returns */ - async redirect(hashid: string): Promise { + async redirect(hashid: string): Promise<{reRouteurl:string,redirectedLink:link}> { return this.redisUtils.fetchKey(hashid).then((value: string) => { const link = JSON.parse(value); @@ -226,16 +226,19 @@ export class AppService { const ret = []; if(params?.["status"] == "expired"){ - return ""; + return { reRouteurl : '' , redirectedLink:null }; + // return ""; } if(params == null){ - return url; + return { reRouteurl : url , redirectedLink:link }; + // return url; }else { Object.keys(params).forEach(function(d) { ret.push(encodeURIComponent(d) + '=' + encodeURIComponent(params[d])); }) - return `${url}?${ret.join('&')}` || ''; + return { reRouteurl : `${url}?${ret.join('&')}` || '' , redirectedLink:link }; + // return `${url}?${ret.join('&')}` || ''; } }) .catch(err => { @@ -251,7 +254,7 @@ export class AppService { * @param hashid * @returns */ - async redirectFromDB(hashid: string): Promise { + async redirectFromDB(hashid: string): Promise<{reRouteurl:string , redirectedLink:link}> { return this.prisma.link.findMany({ where: { OR: [ @@ -278,23 +281,27 @@ export class AppService { // delete from DB and redis !!! // this.deleteLink({id: response[0].id}); // don't delete from DB keep it there this.redisUtils.clearKey(response[0]); - return ""; + return { reRouteurl : "" , redirectedLink: null }; + // return ""; } this.redisUtils.setKey(response[0]); if(params == null){ - return url; + return { reRouteurl : url , redirectedLink: null }; + // return url; }else { Object.keys(params).forEach(function(d) { ret.push(encodeURIComponent(d) + '=' + encodeURIComponent(params[d])); }) - return `${url}?${ret.join('&')}` || ''; + return { reRouteurl : `${url}?${ret.join('&')}` || '' , redirectedLink:response[0] }; + // return `${url}?${ret.join('&')}` || ''; } }) .catch(err => { this.telemetryService.sendEvent(this.configService.get('POSTHOG_DISTINCT_KEY'), "Exception in getLinkFromHashIdOrCustomHashId query", {error: err.message}) - return ''; + return { reRouteurl : "" , redirectedLink: null }; + // return ''; }); } } From d39a38d736bdc752670ca4f67595b5e83e3ce32b Mon Sep 17 00:00:00 2001 From: Rishabh Jain Date: Tue, 22 Aug 2023 18:03:16 +0530 Subject: [PATCH 4/6] chores : modified redirect event data name --- apps/api/src/app/app.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api/src/app/app.controller.ts b/apps/api/src/app/app.controller.ts index 578732f6..8daecd23 100644 --- a/apps/api/src/app/app.controller.ts +++ b/apps/api/src/app/app.controller.ts @@ -112,7 +112,7 @@ export class AppController { { linkId:redirectedLink.id, routeName: `/${hashid}}`, - routeParam: redirectedLink?.params, + queryParams : redirectedLink?.params, link: reRouteURL, } ); From 92154f7c1cc2556ee6aa78acc103e8015f225a29 Mon Sep 17 00:00:00 2001 From: Rishabh Jain Date: Tue, 22 Aug 2023 18:36:27 +0530 Subject: [PATCH 5/6] enhanced redirect telemetry --- apps/api/src/app/app.controller.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/api/src/app/app.controller.ts b/apps/api/src/app/app.controller.ts index 8daecd23..552c340d 100644 --- a/apps/api/src/app/app.controller.ts +++ b/apps/api/src/app/app.controller.ts @@ -113,7 +113,8 @@ export class AppController { linkId:redirectedLink.id, routeName: `/${hashid}}`, queryParams : redirectedLink?.params, - link: reRouteURL, + originalUrl: redirectedLink?.url, + redirectUrl: reRouteURL, } ); return res.redirect(302, reRouteURL); From 3a426c16eca634de29f0e01877dac854f624e81d Mon Sep 17 00:00:00 2001 From: Rishabh Jain Date: Fri, 25 Aug 2023 23:15:46 +0530 Subject: [PATCH 6/6] fix : params in the redirected url --- apps/api/src/app/app.service.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/apps/api/src/app/app.service.ts b/apps/api/src/app/app.service.ts index 13e5ff63..1e5bc6ad 100644 --- a/apps/api/src/app/app.service.ts +++ b/apps/api/src/app/app.service.ts @@ -221,7 +221,7 @@ export class AppService { return this.redisUtils.fetchKey(hashid).then((value: string) => { const link = JSON.parse(value); - const url = link.url + let url = link.url const params = link.params const ret = []; @@ -237,7 +237,11 @@ export class AppService { Object.keys(params).forEach(function(d) { ret.push(encodeURIComponent(d) + '=' + encodeURIComponent(params[d])); }) - return { reRouteurl : `${url}?${ret.join('&')}` || '' , redirectedLink:link }; + // check if url already has query params + const queryParams = new URL(url).searchParams; + url = ((queryParams.toString() === "") ? `${url}?${ret.join('&')}` || '' : `${url}&${ret.join('&')}` || ''); + + return { reRouteurl : url , redirectedLink:link }; // return `${url}?${ret.join('&')}` || ''; } }) @@ -267,7 +271,7 @@ export class AppService { take: 1 }) .then(response => { - const url = response[0].url + let url = response[0].url const params = response[0].params const ret = []; @@ -294,7 +298,12 @@ export class AppService { Object.keys(params).forEach(function(d) { ret.push(encodeURIComponent(d) + '=' + encodeURIComponent(params[d])); }) - return { reRouteurl : `${url}?${ret.join('&')}` || '' , redirectedLink:response[0] }; + + // check if url already has query params + const queryParams = new URL(url).searchParams; + url = ((queryParams.toString() === "") ? `${url}?${ret.join('&')}` || '' : `${url}&${ret.join('&')}` || ''); + + return { reRouteurl : url , redirectedLink:response[0] }; // return `${url}?${ret.join('&')}` || ''; } })