From c6e8f487dcce03c31f14d0736121fae6d031b5f9 Mon Sep 17 00:00:00 2001 From: rajdip-b Date: Mon, 11 Mar 2024 11:29:03 +0530 Subject: [PATCH 1/3] docs(api): Add swagger docs of API key controller --- .../api-key/controller/api-key.controller.ts | 150 +++++++++++++++++- .../dto/create.api-key/create.api-key.ts | 24 +++ apps/api/src/common/mock-data/users.ts | 40 ----- 3 files changed, 173 insertions(+), 41 deletions(-) delete mode 100644 apps/api/src/common/mock-data/users.ts diff --git a/apps/api/src/api-key/controller/api-key.controller.ts b/apps/api/src/api-key/controller/api-key.controller.ts index 7383fa7b..8a6a2764 100644 --- a/apps/api/src/api-key/controller/api-key.controller.ts +++ b/apps/api/src/api-key/controller/api-key.controller.ts @@ -3,6 +3,7 @@ import { Controller, Delete, Get, + HttpCode, Param, Post, Put, @@ -14,19 +15,84 @@ import { CreateApiKey } from '../dto/create.api-key/create.api-key' import { UpdateApiKey } from '../dto/update.api-key/update.api-key' import { Authority, User } from '@prisma/client' import { RequiredApiKeyAuthorities } from '../../decorators/required-api-key-authorities.decorator' +import { + ApiBearerAuth, + ApiCreatedResponse, + ApiForbiddenResponse, + ApiNoContentResponse, + ApiOkResponse, + ApiOperation, + ApiParam, + ApiQuery, + ApiSecurity, + ApiTags +} from '@nestjs/swagger' +import { invalidAuthenticationResponse } from '../../common/static' + +const apiKeySchemaWithValue = { + type: 'object', + properties: { + id: { type: 'string' }, + name: { type: 'string' }, + value: { type: 'string' }, + expiresAt: { type: 'string' }, + authorities: { type: 'array', items: { type: 'string' } }, + createdAt: { type: 'string' }, + updatedAt: { type: 'string' } + } +} + +const apiKeySchema = { + type: 'object', + properties: { + id: { type: 'string' }, + name: { type: 'string' }, + expiresAt: { type: 'string' }, + authorities: { type: 'array', items: { type: 'string' } }, + createdAt: { type: 'string' }, + updatedAt: { type: 'string' } + } +} @Controller('api-key') +@ApiBearerAuth() +@ApiSecurity('api_key') +@ApiTags('API Key Controller') export class ApiKeyController { constructor(private readonly apiKeyService: ApiKeyService) {} @Post() @RequiredApiKeyAuthorities(Authority.CREATE_API_KEY) + @ApiOperation({ + summary: 'Create API key', + description: 'This endpoint creates a new API key' + }) + @ApiCreatedResponse({ + schema: apiKeySchemaWithValue, + description: 'API key created successfully' + }) + @ApiForbiddenResponse(invalidAuthenticationResponse) async createApiKey(@CurrentUser() user: User, @Body() dto: CreateApiKey) { return this.apiKeyService.createApiKey(user, dto) } @Put(':id') @RequiredApiKeyAuthorities(Authority.UPDATE_API_KEY) + @ApiOperation({ + summary: 'Update API key', + description: 'This endpoint updates an existing API key' + }) + @ApiParam({ + name: 'id', + description: 'API key ID', + required: true, + type: String + }) + @ApiOkResponse({ + schema: apiKeySchema, + description: 'API key updated successfully' + }) + @ApiForbiddenResponse(invalidAuthenticationResponse) async updateApiKey( @CurrentUser() user: User, @Body() dto: UpdateApiKey, @@ -37,21 +103,103 @@ export class ApiKeyController { @Delete(':id') @RequiredApiKeyAuthorities(Authority.DELETE_API_KEY) + @HttpCode(204) + @ApiOperation({ + summary: 'Delete API key', + description: 'This endpoint deletes an existing API key' + }) + @ApiParam({ + name: 'id', + description: 'API key ID', + required: true, + type: String + }) + @ApiNoContentResponse({ + description: 'API key deleted successfully' + }) + @ApiForbiddenResponse(invalidAuthenticationResponse) async deleteApiKey(@CurrentUser() user: User, @Param('id') id: string) { return this.apiKeyService.deleteApiKey(user, id) } @Get(':id') @RequiredApiKeyAuthorities(Authority.READ_API_KEY) + @ApiOperation({ + summary: 'Get API key', + description: 'This endpoint returns the details of an API key' + }) + @ApiParam({ + name: 'id', + description: 'API key ID', + required: true, + type: String + }) + @ApiOkResponse({ + schema: apiKeySchemaWithValue, + description: 'API key details' + }) + @ApiForbiddenResponse(invalidAuthenticationResponse) async getApiKey(@CurrentUser() user: User, @Param('id') id: string) { return this.apiKeyService.getApiKeyById(user, id) } @Get('all') @RequiredApiKeyAuthorities(Authority.READ_API_KEY) + @ApiOperation({ + summary: 'Get all API keys', + description: 'This endpoint returns all API keys of the user' + }) + @ApiQuery({ + name: 'page', + description: 'Page number', + required: false, + type: Number, + example: 1, + allowEmptyValue: false + }) + @ApiQuery({ + name: 'limit', + description: 'Number of items per page', + required: false, + type: Number, + example: 10, + allowEmptyValue: false + }) + @ApiQuery({ + name: 'sort', + description: 'Sort by field', + required: false, + type: String, + example: 'name', + allowEmptyValue: false + }) + @ApiQuery({ + name: 'order', + description: 'Sort order', + required: false, + type: String, + example: 'asc', + allowEmptyValue: false + }) + @ApiQuery({ + name: 'search', + description: 'Search by name', + required: false, + type: String, + example: 'My API Key', + allowEmptyValue: false + }) + @ApiOkResponse({ + schema: { + type: 'array', + items: apiKeySchema + }, + description: 'API keys' + }) + @ApiForbiddenResponse(invalidAuthenticationResponse) async getApiKeysOfUser( @CurrentUser() user: User, - @Query('page') page: number = 1, + @Query('page') page: number = 0, @Query('limit') limit: number = 10, @Query('sort') sort: string = 'name', @Query('order') order: string = 'asc', diff --git a/apps/api/src/api-key/dto/create.api-key/create.api-key.ts b/apps/api/src/api-key/dto/create.api-key/create.api-key.ts index c256390b..b1537848 100644 --- a/apps/api/src/api-key/dto/create.api-key/create.api-key.ts +++ b/apps/api/src/api-key/dto/create.api-key/create.api-key.ts @@ -1,15 +1,39 @@ +import { ApiProperty } from '@nestjs/swagger' import { ApiKey, Authority } from '@prisma/client' import { IsArray, IsOptional, IsString } from 'class-validator' export class CreateApiKey { @IsString() + @ApiProperty({ + name: 'name', + description: 'Name of the API key', + required: true, + type: String, + example: 'My API Key' + }) name: ApiKey['name'] @IsString() @IsOptional() + @ApiProperty({ + name: 'expiresAfter', + description: 'API key expiration time in hours', + required: false, + type: String, + example: '24', + default: 'never' + }) expiresAfter?: '24' | '168' | '720' | '8760' | 'never' = 'never' @IsArray() @IsOptional() + @ApiProperty({ + name: 'authorities', + description: 'API key authorities', + required: false, + type: [String], + example: ['READ_SELF', 'UPDATE_SELF'], + default: [] + }) authorities?: Authority[] = [] } diff --git a/apps/api/src/common/mock-data/users.ts b/apps/api/src/common/mock-data/users.ts deleted file mode 100644 index 011b7404..00000000 --- a/apps/api/src/common/mock-data/users.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { User } from '@prisma/client' - -export const users: Array = [ - { - id: '1', - name: 'Cristobal Hessel', - email: 'cristobal@keyshade.xyz', - profilePictureUrl: 'https://keyshade.xyz/cristobal.jpg', - isActive: true, - isOnboardingFinished: false, - isAdmin: false - }, - { - id: '2', - name: 'John Doe', - email: 'johndoe@keyshade.xyz', - profilePictureUrl: 'https://keyshade.xyz/johndoe.jpg', - isActive: true, - isOnboardingFinished: false, - isAdmin: false - }, - { - id: '3', - name: 'Jocelyn Larkin', - email: 'jocelyn@keyshade.xyz', - profilePictureUrl: 'https://keyshade.xyz/jocelyn.jpg', - isActive: false, - isOnboardingFinished: false, - isAdmin: false - }, - { - id: '4', - name: 'Kadin Stiedemann', - email: 'kadin@keyshade.xyz', - profilePictureUrl: 'https://keyshade.xyz/kadin.jpg', - isActive: true, - isOnboardingFinished: false, - isAdmin: true - } -] From 719b0490dd1e9521a33d3c70e7680733304e6907 Mon Sep 17 00:00:00 2001 From: rajdip-b Date: Mon, 11 Mar 2024 11:37:07 +0530 Subject: [PATCH 2/3] implement code suggestions --- .../api-key/controller/api-key.controller.ts | 33 +++++++++---------- .../src/api-key/service/api-key.service.ts | 2 +- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/apps/api/src/api-key/controller/api-key.controller.ts b/apps/api/src/api-key/controller/api-key.controller.ts index 8a6a2764..f26d1b91 100644 --- a/apps/api/src/api-key/controller/api-key.controller.ts +++ b/apps/api/src/api-key/controller/api-key.controller.ts @@ -29,29 +29,26 @@ import { } from '@nestjs/swagger' import { invalidAuthenticationResponse } from '../../common/static' +const baseProperties = { + id: { type: 'string' }, + name: { type: 'string' }, + expiresAt: { type: 'string' }, + authorities: { type: 'array', items: { type: 'string' } }, + createdAt: { type: 'string' }, + updatedAt: { type: 'string' } +} + const apiKeySchemaWithValue = { type: 'object', properties: { - id: { type: 'string' }, - name: { type: 'string' }, - value: { type: 'string' }, - expiresAt: { type: 'string' }, - authorities: { type: 'array', items: { type: 'string' } }, - createdAt: { type: 'string' }, - updatedAt: { type: 'string' } + ...baseProperties, + value: { type: 'string' } } } const apiKeySchema = { type: 'object', - properties: { - id: { type: 'string' }, - name: { type: 'string' }, - expiresAt: { type: 'string' }, - authorities: { type: 'array', items: { type: 'string' } }, - createdAt: { type: 'string' }, - updatedAt: { type: 'string' } - } + properties: baseProperties } @Controller('api-key') @@ -86,7 +83,7 @@ export class ApiKeyController { name: 'id', description: 'API key ID', required: true, - type: String + schema: { type: 'string' } }) @ApiOkResponse({ schema: apiKeySchema, @@ -112,7 +109,7 @@ export class ApiKeyController { name: 'id', description: 'API key ID', required: true, - type: String + schema: { type: 'string' } }) @ApiNoContentResponse({ description: 'API key deleted successfully' @@ -132,7 +129,7 @@ export class ApiKeyController { name: 'id', description: 'API key ID', required: true, - type: String + schema: { type: 'string' } }) @ApiOkResponse({ schema: apiKeySchemaWithValue, diff --git a/apps/api/src/api-key/service/api-key.service.ts b/apps/api/src/api-key/service/api-key.service.ts index 6a21d322..1b793ae0 100644 --- a/apps/api/src/api-key/service/api-key.service.ts +++ b/apps/api/src/api-key/service/api-key.service.ts @@ -131,7 +131,7 @@ export class ApiKeyService { contains: search } }, - skip: (page - 1) * limit, + skip: page * limit, take: limit, orderBy: { [sort]: order From d9625f1b18d5adf2cd90346402044822a7f28cbe Mon Sep 17 00:00:00 2001 From: rajdip-b Date: Mon, 11 Mar 2024 11:44:15 +0530 Subject: [PATCH 3/3] update e2e test --- apps/api/src/api-key/api-key.e2e.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api/src/api-key/api-key.e2e.spec.ts b/apps/api/src/api-key/api-key.e2e.spec.ts index 8b1fe6bc..fbec2377 100644 --- a/apps/api/src/api-key/api-key.e2e.spec.ts +++ b/apps/api/src/api-key/api-key.e2e.spec.ts @@ -261,7 +261,7 @@ describe('Api Key Role Controller Tests', () => { } }) - expect(response.statusCode).toBe(200) + expect(response.statusCode).toBe(204) }) afterAll(async () => {