Skip to content

Commit

Permalink
Merge pull request #86 from openbankingnigeria/refactoring
Browse files Browse the repository at this point in the history
optimizes usage of request context.
  • Loading branch information
dami-laare authored Jan 14, 2024
2 parents 064e103 + b5e217d commit a230ef7
Show file tree
Hide file tree
Showing 33 changed files with 476 additions and 292 deletions.
6 changes: 3 additions & 3 deletions apps/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json",
"typeorm": "dotenv -e .env -- typeorm-ts-node-commonjs",
"migration:run": "npm run typeorm migration:run -- -d ./src/common/database/migration.config.ts",
"migration:generate": "npm run typeorm -- migration:generate ./src/common/database/migrations/migration -d ./src/common/database/migration.config.ts -p",
"migration:create": "npm run typeorm -- migration:create ./src/common/database/migrations/migration"
"migration:run": "pnpm typeorm migration:run -d ./src/common/database/migration.config.ts",
"migration:generate": "pnpm typeorm migration:generate ./src/common/database/migrations/migration -d ./src/common/database/migration.config.ts -p",
"migration:create": "pnpm typeorm migration:create ./src/common/database/migrations/migration"
},
"dependencies": {
"@elastic/elasticsearch": "^8.11.0",
Expand Down
76 changes: 58 additions & 18 deletions apps/server/src/apis/apis.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ import {
} from '@common/utils/pipes/query/pagination.pipe';
import { FilterPipe } from '@common/utils/pipes/query/filter.pipe';
import { APIFilters } from './apis.filter';
import { RequireTwoFA } from '@common/utils/authentication/auth.decorator';
import { Ctx, RequireTwoFA } from '@common/utils/authentication/auth.decorator';
import { RequestContext } from '@common/utils/request/request-context';

@Controller('apis/:environment')
export class APIController {
Expand All @@ -34,86 +35,125 @@ export class APIController {
@Get('')
@UsePipes(IValidationPipe)
viewAPIs(
@Ctx() ctx: RequestContext,
@Param() params: APIParam,
@Query(PaginationPipe) pagination: PaginationParameters,
@Query(new FilterPipe(APIFilters.listAPIs))
filters: any,
@Query(new FilterPipe(APIFilters.listAPIs)) filters: any,
) {
return this.apiService.viewAPIs(params.environment, pagination, filters);
return this.apiService.viewAPIs(
ctx,
params.environment,
pagination,
filters,
);
}

@Post('')
@UsePipes(IValidationPipe)
@RequireTwoFA()
createAPI(@Param() params: APIParam, @Body() data: CreateAPIDto) {
return this.apiService.createAPI(params.environment, data);
createAPI(
@Ctx() ctx: RequestContext,
@Param() params: APIParam,
@Body() data: CreateAPIDto,
) {
return this.apiService.createAPI(ctx, params.environment, data);
}

@Put('company/:companyId/assign')
@UsePipes(IValidationPipe)
@RequireTwoFA()
assignAPIs(
@Ctx() ctx: RequestContext,
@Param() params: APIParam,
@Param('companyId') companyId: string,
@Body() data: AssignAPIsDto,
) {
return this.apiService.assignAPIs(params.environment, companyId, data);
return this.apiService.assignAPIs(ctx, params.environment, companyId, data);
}

@Put('company/:companyId/unassign')
@UsePipes(IValidationPipe)
@RequireTwoFA()
unassignAPIs(
@Ctx() ctx: RequestContext,
@Param() params: APIParam,
@Param('companyId') companyId: string,
@Body() data: AssignAPIsDto,
) {
return this.apiService.unassignAPIs(params.environment, companyId, data);
return this.apiService.unassignAPIs(
ctx,
params.environment,
companyId,
data,
);
}

@Get('logs')
@UsePipes(IValidationPipe)
getAPILogs(
@Ctx() ctx: RequestContext,
@Param() params: APIParam,
@Query(PaginationPipe) pagination: PaginationParameters,
@Query() filters: GetAPILogsDto,
) {
return this.apiService.getAPILogs(params.environment, pagination, filters);
return this.apiService.getAPILogs(
ctx,
params.environment,
pagination,
filters,
);
}

@Get('logs/stats')
@UsePipes(IValidationPipe)
getAPILogsStats(@Param() params: APIParam, @Query() filters: GetAPILogsDto) {
return this.apiService.getAPILogsStats(params.environment, filters);
getAPILogsStats(
@Ctx() ctx: RequestContext,
@Param() params: APIParam,
@Query() filters: GetAPILogsDto,
) {
return this.apiService.getAPILogsStats(ctx, params.environment, filters);
}

@Get('logs/:id')
@UsePipes(IValidationPipe)
getAPILog(@Param() params: APIParam, @Param('id') id: string) {
return this.apiService.getAPILog(params.environment, id);
getAPILog(
@Ctx() ctx: RequestContext,
@Param() params: APIParam,
@Param('id') id: string,
) {
return this.apiService.getAPILog(ctx, params.environment, id);
}

@Get(':id')
@UsePipes(IValidationPipe)
viewAPI(@Param() params: APIParam, @Param('id') id: string) {
return this.apiService.viewAPI(params.environment, id);
viewAPI(
@Ctx() ctx: RequestContext,
@Param() params: APIParam,
@Param('id') id: string,
) {
return this.apiService.viewAPI(ctx, params.environment, id);
}

@Delete(':id')
@UsePipes(IValidationPipe)
@RequireTwoFA()
deletAPI(@Param() params: APIParam, @Param('id') id: string) {
return this.apiService.deleteAPI(params.environment, id);
deletAPI(
@Ctx() ctx: RequestContext,
@Param() params: APIParam,
@Param('id') id: string,
) {
return this.apiService.deleteAPI(ctx, params.environment, id);
}

@Patch(':id')
@UsePipes(IValidationPipe)
@RequireTwoFA()
updateAPI(
@Ctx() ctx: RequestContext,
@Param() params: APIParam,
@Param('id') id: string,
@Body() data: UpdateAPIDto,
) {
return this.apiService.updateAPI(params.environment, id, data);
return this.apiService.updateAPI(ctx, params.environment, id, data);
}
}
2 changes: 0 additions & 2 deletions apps/server/src/apis/apis.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { Collection } from '@common/database/entities/collection.entity';
import { CollectionRoute } from '@common/database/entities/collectionroute.entity';
import { ElasticsearchModule } from '@nestjs/elasticsearch';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { RequestContextService } from '@common/utils/request/request-context.service';
import { Company } from '@common/database/entities';
import { KongConsumerService } from '@shared/integrations/kong/consumer/consumer.kong.service';
import { ConsumerAcl } from '@common/database/entities/consumeracl.entity';
Expand All @@ -22,7 +21,6 @@ import { ConsumerAcl } from '@common/database/entities/consumeracl.entity';
KongServiceService,
KongConsumerService,
KongRouteService,
RequestContextService,
],
imports: [
TypeOrmModule.forFeature([
Expand Down
58 changes: 38 additions & 20 deletions apps/server/src/apis/apis.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ import { PaginationParameters } from '@common/utils/pipes/query/pagination.pipe'
import { KONG_ENVIRONMENT } from '@shared/integrations/kong.interface';
import { apiErrorMessages, apiSuccessMessages } from './apis.constants';
import { ElasticsearchService } from '@nestjs/elasticsearch';
import { RequestContextService } from '@common/utils/request/request-context.service';
import { v4 as uuidV4 } from 'uuid';
import { KongConsumerService } from '@shared/integrations/kong/consumer/consumer.kong.service';
import { Company } from '@common/database/entities';
import { companyErrors } from '@company/company.errors';
import { ConsumerAcl } from '@common/database/entities/consumeracl.entity';
import { CompanyTypes } from '@common/database/constants';
import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
import { RequestContext } from '@common/utils/request/request-context';

// TODO return DTO based on parent type, i.e. we dont want to return sensitive API info data to API consumer for e.g.
@Injectable()
Expand All @@ -55,10 +55,10 @@ export class APIService {
private readonly kongRouteService: KongRouteService,
private readonly kongConsumerService: KongConsumerService,
private readonly elasticsearchService: ElasticsearchService,
private readonly requestContext: RequestContextService,
) {}

async viewAPIs(
ctx: RequestContext,
environment: KONG_ENVIRONMENT,
{ limit, page }: PaginationParameters,
filters?: any,
Expand Down Expand Up @@ -117,7 +117,11 @@ export class APIService {
);
}

async viewAPI(environment: KONG_ENVIRONMENT, id: string) {
async viewAPI(
ctx: RequestContext,
environment: KONG_ENVIRONMENT,
id: string,
) {
const route = await this.routeRepository.findOne({
where: { id, environment },
relations: { collection: true },
Expand Down Expand Up @@ -165,7 +169,11 @@ export class APIService {
);
}

async deleteAPI(environment: KONG_ENVIRONMENT, id: string) {
async deleteAPI(
ctx: RequestContext,
environment: KONG_ENVIRONMENT,
id: string,
) {
const route = await this.routeRepository.findOne({
where: { id, environment },
});
Expand All @@ -190,7 +198,11 @@ export class APIService {
return ResponseFormatter.success(apiSuccessMessages.deletedAPI);
}

async createAPI(environment: KONG_ENVIRONMENT, data: CreateAPIDto) {
async createAPI(
ctx: RequestContext,
environment: KONG_ENVIRONMENT,
data: CreateAPIDto,
) {
const { name, enabled, url } = data;
const { paths, methods } = data.route;

Expand Down Expand Up @@ -241,23 +253,23 @@ export class APIService {

const aclAllowedGroupName = uuidV4();

let apiProviderConsumerId = this.requestContext.user!.company.consumerId;
let apiProviderConsumerId = ctx.activeCompany.consumerId;

// If the api provider does not already have an associated consumer on the API gateway, create a new consumer for the API provider
if (!apiProviderConsumerId) {
// Update API provider consumer to allow access to route
const response = await this.kongConsumerService.updateOrCreateConsumer(
environment,
{
custom_id: this.requestContext.user!.company.id,
custom_id: ctx.activeCompany.id,
},
);

apiProviderConsumerId = response.id;

await this.companyRepository.update(
{
id: this.requestContext.user!.company.id,
id: ctx.activeCompany.id,
},
{ consumerId: apiProviderConsumerId },
);
Expand Down Expand Up @@ -325,6 +337,7 @@ export class APIService {
}

async assignAPIs(
ctx: RequestContext,
environment: KONG_ENVIRONMENT,
companyId: string,
{ apiIds }: AssignAPIsDto,
Expand Down Expand Up @@ -353,15 +366,15 @@ export class APIService {
const response = await this.kongConsumerService.updateOrCreateConsumer(
environment,
{
custom_id: this.requestContext.user!.company.id,
custom_id: ctx.activeCompany.id,
},
);

consumerId = response.id;

await this.companyRepository.update(
{
id: this.requestContext.user!.company.id,
id: ctx.activeCompany.id,
},
{ consumerId },
);
Expand Down Expand Up @@ -412,6 +425,7 @@ export class APIService {
}

async unassignAPIs(
ctx: RequestContext,
environment: KONG_ENVIRONMENT,
companyId: string,
{ apiIds }: AssignAPIsDto,
Expand Down Expand Up @@ -463,6 +477,7 @@ export class APIService {
}

async updateAPI(
ctx: RequestContext,
environment: KONG_ENVIRONMENT,
routeId: string,
data: UpdateAPIDto,
Expand Down Expand Up @@ -596,6 +611,7 @@ export class APIService {

// TODO ensure only AP can view logs for all users;
async getAPILogs(
ctx: RequestContext,
environment: KONG_ENVIRONMENT,
{ limit, page }: PaginationParameters,
filters?: GetAPILogsDto,
Expand All @@ -610,10 +626,9 @@ export class APIService {
{
wildcard: {
'consumer.id.keyword':
this.requestContext.user!.company.type ===
CompanyTypes.API_PROVIDER
ctx.activeCompany.type === CompanyTypes.API_PROVIDER
? '*'
: this.requestContext.user!.companyId,
: ctx.activeUser.companyId,
},
},
...this.convertFilterToSearchDSLQuery<GetAPILogsFilterDto>(
Expand All @@ -632,7 +647,11 @@ export class APIService {
}

// TODO ensure only AP can view logs for all users;
async getAPILog(environment: KONG_ENVIRONMENT, requestId: string) {
async getAPILog(
ctx: RequestContext,
environment: KONG_ENVIRONMENT,
requestId: string,
) {
const logs = await this.elasticsearchService.search({
size: 1,
query: {
Expand All @@ -647,10 +666,9 @@ export class APIService {
{
wildcard: {
'consumer.id.keyword':
this.requestContext.user!.company.type ===
CompanyTypes.API_PROVIDER
ctx.activeCompany.type === CompanyTypes.API_PROVIDER
? '*'
: this.requestContext.user!.companyId,
: ctx.activeUser.companyId,
},
},
],
Expand All @@ -672,6 +690,7 @@ export class APIService {

// TODO ensure only AP can view logs for all users;
async getAPILogsStats(
ctx: RequestContext,
environment: KONG_ENVIRONMENT,
filters?: GetAPILogsDto,
) {
Expand All @@ -684,10 +703,9 @@ export class APIService {
{
wildcard: {
'consumer.id.keyword':
this.requestContext.user!.company.type ===
CompanyTypes.API_PROVIDER
ctx.activeCompany.type === CompanyTypes.API_PROVIDER
? '*'
: this.requestContext.user!.companyId,
: ctx.activeUser.companyId,
},
},
...this.convertFilterToSearchDSLQuery<GetAPILogsFilterDto>(
Expand Down
4 changes: 4 additions & 0 deletions apps/server/src/apis/dto/index.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ class CreateRouteDTO {
}

export class CreateAPIDto {
constructor(partial: CreateAPIDto) {
Object.assign(this, partial);
}

@IsNotEmpty()
@IsString()
collectionId: string;
Expand Down
Loading

0 comments on commit a230ef7

Please sign in to comment.