Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added geolocation APIs in CREDEBL. #796

Merged
merged 9 commits into from
Jun 24, 2024
15 changes: 8 additions & 7 deletions apps/api-gateway/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import * as redisStore from 'cache-manager-redis-store';
import { WebhookModule } from './webhook/webhook.module';
import { UtilitiesModule } from './utilities/utilities.module';
import { NotificationModule } from './notification/notification.module';
import { GeoLocationModule } from './geo-location/geo-location.module';
import { CommonConstants } from '@credebl/common/common.constant';

@Module({
Expand Down Expand Up @@ -54,15 +55,18 @@ import { CommonConstants } from '@credebl/common/common.constant';
UtilitiesModule,
WebhookModule,
NotificationModule,
CacheModule.register({ store: redisStore, host: process.env.REDIS_HOST, port: process.env.REDIS_PORT })
CacheModule.register({ store: redisStore, host: process.env.REDIS_HOST, port: process.env.REDIS_PORT }),
GeoLocationModule
],
controllers: [AppController],
providers: [AppService]
})
export class AppModule {
configure(userContext: MiddlewareConsumer): void {
userContext.apply(AuthzMiddleware)
.exclude({ path: 'authz', method: RequestMethod.ALL },
userContext
.apply(AuthzMiddleware)
.exclude(
{ path: 'authz', method: RequestMethod.ALL },
'authz/:splat*',
'admin/subscriptions',
'registry/organizations/',
Expand Down Expand Up @@ -91,9 +95,6 @@ export class AppModule {
'issue-credentials/national-id',
'labels/:id'
)
.forRoutes(
AgentController,
RevocationController
);
.forRoutes(AgentController, RevocationController);
}
}
64 changes: 64 additions & 0 deletions apps/api-gateway/src/geo-location/geo-location.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Controller, Get, HttpStatus, Logger, Param, Res } from '@nestjs/common';
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { GeoLocationService } from './geo-location.service';
import { ApiResponseDto } from '../dtos/apiResponse.dto';
import IResponseType from '@credebl/common/interfaces/response.interface';
import { ResponseMessages } from '@credebl/common/response-messages';
import { Response } from 'express';
@Controller('/')
@ApiTags('geolocation')
export class GeoLocationController {
constructor(
private readonly geolocationService: GeoLocationService,
private readonly logger: Logger
) {}

/**
* @returns get all countries
*/
@Get('countries')
@ApiOperation({ summary: 'Retrieve a list of all countries', description: 'Fetches and returns the details of all available countries.' })
@ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto })
async getAllCountries(@Res() res: Response): Promise<Response> {
const countriesDetails = await this.geolocationService.getAllCountries();
const finalResponse: IResponseType = {
statusCode: HttpStatus.OK,
message: ResponseMessages.user.success.countriesVerificationCode,
data: countriesDetails
};
return res.status(HttpStatus.OK).json(finalResponse);
}

/**
* @returns get all states by countryId
*/

@Get('countries/:countryId/states')
@ApiOperation({summary: 'Retrieve a list of all states within a specified country', description: 'Fetches and returns the details of all states associated with a given countryId. '
})
@ApiResponse({status: HttpStatus.OK, description: 'Success', type: ApiResponseDto})
async getStatesByCountryId(@Param('countryId') countryId: number, @Res() res: Response): Promise<Response> {
const statesDetails = await this.geolocationService.getStatesByCountryId(countryId);
const finalResponse: IResponseType = {
statusCode: HttpStatus.OK,
message: ResponseMessages.user.success.stateVerificationCode,
data: statesDetails
};
return res.status(HttpStatus.OK).json(finalResponse);
}
/**
* @returns get all cities by countryId and stateId
*/
@Get('countries/:countryId/states/:stateId/cities')
@ApiOperation({summary: 'Retrieve a list of all cities within a specified state and country', description: 'Fetches and returns the details of all cities associated with a given countryId and stateId'})
@ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto })
async getCitiesByStateAndCountry(@Param('countryId') countryId: number, @Param('stateId') stateId: number, @Res() res: Response): Promise<Response> {
const citiesDetails = await this.geolocationService.getCitiesByStateAndCountry(countryId, stateId);
const finalResponse: IResponseType = {
statusCode: HttpStatus.OK,
message: ResponseMessages.user.success.cityVerificationCode,
data: citiesDetails
};
return res.status(HttpStatus.OK).json(finalResponse);
}
}
41 changes: 41 additions & 0 deletions apps/api-gateway/src/geo-location/geo-location.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Logger, Module } from '@nestjs/common';
import { GeoLocationController } from './geo-location.controller';
import { GeoLocationService } from './geo-location.service';
import { HttpModule } from '@nestjs/axios';
import { ConfigModule } from '@nestjs/config';
import { ClientsModule, Transport } from '@nestjs/microservices';
import { getNatsOptions } from '@credebl/common/nats.config';
import { CommonModule } from '@credebl/common';
import { RateLimiterModule, RateLimiterGuard } from 'nestjs-rate-limiter';
import { APP_GUARD } from '@nestjs/core';

@Module({
imports: [
HttpModule,
ConfigModule.forRoot(),
ClientsModule.register([
{
name: 'NATS_CLIENT',
transport: Transport.NATS,
options: getNatsOptions(process.env.API_GATEWAY_NKEY_SEED)
ganeshawle25 marked this conversation as resolved.
Show resolved Hide resolved
},
CommonModule
]),
RateLimiterModule.register({
points: 50,
duration: 1,
keyPrefix: 'rateLimiter',
errorMessage: 'Rate limit exceeded, please try again later.'
})
],
controllers: [GeoLocationController],
providers: [
GeoLocationService,
Logger,
{
provide: APP_GUARD,
useClass: RateLimiterGuard
}
]
})
export class GeoLocationModule {}
46 changes: 46 additions & 0 deletions apps/api-gateway/src/geo-location/geo-location.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { CountryInterface, StateInterface, CityInterface } from '@credebl/common/interfaces/geolocation.interface';
import { Inject, Injectable } from '@nestjs/common';
import { ClientProxy } from '@nestjs/microservices';
import { BaseService } from 'libs/service/base.service';

@Injectable()
export class GeoLocationService extends BaseService {
constructor(@Inject('NATS_CLIENT') private readonly serviceProxy: ClientProxy) {
super('GeoLocationService');
}

/**
*
* @param
* @returns Get all Countries list
*/
async getAllCountries(): Promise<CountryInterface[]> {
this.logger.log(`Finding all countries,GeoLocationService::getAllCountries`);
return this.sendNatsMessage(this.serviceProxy, 'get-all-countries', '');
}

/**
*
* @param
* @returns Get all states list by using countryId
*/
async getStatesByCountryId(countryId: number): Promise<StateInterface[]> {
const payload = { countryId };
this.logger.log(`Finding cities for countryId= ${countryId},GeoLocationService::getCitiesByStateAndCountry`);
return this.sendNatsMessage(this.serviceProxy, 'get-all-states', payload);
}

/**
*
* @param
* @returns Get all cities list by using stateId and countryId
*/

async getCitiesByStateAndCountry(countryId: number, stateId: number): Promise<CityInterface[]> {
const payload = { countryId, stateId };
this.logger.log(
`Finding cities for stateId= ${stateId} and countryId= ${countryId},GeoLocationService::getCitiesByStateAndCountry`
);
return this.sendNatsMessage(this.serviceProxy, 'get-all-cities', payload);
}
}
35 changes: 19 additions & 16 deletions apps/api-gateway/src/organization/dtos/create-organization-dto.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ApiExtraModels, ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsNotEmpty, IsOptional, IsString, IsUrl, MaxLength, MinLength } from 'class-validator';
import { IsNotEmpty, IsNumber, IsOptional, IsString, IsUrl, MaxLength, MinLength } from 'class-validator';

import { Transform } from 'class-transformer';
import { IsNotSQLInjection, trim } from '@credebl/common/cast.helper';
Expand Down Expand Up @@ -53,21 +53,24 @@ export class CreateOrganizationDto {
@IsString({ message: 'registrationNumber must be in string format.' })
registrationNumber?: string;

@ApiPropertyOptional()
@IsOptional()
@Transform(({ value }) => trim(value))
@IsString({ message: 'country must be in string format.' })
country?: string;
@ApiProperty({ example: 'IN' })
@IsNotEmpty({ message: 'country is required' })
@MinLength(2, { message: 'country must be at least 2 characters' })
@MaxLength(50, { message: 'country must be at most 50 characters' })
@IsNumber()
countryId?: number;

@ApiPropertyOptional()
@IsOptional()
@Transform(({ value }) => trim(value))
@IsString({ message: 'state must be in string format.' })
state?: string;
@ApiProperty({ example: 'MH' })
@IsNotEmpty({ message: 'state is required' })
@MinLength(2, { message: 'state must be at least 2 characters' })
@MaxLength(50, { message: 'state must be at most 50 characters' })
@IsNumber()
stateId?: number;

@ApiPropertyOptional()
@IsOptional()
@Transform(({ value }) => trim(value))
@IsString({ message: 'city must be in string format.' })
city?: string;
@ApiProperty({ example: 'Mumbai' })
@IsNotEmpty({ message: 'city is required' })
@MinLength(2, { message: 'city must be at least 2 characters' })
@MaxLength(50, { message: 'city must be at most 50 characters' })
@IsNumber()
cityId?: number;
}
24 changes: 24 additions & 0 deletions apps/geo-location/src/geo-location.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Controller } from '@nestjs/common';
import { GeoLocationService } from './geo-location.service';
import { MessagePattern } from '@nestjs/microservices';
import { CountryInterface, StateInterface, CityInterface } from '@credebl/common/interfaces/geolocation.interface';

@Controller()
export class GeoLocationController {
constructor(private readonly geoLocationService: GeoLocationService) {}

@MessagePattern({ cmd: 'get-all-countries' })
async getAllCountries(): Promise<CountryInterface[]> {
return this.geoLocationService.getAllCountries();
}

@MessagePattern({ cmd: 'get-all-states' })
async getStatesByCountryId(payload: { countryId: number }): Promise<StateInterface[]> {
return this.geoLocationService.getStatesByCountryId(payload.countryId);
}

@MessagePattern({ cmd: 'get-all-cities' })
async getCitiesByStateAndCountry(payload: { countryId: number; stateId: number }): Promise<CityInterface[]> {
return this.geoLocationService.getCitiesByStateAndCountry(payload.countryId, payload.stateId);
}
}
26 changes: 26 additions & 0 deletions apps/geo-location/src/geo-location.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Logger, Module } from '@nestjs/common';
import { GeoLocationController } from './geo-location.controller';
import { GeoLocationService } from './geo-location.service';
import { ClientsModule, Transport } from '@nestjs/microservices';
import { CommonModule } from '@credebl/common';
import { CacheModule } from '@nestjs/cache-manager';
import { getNatsOptions } from '@credebl/common/nats.config';
import { PrismaService } from '@credebl/prisma-service';
import { GeoLocationRepository } from './geo-location.repository';

@Module({
imports: [
ClientsModule.register([
{
name: 'NATS_CLIENT',
transport: Transport.NATS,
options: getNatsOptions(process.env.GEOLOCATION_NKEY_SEED)
}
]),
CommonModule,
CacheModule.register()
],
controllers: [GeoLocationController],
providers: [GeoLocationService, Logger, PrismaService, GeoLocationRepository]
})
export class GeoLocationModule {}
63 changes: 63 additions & 0 deletions apps/geo-location/src/geo-location.repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { CountryInterface, StateInterface, CityInterface } from '@credebl/common/interfaces/geolocation.interface';
import { PrismaService } from '@credebl/prisma-service';
import { Injectable, Logger } from '@nestjs/common';

@Injectable()
export class GeoLocationRepository {
constructor(
private readonly prisma: PrismaService,
private readonly logger: Logger
) {}

async findAllCountries(): Promise<CountryInterface[]> {
try {
return await this.prisma.countries.findMany({
select: {
id: true,
name: true
}
});
} catch (error) {
this.logger.error(`Error in GeoLocationRepository::[findAllCountries]: ${error}`);
throw error;
}
}
async findStatesByCountryId(countryId: number): Promise<StateInterface[]> {
try {
return await this.prisma.states.findMany({
where: { countryId: Number(countryId) },
select: {
id: true,
name: true,
countryId: true,
countryCode: true
}
});
} catch (error) {
this.logger.error(`Error in GeoLocationRepository::[findStatesByCountryId]: ${error} `);
throw error;
}
}

async findCitiesByStateAndCountry(countryId: number, stateId: number): Promise<CityInterface[]> {
try {
return await this.prisma.cities.findMany({
where: {
stateId: Number(stateId),
countryId: Number(countryId)
},
select: {
id: true,
name: true,
stateId: true,
stateCode: true,
countryId: true,
countryCode: true
}
});
} catch (error) {
this.logger.error(`Error finding cities for stateId ${stateId} and countryId ${countryId}: ${error}`);
throw error;
}
}
}
Loading