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

optimizes usage of request context. #86

Merged
merged 1 commit into from
Jan 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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