Skip to content

Commit be685a9

Browse files
committed
add caching to user search API
1 parent fa36170 commit be685a9

File tree

3 files changed

+127
-8
lines changed

3 files changed

+127
-8
lines changed

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"@sentry/minimal": "^6.19.7",
4343
"@sentry/node": "^7.57.0",
4444
"ajv": "^8.12.0",
45+
"axios": "^1.7.9",
4546
"class-transformer": "^0.5.1",
4647
"class-validator": "^0.14.0",
4748
"crypto-js": "^4.1.1",

src/api/api.controller.ts

+122-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import {
1414
ValidationPipe,
1515
All,
1616
Req,
17+
HttpException,
18+
HttpStatus,
1719
} from '@nestjs/common';
1820
import {
1921
SignupResponse,
@@ -43,6 +45,8 @@ import { GupshupWhatsappService } from './sms/gupshupWhatsapp/gupshupWhatsapp.se
4345
import { TelemetryService } from 'src/telemetry/telemetry.service';
4446
// eslint-disable-next-line @typescript-eslint/no-var-requires
4547
const CryptoJS = require('crypto-js');
48+
import { InjectRedis } from '@nestjs-modules/ioredis';
49+
import Redis from 'ioredis';
4650

4751
CryptoJS.lib.WordArray.words;
4852

@@ -56,7 +60,8 @@ export class ApiController {
5660
private readonly apiService: ApiService,
5761
private readonly configResolverService: ConfigResolverService,
5862
private readonly gupshupWhatsappService: GupshupWhatsappService,
59-
private readonly telemetryService: TelemetryService
63+
private readonly telemetryService: TelemetryService,
64+
@InjectRedis() private readonly redis: Redis
6065
) {}
6166

6267
@Get()
@@ -438,6 +443,122 @@ export class ApiController {
438443
return await this.apiService.logout(body.token);
439444
}
440445

446+
@Get('user/search')
447+
async searchUserFA(
448+
@Headers('Authorization') authorization: string,
449+
@Headers('X-FusionAuth-Application-Id') appId: string,
450+
@Query() query: any,
451+
@Param() params: any,
452+
) {
453+
if (!authorization || !appId) {
454+
throw new HttpException(
455+
'Authorization and X-FusionAuth-Application-Id headers are required',
456+
HttpStatus.BAD_REQUEST,
457+
);
458+
}
459+
460+
const fusionAuthBaseUrl = this.configService.get('FUSIONAUTH_BASE_URL');
461+
const url = new URL(`${fusionAuthBaseUrl}/api/user/search`);
462+
// Add query params to URL
463+
if (query) {
464+
Object.keys(query).forEach(key => {
465+
url.searchParams.append(key, query[key]);
466+
});
467+
}
468+
469+
// Add params to URL
470+
if (params) {
471+
Object.keys(params).forEach(key => {
472+
url.searchParams.append(key, params[key]);
473+
});
474+
}
475+
476+
const cacheKey = `search_${url}`;
477+
const cachedData = await this.redis.get(cacheKey);
478+
if (cachedData) {
479+
return JSON.parse(cachedData);
480+
}
481+
482+
const searchData = await this.searchUserData(url, authorization, appId);
483+
484+
await this.redis.set(cacheKey, JSON.stringify(searchData));
485+
return searchData;
486+
}
487+
488+
@Get('user/:id')
489+
async getUserById(
490+
@Param('id') id: string,
491+
@Headers('Authorization') authorization: string,
492+
@Headers('X-FusionAuth-Application-Id') appId: string,
493+
) {
494+
if (!authorization || !appId) {
495+
throw new HttpException(
496+
'Authorization and X-FusionAuth-Application-Id headers are required',
497+
HttpStatus.BAD_REQUEST,
498+
);
499+
}
500+
501+
const cacheKey = `user_${id}`;
502+
const cachedData = await this.redis.get(cacheKey);
503+
if (cachedData) {
504+
return JSON.parse(cachedData);
505+
}
506+
507+
const userData = await this.fetchUserDataFromService(id, authorization, appId);
508+
509+
await this.redis.set(cacheKey, JSON.stringify(userData));
510+
return userData;
511+
}
512+
513+
private async fetchUserDataFromService(id: string, authorization: string, appId: string) {
514+
try {
515+
const fusionAuthBaseUrl = this.configService.get('FUSIONAUTH_BASE_URL');
516+
const url = new URL(`${fusionAuthBaseUrl}/api/user/${id}`);
517+
const response = await fetch(
518+
url,
519+
{
520+
method: "GET",
521+
headers: {
522+
Authorization: authorization,
523+
'Content-Type': 'application/json',
524+
'X-FusionAuth-Application-Id': appId,
525+
},
526+
},
527+
);
528+
return response.json();
529+
} catch (error) {
530+
throw new HttpException(
531+
`Failed to fetch user data: ${error.message}`,
532+
HttpStatus.INTERNAL_SERVER_ERROR,
533+
);
534+
}
535+
}
536+
537+
private async searchUserData(
538+
url: URL,
539+
authorization: string,
540+
appId: string,
541+
) {
542+
try {
543+
const response = await fetch(
544+
url,
545+
{
546+
headers: {
547+
Authorization: authorization,
548+
'Content-Type': 'application/json',
549+
'X-FusionAuth-Application-Id': appId,
550+
}
551+
},
552+
);
553+
return response.json();
554+
} catch (error) {
555+
throw new HttpException(
556+
`Failed to search user data: ${error.message}`,
557+
HttpStatus.INTERNAL_SERVER_ERROR,
558+
);
559+
}
560+
}
561+
441562
@All('*')
442563
async defaultRoute(
443564
@Req() request: Request,

src/api/sms/gupshupWhatsapp/gupshupWhatsapp.service.ts

+4-7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { SMSData, SMSProvider, SMSResponse, SMSResponseStatus } from "../sms.int
22
import { InjectRedis } from '@nestjs-modules/ioredis';
33
import Redis from 'ioredis';
44
import { Injectable } from "@nestjs/common";
5-
5+
import axios from 'axios';
66

77
@Injectable()
88
export class GupshupWhatsappService {
@@ -41,8 +41,7 @@ export class GupshupWhatsappService {
4141

4242
let optinURL = process.env.GUPSHUP_WHATSAPP_BASEURL + '?' + optInParams.toString();
4343

44-
await fetch(optinURL, {
45-
method: 'GET',
44+
await axios.get(optinURL, {
4645
headers: {
4746
'Content-Type': 'application/json'
4847
}
@@ -65,8 +64,7 @@ export class GupshupWhatsappService {
6564

6665
let sendOtpURL = process.env.GUPSHUP_WHATSAPP_BASEURL + '?' + sendOtpParams.toString();
6766

68-
const response = await fetch(sendOtpURL, {
69-
method: 'GET',
67+
const response = await axios.get(sendOtpURL, {
7068
headers: {
7169
'Content-Type': 'application/json'
7270
}
@@ -76,7 +74,7 @@ export class GupshupWhatsappService {
7674
// Store OTP in Redis with 30 minute expiry
7775
await this.redis.set(`whatsapp_otp:${smsData.phone}`, otp.toString(), 'EX', 1800);
7876

79-
status.providerSuccessResponse = await response.text();
77+
status.providerSuccessResponse = JSON.stringify(response.data);
8078
status.status = SMSResponseStatus.success;
8179
status.messageID = otp.toString();
8280
}
@@ -107,7 +105,6 @@ export class GupshupWhatsappService {
107105
try {
108106
// Get stored OTP from Redis
109107
const storedOTP = await this.redis.get(`whatsapp_otp:${phone}`);
110-
console.log("storedOTP",storedOTP)
111108

112109
if (!storedOTP) {
113110
status.error = {

0 commit comments

Comments
 (0)