diff --git a/apps/api/src/attachments/attachments.controller.ts b/apps/api/src/attachments/attachments.controller.ts index acd1cc7b0..b320e9a1a 100644 --- a/apps/api/src/attachments/attachments.controller.ts +++ b/apps/api/src/attachments/attachments.controller.ts @@ -37,19 +37,23 @@ export class AttachmentsController { @ApiResponse({ status: 200, description: 'Download URL generated successfully', - schema: { - type: 'object', - properties: { - downloadUrl: { - type: 'string', - description: 'Signed URL for downloading the file', - example: - 'https://bucket.s3.amazonaws.com/path/to/file.pdf?signature=...', - }, - expiresIn: { - type: 'number', - description: 'URL expiration time in seconds', - example: 900, + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + downloadUrl: { + type: 'string', + description: 'Signed URL for downloading the file', + example: + 'https://bucket.s3.amazonaws.com/path/to/file.pdf?signature=...', + }, + expiresIn: { + type: 'number', + description: 'URL expiration time in seconds', + example: 900, + }, + }, }, }, }, diff --git a/apps/api/src/comments/comments.controller.ts b/apps/api/src/comments/comments.controller.ts index 1769ae803..2026055e3 100644 --- a/apps/api/src/comments/comments.controller.ts +++ b/apps/api/src/comments/comments.controller.ts @@ -137,7 +137,6 @@ export class CommentsController { } @Delete(':commentId') - @HttpCode(HttpStatus.NO_CONTENT) @ApiOperation({ summary: 'Delete a comment', description: 'Delete a comment and all its attachments (author only)', @@ -148,22 +147,72 @@ export class CommentsController { example: 'cmt_abc123def456', }) @ApiResponse({ - status: 204, + status: 200, description: 'Comment deleted successfully', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + success: { type: 'boolean', example: true }, + deletedCommentId: { type: 'string', example: 'cmt_abc123def456' }, + message: { + type: 'string', + example: 'Comment deleted successfully', + }, + }, + }, + }, + }, + }) + @ApiResponse({ + status: 401, + description: 'Unauthorized - Invalid authentication', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { message: { type: 'string', example: 'Unauthorized' } }, + }, + }, + }, + }) + @ApiResponse({ + status: 404, + description: 'Comment not found', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Comment with ID cmt_abc123def456 not found', + }, + }, + }, + }, + }, }) async deleteComment( @OrganizationId() organizationId: string, @AuthContext() authContext: AuthContextType, @Param('commentId') commentId: string, - ): Promise { + ): Promise<{ success: boolean; deletedCommentId: string; message: string }> { if (!authContext.userId) { throw new BadRequestException('User ID is required'); } - return await this.commentsService.deleteComment( + await this.commentsService.deleteComment( organizationId, commentId, authContext.userId, ); + + return { + success: true, + deletedCommentId: commentId, + message: 'Comment deleted successfully', + }; } } diff --git a/apps/api/src/context/schemas/create-context.responses.ts b/apps/api/src/context/schemas/create-context.responses.ts index cae23afb1..6f7fa10d3 100644 --- a/apps/api/src/context/schemas/create-context.responses.ts +++ b/apps/api/src/context/schemas/create-context.responses.ts @@ -2,14 +2,29 @@ import type { ApiResponseOptions } from '@nestjs/swagger'; export const CREATE_CONTEXT_RESPONSES: Record = { 201: { + status: 201, description: 'Context entry created successfully', content: { 'application/json': { + schema: { + type: 'object', + properties: { + id: { type: 'string' }, + organizationId: { type: 'string' }, + question: { type: 'string' }, + answer: { type: 'string' }, + tags: { type: 'array', items: { type: 'string' } }, + createdAt: { type: 'string', format: 'date-time' }, + updatedAt: { type: 'string', format: 'date-time' }, + authType: { type: 'string', enum: ['api-key', 'session'] }, + }, + }, example: { id: 'ctx_abc123def456', organizationId: 'org_xyz789uvw012', question: 'How do we handle user authentication in our application?', - answer: 'We use a hybrid authentication system supporting both API keys and session-based authentication.', + answer: + 'We use a hybrid authentication system supporting both API keys and session-based authentication.', tags: ['authentication', 'security', 'api', 'sessions'], createdAt: '2024-01-15T10:30:00.000Z', updatedAt: '2024-01-15T10:30:00.000Z', @@ -19,11 +34,23 @@ export const CREATE_CONTEXT_RESPONSES: Record = { }, }, 400: { + status: 400, description: 'Bad request - Invalid input data', content: { 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'array', items: { type: 'string' } }, + error: { type: 'string' }, + statusCode: { type: 'number' }, + }, + }, example: { - message: ['question should not be empty', 'answer should not be empty'], + message: [ + 'question should not be empty', + 'answer should not be empty', + ], error: 'Bad Request', statusCode: 400, }, @@ -31,9 +58,17 @@ export const CREATE_CONTEXT_RESPONSES: Record = { }, }, 401: { + status: 401, description: 'Unauthorized - Invalid or missing authentication', content: { 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string' }, + statusCode: { type: 'number', example: 401 }, + }, + }, example: { message: 'Unauthorized', statusCode: 401, @@ -42,9 +77,17 @@ export const CREATE_CONTEXT_RESPONSES: Record = { }, }, 404: { + status: 404, description: 'Organization not found', content: { 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string' }, + statusCode: { type: 'number', example: 404 }, + }, + }, example: { message: 'Organization not found', statusCode: 404, @@ -53,9 +96,17 @@ export const CREATE_CONTEXT_RESPONSES: Record = { }, }, 500: { + status: 500, description: 'Internal server error', content: { 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string' }, + statusCode: { type: 'number', example: 500 }, + }, + }, example: { message: 'Internal server error', statusCode: 500, diff --git a/apps/api/src/context/schemas/delete-context.responses.ts b/apps/api/src/context/schemas/delete-context.responses.ts index f637a7542..505c62532 100644 --- a/apps/api/src/context/schemas/delete-context.responses.ts +++ b/apps/api/src/context/schemas/delete-context.responses.ts @@ -2,14 +2,30 @@ import type { ApiResponseOptions } from '@nestjs/swagger'; export const DELETE_CONTEXT_RESPONSES: Record = { 200: { + status: 200, description: 'Context entry deleted successfully', content: { 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string' }, + deletedContext: { + type: 'object', + properties: { + id: { type: 'string' }, + question: { type: 'string' }, + }, + }, + authType: { type: 'string', enum: ['api-key', 'session'] }, + }, + }, example: { message: 'Context entry deleted successfully', deletedContext: { id: 'ctx_abc123def456', - question: 'How do we handle user authentication in our application?', + question: + 'How do we handle user authentication in our application?', }, authType: 'apikey', }, @@ -17,9 +33,17 @@ export const DELETE_CONTEXT_RESPONSES: Record = { }, }, 401: { + status: 401, description: 'Unauthorized - Invalid or missing authentication', content: { 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string' }, + statusCode: { type: 'number', example: 401 }, + }, + }, example: { message: 'Unauthorized', statusCode: 401, @@ -28,20 +52,37 @@ export const DELETE_CONTEXT_RESPONSES: Record = { }, }, 404: { + status: 404, description: 'Context entry not found', content: { 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string' }, + statusCode: { type: 'number', example: 404 }, + }, + }, example: { - message: 'Context entry with ID ctx_abc123def456 not found in organization org_xyz789uvw012', + message: + 'Context entry with ID ctx_abc123def456 not found in organization org_xyz789uvw012', statusCode: 404, }, }, }, }, 500: { + status: 500, description: 'Internal server error', content: { 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string' }, + statusCode: { type: 'number', example: 500 }, + }, + }, example: { message: 'Internal server error', statusCode: 500, diff --git a/apps/api/src/context/schemas/get-all-context.responses.ts b/apps/api/src/context/schemas/get-all-context.responses.ts index f19a600a1..47bec451a 100644 --- a/apps/api/src/context/schemas/get-all-context.responses.ts +++ b/apps/api/src/context/schemas/get-all-context.responses.ts @@ -2,16 +2,41 @@ import type { ApiResponseOptions } from '@nestjs/swagger'; export const GET_ALL_CONTEXT_RESPONSES: Record = { 200: { + status: 200, description: 'Context entries retrieved successfully', content: { 'application/json': { + schema: { + type: 'object', + properties: { + data: { + type: 'array', + items: { + type: 'object', + properties: { + id: { type: 'string' }, + organizationId: { type: 'string' }, + question: { type: 'string' }, + answer: { type: 'string' }, + tags: { type: 'array', items: { type: 'string' } }, + createdAt: { type: 'string', format: 'date-time' }, + updatedAt: { type: 'string', format: 'date-time' }, + }, + }, + }, + count: { type: 'number' }, + authType: { type: 'string', enum: ['api-key', 'session'] }, + }, + }, example: { data: [ { id: 'ctx_abc123def456', organizationId: 'org_xyz789uvw012', - question: 'How do we handle user authentication in our application?', - answer: 'We use a hybrid authentication system supporting both API keys and session-based authentication.', + question: + 'How do we handle user authentication in our application?', + answer: + 'We use a hybrid authentication system supporting both API keys and session-based authentication.', tags: ['authentication', 'security', 'api', 'sessions'], createdAt: '2024-01-15T10:30:00.000Z', updatedAt: '2024-01-15T14:20:00.000Z', @@ -20,7 +45,8 @@ export const GET_ALL_CONTEXT_RESPONSES: Record = { id: 'ctx_ghi789jkl012', organizationId: 'org_xyz789uvw012', question: 'What database do we use and why?', - answer: 'We use PostgreSQL as our primary database with Prisma as the ORM.', + answer: + 'We use PostgreSQL as our primary database with Prisma as the ORM.', tags: ['database', 'postgresql', 'prisma', 'architecture'], createdAt: '2024-01-14T09:15:00.000Z', updatedAt: '2024-01-14T09:15:00.000Z', @@ -33,9 +59,17 @@ export const GET_ALL_CONTEXT_RESPONSES: Record = { }, }, 401: { + status: 401, description: 'Unauthorized - Invalid or missing authentication', content: { 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string' }, + statusCode: { type: 'number', example: 401 }, + }, + }, example: { message: 'Unauthorized', statusCode: 401, @@ -44,9 +78,17 @@ export const GET_ALL_CONTEXT_RESPONSES: Record = { }, }, 404: { + status: 404, description: 'Organization not found', content: { 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string' }, + statusCode: { type: 'number', example: 404 }, + }, + }, example: { message: 'Organization not found', statusCode: 404, @@ -55,9 +97,17 @@ export const GET_ALL_CONTEXT_RESPONSES: Record = { }, }, 500: { + status: 500, description: 'Internal server error', content: { 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string' }, + statusCode: { type: 'number', example: 500 }, + }, + }, example: { message: 'Internal server error', statusCode: 500, diff --git a/apps/api/src/context/schemas/get-context-by-id.responses.ts b/apps/api/src/context/schemas/get-context-by-id.responses.ts index f1a8fff03..a6f636ae4 100644 --- a/apps/api/src/context/schemas/get-context-by-id.responses.ts +++ b/apps/api/src/context/schemas/get-context-by-id.responses.ts @@ -2,14 +2,33 @@ import type { ApiResponseOptions } from '@nestjs/swagger'; export const GET_CONTEXT_BY_ID_RESPONSES: Record = { 200: { + status: 200, description: 'Context entry retrieved successfully', content: { 'application/json': { + schema: { + type: 'object', + properties: { + id: { type: 'string', example: 'ctx_abc123def456' }, + organizationId: { type: 'string', example: 'org_xyz789uvw012' }, + question: { type: 'string' }, + answer: { type: 'string' }, + tags: { + type: 'array', + items: { type: 'string' }, + example: ['authentication', 'security'], + }, + createdAt: { type: 'string', format: 'date-time' }, + updatedAt: { type: 'string', format: 'date-time' }, + authType: { type: 'string', enum: ['api-key', 'session'] }, + }, + }, example: { id: 'ctx_abc123def456', organizationId: 'org_xyz789uvw012', question: 'How do we handle user authentication in our application?', - answer: 'We use a hybrid authentication system supporting both API keys and session-based authentication.', + answer: + 'We use a hybrid authentication system supporting both API keys and session-based authentication.', tags: ['authentication', 'security', 'api', 'sessions'], createdAt: '2024-01-15T10:30:00.000Z', updatedAt: '2024-01-15T14:20:00.000Z', @@ -19,9 +38,17 @@ export const GET_CONTEXT_BY_ID_RESPONSES: Record = { }, }, 401: { + status: 401, description: 'Unauthorized - Invalid or missing authentication', content: { 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string', example: 'Unauthorized' }, + statusCode: { type: 'number', example: 401 }, + }, + }, example: { message: 'Unauthorized', statusCode: 401, @@ -30,20 +57,37 @@ export const GET_CONTEXT_BY_ID_RESPONSES: Record = { }, }, 404: { + status: 404, description: 'Context entry not found', content: { 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string' }, + statusCode: { type: 'number', example: 404 }, + }, + }, example: { - message: 'Context entry with ID ctx_abc123def456 not found in organization org_xyz789uvw012', + message: + 'Context entry with ID ctx_abc123def456 not found in organization org_xyz789uvw012', statusCode: 404, }, }, }, }, 500: { + status: 500, description: 'Internal server error', content: { 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string' }, + statusCode: { type: 'number', example: 500 }, + }, + }, example: { message: 'Internal server error', statusCode: 500, diff --git a/apps/api/src/context/schemas/update-context.responses.ts b/apps/api/src/context/schemas/update-context.responses.ts index c2de92eb3..843f45408 100644 --- a/apps/api/src/context/schemas/update-context.responses.ts +++ b/apps/api/src/context/schemas/update-context.responses.ts @@ -2,6 +2,7 @@ import type { ApiResponseOptions } from '@nestjs/swagger'; export const UPDATE_CONTEXT_RESPONSES: Record = { 200: { + status: 200, description: 'Context entry updated successfully', content: { 'application/json': { @@ -9,7 +10,8 @@ export const UPDATE_CONTEXT_RESPONSES: Record = { id: 'ctx_abc123def456', organizationId: 'org_xyz789uvw012', question: 'How do we handle user authentication in our application?', - answer: 'Updated: We use a hybrid authentication system supporting both API keys and session-based authentication with OAuth2 support.', + answer: + 'Updated: We use a hybrid authentication system supporting both API keys and session-based authentication with OAuth2 support.', tags: ['authentication', 'security', 'api', 'sessions', 'oauth2'], createdAt: '2024-01-15T10:30:00.000Z', updatedAt: '2024-01-15T15:45:00.000Z', @@ -19,6 +21,7 @@ export const UPDATE_CONTEXT_RESPONSES: Record = { }, }, 400: { + status: 400, description: 'Bad request - Invalid input data', content: { 'application/json': { @@ -31,6 +34,7 @@ export const UPDATE_CONTEXT_RESPONSES: Record = { }, }, 401: { + status: 401, description: 'Unauthorized - Invalid or missing authentication', content: { 'application/json': { @@ -42,17 +46,20 @@ export const UPDATE_CONTEXT_RESPONSES: Record = { }, }, 404: { + status: 404, description: 'Context entry not found', content: { 'application/json': { example: { - message: 'Context entry with ID ctx_abc123def456 not found in organization org_xyz789uvw012', + message: + 'Context entry with ID ctx_abc123def456 not found in organization org_xyz789uvw012', statusCode: 404, }, }, }, }, 500: { + status: 500, description: 'Internal server error', content: { 'application/json': { diff --git a/apps/api/src/devices/devices.controller.ts b/apps/api/src/devices/devices.controller.ts index 9e8323071..c7015baf0 100644 --- a/apps/api/src/devices/devices.controller.ts +++ b/apps/api/src/devices/devices.controller.ts @@ -1,9 +1,4 @@ -import { - Controller, - Get, - Param, - UseGuards -} from '@nestjs/common'; +import { Controller, Get, Param, UseGuards } from '@nestjs/common'; import { ApiHeader, ApiOperation, @@ -12,10 +7,7 @@ import { ApiSecurity, ApiTags, } from '@nestjs/swagger'; -import { - AuthContext, - OrganizationId, -} from '../auth/auth-context.decorator'; +import { AuthContext, OrganizationId } from '../auth/auth-context.decorator'; import { HybridAuthGuard } from '../auth/hybrid-auth.guard'; import type { AuthContext as AuthContextType } from '../auth/types'; import { DevicesByMemberResponseDto } from './dto/devices-by-member-response.dto'; @@ -43,35 +35,39 @@ export class DevicesController { @ApiResponse({ status: 200, description: 'Devices retrieved successfully', - schema: { - type: 'object', - properties: { - data: { - type: 'array', - items: { $ref: '#/components/schemas/DeviceResponseDto' }, - }, - count: { - type: 'number', - description: 'Total number of devices', - example: 25, - }, - authType: { - type: 'string', - enum: ['api-key', 'session'], - description: 'How the request was authenticated', - }, - authenticatedUser: { + content: { + 'application/json': { + schema: { type: 'object', properties: { - id: { - type: 'string', - description: 'User ID', - example: 'usr_abc123def456', + data: { + type: 'array', + items: { $ref: '#/components/schemas/DeviceResponseDto' }, }, - email: { + count: { + type: 'number', + description: 'Total number of devices', + example: 25, + }, + authType: { type: 'string', - description: 'User email', - example: 'user@company.com', + enum: ['api-key', 'session'], + description: 'How the request was authenticated', + }, + authenticatedUser: { + type: 'object', + properties: { + id: { + type: 'string', + description: 'User ID', + example: 'usr_abc123def456', + }, + email: { + type: 'string', + description: 'User email', + example: 'user@company.com', + }, + }, }, }, }, @@ -82,17 +78,16 @@ export class DevicesController { status: 401, description: 'Unauthorized - Invalid authentication or insufficient permissions', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Invalid or expired API key', - 'Invalid or expired session', - 'User does not have access to organization', - 'Organization context required', - ], + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Invalid or expired API key', + }, + }, }, }, }, @@ -100,12 +95,16 @@ export class DevicesController { @ApiResponse({ status: 404, description: 'Organization not found', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Organization with ID org_abc123def456 not found', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Organization with ID org_abc123def456 not found', + }, + }, }, }, }, @@ -113,15 +112,16 @@ export class DevicesController { @ApiResponse({ status: 500, description: 'Internal server error - FleetDM integration issue', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Failed to retrieve devices: FleetDM connection failed', - 'Organization does not have FleetDM configured', - ], + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Organization does not have FleetDM configured', + }, + }, }, }, }, @@ -130,18 +130,20 @@ export class DevicesController { @OrganizationId() organizationId: string, @AuthContext() authContext: AuthContextType, ) { - const devices = await this.devicesService.findAllByOrganization(organizationId); + const devices = + await this.devicesService.findAllByOrganization(organizationId); return { data: devices, count: devices.length, authType: authContext.authType, - ...(authContext.userId && authContext.userEmail && { - authenticatedUser: { - id: authContext.userId, - email: authContext.userEmail, - }, - }), + ...(authContext.userId && + authContext.userEmail && { + authenticatedUser: { + id: authContext.userId, + email: authContext.userEmail, + }, + }), }; } @@ -149,7 +151,7 @@ export class DevicesController { @ApiOperation({ summary: 'Get devices by member ID', description: - 'Returns all devices assigned to a specific member within the authenticated organization. Devices are fetched from FleetDM using the member\'s dedicated fleetDmLabelId. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).', + "Returns all devices assigned to a specific member within the authenticated organization. Devices are fetched from FleetDM using the member's dedicated fleetDmLabelId. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", }) @ApiParam({ name: 'memberId', @@ -165,19 +167,31 @@ export class DevicesController { status: 401, description: 'Unauthorized - Invalid authentication or insufficient permissions', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string', example: 'Unauthorized' }, + }, + }, + }, + }, }) @ApiResponse({ status: 404, description: 'Organization or member not found', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Organization with ID org_abc123def456 not found', - 'Member with ID mem_abc123def456 not found in organization org_abc123def456', - ], + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: + 'Member with ID mem_abc123def456 not found in organization org_abc123def456', + }, + }, }, }, }, @@ -201,12 +215,13 @@ export class DevicesController { count: devices.length, member, authType: authContext.authType, - ...(authContext.userId && authContext.userEmail && { - authenticatedUser: { - id: authContext.userId, - email: authContext.userEmail, - }, - }), + ...(authContext.userId && + authContext.userEmail && { + authenticatedUser: { + id: authContext.userId, + email: authContext.userEmail, + }, + }), }; } } diff --git a/apps/api/src/health/health.controller.ts b/apps/api/src/health/health.controller.ts index 5029f855c..e93fb62d1 100644 --- a/apps/api/src/health/health.controller.ts +++ b/apps/api/src/health/health.controller.ts @@ -12,24 +12,28 @@ export class HealthController { @ApiResponse({ status: 200, description: 'API is healthy', - schema: { - type: 'object', - properties: { - status: { - type: 'string', - example: 'ok', - }, - timestamp: { - type: 'string', - format: 'date-time', - }, - uptime: { - type: 'number', - description: 'Process uptime in seconds', - }, - version: { - type: 'string', - example: '1.0.0', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + status: { + type: 'string', + example: 'ok', + }, + timestamp: { + type: 'string', + format: 'date-time', + }, + uptime: { + type: 'number', + description: 'Process uptime in seconds', + }, + version: { + type: 'string', + example: '1.0.0', + }, + }, }, }, }, diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts index c00312b31..9ba9f2a6f 100644 --- a/apps/api/src/main.ts +++ b/apps/api/src/main.ts @@ -5,6 +5,8 @@ import type { OpenAPIObject } from '@nestjs/swagger'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import * as express from 'express'; import { AppModule } from './app.module'; +import { existsSync, mkdirSync, writeFileSync } from 'fs'; +import path from 'path'; async function bootstrap(): Promise { const app: INestApplication = await NestFactory.create(AppModule); @@ -56,6 +58,7 @@ async function bootstrap(): Promise { // Setup Swagger UI at /api/docs SwaggerModule.setup('api/docs', app, document, { + raw: ['json'], swaggerOptions: { persistAuthorization: true, // Keep auth between page refreshes }, @@ -68,6 +71,22 @@ async function bootstrap(): Promise { console.log(`Application is running on: ${actualUrl}`); console.log(`API Documentation available at: ${actualUrl}/api/docs`); + + // Write OpenAPI documentation to packages/docs/openapi.json only in development + if (process.env.NODE_ENV !== 'production') { + const openapiPath = path.join( + __dirname, + '../../../../packages/docs/openapi.json', + ); + + const docsDir = path.dirname(openapiPath); + if (!existsSync(docsDir)) { + mkdirSync(docsDir, { recursive: true }); + } + + writeFileSync(openapiPath, JSON.stringify(document, null, 2)); + console.log('OpenAPI documentation written to packages/docs/openapi.json'); + } } // Handle bootstrap errors properly diff --git a/apps/api/src/organization/schemas/delete-organization.responses.ts b/apps/api/src/organization/schemas/delete-organization.responses.ts index 8712db454..5c4f4eea8 100644 --- a/apps/api/src/organization/schemas/delete-organization.responses.ts +++ b/apps/api/src/organization/schemas/delete-organization.responses.ts @@ -1,70 +1,78 @@ import { ApiResponseOptions } from '@nestjs/swagger'; -export const DELETE_ORGANIZATION_RESPONSES: Record = { - 200: { - status: 200, - description: 'Organization deleted successfully', - schema: { - type: 'object', - properties: { - success: { - type: 'boolean', - description: 'Indicates successful deletion', - example: true, - }, - deletedOrganization: { - type: 'object', - properties: { - id: { - type: 'string', - description: 'The deleted organization ID', - example: 'org_abc123def456', - }, - name: { - type: 'string', - description: 'The deleted organization name', - example: 'Acme Corporation', +export const DELETE_ORGANIZATION_RESPONSES: Record = + { + 200: { + status: 200, + description: 'Organization deleted successfully', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + success: { + type: 'boolean', + description: 'Indicates successful deletion', + example: true, + }, + deletedOrganization: { + type: 'object', + properties: { + id: { + type: 'string', + description: 'The deleted organization ID', + example: 'org_abc123def456', + }, + name: { + type: 'string', + description: 'The deleted organization name', + example: 'Acme Corporation', + }, + }, + }, + authType: { + type: 'string', + enum: ['api-key', 'session'], + description: 'How the request was authenticated', + }, }, }, }, - authType: { - type: 'string', - enum: ['api-key', 'session'], - description: 'How the request was authenticated', - }, }, }, - }, - 401: { - status: 401, - description: - 'Unauthorized - Invalid authentication or insufficient permissions', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Invalid or expired API key', - 'Invalid or expired session', - 'User does not have access to organization', - 'Organization context required', - ], + 401: { + status: 401, + description: + 'Unauthorized - Invalid authentication or insufficient permissions', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Invalid or expired API key', + }, + }, + }, }, }, }, - }, - 404: { - status: 404, - description: 'Organization not found', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Organization with ID org_abc123def456 not found', + 404: { + status: 404, + description: 'Organization not found', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Organization with ID org_abc123def456 not found', + }, + }, + }, }, }, }, - }, -}; + }; diff --git a/apps/api/src/organization/schemas/get-organization.responses.ts b/apps/api/src/organization/schemas/get-organization.responses.ts index d482556b2..1cd7de638 100644 --- a/apps/api/src/organization/schemas/get-organization.responses.ts +++ b/apps/api/src/organization/schemas/get-organization.responses.ts @@ -4,72 +4,76 @@ export const GET_ORGANIZATION_RESPONSES: Record = { 200: { status: 200, description: 'Organization information retrieved successfully', - schema: { - type: 'object', - properties: { - id: { - type: 'string', - description: 'The organization ID', - example: 'org_abc123def456', - }, - name: { - type: 'string', - description: 'Organization name', - example: 'Acme Corporation', - }, - slug: { - type: 'string', - description: 'Organization slug', - example: 'acme-corp', - }, - logo: { - type: 'string', - nullable: true, - description: 'Organization logo URL', - example: 'https://example.com/logo.png', - }, - metadata: { - type: 'string', - nullable: true, - description: 'Additional metadata in JSON format', - example: '{"theme": "dark", "preferences": {}}', - }, - website: { - type: 'string', - nullable: true, - description: 'Organization website URL', - example: 'https://acme-corp.com', - }, - onboardingCompleted: { - type: 'boolean', - description: 'Whether onboarding is completed', - example: true, - }, - hasAccess: { - type: 'boolean', - description: 'Whether organization has access to the platform', - example: true, - }, - fleetDmLabelId: { - type: 'integer', - nullable: true, - description: 'FleetDM label ID for device management', - example: 123, - }, - isFleetSetupCompleted: { - type: 'boolean', - description: 'Whether FleetDM setup is completed', - example: false, - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'When the organization was created', - }, - authType: { - type: 'string', - enum: ['api-key', 'session'], - description: 'How the request was authenticated', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + id: { + type: 'string', + description: 'The organization ID', + example: 'org_abc123def456', + }, + name: { + type: 'string', + description: 'Organization name', + example: 'Acme Corporation', + }, + slug: { + type: 'string', + description: 'Organization slug', + example: 'acme-corp', + }, + logo: { + type: 'string', + nullable: true, + description: 'Organization logo URL', + example: 'https://example.com/logo.png', + }, + metadata: { + type: 'string', + nullable: true, + description: 'Additional metadata in JSON format', + example: '{"theme": "dark", "preferences": {}}', + }, + website: { + type: 'string', + nullable: true, + description: 'Organization website URL', + example: 'https://acme-corp.com', + }, + onboardingCompleted: { + type: 'boolean', + description: 'Whether onboarding is completed', + example: true, + }, + hasAccess: { + type: 'boolean', + description: 'Whether organization has access to the platform', + example: true, + }, + fleetDmLabelId: { + type: 'integer', + nullable: true, + description: 'FleetDM label ID for device management', + example: 123, + }, + isFleetSetupCompleted: { + type: 'boolean', + description: 'Whether FleetDM setup is completed', + example: false, + }, + createdAt: { + type: 'string', + format: 'date-time', + description: 'When the organization was created', + }, + authType: { + type: 'string', + enum: ['api-key', 'session'], + description: 'How the request was authenticated', + }, + }, }, }, }, @@ -78,17 +82,16 @@ export const GET_ORGANIZATION_RESPONSES: Record = { status: 401, description: 'Unauthorized - Invalid authentication or insufficient permissions', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Invalid or expired API key', - 'Invalid or expired session', - 'User does not have access to organization', - 'Organization context required', - ], + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Invalid or expired API key', + }, + }, }, }, }, diff --git a/apps/api/src/organization/schemas/update-organization.responses.ts b/apps/api/src/organization/schemas/update-organization.responses.ts index 118d4afbf..6aa7d42ee 100644 --- a/apps/api/src/organization/schemas/update-organization.responses.ts +++ b/apps/api/src/organization/schemas/update-organization.responses.ts @@ -1,126 +1,134 @@ import { ApiResponseOptions } from '@nestjs/swagger'; -export const UPDATE_ORGANIZATION_RESPONSES: Record = { - 200: { - status: 200, - description: 'Organization updated successfully', - schema: { - type: 'object', - properties: { - id: { - type: 'string', - description: 'The organization ID', - example: 'org_abc123def456', - }, - name: { - type: 'string', - description: 'Organization name', - example: 'New Acme Corporation', - }, - slug: { - type: 'string', - description: 'Organization slug', - example: 'new-acme-corp', - }, - logo: { - type: 'string', - nullable: true, - description: 'Organization logo URL', - example: 'https://example.com/logo.png', - }, - metadata: { - type: 'string', - nullable: true, - description: 'Additional metadata in JSON format', - example: '{"theme": "dark", "preferences": {}}', - }, - website: { - type: 'string', - nullable: true, - description: 'Organization website URL', - example: 'https://acme-corp.com', - }, - onboardingCompleted: { - type: 'boolean', - description: 'Whether onboarding is completed', - example: true, - }, - hasAccess: { - type: 'boolean', - description: 'Whether organization has access to the platform', - example: true, - }, - fleetDmLabelId: { - type: 'integer', - nullable: true, - description: 'FleetDM label ID for device management', - example: 123, - }, - isFleetSetupCompleted: { - type: 'boolean', - description: 'Whether FleetDM setup is completed', - example: false, - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'When the organization was created', - }, - authType: { - type: 'string', - enum: ['api-key', 'session'], - description: 'How the request was authenticated', +export const UPDATE_ORGANIZATION_RESPONSES: Record = + { + 200: { + status: 200, + description: 'Organization updated successfully', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + id: { + type: 'string', + description: 'The organization ID', + example: 'org_abc123def456', + }, + name: { + type: 'string', + description: 'Organization name', + example: 'New Acme Corporation', + }, + slug: { + type: 'string', + description: 'Organization slug', + example: 'new-acme-corp', + }, + logo: { + type: 'string', + nullable: true, + description: 'Organization logo URL', + example: 'https://example.com/logo.png', + }, + metadata: { + type: 'string', + nullable: true, + description: 'Additional metadata in JSON format', + example: '{"theme": "dark", "preferences": {}}', + }, + website: { + type: 'string', + nullable: true, + description: 'Organization website URL', + example: 'https://acme-corp.com', + }, + onboardingCompleted: { + type: 'boolean', + description: 'Whether onboarding is completed', + example: true, + }, + hasAccess: { + type: 'boolean', + description: 'Whether organization has access to the platform', + example: true, + }, + fleetDmLabelId: { + type: 'integer', + nullable: true, + description: 'FleetDM label ID for device management', + example: 123, + }, + isFleetSetupCompleted: { + type: 'boolean', + description: 'Whether FleetDM setup is completed', + example: false, + }, + createdAt: { + type: 'string', + format: 'date-time', + description: 'When the organization was created', + }, + authType: { + type: 'string', + enum: ['api-key', 'session'], + description: 'How the request was authenticated', + }, + }, + }, }, }, }, - }, - 400: { - status: 400, - description: 'Bad Request - Invalid update data', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Validation failed', - 'Invalid slug format', - 'Organization name already exists', - ], + 400: { + status: 400, + description: 'Bad Request - Invalid update data', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Invalid slug format', + }, + }, + }, }, }, }, - }, - 401: { - status: 401, - description: - 'Unauthorized - Invalid authentication or insufficient permissions', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Invalid or expired API key', - 'Invalid or expired session', - 'User does not have access to organization', - 'Organization context required', - ], + 401: { + status: 401, + description: + 'Unauthorized - Invalid authentication or insufficient permissions', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Invalid or expired API key', + }, + }, + }, }, }, }, - }, - 404: { - status: 404, - description: 'Organization not found', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Organization with ID org_abc123def456 not found', + 404: { + status: 404, + description: 'Organization not found', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Organization with ID org_abc123def456 not found', + }, + }, + }, }, }, }, - }, -}; + }; diff --git a/apps/api/src/people/people.controller.ts b/apps/api/src/people/people.controller.ts index 96b03f43e..70ba52e1d 100644 --- a/apps/api/src/people/people.controller.ts +++ b/apps/api/src/people/people.controller.ts @@ -1,15 +1,16 @@ -import { - Controller, - Get, +import { + Controller, + Get, Post, Patch, Delete, Body, Param, - UseGuards + UseGuards, } from '@nestjs/common'; import { ApiBody, + ApiExtraModels, ApiHeader, ApiOperation, ApiParam, @@ -17,16 +18,13 @@ import { ApiSecurity, ApiTags, } from '@nestjs/swagger'; -import { - AuthContext, - OrganizationId, -} from '../auth/auth-context.decorator'; +import { AuthContext, OrganizationId } from '../auth/auth-context.decorator'; import { HybridAuthGuard } from '../auth/hybrid-auth.guard'; import type { AuthContext as AuthContextType } from '../auth/types'; import { CreatePeopleDto } from './dto/create-people.dto'; import { UpdatePeopleDto } from './dto/update-people.dto'; import { BulkCreatePeopleDto } from './dto/bulk-create-people.dto'; -import { PeopleResponseDto } from './dto/people-responses.dto'; +import { PeopleResponseDto, UserResponseDto } from './dto/people-responses.dto'; import { PeopleService } from './people.service'; import { GET_ALL_PEOPLE_RESPONSES } from './schemas/get-all-people.responses'; import { CREATE_MEMBER_RESPONSES } from './schemas/create-member.responses'; @@ -39,6 +37,7 @@ import { PEOPLE_PARAMS } from './schemas/people-params'; import { PEOPLE_BODIES } from './schemas/people-bodies'; @ApiTags('People') +@ApiExtraModels(PeopleResponseDto, UserResponseDto) @Controller({ path: 'people', version: '1' }) @UseGuards(HybridAuthGuard) @ApiSecurity('apikey') @@ -61,18 +60,20 @@ export class PeopleController { @OrganizationId() organizationId: string, @AuthContext() authContext: AuthContextType, ) { - const people = await this.peopleService.findAllByOrganization(organizationId); + const people = + await this.peopleService.findAllByOrganization(organizationId); return { data: people, count: people.length, authType: authContext.authType, - ...(authContext.userId && authContext.userEmail && { - authenticatedUser: { - id: authContext.userId, - email: authContext.userEmail, - }, - }), + ...(authContext.userId && + authContext.userEmail && { + authenticatedUser: { + id: authContext.userId, + email: authContext.userEmail, + }, + }), }; } @@ -94,12 +95,13 @@ export class PeopleController { return { ...member, authType: authContext.authType, - ...(authContext.userId && authContext.userEmail && { - authenticatedUser: { - id: authContext.userId, - email: authContext.userEmail, - }, - }), + ...(authContext.userId && + authContext.userEmail && { + authenticatedUser: { + id: authContext.userId, + email: authContext.userEmail, + }, + }), }; } @@ -116,17 +118,21 @@ export class PeopleController { @OrganizationId() organizationId: string, @AuthContext() authContext: AuthContextType, ) { - const result = await this.peopleService.bulkCreate(organizationId, bulkCreateData); + const result = await this.peopleService.bulkCreate( + organizationId, + bulkCreateData, + ); return { ...result, authType: authContext.authType, - ...(authContext.userId && authContext.userEmail && { - authenticatedUser: { - id: authContext.userId, - email: authContext.userEmail, - }, - }), + ...(authContext.userId && + authContext.userEmail && { + authenticatedUser: { + id: authContext.userId, + email: authContext.userEmail, + }, + }), }; } @@ -147,12 +153,13 @@ export class PeopleController { return { ...person, authType: authContext.authType, - ...(authContext.userId && authContext.userEmail && { - authenticatedUser: { - id: authContext.userId, - email: authContext.userEmail, - }, - }), + ...(authContext.userId && + authContext.userEmail && { + authenticatedUser: { + id: authContext.userId, + email: authContext.userEmail, + }, + }), }; } @@ -180,12 +187,13 @@ export class PeopleController { return { ...updatedMember, authType: authContext.authType, - ...(authContext.userId && authContext.userEmail && { - authenticatedUser: { - id: authContext.userId, - email: authContext.userEmail, - }, - }), + ...(authContext.userId && + authContext.userEmail && { + authenticatedUser: { + id: authContext.userId, + email: authContext.userEmail, + }, + }), }; } @@ -201,17 +209,21 @@ export class PeopleController { @OrganizationId() organizationId: string, @AuthContext() authContext: AuthContextType, ) { - const result = await this.peopleService.deleteById(memberId, organizationId); + const result = await this.peopleService.deleteById( + memberId, + organizationId, + ); return { ...result, authType: authContext.authType, - ...(authContext.userId && authContext.userEmail && { - authenticatedUser: { - id: authContext.userId, - email: authContext.userEmail, - }, - }), + ...(authContext.userId && + authContext.userEmail && { + authenticatedUser: { + id: authContext.userId, + email: authContext.userEmail, + }, + }), }; } } diff --git a/apps/api/src/people/schemas/bulk-create-members.responses.ts b/apps/api/src/people/schemas/bulk-create-members.responses.ts index 1e20a1904..8467e1e40 100644 --- a/apps/api/src/people/schemas/bulk-create-members.responses.ts +++ b/apps/api/src/people/schemas/bulk-create-members.responses.ts @@ -1,121 +1,192 @@ import { ApiResponseOptions } from '@nestjs/swagger'; -export const BULK_CREATE_MEMBERS_RESPONSES: Record = { - 201: { - status: 201, - description: 'Bulk member creation completed', - schema: { - type: 'object', - properties: { - created: { - type: 'array', - items: { $ref: '#/components/schemas/PeopleResponseDto' }, - description: 'Successfully created members', - }, - errors: { - type: 'array', - items: { +export const BULK_CREATE_MEMBERS_RESPONSES: Record = + { + 201: { + status: 201, + description: 'Bulk member creation completed', + content: { + 'application/json': { + schema: { type: 'object', properties: { - index: { - type: 'number', - description: 'Index in the original array where the error occurred', - example: 2, + created: { + type: 'array', + items: { $ref: '#/components/schemas/PeopleResponseDto' }, + description: 'Successfully created members', }, - userId: { - type: 'string', - description: 'User ID that failed to be added', - example: 'usr_abc123def456', + errors: { + type: 'array', + items: { + type: 'object', + properties: { + index: { + type: 'number', + description: + 'Index in the original array where the error occurred', + example: 2, + }, + userId: { + type: 'string', + description: 'User ID that failed to be added', + example: 'usr_abc123def456', + }, + error: { + type: 'string', + description: + 'Error message explaining why the member could not be created', + example: + 'User user@example.com is already a member of this organization', + }, + }, + }, + description: + 'Members that failed to be created with error details', }, - error: { + summary: { + type: 'object', + properties: { + total: { + type: 'number', + description: 'Total number of members in the request', + example: 5, + }, + successful: { + type: 'number', + description: 'Number of members successfully created', + example: 3, + }, + failed: { + type: 'number', + description: 'Number of members that failed to be created', + example: 2, + }, + }, + }, + authType: { type: 'string', - description: 'Error message explaining why the member could not be created', - example: 'User user@example.com is already a member of this organization', + enum: ['api-key', 'session'], + description: 'How the request was authenticated', + }, + authenticatedUser: { + type: 'object', + properties: { + id: { + type: 'string', + description: 'User ID', + example: 'usr_abc123def456', + }, + email: { + type: 'string', + description: 'User email', + example: 'user@company.com', + }, + }, }, }, }, - description: 'Members that failed to be created with error details', - }, - summary: { - type: 'object', - properties: { - total: { - type: 'number', - description: 'Total number of members in the request', - example: 5, - }, - successful: { - type: 'number', - description: 'Number of members successfully created', - example: 3, - }, - failed: { - type: 'number', - description: 'Number of members that failed to be created', - example: 2, + example: { + created: [ + { + id: 'mem_abc123def456', + organizationId: 'org_abc123def456', + userId: 'usr_abc123def456', + role: 'member', + createdAt: '2024-01-01T00:00:00Z', + department: 'it', + isActive: true, + fleetDmLabelId: 123, + user: { + id: 'usr_abc123def456', + name: 'John Doe', + email: 'john.doe@company.com', + emailVerified: true, + image: 'https://example.com/avatar.jpg', + createdAt: '2024-01-01T00:00:00Z', + updatedAt: '2024-01-15T00:00:00Z', + lastLogin: '2024-01-15T12:00:00Z', + }, + }, + ], + errors: [ + { + index: 2, + userId: 'usr_xyz789abc123', + error: + 'User user2@example.com is already a member of this organization', + }, + ], + summary: { total: 2, successful: 1, failed: 1 }, + authType: 'api-key', + authenticatedUser: { + id: 'usr_admin123', + email: 'admin@company.com', }, }, }, - authType: { - type: 'string', - enum: ['api-key', 'session'], - description: 'How the request was authenticated', - }, - authenticatedUser: { - type: 'object', - properties: { - id: { - type: 'string', - description: 'User ID', - example: 'usr_abc123def456', + }, + }, + 400: { + status: 400, + description: 'Bad Request - Invalid bulk data or validation errors', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Members array cannot be empty', + }, }, - email: { - type: 'string', - description: 'User email', - example: 'user@company.com', + }, + }, + }, + }, + 401: { + status: 401, + description: + 'Unauthorized - Invalid authentication or insufficient permissions', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string', example: 'Unauthorized' }, }, }, }, }, }, - }, - 400: { - status: 400, - description: 'Bad Request - Invalid bulk data or validation errors', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Validation failed', - 'Members array cannot be empty', - 'Maximum 100 members allowed per bulk request', - ], + 404: { + status: 404, + description: 'Organization not found', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Organization with ID org_abc123def456 not found', + }, + }, + }, }, }, }, - }, - 401: { - status: 401, - description: - 'Unauthorized - Invalid authentication or insufficient permissions', - }, - 404: { - status: 404, - description: 'Organization not found', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Organization with ID org_abc123def456 not found', + 500: { + status: 500, + description: 'Internal server error', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string', example: 'Bulk creation failed' }, + }, + }, }, }, }, - }, - 500: { - status: 500, - description: 'Internal server error', - }, -}; + }; diff --git a/apps/api/src/people/schemas/create-member.responses.ts b/apps/api/src/people/schemas/create-member.responses.ts index 9059201b8..50cdc7aab 100644 --- a/apps/api/src/people/schemas/create-member.responses.ts +++ b/apps/api/src/people/schemas/create-member.responses.ts @@ -4,21 +4,46 @@ export const CREATE_MEMBER_RESPONSES: Record = { 201: { status: 201, description: 'Member created successfully', - type: 'PeopleResponseDto', + content: { + 'application/json': { + schema: { $ref: '#/components/schemas/PeopleResponseDto' }, + example: { + id: 'mem_abc123def456', + organizationId: 'org_abc123def456', + userId: 'usr_abc123def456', + role: 'admin', + createdAt: '2024-01-01T00:00:00Z', + department: 'it', + isActive: true, + fleetDmLabelId: 123, + user: { + id: 'usr_abc123def456', + name: 'John Doe', + email: 'john.doe@company.com', + emailVerified: true, + image: 'https://example.com/avatar.jpg', + createdAt: '2024-01-01T00:00:00Z', + updatedAt: '2024-01-15T00:00:00Z', + lastLogin: '2024-01-15T12:00:00Z', + }, + }, + }, + }, }, 400: { status: 400, description: 'Bad Request - Invalid member data or user already exists', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Validation failed', - 'User user@example.com is already a member of this organization', - 'Invalid user ID or role', - ], + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: + 'User user@example.com is already a member of this organization', + }, + }, }, }, }, @@ -27,19 +52,30 @@ export const CREATE_MEMBER_RESPONSES: Record = { status: 401, description: 'Unauthorized - Invalid authentication or insufficient permissions', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string', example: 'Unauthorized' }, + }, + }, + }, + }, }, 404: { status: 404, description: 'Organization or user not found', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Organization with ID org_abc123def456 not found', - 'User with ID usr_abc123def456 not found', - ], + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'User with ID usr_abc123def456 not found', + }, + }, }, }, }, @@ -47,5 +83,18 @@ export const CREATE_MEMBER_RESPONSES: Record = { 500: { status: 500, description: 'Internal server error', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Failed to create member', + }, + }, + }, + }, + }, }, }; diff --git a/apps/api/src/people/schemas/delete-member.responses.ts b/apps/api/src/people/schemas/delete-member.responses.ts index efc7b5272..237211506 100644 --- a/apps/api/src/people/schemas/delete-member.responses.ts +++ b/apps/api/src/people/schemas/delete-member.responses.ts @@ -4,39 +4,43 @@ export const DELETE_MEMBER_RESPONSES: Record = { 200: { status: 200, description: 'Member deleted successfully', - schema: { - type: 'object', - properties: { - success: { - type: 'boolean', - description: 'Indicates successful deletion', - example: true, - }, - deletedMember: { + content: { + 'application/json': { + schema: { type: 'object', properties: { - id: { - type: 'string', - description: 'The deleted member ID', - example: 'mem_abc123def456', + success: { + type: 'boolean', + description: 'Indicates successful deletion', + example: true, }, - name: { - type: 'string', - description: 'The deleted member name', - example: 'John Doe', + deletedMember: { + type: 'object', + properties: { + id: { + type: 'string', + description: 'The deleted member ID', + example: 'mem_abc123def456', + }, + name: { + type: 'string', + description: 'The deleted member name', + example: 'John Doe', + }, + email: { + type: 'string', + description: 'The deleted member email', + example: 'john.doe@company.com', + }, + }, }, - email: { + authType: { type: 'string', - description: 'The deleted member email', - example: 'john.doe@company.com', + enum: ['api-key', 'session'], + description: 'How the request was authenticated', }, }, }, - authType: { - type: 'string', - enum: ['api-key', 'session'], - description: 'How the request was authenticated', - }, }, }, }, @@ -44,19 +48,31 @@ export const DELETE_MEMBER_RESPONSES: Record = { status: 401, description: 'Unauthorized - Invalid authentication or insufficient permissions', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string', example: 'Unauthorized' }, + }, + }, + }, + }, }, 404: { status: 404, description: 'Organization or member not found', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Organization with ID org_abc123def456 not found', - 'Member with ID mem_abc123def456 not found in organization org_abc123def456', - ], + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: + 'Member with ID mem_abc123def456 not found in organization org_abc123def456', + }, + }, }, }, }, @@ -64,5 +80,15 @@ export const DELETE_MEMBER_RESPONSES: Record = { 500: { status: 500, description: 'Internal server error', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string', example: 'Failed to delete member' }, + }, + }, + }, + }, }, }; diff --git a/apps/api/src/people/schemas/get-all-people.responses.ts b/apps/api/src/people/schemas/get-all-people.responses.ts index a82491b1b..66c1b7a64 100644 --- a/apps/api/src/people/schemas/get-all-people.responses.ts +++ b/apps/api/src/people/schemas/get-all-people.responses.ts @@ -4,38 +4,72 @@ export const GET_ALL_PEOPLE_RESPONSES: Record = { 200: { status: 200, description: 'People retrieved successfully', - schema: { - type: 'object', - properties: { - data: { - type: 'array', - items: { $ref: '#/components/schemas/PeopleResponseDto' }, - }, - count: { - type: 'number', - description: 'Total number of people', - example: 25, - }, - authType: { - type: 'string', - enum: ['api-key', 'session'], - description: 'How the request was authenticated', - }, - authenticatedUser: { + content: { + 'application/json': { + schema: { type: 'object', properties: { - id: { - type: 'string', - description: 'User ID', - example: 'usr_abc123def456', + data: { + type: 'array', + items: { $ref: '#/components/schemas/PeopleResponseDto' }, }, - email: { + count: { + type: 'number', + description: 'Total number of people', + example: 25, + }, + authType: { type: 'string', - description: 'User email', - example: 'user@company.com', + enum: ['api-key', 'session'], + description: 'How the request was authenticated', + }, + authenticatedUser: { + type: 'object', + properties: { + id: { + type: 'string', + description: 'User ID', + example: 'usr_abc123def456', + }, + email: { + type: 'string', + description: 'User email', + example: 'user@company.com', + }, + }, }, }, }, + example: { + data: [ + { + id: 'mem_abc123def456', + organizationId: 'org_abc123def456', + userId: 'usr_abc123def456', + role: 'admin', + createdAt: '2024-01-01T00:00:00Z', + department: 'it', + isActive: true, + fleetDmLabelId: 123, + user: { + id: 'usr_abc123def456', + name: 'John Doe', + email: 'john.doe@company.com', + emailVerified: true, + image: 'https://example.com/avatar.jpg', + createdAt: '2024-01-01T00:00:00Z', + updatedAt: '2024-01-15T00:00:00Z', + lastLogin: '2024-01-15T12:00:00Z', + }, + }, + ], + count: 1, + authType: 'api-key', + authenticatedUser: { + id: 'usr_abc123def456', + email: 'user@company.com', + }, + }, }, }, }, @@ -43,17 +77,16 @@ export const GET_ALL_PEOPLE_RESPONSES: Record = { status: 401, description: 'Unauthorized - Invalid authentication or insufficient permissions', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Invalid or expired API key', - 'Invalid or expired session', - 'User does not have access to organization', - 'Organization context required', - ], + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Invalid or expired API key', + }, + }, }, }, }, @@ -61,12 +94,16 @@ export const GET_ALL_PEOPLE_RESPONSES: Record = { 404: { status: 404, description: 'Organization not found', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Organization with ID org_abc123def456 not found', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Organization with ID org_abc123def456 not found', + }, + }, }, }, }, @@ -74,12 +111,16 @@ export const GET_ALL_PEOPLE_RESPONSES: Record = { 500: { status: 500, description: 'Internal server error', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Failed to retrieve members', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Failed to retrieve members', + }, + }, }, }, }, diff --git a/apps/api/src/people/schemas/get-person-by-id.responses.ts b/apps/api/src/people/schemas/get-person-by-id.responses.ts index aa8be75b6..f3584d741 100644 --- a/apps/api/src/people/schemas/get-person-by-id.responses.ts +++ b/apps/api/src/people/schemas/get-person-by-id.responses.ts @@ -4,25 +4,61 @@ export const GET_PERSON_BY_ID_RESPONSES: Record = { 200: { status: 200, description: 'Person retrieved successfully', - type: 'PeopleResponseDto', + content: { + 'application/json': { + schema: { $ref: '#/components/schemas/PeopleResponseDto' }, + example: { + id: 'mem_abc123def456', + organizationId: 'org_abc123def456', + userId: 'usr_abc123def456', + role: 'admin', + createdAt: '2024-01-01T00:00:00Z', + department: 'it', + isActive: true, + fleetDmLabelId: 123, + user: { + id: 'usr_abc123def456', + name: 'John Doe', + email: 'john.doe@company.com', + emailVerified: true, + image: 'https://example.com/avatar.jpg', + createdAt: '2024-01-01T00:00:00Z', + updatedAt: '2024-01-15T00:00:00Z', + lastLogin: '2024-01-15T12:00:00Z', + }, + }, + }, + }, }, 401: { status: 401, description: 'Unauthorized - Invalid authentication or insufficient permissions', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string', example: 'Unauthorized' }, + }, + }, + }, + }, }, 404: { status: 404, description: 'Organization or member not found', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Organization with ID org_abc123def456 not found', - 'Member with ID mem_abc123def456 not found in organization org_abc123def456', - ], + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: + 'Member with ID mem_abc123def456 not found in organization org_abc123def456', + }, + }, }, }, }, @@ -30,5 +66,15 @@ export const GET_PERSON_BY_ID_RESPONSES: Record = { 500: { status: 500, description: 'Internal server error', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string', example: 'Internal server error' }, + }, + }, + }, + }, }, }; diff --git a/apps/api/src/people/schemas/update-member.responses.ts b/apps/api/src/people/schemas/update-member.responses.ts index 60a0de06d..f3b400979 100644 --- a/apps/api/src/people/schemas/update-member.responses.ts +++ b/apps/api/src/people/schemas/update-member.responses.ts @@ -4,21 +4,46 @@ export const UPDATE_MEMBER_RESPONSES: Record = { 200: { status: 200, description: 'Member updated successfully', - type: 'PeopleResponseDto', + content: { + 'application/json': { + schema: { $ref: '#/components/schemas/PeopleResponseDto' }, + example: { + id: 'mem_abc123def456', + organizationId: 'org_abc123def456', + userId: 'usr_abc123def456', + role: 'member', + createdAt: '2024-01-01T00:00:00Z', + department: 'it', + isActive: true, + fleetDmLabelId: 123, + user: { + id: 'usr_abc123def456', + name: 'John Doe', + email: 'john.doe@company.com', + emailVerified: true, + image: 'https://example.com/avatar.jpg', + createdAt: '2024-01-01T00:00:00Z', + updatedAt: '2024-01-15T00:00:00Z', + lastLogin: '2024-01-15T12:00:00Z', + }, + }, + }, + }, }, 400: { status: 400, description: 'Bad Request - Invalid update data or user conflict', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Validation failed', - 'User user@example.com is already a member of this organization', - 'Invalid user ID or role', - ], + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: + 'User user@example.com is already a member of this organization', + }, + }, }, }, }, @@ -27,20 +52,31 @@ export const UPDATE_MEMBER_RESPONSES: Record = { status: 401, description: 'Unauthorized - Invalid authentication or insufficient permissions', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string', example: 'Unauthorized' }, + }, + }, + }, + }, }, 404: { status: 404, description: 'Organization, member, or user not found', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Organization with ID org_abc123def456 not found', - 'Member with ID mem_abc123def456 not found in organization org_abc123def456', - 'User with ID usr_abc123def456 not found', - ], + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: + 'Member with ID mem_abc123def456 not found in organization org_abc123def456', + }, + }, }, }, }, @@ -48,5 +84,15 @@ export const UPDATE_MEMBER_RESPONSES: Record = { 500: { status: 500, description: 'Internal server error', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string', example: 'Internal server error' }, + }, + }, + }, + }, }, }; diff --git a/apps/api/src/policies/dto/create-policy.dto.ts b/apps/api/src/policies/dto/create-policy.dto.ts index 5fb72b68a..776a0763e 100644 --- a/apps/api/src/policies/dto/create-policy.dto.ts +++ b/apps/api/src/policies/dto/create-policy.dto.ts @@ -1,5 +1,13 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsString, IsOptional, IsEnum, IsBoolean, IsArray, IsDateString } from 'class-validator'; +import { + IsString, + IsOptional, + IsEnum, + IsBoolean, + IsArray, + IsDateString, + IsObject, +} from 'class-validator'; export enum PolicyStatus { DRAFT = 'draft', @@ -51,13 +59,29 @@ export class CreatePolicyDto { status?: PolicyStatus; @ApiProperty({ - description: 'Content of the policy in JSON format', - example: [{ type: 'paragraph', content: 'Policy content here' }], + description: 'Content of the policy as TipTap JSON (array of nodes)', + example: [ + { + type: 'heading', + attrs: { level: 2, textAlign: null }, + content: [{ type: 'text', text: 'Purpose' }], + }, + { + type: 'paragraph', + attrs: { textAlign: null }, + content: [ + { + type: 'text', + text: 'Verify workforce integrity and grant the right access at start, revoke at end.', + }, + ], + }, + ], type: 'array', - items: { type: 'object' }, + items: { type: 'object', additionalProperties: true }, }) @IsArray() - content: any[]; + content: unknown[]; @ApiProperty({ description: 'Review frequency of the policy', diff --git a/apps/api/src/policies/dto/policy-responses.dto.ts b/apps/api/src/policies/dto/policy-responses.dto.ts index a24ec72d5..755d708cd 100644 --- a/apps/api/src/policies/dto/policy-responses.dto.ts +++ b/apps/api/src/policies/dto/policy-responses.dto.ts @@ -29,12 +29,28 @@ export class PolicyResponseDto { status: PolicyStatus; @ApiProperty({ - description: 'Content of the policy in JSON format', - example: [{ type: 'paragraph', content: 'Policy content here' }], + description: 'Content of the policy as TipTap JSON (array of nodes)', + example: [ + { + type: 'heading', + attrs: { level: 2, textAlign: null }, + content: [{ type: 'text', text: 'Purpose' }], + }, + { + type: 'paragraph', + attrs: { textAlign: null }, + content: [ + { + type: 'text', + text: 'Verify workforce integrity and grant the right access at start, revoke at end.', + }, + ], + }, + ], type: 'array', - items: { type: 'object' }, + items: { type: 'object', additionalProperties: true }, }) - content: any[]; + content: unknown[]; @ApiProperty({ description: 'Review frequency of the policy', diff --git a/apps/api/src/policies/policies.controller.ts b/apps/api/src/policies/policies.controller.ts index c5242a4a3..49ccf73c3 100644 --- a/apps/api/src/policies/policies.controller.ts +++ b/apps/api/src/policies/policies.controller.ts @@ -1,12 +1,12 @@ -import { - Body, - Controller, - Delete, - Get, - Param, - Patch, - Post, - UseGuards +import { + Body, + Controller, + Delete, + Get, + Param, + Patch, + Post, + UseGuards, } from '@nestjs/common'; import { ApiBody, @@ -16,11 +16,9 @@ import { ApiResponse, ApiSecurity, ApiTags, + ApiExtraModels, } from '@nestjs/swagger'; -import { - AuthContext, - OrganizationId, -} from '../auth/auth-context.decorator'; +import { AuthContext, OrganizationId } from '../auth/auth-context.decorator'; import { HybridAuthGuard } from '../auth/hybrid-auth.guard'; import type { AuthContext as AuthContextType } from '../auth/types'; import { CreatePolicyDto } from './dto/create-policy.dto'; @@ -34,8 +32,10 @@ import { DELETE_POLICY_RESPONSES } from './schemas/delete-policy.responses'; import { POLICY_OPERATIONS } from './schemas/policy-operations'; import { POLICY_PARAMS } from './schemas/policy-params'; import { POLICY_BODIES } from './schemas/policy-bodies'; +import { PolicyResponseDto } from './dto/policy-responses.dto'; @ApiTags('Policies') +@ApiExtraModels(PolicyResponseDto) @Controller({ path: 'policies', version: '1' }) @UseGuards(HybridAuthGuard) @ApiSecurity('apikey') @@ -106,7 +106,10 @@ export class PoliciesController { @OrganizationId() organizationId: string, @AuthContext() authContext: AuthContextType, ) { - const policy = await this.policiesService.create(organizationId, createData); + const policy = await this.policiesService.create( + organizationId, + createData, + ); return { ...policy, diff --git a/apps/api/src/policies/policies.service.ts b/apps/api/src/policies/policies.service.ts index 1128a2e5e..60875cdd8 100644 --- a/apps/api/src/policies/policies.service.ts +++ b/apps/api/src/policies/policies.service.ts @@ -1,5 +1,6 @@ import { Injectable, NotFoundException, Logger } from '@nestjs/common'; import { db } from '@trycompai/db'; +import type { Prisma } from '@trycompai/db'; import type { CreatePolicyDto } from './dto/create-policy.dto'; import type { UpdatePolicyDto } from './dto/update-policy.dto'; @@ -35,10 +36,15 @@ export class PoliciesService { orderBy: { createdAt: 'desc' }, }); - this.logger.log(`Retrieved ${policies.length} policies for organization ${organizationId}`); + this.logger.log( + `Retrieved ${policies.length} policies for organization ${organizationId}`, + ); return policies; } catch (error) { - this.logger.error(`Failed to retrieve policies for organization ${organizationId}:`, error); + this.logger.error( + `Failed to retrieve policies for organization ${organizationId}:`, + error, + ); throw error; } } @@ -46,7 +52,7 @@ export class PoliciesService { async findById(id: string, organizationId: string) { try { const policy = await db.policy.findFirst({ - where: { + where: { id, organizationId, }, @@ -93,6 +99,8 @@ export class PoliciesService { const policy = await db.policy.create({ data: { ...createData, + // Ensure JSON[] type compatibility for Prisma + content: createData.content as Prisma.InputJsonValue[], organizationId, status: createData.status || 'draft', isRequiredToSign: createData.isRequiredToSign ?? true, @@ -123,16 +131,23 @@ export class PoliciesService { this.logger.log(`Created policy: ${policy.name} (${policy.id})`); return policy; } catch (error) { - this.logger.error(`Failed to create policy for organization ${organizationId}:`, error); + this.logger.error( + `Failed to create policy for organization ${organizationId}:`, + error, + ); throw error; } } - async updateById(id: string, organizationId: string, updateData: UpdatePolicyDto) { + async updateById( + id: string, + organizationId: string, + updateData: UpdatePolicyDto, + ) { try { // First check if the policy exists and belongs to the organization const existingPolicy = await db.policy.findFirst({ - where: { + where: { id, organizationId, }, @@ -144,18 +159,23 @@ export class PoliciesService { } // Prepare update data with special handling for status changes - const updatePayload: any = { ...updateData }; - + const updatePayload: Record = { ...updateData }; + // If status is being changed to published, update lastPublishedAt if (updateData.status === 'published') { updatePayload.lastPublishedAt = new Date(); } - + // If isArchived is being set to true, update lastArchivedAt if (updateData.isArchived === true) { updatePayload.lastArchivedAt = new Date(); } + // Coerce content to Prisma JSON[] input if provided + if (Array.isArray(updateData.content)) { + updatePayload.content = updateData.content as Prisma.InputJsonValue[]; + } + // Update the policy const updatedPolicy = await db.policy.update({ where: { id }, @@ -198,7 +218,7 @@ export class PoliciesService { try { // First check if the policy exists and belongs to the organization const policy = await db.policy.findFirst({ - where: { + where: { id, organizationId, }, diff --git a/apps/api/src/policies/schemas/create-policy.responses.ts b/apps/api/src/policies/schemas/create-policy.responses.ts index fc04fdfc6..507330b01 100644 --- a/apps/api/src/policies/schemas/create-policy.responses.ts +++ b/apps/api/src/policies/schemas/create-policy.responses.ts @@ -4,21 +4,49 @@ export const CREATE_POLICY_RESPONSES: Record = { 201: { status: 201, description: 'Policy created successfully', - type: 'PolicyResponseDto', + content: { + 'application/json': { + schema: { $ref: '#/components/schemas/PolicyResponseDto' }, + example: { + id: 'pol_abc123def456', + name: 'Data Privacy Policy', + description: + 'This policy outlines how we handle and protect personal data', + status: 'draft', + content: [ + { + type: 'paragraph', + content: [{ type: 'text', text: 'Policy content here' }], + }, + ], + frequency: 'yearly', + department: 'it', + isRequiredToSign: true, + signedBy: [], + reviewDate: '2024-12-31T00:00:00.000Z', + isArchived: false, + createdAt: '2024-01-01T00:00:00.000Z', + updatedAt: '2024-01-15T00:00:00.000Z', + organizationId: 'org_abc123def456', + assigneeId: 'usr_abc123def456', + approverId: 'usr_xyz789abc123', + }, + }, + }, }, 400: { status: 400, description: 'Bad Request - Invalid policy data', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Validation failed', - 'Invalid policy content format', - 'Policy name already exists', - ], + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Invalid policy content format', + }, + }, }, }, }, @@ -27,5 +55,15 @@ export const CREATE_POLICY_RESPONSES: Record = { status: 401, description: 'Unauthorized - Invalid authentication or insufficient permissions', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string', example: 'Unauthorized' }, + }, + }, + }, + }, }, }; diff --git a/apps/api/src/policies/schemas/delete-policy.responses.ts b/apps/api/src/policies/schemas/delete-policy.responses.ts index 6a0ffc2dc..3be960cec 100644 --- a/apps/api/src/policies/schemas/delete-policy.responses.ts +++ b/apps/api/src/policies/schemas/delete-policy.responses.ts @@ -4,34 +4,38 @@ export const DELETE_POLICY_RESPONSES: Record = { 200: { status: 200, description: 'Policy deleted successfully', - schema: { - type: 'object', - properties: { - success: { - type: 'boolean', - description: 'Indicates successful deletion', - example: true, - }, - deletedPolicy: { + content: { + 'application/json': { + schema: { type: 'object', properties: { - id: { - type: 'string', - description: 'The deleted policy ID', - example: 'pol_abc123def456', + success: { + type: 'boolean', + description: 'Indicates successful deletion', + example: true, + }, + deletedPolicy: { + type: 'object', + properties: { + id: { + type: 'string', + description: 'The deleted policy ID', + example: 'pol_abc123def456', + }, + name: { + type: 'string', + description: 'The deleted policy name', + example: 'Data Privacy Policy', + }, + }, }, - name: { + authType: { type: 'string', - description: 'The deleted policy name', - example: 'Data Privacy Policy', + enum: ['api-key', 'session'], + description: 'How the request was authenticated', }, }, }, - authType: { - type: 'string', - enum: ['api-key', 'session'], - description: 'How the request was authenticated', - }, }, }, }, @@ -39,9 +43,32 @@ export const DELETE_POLICY_RESPONSES: Record = { status: 401, description: 'Unauthorized - Invalid authentication or insufficient permissions', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string', example: 'Unauthorized' }, + }, + }, + }, + }, }, 404: { status: 404, description: 'Policy not found', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Policy with ID pol_abc123def456 not found', + }, + }, + }, + }, + }, }, }; diff --git a/apps/api/src/policies/schemas/get-all-policies.responses.ts b/apps/api/src/policies/schemas/get-all-policies.responses.ts index e1ab68248..fe75f07ee 100644 --- a/apps/api/src/policies/schemas/get-all-policies.responses.ts +++ b/apps/api/src/policies/schemas/get-all-policies.responses.ts @@ -4,24 +4,39 @@ export const GET_ALL_POLICIES_RESPONSES: Record = { 200: { status: 200, description: 'Policies retrieved successfully', - type: 'PolicyResponseDto', - isArray: true, + content: { + 'application/json': { + schema: { + type: 'array', + items: { $ref: '#/components/schemas/PolicyResponseDto' }, + }, + example: [ + { + id: 'pol_abc123def456', + name: 'Data Privacy Policy', + status: 'draft', + content: [ + { type: 'paragraph', content: [{ type: 'text', text: '...' }] }, + ], + isRequiredToSign: true, + signedBy: [], + createdAt: '2024-01-01T00:00:00.000Z', + updatedAt: '2024-01-15T00:00:00.000Z', + organizationId: 'org_abc123def456', + }, + ], + }, + }, }, 401: { status: 401, description: 'Unauthorized - Invalid authentication or insufficient permissions', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Invalid or expired API key', - 'Invalid or expired session', - 'User does not have access to organization', - 'Organization context required', - ], + content: { + 'application/json': { + schema: { + type: 'object', + properties: { message: { type: 'string', example: 'Unauthorized' } }, }, }, }, diff --git a/apps/api/src/policies/schemas/get-policy-by-id.responses.ts b/apps/api/src/policies/schemas/get-policy-by-id.responses.ts index 85c4daeb4..74a6b28d6 100644 --- a/apps/api/src/policies/schemas/get-policy-by-id.responses.ts +++ b/apps/api/src/policies/schemas/get-policy-by-id.responses.ts @@ -4,22 +4,53 @@ export const GET_POLICY_BY_ID_RESPONSES: Record = { 200: { status: 200, description: 'Policy retrieved successfully', - type: 'PolicyResponseDto', + content: { + 'application/json': { + schema: { $ref: '#/components/schemas/PolicyResponseDto' }, + example: { + id: 'pol_abc123def456', + name: 'Data Privacy Policy', + status: 'draft', + content: [ + { type: 'paragraph', content: [{ type: 'text', text: '...' }] }, + ], + isRequiredToSign: true, + signedBy: [], + createdAt: '2024-01-01T00:00:00.000Z', + updatedAt: '2024-01-15T00:00:00.000Z', + organizationId: 'org_abc123def456', + }, + }, + }, }, 401: { status: 401, description: 'Unauthorized - Invalid authentication or insufficient permissions', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string', example: 'Unauthorized' }, + }, + }, + }, + }, }, 404: { status: 404, description: 'Policy not found', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Policy with ID pol_abc123def456 not found', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Policy with ID pol_abc123def456 not found', + }, + }, }, }, }, diff --git a/apps/api/src/policies/schemas/update-policy.responses.ts b/apps/api/src/policies/schemas/update-policy.responses.ts index b35bc4ba5..77824e000 100644 --- a/apps/api/src/policies/schemas/update-policy.responses.ts +++ b/apps/api/src/policies/schemas/update-policy.responses.ts @@ -4,19 +4,81 @@ export const UPDATE_POLICY_RESPONSES: Record = { 200: { status: 200, description: 'Policy updated successfully', - type: 'PolicyResponseDto', + content: { + 'application/json': { + schema: { $ref: '#/components/schemas/PolicyResponseDto' }, + example: { + id: 'pol_abc123def456', + name: 'Data Privacy Policy', + description: + 'This policy outlines how we handle and protect personal data', + status: 'published', + content: [ + { + type: 'heading', + attrs: { level: 2 }, + content: [{ type: 'text', text: 'Purpose' }], + }, + ], + frequency: 'yearly', + department: 'it', + isRequiredToSign: true, + signedBy: ['usr_123'], + reviewDate: '2024-12-31T00:00:00.000Z', + isArchived: false, + createdAt: '2024-01-01T00:00:00.000Z', + updatedAt: '2024-01-15T00:00:00.000Z', + organizationId: 'org_abc123def456', + assigneeId: 'usr_abc123def456', + approverId: 'usr_xyz789abc123', + }, + }, + }, }, 400: { status: 400, description: 'Bad Request - Invalid update data', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string', example: 'Validation failed' }, + }, + }, + }, + }, }, 401: { status: 401, description: 'Unauthorized - Invalid authentication or insufficient permissions', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { type: 'string', example: 'Unauthorized' }, + }, + }, + }, + }, }, 404: { status: 404, description: 'Policy not found', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Policy with ID pol_abc123def456 not found', + }, + }, + }, + }, + }, }, }; diff --git a/apps/api/src/risks/schemas/create-risk.responses.ts b/apps/api/src/risks/schemas/create-risk.responses.ts index aeebd53a9..14eae783b 100644 --- a/apps/api/src/risks/schemas/create-risk.responses.ts +++ b/apps/api/src/risks/schemas/create-risk.responses.ts @@ -4,101 +4,131 @@ export const CREATE_RISK_RESPONSES: Record = { 201: { status: 201, description: 'Risk created successfully', - schema: { - type: 'object', - properties: { - id: { - type: 'string', - description: 'Risk ID', - example: 'rsk_abc123def456', - }, - title: { - type: 'string', - description: 'Risk title', - example: 'Data breach vulnerability in user authentication system', - }, - description: { - type: 'string', - description: 'Risk description', - example: 'Weak password requirements could lead to unauthorized access to user accounts', - }, - category: { - type: 'string', - enum: ['customer', 'governance', 'operations', 'other', 'people', 'regulatory', 'reporting', 'resilience', 'technology', 'vendor_management'], - example: 'technology', - }, - department: { - type: 'string', - enum: ['none', 'admin', 'gov', 'hr', 'it', 'itsm', 'qms'], - nullable: true, - example: 'it', - }, - status: { - type: 'string', - enum: ['open', 'pending', 'closed', 'archived'], - example: 'open', - }, - likelihood: { - type: 'string', - enum: ['very_unlikely', 'unlikely', 'possible', 'likely', 'very_likely'], - example: 'possible', - }, - impact: { - type: 'string', - enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], - example: 'major', - }, - residualLikelihood: { - type: 'string', - enum: ['very_unlikely', 'unlikely', 'possible', 'likely', 'very_likely'], - example: 'unlikely', - }, - residualImpact: { - type: 'string', - enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], - example: 'minor', - }, - treatmentStrategyDescription: { - type: 'string', - nullable: true, - example: 'Implement multi-factor authentication and strengthen password requirements', - }, - treatmentStrategy: { - type: 'string', - enum: ['accept', 'avoid', 'mitigate', 'transfer'], - example: 'mitigate', - }, - organizationId: { - type: 'string', - example: 'org_abc123def456', - }, - assigneeId: { - type: 'string', - nullable: true, - description: 'ID of the user assigned to this risk', - example: 'mem_abc123def456', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'When the risk was created', - }, - updatedAt: { - type: 'string', - format: 'date-time', - description: 'When the risk was last updated', - }, - authType: { - type: 'string', - enum: ['api-key', 'session'], - description: 'How the request was authenticated', - }, - authenticatedUser: { + content: { + 'application/json': { + schema: { type: 'object', - description: 'User information (only for session auth)', properties: { - id: { type: 'string', example: 'usr_def456ghi789' }, - email: { type: 'string', example: 'user@example.com' }, + id: { + type: 'string', + description: 'Risk ID', + example: 'rsk_abc123def456', + }, + title: { + type: 'string', + description: 'Risk title', + example: + 'Data breach vulnerability in user authentication system', + }, + description: { + type: 'string', + description: 'Risk description', + example: + 'Weak password requirements could lead to unauthorized access to user accounts', + }, + category: { + type: 'string', + enum: [ + 'customer', + 'governance', + 'operations', + 'other', + 'people', + 'regulatory', + 'reporting', + 'resilience', + 'technology', + 'vendor_management', + ], + example: 'technology', + }, + department: { + type: 'string', + enum: ['none', 'admin', 'gov', 'hr', 'it', 'itsm', 'qms'], + nullable: true, + example: 'it', + }, + status: { + type: 'string', + enum: ['open', 'pending', 'closed', 'archived'], + example: 'open', + }, + likelihood: { + type: 'string', + enum: [ + 'very_unlikely', + 'unlikely', + 'possible', + 'likely', + 'very_likely', + ], + example: 'possible', + }, + impact: { + type: 'string', + enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], + example: 'major', + }, + residualLikelihood: { + type: 'string', + enum: [ + 'very_unlikely', + 'unlikely', + 'possible', + 'likely', + 'very_likely', + ], + example: 'unlikely', + }, + residualImpact: { + type: 'string', + enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], + example: 'minor', + }, + treatmentStrategyDescription: { + type: 'string', + nullable: true, + example: + 'Implement multi-factor authentication and strengthen password requirements', + }, + treatmentStrategy: { + type: 'string', + enum: ['accept', 'avoid', 'mitigate', 'transfer'], + example: 'mitigate', + }, + organizationId: { + type: 'string', + example: 'org_abc123def456', + }, + assigneeId: { + type: 'string', + nullable: true, + description: 'ID of the user assigned to this risk', + example: 'mem_abc123def456', + }, + createdAt: { + type: 'string', + format: 'date-time', + description: 'When the risk was created', + }, + updatedAt: { + type: 'string', + format: 'date-time', + description: 'When the risk was last updated', + }, + authType: { + type: 'string', + enum: ['api-key', 'session'], + description: 'How the request was authenticated', + }, + authenticatedUser: { + type: 'object', + description: 'User information (only for session auth)', + properties: { + id: { type: 'string', example: 'usr_def456ghi789' }, + email: { type: 'string', example: 'user@example.com' }, + }, + }, }, }, }, @@ -107,37 +137,41 @@ export const CREATE_RISK_RESPONSES: Record = { 400: { status: 400, description: 'Bad request - Invalid input data', - schema: { - type: 'object', - properties: { - message: { - type: 'array', - items: { type: 'string' }, - example: [ - 'title should not be empty', - 'description should not be empty', - 'category must be a valid enum value', - ], + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'array', + items: { type: 'string' }, + example: [ + 'title should not be empty', + 'description should not be empty', + 'category must be a valid enum value', + ], + }, + error: { type: 'string', example: 'Bad Request' }, + statusCode: { type: 'number', example: 400 }, + }, }, - error: { type: 'string', example: 'Bad Request' }, - statusCode: { type: 'number', example: 400 }, }, }, }, 401: { status: 401, - description: 'Unauthorized - Invalid authentication or insufficient permissions', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Invalid or expired API key', - 'Invalid or expired session', - 'User does not have access to organization', - 'Organization context required', - ], + description: + 'Unauthorized - Invalid authentication or insufficient permissions', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Invalid or expired API key', + }, + }, }, }, }, @@ -145,12 +179,16 @@ export const CREATE_RISK_RESPONSES: Record = { 404: { status: 404, description: 'Organization not found', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Organization with ID org_abc123def456 not found', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Organization with ID org_abc123def456 not found', + }, + }, }, }, }, @@ -158,12 +196,16 @@ export const CREATE_RISK_RESPONSES: Record = { 500: { status: 500, description: 'Internal server error', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Internal server error', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Internal server error', + }, + }, }, }, }, diff --git a/apps/api/src/risks/schemas/delete-risk.responses.ts b/apps/api/src/risks/schemas/delete-risk.responses.ts index c8c9593ce..6750f27e5 100644 --- a/apps/api/src/risks/schemas/delete-risk.responses.ts +++ b/apps/api/src/risks/schemas/delete-risk.responses.ts @@ -4,39 +4,44 @@ export const DELETE_RISK_RESPONSES: Record = { 200: { status: 200, description: 'Risk deleted successfully', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Risk deleted successfully', - }, - deletedRisk: { + content: { + 'application/json': { + schema: { type: 'object', properties: { - id: { + message: { type: 'string', - description: 'Deleted risk ID', - example: 'rsk_abc123def456', + example: 'Risk deleted successfully', + }, + deletedRisk: { + type: 'object', + properties: { + id: { + type: 'string', + description: 'Deleted risk ID', + example: 'rsk_abc123def456', + }, + title: { + type: 'string', + description: 'Deleted risk title', + example: + 'Data breach vulnerability in user authentication system', + }, + }, }, - title: { + authType: { type: 'string', - description: 'Deleted risk title', - example: 'Data breach vulnerability in user authentication system', + enum: ['api-key', 'session'], + description: 'How the request was authenticated', + }, + authenticatedUser: { + type: 'object', + description: 'User information (only for session auth)', + properties: { + id: { type: 'string', example: 'usr_def456ghi789' }, + email: { type: 'string', example: 'user@example.com' }, + }, }, - }, - }, - authType: { - type: 'string', - enum: ['api-key', 'session'], - description: 'How the request was authenticated', - }, - authenticatedUser: { - type: 'object', - description: 'User information (only for session auth)', - properties: { - id: { type: 'string', example: 'usr_def456ghi789' }, - email: { type: 'string', example: 'user@example.com' }, }, }, }, @@ -44,18 +49,18 @@ export const DELETE_RISK_RESPONSES: Record = { }, 401: { status: 401, - description: 'Unauthorized - Invalid authentication or insufficient permissions', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Invalid or expired API key', - 'Invalid or expired session', - 'User does not have access to organization', - 'Organization context required', - ], + description: + 'Unauthorized - Invalid authentication or insufficient permissions', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Invalid or expired API key', + }, + }, }, }, }, @@ -63,12 +68,17 @@ export const DELETE_RISK_RESPONSES: Record = { 404: { status: 404, description: 'Risk not found', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Risk with ID rsk_abc123def456 not found in organization org_abc123def456', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: + 'Risk with ID rsk_abc123def456 not found in organization org_abc123def456', + }, + }, }, }, }, @@ -76,12 +86,16 @@ export const DELETE_RISK_RESPONSES: Record = { 500: { status: 500, description: 'Internal server error', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Internal server error', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Internal server error', + }, + }, }, }, }, diff --git a/apps/api/src/risks/schemas/get-all-risks.responses.ts b/apps/api/src/risks/schemas/get-all-risks.responses.ts index fdbca3dde..53e12a4f2 100644 --- a/apps/api/src/risks/schemas/get-all-risks.responses.ts +++ b/apps/api/src/risks/schemas/get-all-risks.responses.ts @@ -4,108 +4,137 @@ export const GET_ALL_RISKS_RESPONSES: Record = { 200: { status: 200, description: 'Risks retrieved successfully', - schema: { - type: 'object', - properties: { - data: { - type: 'array', - items: { - type: 'object', - properties: { - id: { - type: 'string', - description: 'Risk ID', - example: 'rsk_abc123def456', - }, - title: { - type: 'string', - description: 'Risk title', - example: 'Data breach vulnerability in user authentication system', - }, - description: { - type: 'string', - description: 'Risk description', - example: 'Weak password requirements could lead to unauthorized access to user accounts', - }, - category: { - type: 'string', - enum: ['customer', 'governance', 'operations', 'other', 'people', 'regulatory', 'reporting', 'resilience', 'technology', 'vendor_management'], - example: 'technology', - }, - status: { - type: 'string', - enum: ['open', 'pending', 'closed', 'archived'], - example: 'open', - }, - likelihood: { - type: 'string', - enum: ['very_unlikely', 'unlikely', 'possible', 'likely', 'very_likely'], - example: 'possible', - }, - impact: { - type: 'string', - enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], - example: 'major', - }, - treatmentStrategy: { - type: 'string', - enum: ['accept', 'avoid', 'mitigate', 'transfer'], - example: 'mitigate', - }, - assigneeId: { - type: 'string', - nullable: true, - description: 'ID of the user assigned to this risk', - example: 'mem_abc123def456', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'When the risk was created', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + data: { + type: 'array', + items: { + type: 'object', + properties: { + id: { + type: 'string', + description: 'Risk ID', + example: 'rsk_abc123def456', + }, + title: { + type: 'string', + description: 'Risk title', + example: + 'Data breach vulnerability in user authentication system', + }, + description: { + type: 'string', + description: 'Risk description', + example: + 'Weak password requirements could lead to unauthorized access to user accounts', + }, + category: { + type: 'string', + enum: [ + 'customer', + 'governance', + 'operations', + 'other', + 'people', + 'regulatory', + 'reporting', + 'resilience', + 'technology', + 'vendor_management', + ], + example: 'technology', + }, + status: { + type: 'string', + enum: ['open', 'pending', 'closed', 'archived'], + example: 'open', + }, + likelihood: { + type: 'string', + enum: [ + 'very_unlikely', + 'unlikely', + 'possible', + 'likely', + 'very_likely', + ], + example: 'possible', + }, + impact: { + type: 'string', + enum: [ + 'insignificant', + 'minor', + 'moderate', + 'major', + 'severe', + ], + example: 'major', + }, + treatmentStrategy: { + type: 'string', + enum: ['accept', 'avoid', 'mitigate', 'transfer'], + example: 'mitigate', + }, + assigneeId: { + type: 'string', + nullable: true, + description: 'ID of the user assigned to this risk', + example: 'mem_abc123def456', + }, + createdAt: { + type: 'string', + format: 'date-time', + description: 'When the risk was created', + }, + updatedAt: { + type: 'string', + format: 'date-time', + description: 'When the risk was last updated', + }, + }, }, - updatedAt: { - type: 'string', - format: 'date-time', - description: 'When the risk was last updated', + }, + count: { + type: 'number', + description: 'Total number of risks', + example: 15, + }, + authType: { + type: 'string', + enum: ['api-key', 'session'], + description: 'How the request was authenticated', + }, + authenticatedUser: { + type: 'object', + description: 'User information (only for session auth)', + properties: { + id: { type: 'string', example: 'usr_def456ghi789' }, + email: { type: 'string', example: 'user@example.com' }, }, }, }, }, - count: { - type: 'number', - description: 'Total number of risks', - example: 15, - }, - authType: { - type: 'string', - enum: ['api-key', 'session'], - description: 'How the request was authenticated', - }, - authenticatedUser: { - type: 'object', - description: 'User information (only for session auth)', - properties: { - id: { type: 'string', example: 'usr_def456ghi789' }, - email: { type: 'string', example: 'user@example.com' }, - }, - }, }, }, }, 401: { status: 401, - description: 'Unauthorized - Invalid authentication or insufficient permissions', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Invalid or expired API key', - 'Invalid or expired session', - 'User does not have access to organization', - 'Organization context required', - ], + description: + 'Unauthorized - Invalid authentication or insufficient permissions', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Invalid or expired API key', + }, + }, }, }, }, @@ -113,12 +142,16 @@ export const GET_ALL_RISKS_RESPONSES: Record = { 404: { status: 404, description: 'Organization not found', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Organization with ID org_abc123def456 not found', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Organization with ID org_abc123def456 not found', + }, + }, }, }, }, @@ -126,12 +159,16 @@ export const GET_ALL_RISKS_RESPONSES: Record = { 500: { status: 500, description: 'Internal server error', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Internal server error', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Internal server error', + }, + }, }, }, }, diff --git a/apps/api/src/risks/schemas/get-risk-by-id.responses.ts b/apps/api/src/risks/schemas/get-risk-by-id.responses.ts index 89ca6dcb4..6870eb655 100644 --- a/apps/api/src/risks/schemas/get-risk-by-id.responses.ts +++ b/apps/api/src/risks/schemas/get-risk-by-id.responses.ts @@ -4,101 +4,131 @@ export const GET_RISK_BY_ID_RESPONSES: Record = { 200: { status: 200, description: 'Risk retrieved successfully', - schema: { - type: 'object', - properties: { - id: { - type: 'string', - description: 'Risk ID', - example: 'rsk_abc123def456', - }, - title: { - type: 'string', - description: 'Risk title', - example: 'Data breach vulnerability in user authentication system', - }, - description: { - type: 'string', - description: 'Risk description', - example: 'Weak password requirements could lead to unauthorized access to user accounts', - }, - category: { - type: 'string', - enum: ['customer', 'governance', 'operations', 'other', 'people', 'regulatory', 'reporting', 'resilience', 'technology', 'vendor_management'], - example: 'technology', - }, - department: { - type: 'string', - enum: ['none', 'admin', 'gov', 'hr', 'it', 'itsm', 'qms'], - nullable: true, - example: 'it', - }, - status: { - type: 'string', - enum: ['open', 'pending', 'closed', 'archived'], - example: 'open', - }, - likelihood: { - type: 'string', - enum: ['very_unlikely', 'unlikely', 'possible', 'likely', 'very_likely'], - example: 'possible', - }, - impact: { - type: 'string', - enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], - example: 'major', - }, - residualLikelihood: { - type: 'string', - enum: ['very_unlikely', 'unlikely', 'possible', 'likely', 'very_likely'], - example: 'unlikely', - }, - residualImpact: { - type: 'string', - enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], - example: 'minor', - }, - treatmentStrategyDescription: { - type: 'string', - nullable: true, - example: 'Implement multi-factor authentication and strengthen password requirements', - }, - treatmentStrategy: { - type: 'string', - enum: ['accept', 'avoid', 'mitigate', 'transfer'], - example: 'mitigate', - }, - organizationId: { - type: 'string', - example: 'org_abc123def456', - }, - assigneeId: { - type: 'string', - nullable: true, - description: 'ID of the user assigned to this risk', - example: 'mem_abc123def456', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'When the risk was created', - }, - updatedAt: { - type: 'string', - format: 'date-time', - description: 'When the risk was last updated', - }, - authType: { - type: 'string', - enum: ['api-key', 'session'], - description: 'How the request was authenticated', - }, - authenticatedUser: { + content: { + 'application/json': { + schema: { type: 'object', - description: 'User information (only for session auth)', properties: { - id: { type: 'string', example: 'usr_def456ghi789' }, - email: { type: 'string', example: 'user@example.com' }, + id: { + type: 'string', + description: 'Risk ID', + example: 'rsk_abc123def456', + }, + title: { + type: 'string', + description: 'Risk title', + example: + 'Data breach vulnerability in user authentication system', + }, + description: { + type: 'string', + description: 'Risk description', + example: + 'Weak password requirements could lead to unauthorized access to user accounts', + }, + category: { + type: 'string', + enum: [ + 'customer', + 'governance', + 'operations', + 'other', + 'people', + 'regulatory', + 'reporting', + 'resilience', + 'technology', + 'vendor_management', + ], + example: 'technology', + }, + department: { + type: 'string', + enum: ['none', 'admin', 'gov', 'hr', 'it', 'itsm', 'qms'], + nullable: true, + example: 'it', + }, + status: { + type: 'string', + enum: ['open', 'pending', 'closed', 'archived'], + example: 'open', + }, + likelihood: { + type: 'string', + enum: [ + 'very_unlikely', + 'unlikely', + 'possible', + 'likely', + 'very_likely', + ], + example: 'possible', + }, + impact: { + type: 'string', + enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], + example: 'major', + }, + residualLikelihood: { + type: 'string', + enum: [ + 'very_unlikely', + 'unlikely', + 'possible', + 'likely', + 'very_likely', + ], + example: 'unlikely', + }, + residualImpact: { + type: 'string', + enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], + example: 'minor', + }, + treatmentStrategyDescription: { + type: 'string', + nullable: true, + example: + 'Implement multi-factor authentication and strengthen password requirements', + }, + treatmentStrategy: { + type: 'string', + enum: ['accept', 'avoid', 'mitigate', 'transfer'], + example: 'mitigate', + }, + organizationId: { + type: 'string', + example: 'org_abc123def456', + }, + assigneeId: { + type: 'string', + nullable: true, + description: 'ID of the user assigned to this risk', + example: 'mem_abc123def456', + }, + createdAt: { + type: 'string', + format: 'date-time', + description: 'When the risk was created', + }, + updatedAt: { + type: 'string', + format: 'date-time', + description: 'When the risk was last updated', + }, + authType: { + type: 'string', + enum: ['api-key', 'session'], + description: 'How the request was authenticated', + }, + authenticatedUser: { + type: 'object', + description: 'User information (only for session auth)', + properties: { + id: { type: 'string', example: 'usr_def456ghi789' }, + email: { type: 'string', example: 'user@example.com' }, + }, + }, }, }, }, @@ -106,18 +136,18 @@ export const GET_RISK_BY_ID_RESPONSES: Record = { }, 401: { status: 401, - description: 'Unauthorized - Invalid authentication or insufficient permissions', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Invalid or expired API key', - 'Invalid or expired session', - 'User does not have access to organization', - 'Organization context required', - ], + description: + 'Unauthorized - Invalid authentication or insufficient permissions', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Invalid or expired API key', + }, + }, }, }, }, @@ -125,12 +155,17 @@ export const GET_RISK_BY_ID_RESPONSES: Record = { 404: { status: 404, description: 'Risk not found', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Risk with ID rsk_abc123def456 not found in organization org_abc123def456', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: + 'Risk with ID rsk_abc123def456 not found in organization org_abc123def456', + }, + }, }, }, }, @@ -138,12 +173,16 @@ export const GET_RISK_BY_ID_RESPONSES: Record = { 500: { status: 500, description: 'Internal server error', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Internal server error', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Internal server error', + }, + }, }, }, }, diff --git a/apps/api/src/risks/schemas/update-risk.responses.ts b/apps/api/src/risks/schemas/update-risk.responses.ts index 0f25c1478..de1fd1b9e 100644 --- a/apps/api/src/risks/schemas/update-risk.responses.ts +++ b/apps/api/src/risks/schemas/update-risk.responses.ts @@ -4,101 +4,131 @@ export const UPDATE_RISK_RESPONSES: Record = { 200: { status: 200, description: 'Risk updated successfully', - schema: { - type: 'object', - properties: { - id: { - type: 'string', - description: 'Risk ID', - example: 'rsk_abc123def456', - }, - title: { - type: 'string', - description: 'Risk title', - example: 'Data breach vulnerability in user authentication system', - }, - description: { - type: 'string', - description: 'Risk description', - example: 'Weak password requirements could lead to unauthorized access to user accounts', - }, - category: { - type: 'string', - enum: ['customer', 'governance', 'operations', 'other', 'people', 'regulatory', 'reporting', 'resilience', 'technology', 'vendor_management'], - example: 'technology', - }, - department: { - type: 'string', - enum: ['none', 'admin', 'gov', 'hr', 'it', 'itsm', 'qms'], - nullable: true, - example: 'it', - }, - status: { - type: 'string', - enum: ['open', 'pending', 'closed', 'archived'], - example: 'open', - }, - likelihood: { - type: 'string', - enum: ['very_unlikely', 'unlikely', 'possible', 'likely', 'very_likely'], - example: 'possible', - }, - impact: { - type: 'string', - enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], - example: 'major', - }, - residualLikelihood: { - type: 'string', - enum: ['very_unlikely', 'unlikely', 'possible', 'likely', 'very_likely'], - example: 'unlikely', - }, - residualImpact: { - type: 'string', - enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], - example: 'minor', - }, - treatmentStrategyDescription: { - type: 'string', - nullable: true, - example: 'Implement multi-factor authentication and strengthen password requirements', - }, - treatmentStrategy: { - type: 'string', - enum: ['accept', 'avoid', 'mitigate', 'transfer'], - example: 'mitigate', - }, - organizationId: { - type: 'string', - example: 'org_abc123def456', - }, - assigneeId: { - type: 'string', - nullable: true, - description: 'ID of the user assigned to this risk', - example: 'mem_abc123def456', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'When the risk was created', - }, - updatedAt: { - type: 'string', - format: 'date-time', - description: 'When the risk was last updated', - }, - authType: { - type: 'string', - enum: ['api-key', 'session'], - description: 'How the request was authenticated', - }, - authenticatedUser: { + content: { + 'application/json': { + schema: { type: 'object', - description: 'User information (only for session auth)', properties: { - id: { type: 'string', example: 'usr_def456ghi789' }, - email: { type: 'string', example: 'user@example.com' }, + id: { + type: 'string', + description: 'Risk ID', + example: 'rsk_abc123def456', + }, + title: { + type: 'string', + description: 'Risk title', + example: + 'Data breach vulnerability in user authentication system', + }, + description: { + type: 'string', + description: 'Risk description', + example: + 'Weak password requirements could lead to unauthorized access to user accounts', + }, + category: { + type: 'string', + enum: [ + 'customer', + 'governance', + 'operations', + 'other', + 'people', + 'regulatory', + 'reporting', + 'resilience', + 'technology', + 'vendor_management', + ], + example: 'technology', + }, + department: { + type: 'string', + enum: ['none', 'admin', 'gov', 'hr', 'it', 'itsm', 'qms'], + nullable: true, + example: 'it', + }, + status: { + type: 'string', + enum: ['open', 'pending', 'closed', 'archived'], + example: 'open', + }, + likelihood: { + type: 'string', + enum: [ + 'very_unlikely', + 'unlikely', + 'possible', + 'likely', + 'very_likely', + ], + example: 'possible', + }, + impact: { + type: 'string', + enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], + example: 'major', + }, + residualLikelihood: { + type: 'string', + enum: [ + 'very_unlikely', + 'unlikely', + 'possible', + 'likely', + 'very_likely', + ], + example: 'unlikely', + }, + residualImpact: { + type: 'string', + enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], + example: 'minor', + }, + treatmentStrategyDescription: { + type: 'string', + nullable: true, + example: + 'Implement multi-factor authentication and strengthen password requirements', + }, + treatmentStrategy: { + type: 'string', + enum: ['accept', 'avoid', 'mitigate', 'transfer'], + example: 'mitigate', + }, + organizationId: { + type: 'string', + example: 'org_abc123def456', + }, + assigneeId: { + type: 'string', + nullable: true, + description: 'ID of the user assigned to this risk', + example: 'mem_abc123def456', + }, + createdAt: { + type: 'string', + format: 'date-time', + description: 'When the risk was created', + }, + updatedAt: { + type: 'string', + format: 'date-time', + description: 'When the risk was last updated', + }, + authType: { + type: 'string', + enum: ['api-key', 'session'], + description: 'How the request was authenticated', + }, + authenticatedUser: { + type: 'object', + description: 'User information (only for session auth)', + properties: { + id: { type: 'string', example: 'usr_def456ghi789' }, + email: { type: 'string', example: 'user@example.com' }, + }, + }, }, }, }, @@ -107,37 +137,41 @@ export const UPDATE_RISK_RESPONSES: Record = { 400: { status: 400, description: 'Bad request - Invalid input data', - schema: { - type: 'object', - properties: { - message: { - type: 'array', - items: { type: 'string' }, - example: [ - 'title should not be empty', - 'category must be a valid enum value', - 'status must be a valid enum value', - ], + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'array', + items: { type: 'string' }, + example: [ + 'title should not be empty', + 'category must be a valid enum value', + 'status must be a valid enum value', + ], + }, + error: { type: 'string', example: 'Bad Request' }, + statusCode: { type: 'number', example: 400 }, + }, }, - error: { type: 'string', example: 'Bad Request' }, - statusCode: { type: 'number', example: 400 }, }, }, }, 401: { status: 401, - description: 'Unauthorized - Invalid authentication or insufficient permissions', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Invalid or expired API key', - 'Invalid or expired session', - 'User does not have access to organization', - 'Organization context required', - ], + description: + 'Unauthorized - Invalid authentication or insufficient permissions', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Invalid or expired API key', + }, + }, }, }, }, @@ -145,12 +179,17 @@ export const UPDATE_RISK_RESPONSES: Record = { 404: { status: 404, description: 'Risk not found', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Risk with ID rsk_abc123def456 not found in organization org_abc123def456', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: + 'Risk with ID rsk_abc123def456 not found in organization org_abc123def456', + }, + }, }, }, }, @@ -158,12 +197,16 @@ export const UPDATE_RISK_RESPONSES: Record = { 500: { status: 500, description: 'Internal server error', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Internal server error', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Internal server error', + }, + }, }, }, }, diff --git a/apps/api/src/tasks/tasks.controller.ts b/apps/api/src/tasks/tasks.controller.ts index 92a806985..d6faee26a 100644 --- a/apps/api/src/tasks/tasks.controller.ts +++ b/apps/api/src/tasks/tasks.controller.ts @@ -16,8 +16,10 @@ import { ApiOperation, ApiParam, ApiResponse, + ApiNoContentResponse, ApiSecurity, ApiTags, + ApiExtraModels, } from '@nestjs/swagger'; import { AttachmentsService } from '../attachments/attachments.service'; import { UploadAttachmentDto } from '../attachments/upload-attachment.dto'; @@ -31,6 +33,7 @@ import { import { TasksService } from './tasks.service'; @ApiTags('Tasks') +@ApiExtraModels(TaskResponseDto, AttachmentResponseDto) @Controller({ path: 'tasks', version: '1' }) @UseGuards(HybridAuthGuard) @ApiSecurity('apikey') @@ -56,11 +59,36 @@ export class TasksController { @ApiResponse({ status: 200, description: 'Tasks retrieved successfully', - type: [TaskResponseDto], + content: { + 'application/json': { + schema: { + type: 'array', + items: { $ref: '#/components/schemas/TaskResponseDto' }, + }, + example: [ + { + id: 'tsk_abc123def456', + title: 'Implement user authentication', + description: 'Add OAuth 2.0 authentication to the platform', + status: 'in_progress', + createdAt: '2024-01-15T10:30:00Z', + updatedAt: '2024-01-15T10:30:00Z', + }, + ], + }, + }, }) @ApiResponse({ status: 401, description: 'Unauthorized - Invalid authentication', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { message: { type: 'string', example: 'Unauthorized' } }, + }, + }, + }, }) async getTasks( @OrganizationId() organizationId: string, @@ -81,11 +109,36 @@ export class TasksController { @ApiResponse({ status: 200, description: 'Task retrieved successfully', - type: TaskResponseDto, + content: { + 'application/json': { + schema: { $ref: '#/components/schemas/TaskResponseDto' }, + example: { + id: 'tsk_abc123def456', + title: 'Implement user authentication', + description: 'Add OAuth 2.0 authentication to the platform', + status: 'in_progress', + createdAt: '2024-01-15T10:30:00Z', + updatedAt: '2024-01-15T10:30:00Z', + }, + }, + }, }) @ApiResponse({ status: 404, description: 'Task not found', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Task with ID tsk_abc123def456 not found', + }, + }, + }, + }, + }, }) async getTask( @OrganizationId() organizationId: string, @@ -109,7 +162,54 @@ export class TasksController { @ApiResponse({ status: 200, description: 'Attachments retrieved successfully', - type: [AttachmentResponseDto], + content: { + 'application/json': { + schema: { + type: 'array', + items: { $ref: '#/components/schemas/AttachmentResponseDto' }, + }, + example: [ + { + id: 'att_abc123def456', + name: 'evidence.pdf', + type: 'application/pdf', + size: 123456, + downloadUrl: + 'https://bucket.s3.amazonaws.com/path/to/file.pdf?signature=...', + createdAt: '2024-01-15T10:30:00Z', + }, + ], + }, + }, + }) + @ApiResponse({ + status: 401, + description: 'Unauthorized - Invalid authentication', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { message: { type: 'string', example: 'Unauthorized' } }, + }, + }, + }, + }) + @ApiResponse({ + status: 404, + description: 'Task not found', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Task with ID tsk_abc123def456 not found', + }, + }, + }, + }, + }, }) async getTaskAttachments( @OrganizationId() organizationId: string, @@ -138,11 +238,67 @@ export class TasksController { @ApiResponse({ status: 201, description: 'Attachment uploaded successfully', - type: AttachmentResponseDto, + content: { + 'application/json': { + schema: { $ref: '#/components/schemas/AttachmentResponseDto' }, + example: { + id: 'att_abc123def456', + entityId: 'tsk_abc123def456', + entityType: 'task', + fileName: 'evidence.pdf', + fileType: 'application/pdf', + fileSize: 123456, + createdAt: '2024-01-01T00:00:00Z', + createdBy: 'usr_abc123def456', + }, + }, + }, }) @ApiResponse({ status: 400, description: 'Invalid file data or file too large', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'File exceeds maximum allowed size', + }, + }, + }, + }, + }, + }) + @ApiResponse({ + status: 401, + description: 'Unauthorized - Invalid authentication', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { message: { type: 'string', example: 'Unauthorized' } }, + }, + }, + }, + }) + @ApiResponse({ + status: 404, + description: 'Task not found', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Task with ID tsk_abc123def456 not found', + }, + }, + }, + }, + }, }) async uploadTaskAttachment( @AuthContext() authContext: AuthContextType, @@ -187,19 +343,52 @@ export class TasksController { @ApiResponse({ status: 200, description: 'Download URL generated successfully', - schema: { - type: 'object', - properties: { - downloadUrl: { - type: 'string', - description: 'Signed URL for downloading the file', - example: - 'https://bucket.s3.amazonaws.com/path/to/file.pdf?signature=...', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + downloadUrl: { + type: 'string', + description: 'Signed URL for downloading the file', + example: + 'https://bucket.s3.amazonaws.com/path/to/file.pdf?signature=...', + }, + expiresIn: { + type: 'number', + description: 'URL expiration time in seconds', + example: 900, + }, + }, }, - expiresIn: { - type: 'number', - description: 'URL expiration time in seconds', - example: 900, + }, + }, + }) + @ApiResponse({ + status: 401, + description: 'Unauthorized - Invalid authentication', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { message: { type: 'string', example: 'Unauthorized' } }, + }, + }, + }, + }) + @ApiResponse({ + status: 404, + description: 'Task or attachment not found', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Task or attachment not found', + }, + }, }, }, }, @@ -219,7 +408,6 @@ export class TasksController { } @Delete(':taskId/attachments/:attachmentId') - @HttpCode(HttpStatus.NO_CONTENT) @ApiOperation({ summary: 'Delete task attachment', description: 'Delete a specific attachment from a task', @@ -235,18 +423,65 @@ export class TasksController { example: 'att_abc123def456', }) @ApiResponse({ - status: 204, + status: 200, description: 'Attachment deleted successfully', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + success: { type: 'boolean', example: true }, + deletedAttachmentId: { + type: 'string', + example: 'att_abc123def456', + }, + message: { + type: 'string', + example: 'Attachment deleted successfully', + }, + }, + }, + }, + }, }) @ApiResponse({ status: 404, description: 'Task or attachment not found', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Task or attachment not found', + }, + }, + }, + }, + }, + }) + @ApiResponse({ + status: 401, + description: 'Unauthorized - Invalid authentication', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { message: { type: 'string', example: 'Unauthorized' } }, + }, + }, + }, }) async deleteTaskAttachment( @OrganizationId() organizationId: string, @Param('taskId') taskId: string, @Param('attachmentId') attachmentId: string, - ): Promise { + ): Promise<{ + success: boolean; + deletedAttachmentId: string; + message: string; + }> { // Verify task access await this.tasksService.verifyTaskAccess(organizationId, taskId); @@ -254,5 +489,11 @@ export class TasksController { organizationId, attachmentId, ); + + return { + success: true, + deletedAttachmentId: attachmentId, + message: 'Attachment deleted successfully', + }; } } diff --git a/apps/api/src/vendors/schemas/create-vendor.responses.ts b/apps/api/src/vendors/schemas/create-vendor.responses.ts index 39107607e..6bf937ba1 100644 --- a/apps/api/src/vendors/schemas/create-vendor.responses.ts +++ b/apps/api/src/vendors/schemas/create-vendor.responses.ts @@ -4,90 +4,116 @@ export const CREATE_VENDOR_RESPONSES: Record = { 201: { status: 201, description: 'Vendor created successfully', - schema: { - type: 'object', - properties: { - id: { - type: 'string', - description: 'Vendor ID', - example: 'vnd_abc123def456', - }, - name: { - type: 'string', - description: 'Vendor name', - example: 'CloudTech Solutions Inc.', - }, - description: { - type: 'string', - description: 'Vendor description', - example: 'Cloud infrastructure provider offering AWS-like services including compute, storage, and networking solutions for enterprise customers.', - }, - category: { - type: 'string', - enum: ['cloud', 'infrastructure', 'software_as_a_service', 'finance', 'marketing', 'sales', 'hr', 'other'], - example: 'cloud', - }, - status: { - type: 'string', - enum: ['not_assessed', 'in_progress', 'assessed'], - example: 'not_assessed', - }, - inherentProbability: { - type: 'string', - enum: ['very_unlikely', 'unlikely', 'possible', 'likely', 'very_likely'], - example: 'possible', - }, - inherentImpact: { - type: 'string', - enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], - example: 'moderate', - }, - residualProbability: { - type: 'string', - enum: ['very_unlikely', 'unlikely', 'possible', 'likely', 'very_likely'], - example: 'unlikely', - }, - residualImpact: { - type: 'string', - enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], - example: 'minor', - }, - website: { - type: 'string', - nullable: true, - example: 'https://www.cloudtechsolutions.com', - }, - organizationId: { - type: 'string', - example: 'org_abc123def456', - }, - assigneeId: { - type: 'string', - nullable: true, - description: 'ID of the user assigned to manage this vendor', - example: 'mem_abc123def456', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'When the vendor was created', - }, - updatedAt: { - type: 'string', - format: 'date-time', - description: 'When the vendor was last updated', - }, - authType: { - type: 'string', - enum: ['api-key', 'session'], - description: 'How the request was authenticated', - }, - authenticatedUser: { + content: { + 'application/json': { + schema: { type: 'object', - description: 'User information (only for session auth)', properties: { - id: { type: 'string', example: 'usr_def456ghi789' }, - email: { type: 'string', example: 'user@example.com' }, + id: { + type: 'string', + description: 'Vendor ID', + example: 'vnd_abc123def456', + }, + name: { + type: 'string', + description: 'Vendor name', + example: 'CloudTech Solutions Inc.', + }, + description: { + type: 'string', + description: 'Vendor description', + example: + 'Cloud infrastructure provider offering AWS-like services including compute, storage, and networking solutions for enterprise customers.', + }, + category: { + type: 'string', + enum: [ + 'cloud', + 'infrastructure', + 'software_as_a_service', + 'finance', + 'marketing', + 'sales', + 'hr', + 'other', + ], + example: 'cloud', + }, + status: { + type: 'string', + enum: ['not_assessed', 'in_progress', 'assessed'], + example: 'not_assessed', + }, + inherentProbability: { + type: 'string', + enum: [ + 'very_unlikely', + 'unlikely', + 'possible', + 'likely', + 'very_likely', + ], + example: 'possible', + }, + inherentImpact: { + type: 'string', + enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], + example: 'moderate', + }, + residualProbability: { + type: 'string', + enum: [ + 'very_unlikely', + 'unlikely', + 'possible', + 'likely', + 'very_likely', + ], + example: 'unlikely', + }, + residualImpact: { + type: 'string', + enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], + example: 'minor', + }, + website: { + type: 'string', + nullable: true, + example: 'https://www.cloudtechsolutions.com', + }, + organizationId: { + type: 'string', + example: 'org_abc123def456', + }, + assigneeId: { + type: 'string', + nullable: true, + description: 'ID of the user assigned to manage this vendor', + example: 'mem_abc123def456', + }, + createdAt: { + type: 'string', + format: 'date-time', + description: 'When the vendor was created', + }, + updatedAt: { + type: 'string', + format: 'date-time', + description: 'When the vendor was last updated', + }, + authType: { + type: 'string', + enum: ['api-key', 'session'], + description: 'How the request was authenticated', + }, + authenticatedUser: { + type: 'object', + description: 'User information (only for session auth)', + properties: { + id: { type: 'string', example: 'usr_def456ghi789' }, + email: { type: 'string', example: 'user@example.com' }, + }, + }, }, }, }, @@ -96,38 +122,42 @@ export const CREATE_VENDOR_RESPONSES: Record = { 400: { status: 400, description: 'Bad request - Invalid input data', - schema: { - type: 'object', - properties: { - message: { - type: 'array', - items: { type: 'string' }, - example: [ - 'name should not be empty', - 'description should not be empty', - 'category must be a valid enum value', - 'website must be a URL address', - ], + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'array', + items: { type: 'string' }, + example: [ + 'name should not be empty', + 'description should not be empty', + 'category must be a valid enum value', + 'website must be a URL address', + ], + }, + error: { type: 'string', example: 'Bad Request' }, + statusCode: { type: 'number', example: 400 }, + }, }, - error: { type: 'string', example: 'Bad Request' }, - statusCode: { type: 'number', example: 400 }, }, }, }, 401: { status: 401, - description: 'Unauthorized - Invalid authentication or insufficient permissions', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Invalid or expired API key', - 'Invalid or expired session', - 'User does not have access to organization', - 'Organization context required', - ], + description: + 'Unauthorized - Invalid authentication or insufficient permissions', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Invalid or expired API key', + }, + }, }, }, }, @@ -135,12 +165,16 @@ export const CREATE_VENDOR_RESPONSES: Record = { 404: { status: 404, description: 'Organization not found', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Organization with ID org_abc123def456 not found', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Organization with ID org_abc123def456 not found', + }, + }, }, }, }, @@ -148,12 +182,16 @@ export const CREATE_VENDOR_RESPONSES: Record = { 500: { status: 500, description: 'Internal server error', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Internal server error', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Internal server error', + }, + }, }, }, }, diff --git a/apps/api/src/vendors/schemas/delete-vendor.responses.ts b/apps/api/src/vendors/schemas/delete-vendor.responses.ts index 32a8ac876..4ca203984 100644 --- a/apps/api/src/vendors/schemas/delete-vendor.responses.ts +++ b/apps/api/src/vendors/schemas/delete-vendor.responses.ts @@ -4,39 +4,43 @@ export const DELETE_VENDOR_RESPONSES: Record = { 200: { status: 200, description: 'Vendor deleted successfully', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Vendor deleted successfully', - }, - deletedVendor: { + content: { + 'application/json': { + schema: { type: 'object', properties: { - id: { + message: { type: 'string', - description: 'Deleted vendor ID', - example: 'vnd_abc123def456', + example: 'Vendor deleted successfully', + }, + deletedVendor: { + type: 'object', + properties: { + id: { + type: 'string', + description: 'Deleted vendor ID', + example: 'vnd_abc123def456', + }, + name: { + type: 'string', + description: 'Deleted vendor name', + example: 'CloudTech Solutions Inc.', + }, + }, }, - name: { + authType: { type: 'string', - description: 'Deleted vendor name', - example: 'CloudTech Solutions Inc.', + enum: ['api-key', 'session'], + description: 'How the request was authenticated', + }, + authenticatedUser: { + type: 'object', + description: 'User information (only for session auth)', + properties: { + id: { type: 'string', example: 'usr_def456ghi789' }, + email: { type: 'string', example: 'user@example.com' }, + }, }, - }, - }, - authType: { - type: 'string', - enum: ['api-key', 'session'], - description: 'How the request was authenticated', - }, - authenticatedUser: { - type: 'object', - description: 'User information (only for session auth)', - properties: { - id: { type: 'string', example: 'usr_def456ghi789' }, - email: { type: 'string', example: 'user@example.com' }, }, }, }, @@ -44,18 +48,18 @@ export const DELETE_VENDOR_RESPONSES: Record = { }, 401: { status: 401, - description: 'Unauthorized - Invalid authentication or insufficient permissions', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Invalid or expired API key', - 'Invalid or expired session', - 'User does not have access to organization', - 'Organization context required', - ], + description: + 'Unauthorized - Invalid authentication or insufficient permissions', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Invalid or expired API key', + }, + }, }, }, }, @@ -63,12 +67,17 @@ export const DELETE_VENDOR_RESPONSES: Record = { 404: { status: 404, description: 'Vendor not found', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Vendor with ID vnd_abc123def456 not found in organization org_abc123def456', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: + 'Vendor with ID vnd_abc123def456 not found in organization org_abc123def456', + }, + }, }, }, }, @@ -76,12 +85,16 @@ export const DELETE_VENDOR_RESPONSES: Record = { 500: { status: 500, description: 'Internal server error', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Internal server error', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Internal server error', + }, + }, }, }, }, diff --git a/apps/api/src/vendors/schemas/get-all-vendors.responses.ts b/apps/api/src/vendors/schemas/get-all-vendors.responses.ts index 81fe3a60d..b8b59395c 100644 --- a/apps/api/src/vendors/schemas/get-all-vendors.responses.ts +++ b/apps/api/src/vendors/schemas/get-all-vendors.responses.ts @@ -4,118 +4,157 @@ export const GET_ALL_VENDORS_RESPONSES: Record = { 200: { status: 200, description: 'Vendors retrieved successfully', - schema: { - type: 'object', - properties: { - data: { - type: 'array', - items: { - type: 'object', - properties: { - id: { - type: 'string', - description: 'Vendor ID', - example: 'vnd_abc123def456', - }, - name: { - type: 'string', - description: 'Vendor name', - example: 'CloudTech Solutions Inc.', - }, - description: { - type: 'string', - description: 'Vendor description', - example: 'Cloud infrastructure provider offering AWS-like services', - }, - category: { - type: 'string', - enum: ['cloud', 'infrastructure', 'software_as_a_service', 'finance', 'marketing', 'sales', 'hr', 'other'], - example: 'cloud', - }, - status: { - type: 'string', - enum: ['not_assessed', 'in_progress', 'assessed'], - example: 'not_assessed', - }, - inherentProbability: { - type: 'string', - enum: ['very_unlikely', 'unlikely', 'possible', 'likely', 'very_likely'], - example: 'possible', - }, - inherentImpact: { - type: 'string', - enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], - example: 'moderate', - }, - residualProbability: { - type: 'string', - enum: ['very_unlikely', 'unlikely', 'possible', 'likely', 'very_likely'], - example: 'unlikely', - }, - residualImpact: { - type: 'string', - enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], - example: 'minor', - }, - website: { - type: 'string', - nullable: true, - example: 'https://www.cloudtechsolutions.com', - }, - assigneeId: { - type: 'string', - nullable: true, - description: 'ID of the user assigned to manage this vendor', - example: 'mem_abc123def456', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'When the vendor was created', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + data: { + type: 'array', + items: { + type: 'object', + properties: { + id: { + type: 'string', + description: 'Vendor ID', + example: 'vnd_abc123def456', + }, + name: { + type: 'string', + description: 'Vendor name', + example: 'CloudTech Solutions Inc.', + }, + description: { + type: 'string', + description: 'Vendor description', + example: + 'Cloud infrastructure provider offering AWS-like services', + }, + category: { + type: 'string', + enum: [ + 'cloud', + 'infrastructure', + 'software_as_a_service', + 'finance', + 'marketing', + 'sales', + 'hr', + 'other', + ], + example: 'cloud', + }, + status: { + type: 'string', + enum: ['not_assessed', 'in_progress', 'assessed'], + example: 'not_assessed', + }, + inherentProbability: { + type: 'string', + enum: [ + 'very_unlikely', + 'unlikely', + 'possible', + 'likely', + 'very_likely', + ], + example: 'possible', + }, + inherentImpact: { + type: 'string', + enum: [ + 'insignificant', + 'minor', + 'moderate', + 'major', + 'severe', + ], + example: 'moderate', + }, + residualProbability: { + type: 'string', + enum: [ + 'very_unlikely', + 'unlikely', + 'possible', + 'likely', + 'very_likely', + ], + example: 'unlikely', + }, + residualImpact: { + type: 'string', + enum: [ + 'insignificant', + 'minor', + 'moderate', + 'major', + 'severe', + ], + example: 'minor', + }, + website: { + type: 'string', + nullable: true, + example: 'https://www.cloudtechsolutions.com', + }, + assigneeId: { + type: 'string', + nullable: true, + description: + 'ID of the user assigned to manage this vendor', + example: 'mem_abc123def456', + }, + createdAt: { + type: 'string', + format: 'date-time', + description: 'When the vendor was created', + }, + updatedAt: { + type: 'string', + format: 'date-time', + description: 'When the vendor was last updated', + }, + }, }, - updatedAt: { - type: 'string', - format: 'date-time', - description: 'When the vendor was last updated', + }, + count: { + type: 'number', + description: 'Total number of vendors', + example: 12, + }, + authType: { + type: 'string', + enum: ['api-key', 'session'], + description: 'How the request was authenticated', + }, + authenticatedUser: { + type: 'object', + description: 'User information (only for session auth)', + properties: { + id: { type: 'string', example: 'usr_def456ghi789' }, + email: { type: 'string', example: 'user@example.com' }, }, }, }, }, - count: { - type: 'number', - description: 'Total number of vendors', - example: 12, - }, - authType: { - type: 'string', - enum: ['api-key', 'session'], - description: 'How the request was authenticated', - }, - authenticatedUser: { - type: 'object', - description: 'User information (only for session auth)', - properties: { - id: { type: 'string', example: 'usr_def456ghi789' }, - email: { type: 'string', example: 'user@example.com' }, - }, - }, }, }, }, 401: { status: 401, - description: 'Unauthorized - Invalid authentication or insufficient permissions', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Invalid or expired API key', - 'Invalid or expired session', - 'User does not have access to organization', - 'Organization context required', - ], + description: + 'Unauthorized - Invalid authentication or insufficient permissions', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Invalid or expired API key', + }, + }, }, }, }, @@ -123,12 +162,16 @@ export const GET_ALL_VENDORS_RESPONSES: Record = { 404: { status: 404, description: 'Organization not found', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Organization with ID org_abc123def456 not found', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Organization with ID org_abc123def456 not found', + }, + }, }, }, }, @@ -136,12 +179,16 @@ export const GET_ALL_VENDORS_RESPONSES: Record = { 500: { status: 500, description: 'Internal server error', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Internal server error', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Internal server error', + }, + }, }, }, }, diff --git a/apps/api/src/vendors/schemas/get-vendor-by-id.responses.ts b/apps/api/src/vendors/schemas/get-vendor-by-id.responses.ts index 6a0a135a5..f1661a901 100644 --- a/apps/api/src/vendors/schemas/get-vendor-by-id.responses.ts +++ b/apps/api/src/vendors/schemas/get-vendor-by-id.responses.ts @@ -4,90 +4,116 @@ export const GET_VENDOR_BY_ID_RESPONSES: Record = { 200: { status: 200, description: 'Vendor retrieved successfully', - schema: { - type: 'object', - properties: { - id: { - type: 'string', - description: 'Vendor ID', - example: 'vnd_abc123def456', - }, - name: { - type: 'string', - description: 'Vendor name', - example: 'CloudTech Solutions Inc.', - }, - description: { - type: 'string', - description: 'Vendor description', - example: 'Cloud infrastructure provider offering AWS-like services including compute, storage, and networking solutions for enterprise customers.', - }, - category: { - type: 'string', - enum: ['cloud', 'infrastructure', 'software_as_a_service', 'finance', 'marketing', 'sales', 'hr', 'other'], - example: 'cloud', - }, - status: { - type: 'string', - enum: ['not_assessed', 'in_progress', 'assessed'], - example: 'not_assessed', - }, - inherentProbability: { - type: 'string', - enum: ['very_unlikely', 'unlikely', 'possible', 'likely', 'very_likely'], - example: 'possible', - }, - inherentImpact: { - type: 'string', - enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], - example: 'moderate', - }, - residualProbability: { - type: 'string', - enum: ['very_unlikely', 'unlikely', 'possible', 'likely', 'very_likely'], - example: 'unlikely', - }, - residualImpact: { - type: 'string', - enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], - example: 'minor', - }, - website: { - type: 'string', - nullable: true, - example: 'https://www.cloudtechsolutions.com', - }, - organizationId: { - type: 'string', - example: 'org_abc123def456', - }, - assigneeId: { - type: 'string', - nullable: true, - description: 'ID of the user assigned to manage this vendor', - example: 'mem_abc123def456', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'When the vendor was created', - }, - updatedAt: { - type: 'string', - format: 'date-time', - description: 'When the vendor was last updated', - }, - authType: { - type: 'string', - enum: ['api-key', 'session'], - description: 'How the request was authenticated', - }, - authenticatedUser: { + content: { + 'application/json': { + schema: { type: 'object', - description: 'User information (only for session auth)', properties: { - id: { type: 'string', example: 'usr_def456ghi789' }, - email: { type: 'string', example: 'user@example.com' }, + id: { + type: 'string', + description: 'Vendor ID', + example: 'vnd_abc123def456', + }, + name: { + type: 'string', + description: 'Vendor name', + example: 'CloudTech Solutions Inc.', + }, + description: { + type: 'string', + description: 'Vendor description', + example: + 'Cloud infrastructure provider offering AWS-like services including compute, storage, and networking solutions for enterprise customers.', + }, + category: { + type: 'string', + enum: [ + 'cloud', + 'infrastructure', + 'software_as_a_service', + 'finance', + 'marketing', + 'sales', + 'hr', + 'other', + ], + example: 'cloud', + }, + status: { + type: 'string', + enum: ['not_assessed', 'in_progress', 'assessed'], + example: 'not_assessed', + }, + inherentProbability: { + type: 'string', + enum: [ + 'very_unlikely', + 'unlikely', + 'possible', + 'likely', + 'very_likely', + ], + example: 'possible', + }, + inherentImpact: { + type: 'string', + enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], + example: 'moderate', + }, + residualProbability: { + type: 'string', + enum: [ + 'very_unlikely', + 'unlikely', + 'possible', + 'likely', + 'very_likely', + ], + example: 'unlikely', + }, + residualImpact: { + type: 'string', + enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], + example: 'minor', + }, + website: { + type: 'string', + nullable: true, + example: 'https://www.cloudtechsolutions.com', + }, + organizationId: { + type: 'string', + example: 'org_abc123def456', + }, + assigneeId: { + type: 'string', + nullable: true, + description: 'ID of the user assigned to manage this vendor', + example: 'mem_abc123def456', + }, + createdAt: { + type: 'string', + format: 'date-time', + description: 'When the vendor was created', + }, + updatedAt: { + type: 'string', + format: 'date-time', + description: 'When the vendor was last updated', + }, + authType: { + type: 'string', + enum: ['api-key', 'session'], + description: 'How the request was authenticated', + }, + authenticatedUser: { + type: 'object', + description: 'User information (only for session auth)', + properties: { + id: { type: 'string', example: 'usr_def456ghi789' }, + email: { type: 'string', example: 'user@example.com' }, + }, + }, }, }, }, @@ -95,18 +121,18 @@ export const GET_VENDOR_BY_ID_RESPONSES: Record = { }, 401: { status: 401, - description: 'Unauthorized - Invalid authentication or insufficient permissions', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Invalid or expired API key', - 'Invalid or expired session', - 'User does not have access to organization', - 'Organization context required', - ], + description: + 'Unauthorized - Invalid authentication or insufficient permissions', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Invalid or expired API key', + }, + }, }, }, }, @@ -114,12 +140,17 @@ export const GET_VENDOR_BY_ID_RESPONSES: Record = { 404: { status: 404, description: 'Vendor not found', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Vendor with ID vnd_abc123def456 not found in organization org_abc123def456', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: + 'Vendor with ID vnd_abc123def456 not found in organization org_abc123def456', + }, + }, }, }, }, @@ -127,12 +158,16 @@ export const GET_VENDOR_BY_ID_RESPONSES: Record = { 500: { status: 500, description: 'Internal server error', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Internal server error', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Internal server error', + }, + }, }, }, }, diff --git a/apps/api/src/vendors/schemas/update-vendor.responses.ts b/apps/api/src/vendors/schemas/update-vendor.responses.ts index 20a358a4a..03a0cbaec 100644 --- a/apps/api/src/vendors/schemas/update-vendor.responses.ts +++ b/apps/api/src/vendors/schemas/update-vendor.responses.ts @@ -4,90 +4,116 @@ export const UPDATE_VENDOR_RESPONSES: Record = { 200: { status: 200, description: 'Vendor updated successfully', - schema: { - type: 'object', - properties: { - id: { - type: 'string', - description: 'Vendor ID', - example: 'vnd_abc123def456', - }, - name: { - type: 'string', - description: 'Vendor name', - example: 'CloudTech Solutions Inc.', - }, - description: { - type: 'string', - description: 'Vendor description', - example: 'Cloud infrastructure provider offering AWS-like services including compute, storage, and networking solutions for enterprise customers.', - }, - category: { - type: 'string', - enum: ['cloud', 'infrastructure', 'software_as_a_service', 'finance', 'marketing', 'sales', 'hr', 'other'], - example: 'cloud', - }, - status: { - type: 'string', - enum: ['not_assessed', 'in_progress', 'assessed'], - example: 'assessed', - }, - inherentProbability: { - type: 'string', - enum: ['very_unlikely', 'unlikely', 'possible', 'likely', 'very_likely'], - example: 'possible', - }, - inherentImpact: { - type: 'string', - enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], - example: 'moderate', - }, - residualProbability: { - type: 'string', - enum: ['very_unlikely', 'unlikely', 'possible', 'likely', 'very_likely'], - example: 'unlikely', - }, - residualImpact: { - type: 'string', - enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], - example: 'minor', - }, - website: { - type: 'string', - nullable: true, - example: 'https://www.cloudtechsolutions.com', - }, - organizationId: { - type: 'string', - example: 'org_abc123def456', - }, - assigneeId: { - type: 'string', - nullable: true, - description: 'ID of the user assigned to manage this vendor', - example: 'mem_abc123def456', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'When the vendor was created', - }, - updatedAt: { - type: 'string', - format: 'date-time', - description: 'When the vendor was last updated', - }, - authType: { - type: 'string', - enum: ['api-key', 'session'], - description: 'How the request was authenticated', - }, - authenticatedUser: { + content: { + 'application/json': { + schema: { type: 'object', - description: 'User information (only for session auth)', properties: { - id: { type: 'string', example: 'usr_def456ghi789' }, - email: { type: 'string', example: 'user@example.com' }, + id: { + type: 'string', + description: 'Vendor ID', + example: 'vnd_abc123def456', + }, + name: { + type: 'string', + description: 'Vendor name', + example: 'CloudTech Solutions Inc.', + }, + description: { + type: 'string', + description: 'Vendor description', + example: + 'Cloud infrastructure provider offering AWS-like services including compute, storage, and networking solutions for enterprise customers.', + }, + category: { + type: 'string', + enum: [ + 'cloud', + 'infrastructure', + 'software_as_a_service', + 'finance', + 'marketing', + 'sales', + 'hr', + 'other', + ], + example: 'cloud', + }, + status: { + type: 'string', + enum: ['not_assessed', 'in_progress', 'assessed'], + example: 'assessed', + }, + inherentProbability: { + type: 'string', + enum: [ + 'very_unlikely', + 'unlikely', + 'possible', + 'likely', + 'very_likely', + ], + example: 'possible', + }, + inherentImpact: { + type: 'string', + enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], + example: 'moderate', + }, + residualProbability: { + type: 'string', + enum: [ + 'very_unlikely', + 'unlikely', + 'possible', + 'likely', + 'very_likely', + ], + example: 'unlikely', + }, + residualImpact: { + type: 'string', + enum: ['insignificant', 'minor', 'moderate', 'major', 'severe'], + example: 'minor', + }, + website: { + type: 'string', + nullable: true, + example: 'https://www.cloudtechsolutions.com', + }, + organizationId: { + type: 'string', + example: 'org_abc123def456', + }, + assigneeId: { + type: 'string', + nullable: true, + description: 'ID of the user assigned to manage this vendor', + example: 'mem_abc123def456', + }, + createdAt: { + type: 'string', + format: 'date-time', + description: 'When the vendor was created', + }, + updatedAt: { + type: 'string', + format: 'date-time', + description: 'When the vendor was last updated', + }, + authType: { + type: 'string', + enum: ['api-key', 'session'], + description: 'How the request was authenticated', + }, + authenticatedUser: { + type: 'object', + description: 'User information (only for session auth)', + properties: { + id: { type: 'string', example: 'usr_def456ghi789' }, + email: { type: 'string', example: 'user@example.com' }, + }, + }, }, }, }, @@ -96,38 +122,42 @@ export const UPDATE_VENDOR_RESPONSES: Record = { 400: { status: 400, description: 'Bad request - Invalid input data', - schema: { - type: 'object', - properties: { - message: { - type: 'array', - items: { type: 'string' }, - example: [ - 'name should not be empty', - 'category must be a valid enum value', - 'status must be a valid enum value', - 'website must be a URL address', - ], + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'array', + items: { type: 'string' }, + example: [ + 'name should not be empty', + 'category must be a valid enum value', + 'status must be a valid enum value', + 'website must be a URL address', + ], + }, + error: { type: 'string', example: 'Bad Request' }, + statusCode: { type: 'number', example: 400 }, + }, }, - error: { type: 'string', example: 'Bad Request' }, - statusCode: { type: 'number', example: 400 }, }, }, }, 401: { status: 401, - description: 'Unauthorized - Invalid authentication or insufficient permissions', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - examples: [ - 'Invalid or expired API key', - 'Invalid or expired session', - 'User does not have access to organization', - 'Organization context required', - ], + description: + 'Unauthorized - Invalid authentication or insufficient permissions', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Invalid or expired API key', + }, + }, }, }, }, @@ -135,12 +165,17 @@ export const UPDATE_VENDOR_RESPONSES: Record = { 404: { status: 404, description: 'Vendor not found', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Vendor with ID vnd_abc123def456 not found in organization org_abc123def456', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: + 'Vendor with ID vnd_abc123def456 not found in organization org_abc123def456', + }, + }, }, }, }, @@ -148,12 +183,16 @@ export const UPDATE_VENDOR_RESPONSES: Record = { 500: { status: 500, description: 'Internal server error', - schema: { - type: 'object', - properties: { - message: { - type: 'string', - example: 'Internal server error', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + message: { + type: 'string', + example: 'Internal server error', + }, + }, }, }, }, diff --git a/packages/docs/docs.json b/packages/docs/docs.json index 952e06c76..817868d89 100644 --- a/packages/docs/docs.json +++ b/packages/docs/docs.json @@ -46,7 +46,7 @@ }, { "tab": "API", - "openapi": "https://api.trycomp.ai/api/docs-json" + "openapi": { "source": "./openapi.json" } } ], "global": { diff --git a/packages/docs/openapi.json b/packages/docs/openapi.json new file mode 100644 index 000000000..7e4c58a8f --- /dev/null +++ b/packages/docs/openapi.json @@ -0,0 +1,8420 @@ +{ + "openapi": "3.0.0", + "paths": { + "/v1/organization": { + "get": { + "description": "Returns detailed information about the authenticated organization. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "OrganizationController_getOrganization_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Organization information retrieved successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The organization ID", + "example": "org_abc123def456" + }, + "name": { + "type": "string", + "description": "Organization name", + "example": "Acme Corporation" + }, + "slug": { + "type": "string", + "description": "Organization slug", + "example": "acme-corp" + }, + "logo": { + "type": "string", + "nullable": true, + "description": "Organization logo URL", + "example": "https://example.com/logo.png" + }, + "metadata": { + "type": "string", + "nullable": true, + "description": "Additional metadata in JSON format", + "example": "{\"theme\": \"dark\", \"preferences\": {}}" + }, + "website": { + "type": "string", + "nullable": true, + "description": "Organization website URL", + "example": "https://acme-corp.com" + }, + "onboardingCompleted": { + "type": "boolean", + "description": "Whether onboarding is completed", + "example": true + }, + "hasAccess": { + "type": "boolean", + "description": "Whether organization has access to the platform", + "example": true + }, + "fleetDmLabelId": { + "type": "integer", + "nullable": true, + "description": "FleetDM label ID for device management", + "example": 123 + }, + "isFleetSetupCompleted": { + "type": "boolean", + "description": "Whether FleetDM setup is completed", + "example": false + }, + "createdAt": { + "type": "string", + "format": "date-time", + "description": "When the organization was created" + }, + "authType": { + "type": "string", + "enum": [ + "api-key", + "session" + ], + "description": "How the request was authenticated" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Invalid or expired API key" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Get organization information", + "tags": [ + "Organization" + ] + }, + "patch": { + "description": "Partially updates the authenticated organization. Only provided fields will be updated. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "OrganizationController_updateOrganization_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "description": "Organization update data", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Organization name", + "example": "New Acme Corporation" + }, + "slug": { + "type": "string", + "description": "Organization slug", + "example": "new-acme-corp" + }, + "logo": { + "type": "string", + "description": "Organization logo URL", + "example": "https://example.com/logo.png" + }, + "metadata": { + "type": "string", + "description": "Additional metadata in JSON format", + "example": "{\"theme\": \"dark\", \"preferences\": {}}" + }, + "website": { + "type": "string", + "description": "Organization website URL", + "example": "https://acme-corp.com" + }, + "onboardingCompleted": { + "type": "boolean", + "description": "Whether onboarding is completed", + "example": true + }, + "hasAccess": { + "type": "boolean", + "description": "Whether organization has access to the platform", + "example": true + }, + "fleetDmLabelId": { + "type": "integer", + "description": "FleetDM label ID for device management", + "example": 123 + }, + "isFleetSetupCompleted": { + "type": "boolean", + "description": "Whether FleetDM setup is completed", + "example": false + } + }, + "additionalProperties": false + } + } + } + }, + "responses": { + "200": { + "description": "Organization updated successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The organization ID", + "example": "org_abc123def456" + }, + "name": { + "type": "string", + "description": "Organization name", + "example": "New Acme Corporation" + }, + "slug": { + "type": "string", + "description": "Organization slug", + "example": "new-acme-corp" + }, + "logo": { + "type": "string", + "nullable": true, + "description": "Organization logo URL", + "example": "https://example.com/logo.png" + }, + "metadata": { + "type": "string", + "nullable": true, + "description": "Additional metadata in JSON format", + "example": "{\"theme\": \"dark\", \"preferences\": {}}" + }, + "website": { + "type": "string", + "nullable": true, + "description": "Organization website URL", + "example": "https://acme-corp.com" + }, + "onboardingCompleted": { + "type": "boolean", + "description": "Whether onboarding is completed", + "example": true + }, + "hasAccess": { + "type": "boolean", + "description": "Whether organization has access to the platform", + "example": true + }, + "fleetDmLabelId": { + "type": "integer", + "nullable": true, + "description": "FleetDM label ID for device management", + "example": 123 + }, + "isFleetSetupCompleted": { + "type": "boolean", + "description": "Whether FleetDM setup is completed", + "example": false + }, + "createdAt": { + "type": "string", + "format": "date-time", + "description": "When the organization was created" + }, + "authType": { + "type": "string", + "enum": [ + "api-key", + "session" + ], + "description": "How the request was authenticated" + } + } + } + } + } + }, + "400": { + "description": "Bad Request - Invalid update data", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Invalid slug format" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Invalid or expired API key" + } + } + } + } + } + }, + "404": { + "description": "Organization not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Organization with ID org_abc123def456 not found" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Update organization", + "tags": [ + "Organization" + ] + }, + "delete": { + "description": "Permanently deletes the authenticated organization. This action cannot be undone. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "OrganizationController_deleteOrganization_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Organization deleted successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "Indicates successful deletion", + "example": true + }, + "deletedOrganization": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The deleted organization ID", + "example": "org_abc123def456" + }, + "name": { + "type": "string", + "description": "The deleted organization name", + "example": "Acme Corporation" + } + } + }, + "authType": { + "type": "string", + "enum": [ + "api-key", + "session" + ], + "description": "How the request was authenticated" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Invalid or expired API key" + } + } + } + } + } + }, + "404": { + "description": "Organization not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Organization with ID org_abc123def456 not found" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Delete organization", + "tags": [ + "Organization" + ] + } + }, + "/v1/people": { + "get": { + "description": "Returns all members for the authenticated organization with their user information. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "PeopleController_getAllPeople_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "People retrieved successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PeopleResponseDto" + } + }, + "count": { + "type": "number", + "description": "Total number of people", + "example": 25 + }, + "authType": { + "type": "string", + "enum": [ + "api-key", + "session" + ], + "description": "How the request was authenticated" + }, + "authenticatedUser": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "User ID", + "example": "usr_abc123def456" + }, + "email": { + "type": "string", + "description": "User email", + "example": "user@company.com" + } + } + } + } + }, + "example": { + "data": [ + { + "id": "mem_abc123def456", + "organizationId": "org_abc123def456", + "userId": "usr_abc123def456", + "role": "admin", + "createdAt": "2024-01-01T00:00:00Z", + "department": "it", + "isActive": true, + "fleetDmLabelId": 123, + "user": { + "id": "usr_abc123def456", + "name": "John Doe", + "email": "john.doe@company.com", + "emailVerified": true, + "image": "https://example.com/avatar.jpg", + "createdAt": "2024-01-01T00:00:00Z", + "updatedAt": "2024-01-15T00:00:00Z", + "lastLogin": "2024-01-15T12:00:00Z" + } + } + ], + "count": 1, + "authType": "api-key", + "authenticatedUser": { + "id": "usr_abc123def456", + "email": "user@company.com" + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Invalid or expired API key" + } + } + } + } + } + }, + "404": { + "description": "Organization not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Organization with ID org_abc123def456 not found" + } + } + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Failed to retrieve members" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Get all people", + "tags": [ + "People" + ] + }, + "post": { + "description": "Adds a new member to the authenticated organization. The user must already exist in the system. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "PeopleController_createMember_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "description": "Member creation data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreatePeopleDto" + } + } + } + }, + "responses": { + "201": { + "description": "Member created successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PeopleResponseDto" + }, + "example": { + "id": "mem_abc123def456", + "organizationId": "org_abc123def456", + "userId": "usr_abc123def456", + "role": "admin", + "createdAt": "2024-01-01T00:00:00Z", + "department": "it", + "isActive": true, + "fleetDmLabelId": 123, + "user": { + "id": "usr_abc123def456", + "name": "John Doe", + "email": "john.doe@company.com", + "emailVerified": true, + "image": "https://example.com/avatar.jpg", + "createdAt": "2024-01-01T00:00:00Z", + "updatedAt": "2024-01-15T00:00:00Z", + "lastLogin": "2024-01-15T12:00:00Z" + } + } + } + } + }, + "400": { + "description": "Bad Request - Invalid member data or user already exists", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "User user@example.com is already a member of this organization" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Unauthorized" + } + } + } + } + } + }, + "404": { + "description": "Organization or user not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "User with ID usr_abc123def456 not found" + } + } + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Failed to create member" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Create a new member", + "tags": [ + "People" + ] + } + }, + "/v1/people/bulk": { + "post": { + "description": "Bulk adds multiple members to the authenticated organization. Each member must have a valid user ID that exists in the system. Members who already exist in the organization or have invalid data will be skipped with error details returned. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "PeopleController_bulkCreateMembers_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "description": "Bulk member creation data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BulkCreatePeopleDto" + } + } + } + }, + "responses": { + "201": { + "description": "Bulk member creation completed", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "created": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PeopleResponseDto" + }, + "description": "Successfully created members" + }, + "errors": { + "type": "array", + "items": { + "type": "object", + "properties": { + "index": { + "type": "number", + "description": "Index in the original array where the error occurred", + "example": 2 + }, + "userId": { + "type": "string", + "description": "User ID that failed to be added", + "example": "usr_abc123def456" + }, + "error": { + "type": "string", + "description": "Error message explaining why the member could not be created", + "example": "User user@example.com is already a member of this organization" + } + } + }, + "description": "Members that failed to be created with error details" + }, + "summary": { + "type": "object", + "properties": { + "total": { + "type": "number", + "description": "Total number of members in the request", + "example": 5 + }, + "successful": { + "type": "number", + "description": "Number of members successfully created", + "example": 3 + }, + "failed": { + "type": "number", + "description": "Number of members that failed to be created", + "example": 2 + } + } + }, + "authType": { + "type": "string", + "enum": [ + "api-key", + "session" + ], + "description": "How the request was authenticated" + }, + "authenticatedUser": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "User ID", + "example": "usr_abc123def456" + }, + "email": { + "type": "string", + "description": "User email", + "example": "user@company.com" + } + } + } + } + }, + "example": { + "created": [ + { + "id": "mem_abc123def456", + "organizationId": "org_abc123def456", + "userId": "usr_abc123def456", + "role": "member", + "createdAt": "2024-01-01T00:00:00Z", + "department": "it", + "isActive": true, + "fleetDmLabelId": 123, + "user": { + "id": "usr_abc123def456", + "name": "John Doe", + "email": "john.doe@company.com", + "emailVerified": true, + "image": "https://example.com/avatar.jpg", + "createdAt": "2024-01-01T00:00:00Z", + "updatedAt": "2024-01-15T00:00:00Z", + "lastLogin": "2024-01-15T12:00:00Z" + } + } + ], + "errors": [ + { + "index": 2, + "userId": "usr_xyz789abc123", + "error": "User user2@example.com is already a member of this organization" + } + ], + "summary": { + "total": 2, + "successful": 1, + "failed": 1 + }, + "authType": "api-key", + "authenticatedUser": { + "id": "usr_admin123", + "email": "admin@company.com" + } + } + } + } + }, + "400": { + "description": "Bad Request - Invalid bulk data or validation errors", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Members array cannot be empty" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Unauthorized" + } + } + } + } + } + }, + "404": { + "description": "Organization not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Organization with ID org_abc123def456 not found" + } + } + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Bulk creation failed" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Add multiple members to organization", + "tags": [ + "People" + ] + } + }, + "/v1/people/{id}": { + "get": { + "description": "Returns a specific member by ID for the authenticated organization with their user information. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "PeopleController_getPersonById_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "id", + "required": true, + "in": "path", + "description": "Member ID", + "schema": { + "example": "mem_abc123def456", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Person retrieved successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PeopleResponseDto" + }, + "example": { + "id": "mem_abc123def456", + "organizationId": "org_abc123def456", + "userId": "usr_abc123def456", + "role": "admin", + "createdAt": "2024-01-01T00:00:00Z", + "department": "it", + "isActive": true, + "fleetDmLabelId": 123, + "user": { + "id": "usr_abc123def456", + "name": "John Doe", + "email": "john.doe@company.com", + "emailVerified": true, + "image": "https://example.com/avatar.jpg", + "createdAt": "2024-01-01T00:00:00Z", + "updatedAt": "2024-01-15T00:00:00Z", + "lastLogin": "2024-01-15T12:00:00Z" + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Unauthorized" + } + } + } + } + } + }, + "404": { + "description": "Organization or member not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Member with ID mem_abc123def456 not found in organization org_abc123def456" + } + } + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Internal server error" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Get person by ID", + "tags": [ + "People" + ] + }, + "patch": { + "description": "Partially updates a member. Only provided fields will be updated. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "PeopleController_updateMember_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "id", + "required": true, + "in": "path", + "description": "Member ID", + "schema": { + "example": "mem_abc123def456", + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "description": "Member update data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdatePeopleDto" + } + } + } + }, + "responses": { + "200": { + "description": "Member updated successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PeopleResponseDto" + }, + "example": { + "id": "mem_abc123def456", + "organizationId": "org_abc123def456", + "userId": "usr_abc123def456", + "role": "member", + "createdAt": "2024-01-01T00:00:00Z", + "department": "it", + "isActive": true, + "fleetDmLabelId": 123, + "user": { + "id": "usr_abc123def456", + "name": "John Doe", + "email": "john.doe@company.com", + "emailVerified": true, + "image": "https://example.com/avatar.jpg", + "createdAt": "2024-01-01T00:00:00Z", + "updatedAt": "2024-01-15T00:00:00Z", + "lastLogin": "2024-01-15T12:00:00Z" + } + } + } + } + }, + "400": { + "description": "Bad Request - Invalid update data or user conflict", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "User user@example.com is already a member of this organization" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Unauthorized" + } + } + } + } + } + }, + "404": { + "description": "Organization, member, or user not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Member with ID mem_abc123def456 not found in organization org_abc123def456" + } + } + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Internal server error" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Update member", + "tags": [ + "People" + ] + }, + "delete": { + "description": "Permanently removes a member from the organization. This action cannot be undone. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "PeopleController_deleteMember_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "id", + "required": true, + "in": "path", + "description": "Member ID", + "schema": { + "example": "mem_abc123def456", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Member deleted successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "Indicates successful deletion", + "example": true + }, + "deletedMember": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The deleted member ID", + "example": "mem_abc123def456" + }, + "name": { + "type": "string", + "description": "The deleted member name", + "example": "John Doe" + }, + "email": { + "type": "string", + "description": "The deleted member email", + "example": "john.doe@company.com" + } + } + }, + "authType": { + "type": "string", + "enum": [ + "api-key", + "session" + ], + "description": "How the request was authenticated" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Unauthorized" + } + } + } + } + } + }, + "404": { + "description": "Organization or member not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Member with ID mem_abc123def456 not found in organization org_abc123def456" + } + } + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Failed to delete member" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Delete member", + "tags": [ + "People" + ] + } + }, + "/v1/risks": { + "get": { + "description": "Returns all risks for the authenticated organization. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "RisksController_getAllRisks_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Risks retrieved successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Risk ID", + "example": "rsk_abc123def456" + }, + "title": { + "type": "string", + "description": "Risk title", + "example": "Data breach vulnerability in user authentication system" + }, + "description": { + "type": "string", + "description": "Risk description", + "example": "Weak password requirements could lead to unauthorized access to user accounts" + }, + "category": { + "type": "string", + "enum": [ + "customer", + "governance", + "operations", + "other", + "people", + "regulatory", + "reporting", + "resilience", + "technology", + "vendor_management" + ], + "example": "technology" + }, + "status": { + "type": "string", + "enum": [ + "open", + "pending", + "closed", + "archived" + ], + "example": "open" + }, + "likelihood": { + "type": "string", + "enum": [ + "very_unlikely", + "unlikely", + "possible", + "likely", + "very_likely" + ], + "example": "possible" + }, + "impact": { + "type": "string", + "enum": [ + "insignificant", + "minor", + "moderate", + "major", + "severe" + ], + "example": "major" + }, + "treatmentStrategy": { + "type": "string", + "enum": [ + "accept", + "avoid", + "mitigate", + "transfer" + ], + "example": "mitigate" + }, + "assigneeId": { + "type": "string", + "nullable": true, + "description": "ID of the user assigned to this risk", + "example": "mem_abc123def456" + }, + "createdAt": { + "type": "string", + "format": "date-time", + "description": "When the risk was created" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "description": "When the risk was last updated" + } + } + } + }, + "count": { + "type": "number", + "description": "Total number of risks", + "example": 15 + }, + "authType": { + "type": "string", + "enum": [ + "api-key", + "session" + ], + "description": "How the request was authenticated" + }, + "authenticatedUser": { + "type": "object", + "description": "User information (only for session auth)", + "properties": { + "id": { + "type": "string", + "example": "usr_def456ghi789" + }, + "email": { + "type": "string", + "example": "user@example.com" + } + } + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Invalid or expired API key" + } + } + } + } + } + }, + "404": { + "description": "Organization not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Organization with ID org_abc123def456 not found" + } + } + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Internal server error" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Get all risks", + "tags": [ + "Risks" + ] + }, + "post": { + "description": "Creates a new risk for the authenticated organization. All required fields must be provided. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "RisksController_createRisk_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "description": "Risk creation data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateRiskDto" + } + } + } + }, + "responses": { + "201": { + "description": "Risk created successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Risk ID", + "example": "rsk_abc123def456" + }, + "title": { + "type": "string", + "description": "Risk title", + "example": "Data breach vulnerability in user authentication system" + }, + "description": { + "type": "string", + "description": "Risk description", + "example": "Weak password requirements could lead to unauthorized access to user accounts" + }, + "category": { + "type": "string", + "enum": [ + "customer", + "governance", + "operations", + "other", + "people", + "regulatory", + "reporting", + "resilience", + "technology", + "vendor_management" + ], + "example": "technology" + }, + "department": { + "type": "string", + "enum": [ + "none", + "admin", + "gov", + "hr", + "it", + "itsm", + "qms" + ], + "nullable": true, + "example": "it" + }, + "status": { + "type": "string", + "enum": [ + "open", + "pending", + "closed", + "archived" + ], + "example": "open" + }, + "likelihood": { + "type": "string", + "enum": [ + "very_unlikely", + "unlikely", + "possible", + "likely", + "very_likely" + ], + "example": "possible" + }, + "impact": { + "type": "string", + "enum": [ + "insignificant", + "minor", + "moderate", + "major", + "severe" + ], + "example": "major" + }, + "residualLikelihood": { + "type": "string", + "enum": [ + "very_unlikely", + "unlikely", + "possible", + "likely", + "very_likely" + ], + "example": "unlikely" + }, + "residualImpact": { + "type": "string", + "enum": [ + "insignificant", + "minor", + "moderate", + "major", + "severe" + ], + "example": "minor" + }, + "treatmentStrategyDescription": { + "type": "string", + "nullable": true, + "example": "Implement multi-factor authentication and strengthen password requirements" + }, + "treatmentStrategy": { + "type": "string", + "enum": [ + "accept", + "avoid", + "mitigate", + "transfer" + ], + "example": "mitigate" + }, + "organizationId": { + "type": "string", + "example": "org_abc123def456" + }, + "assigneeId": { + "type": "string", + "nullable": true, + "description": "ID of the user assigned to this risk", + "example": "mem_abc123def456" + }, + "createdAt": { + "type": "string", + "format": "date-time", + "description": "When the risk was created" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "description": "When the risk was last updated" + }, + "authType": { + "type": "string", + "enum": [ + "api-key", + "session" + ], + "description": "How the request was authenticated" + }, + "authenticatedUser": { + "type": "object", + "description": "User information (only for session auth)", + "properties": { + "id": { + "type": "string", + "example": "usr_def456ghi789" + }, + "email": { + "type": "string", + "example": "user@example.com" + } + } + } + } + } + } + } + }, + "400": { + "description": "Bad request - Invalid input data", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "title should not be empty", + "description should not be empty", + "category must be a valid enum value" + ] + }, + "error": { + "type": "string", + "example": "Bad Request" + }, + "statusCode": { + "type": "number", + "example": 400 + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Invalid or expired API key" + } + } + } + } + } + }, + "404": { + "description": "Organization not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Organization with ID org_abc123def456 not found" + } + } + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Internal server error" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Create a new risk", + "tags": [ + "Risks" + ] + } + }, + "/v1/risks/{id}": { + "get": { + "description": "Returns a specific risk by ID for the authenticated organization. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "RisksController_getRiskById_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "id", + "required": true, + "in": "path", + "description": "Risk ID", + "schema": { + "example": "rsk_abc123def456", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Risk retrieved successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Risk ID", + "example": "rsk_abc123def456" + }, + "title": { + "type": "string", + "description": "Risk title", + "example": "Data breach vulnerability in user authentication system" + }, + "description": { + "type": "string", + "description": "Risk description", + "example": "Weak password requirements could lead to unauthorized access to user accounts" + }, + "category": { + "type": "string", + "enum": [ + "customer", + "governance", + "operations", + "other", + "people", + "regulatory", + "reporting", + "resilience", + "technology", + "vendor_management" + ], + "example": "technology" + }, + "department": { + "type": "string", + "enum": [ + "none", + "admin", + "gov", + "hr", + "it", + "itsm", + "qms" + ], + "nullable": true, + "example": "it" + }, + "status": { + "type": "string", + "enum": [ + "open", + "pending", + "closed", + "archived" + ], + "example": "open" + }, + "likelihood": { + "type": "string", + "enum": [ + "very_unlikely", + "unlikely", + "possible", + "likely", + "very_likely" + ], + "example": "possible" + }, + "impact": { + "type": "string", + "enum": [ + "insignificant", + "minor", + "moderate", + "major", + "severe" + ], + "example": "major" + }, + "residualLikelihood": { + "type": "string", + "enum": [ + "very_unlikely", + "unlikely", + "possible", + "likely", + "very_likely" + ], + "example": "unlikely" + }, + "residualImpact": { + "type": "string", + "enum": [ + "insignificant", + "minor", + "moderate", + "major", + "severe" + ], + "example": "minor" + }, + "treatmentStrategyDescription": { + "type": "string", + "nullable": true, + "example": "Implement multi-factor authentication and strengthen password requirements" + }, + "treatmentStrategy": { + "type": "string", + "enum": [ + "accept", + "avoid", + "mitigate", + "transfer" + ], + "example": "mitigate" + }, + "organizationId": { + "type": "string", + "example": "org_abc123def456" + }, + "assigneeId": { + "type": "string", + "nullable": true, + "description": "ID of the user assigned to this risk", + "example": "mem_abc123def456" + }, + "createdAt": { + "type": "string", + "format": "date-time", + "description": "When the risk was created" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "description": "When the risk was last updated" + }, + "authType": { + "type": "string", + "enum": [ + "api-key", + "session" + ], + "description": "How the request was authenticated" + }, + "authenticatedUser": { + "type": "object", + "description": "User information (only for session auth)", + "properties": { + "id": { + "type": "string", + "example": "usr_def456ghi789" + }, + "email": { + "type": "string", + "example": "user@example.com" + } + } + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Invalid or expired API key" + } + } + } + } + } + }, + "404": { + "description": "Risk not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Risk with ID rsk_abc123def456 not found in organization org_abc123def456" + } + } + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Internal server error" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Get risk by ID", + "tags": [ + "Risks" + ] + }, + "patch": { + "description": "Partially updates a risk. Only provided fields will be updated. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "RisksController_updateRisk_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "id", + "required": true, + "in": "path", + "description": "Risk ID", + "schema": { + "example": "rsk_abc123def456", + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "description": "Risk update data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateRiskDto" + } + } + } + }, + "responses": { + "200": { + "description": "Risk updated successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Risk ID", + "example": "rsk_abc123def456" + }, + "title": { + "type": "string", + "description": "Risk title", + "example": "Data breach vulnerability in user authentication system" + }, + "description": { + "type": "string", + "description": "Risk description", + "example": "Weak password requirements could lead to unauthorized access to user accounts" + }, + "category": { + "type": "string", + "enum": [ + "customer", + "governance", + "operations", + "other", + "people", + "regulatory", + "reporting", + "resilience", + "technology", + "vendor_management" + ], + "example": "technology" + }, + "department": { + "type": "string", + "enum": [ + "none", + "admin", + "gov", + "hr", + "it", + "itsm", + "qms" + ], + "nullable": true, + "example": "it" + }, + "status": { + "type": "string", + "enum": [ + "open", + "pending", + "closed", + "archived" + ], + "example": "open" + }, + "likelihood": { + "type": "string", + "enum": [ + "very_unlikely", + "unlikely", + "possible", + "likely", + "very_likely" + ], + "example": "possible" + }, + "impact": { + "type": "string", + "enum": [ + "insignificant", + "minor", + "moderate", + "major", + "severe" + ], + "example": "major" + }, + "residualLikelihood": { + "type": "string", + "enum": [ + "very_unlikely", + "unlikely", + "possible", + "likely", + "very_likely" + ], + "example": "unlikely" + }, + "residualImpact": { + "type": "string", + "enum": [ + "insignificant", + "minor", + "moderate", + "major", + "severe" + ], + "example": "minor" + }, + "treatmentStrategyDescription": { + "type": "string", + "nullable": true, + "example": "Implement multi-factor authentication and strengthen password requirements" + }, + "treatmentStrategy": { + "type": "string", + "enum": [ + "accept", + "avoid", + "mitigate", + "transfer" + ], + "example": "mitigate" + }, + "organizationId": { + "type": "string", + "example": "org_abc123def456" + }, + "assigneeId": { + "type": "string", + "nullable": true, + "description": "ID of the user assigned to this risk", + "example": "mem_abc123def456" + }, + "createdAt": { + "type": "string", + "format": "date-time", + "description": "When the risk was created" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "description": "When the risk was last updated" + }, + "authType": { + "type": "string", + "enum": [ + "api-key", + "session" + ], + "description": "How the request was authenticated" + }, + "authenticatedUser": { + "type": "object", + "description": "User information (only for session auth)", + "properties": { + "id": { + "type": "string", + "example": "usr_def456ghi789" + }, + "email": { + "type": "string", + "example": "user@example.com" + } + } + } + } + } + } + } + }, + "400": { + "description": "Bad request - Invalid input data", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "title should not be empty", + "category must be a valid enum value", + "status must be a valid enum value" + ] + }, + "error": { + "type": "string", + "example": "Bad Request" + }, + "statusCode": { + "type": "number", + "example": 400 + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Invalid or expired API key" + } + } + } + } + } + }, + "404": { + "description": "Risk not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Risk with ID rsk_abc123def456 not found in organization org_abc123def456" + } + } + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Internal server error" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Update risk", + "tags": [ + "Risks" + ] + }, + "delete": { + "description": "Permanently removes a risk from the organization. This action cannot be undone. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "RisksController_deleteRisk_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "id", + "required": true, + "in": "path", + "description": "Risk ID", + "schema": { + "example": "rsk_abc123def456", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Risk deleted successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Risk deleted successfully" + }, + "deletedRisk": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Deleted risk ID", + "example": "rsk_abc123def456" + }, + "title": { + "type": "string", + "description": "Deleted risk title", + "example": "Data breach vulnerability in user authentication system" + } + } + }, + "authType": { + "type": "string", + "enum": [ + "api-key", + "session" + ], + "description": "How the request was authenticated" + }, + "authenticatedUser": { + "type": "object", + "description": "User information (only for session auth)", + "properties": { + "id": { + "type": "string", + "example": "usr_def456ghi789" + }, + "email": { + "type": "string", + "example": "user@example.com" + } + } + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Invalid or expired API key" + } + } + } + } + } + }, + "404": { + "description": "Risk not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Risk with ID rsk_abc123def456 not found in organization org_abc123def456" + } + } + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Internal server error" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Delete risk", + "tags": [ + "Risks" + ] + } + }, + "/v1/vendors": { + "get": { + "description": "Returns all vendors for the authenticated organization. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "VendorsController_getAllVendors_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Vendors retrieved successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Vendor ID", + "example": "vnd_abc123def456" + }, + "name": { + "type": "string", + "description": "Vendor name", + "example": "CloudTech Solutions Inc." + }, + "description": { + "type": "string", + "description": "Vendor description", + "example": "Cloud infrastructure provider offering AWS-like services" + }, + "category": { + "type": "string", + "enum": [ + "cloud", + "infrastructure", + "software_as_a_service", + "finance", + "marketing", + "sales", + "hr", + "other" + ], + "example": "cloud" + }, + "status": { + "type": "string", + "enum": [ + "not_assessed", + "in_progress", + "assessed" + ], + "example": "not_assessed" + }, + "inherentProbability": { + "type": "string", + "enum": [ + "very_unlikely", + "unlikely", + "possible", + "likely", + "very_likely" + ], + "example": "possible" + }, + "inherentImpact": { + "type": "string", + "enum": [ + "insignificant", + "minor", + "moderate", + "major", + "severe" + ], + "example": "moderate" + }, + "residualProbability": { + "type": "string", + "enum": [ + "very_unlikely", + "unlikely", + "possible", + "likely", + "very_likely" + ], + "example": "unlikely" + }, + "residualImpact": { + "type": "string", + "enum": [ + "insignificant", + "minor", + "moderate", + "major", + "severe" + ], + "example": "minor" + }, + "website": { + "type": "string", + "nullable": true, + "example": "https://www.cloudtechsolutions.com" + }, + "assigneeId": { + "type": "string", + "nullable": true, + "description": "ID of the user assigned to manage this vendor", + "example": "mem_abc123def456" + }, + "createdAt": { + "type": "string", + "format": "date-time", + "description": "When the vendor was created" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "description": "When the vendor was last updated" + } + } + } + }, + "count": { + "type": "number", + "description": "Total number of vendors", + "example": 12 + }, + "authType": { + "type": "string", + "enum": [ + "api-key", + "session" + ], + "description": "How the request was authenticated" + }, + "authenticatedUser": { + "type": "object", + "description": "User information (only for session auth)", + "properties": { + "id": { + "type": "string", + "example": "usr_def456ghi789" + }, + "email": { + "type": "string", + "example": "user@example.com" + } + } + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Invalid or expired API key" + } + } + } + } + } + }, + "404": { + "description": "Organization not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Organization with ID org_abc123def456 not found" + } + } + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Internal server error" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Get all vendors", + "tags": [ + "Vendors" + ] + }, + "post": { + "description": "Creates a new vendor for the authenticated organization. All required fields must be provided. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "VendorsController_createVendor_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "description": "Vendor creation data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateVendorDto" + } + } + } + }, + "responses": { + "201": { + "description": "Vendor created successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Vendor ID", + "example": "vnd_abc123def456" + }, + "name": { + "type": "string", + "description": "Vendor name", + "example": "CloudTech Solutions Inc." + }, + "description": { + "type": "string", + "description": "Vendor description", + "example": "Cloud infrastructure provider offering AWS-like services including compute, storage, and networking solutions for enterprise customers." + }, + "category": { + "type": "string", + "enum": [ + "cloud", + "infrastructure", + "software_as_a_service", + "finance", + "marketing", + "sales", + "hr", + "other" + ], + "example": "cloud" + }, + "status": { + "type": "string", + "enum": [ + "not_assessed", + "in_progress", + "assessed" + ], + "example": "not_assessed" + }, + "inherentProbability": { + "type": "string", + "enum": [ + "very_unlikely", + "unlikely", + "possible", + "likely", + "very_likely" + ], + "example": "possible" + }, + "inherentImpact": { + "type": "string", + "enum": [ + "insignificant", + "minor", + "moderate", + "major", + "severe" + ], + "example": "moderate" + }, + "residualProbability": { + "type": "string", + "enum": [ + "very_unlikely", + "unlikely", + "possible", + "likely", + "very_likely" + ], + "example": "unlikely" + }, + "residualImpact": { + "type": "string", + "enum": [ + "insignificant", + "minor", + "moderate", + "major", + "severe" + ], + "example": "minor" + }, + "website": { + "type": "string", + "nullable": true, + "example": "https://www.cloudtechsolutions.com" + }, + "organizationId": { + "type": "string", + "example": "org_abc123def456" + }, + "assigneeId": { + "type": "string", + "nullable": true, + "description": "ID of the user assigned to manage this vendor", + "example": "mem_abc123def456" + }, + "createdAt": { + "type": "string", + "format": "date-time", + "description": "When the vendor was created" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "description": "When the vendor was last updated" + }, + "authType": { + "type": "string", + "enum": [ + "api-key", + "session" + ], + "description": "How the request was authenticated" + }, + "authenticatedUser": { + "type": "object", + "description": "User information (only for session auth)", + "properties": { + "id": { + "type": "string", + "example": "usr_def456ghi789" + }, + "email": { + "type": "string", + "example": "user@example.com" + } + } + } + } + } + } + } + }, + "400": { + "description": "Bad request - Invalid input data", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "name should not be empty", + "description should not be empty", + "category must be a valid enum value", + "website must be a URL address" + ] + }, + "error": { + "type": "string", + "example": "Bad Request" + }, + "statusCode": { + "type": "number", + "example": 400 + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Invalid or expired API key" + } + } + } + } + } + }, + "404": { + "description": "Organization not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Organization with ID org_abc123def456 not found" + } + } + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Internal server error" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Create a new vendor", + "tags": [ + "Vendors" + ] + } + }, + "/v1/vendors/{id}": { + "get": { + "description": "Returns a specific vendor by ID for the authenticated organization. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "VendorsController_getVendorById_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "id", + "required": true, + "in": "path", + "description": "Vendor ID", + "schema": { + "example": "vnd_abc123def456", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Vendor retrieved successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Vendor ID", + "example": "vnd_abc123def456" + }, + "name": { + "type": "string", + "description": "Vendor name", + "example": "CloudTech Solutions Inc." + }, + "description": { + "type": "string", + "description": "Vendor description", + "example": "Cloud infrastructure provider offering AWS-like services including compute, storage, and networking solutions for enterprise customers." + }, + "category": { + "type": "string", + "enum": [ + "cloud", + "infrastructure", + "software_as_a_service", + "finance", + "marketing", + "sales", + "hr", + "other" + ], + "example": "cloud" + }, + "status": { + "type": "string", + "enum": [ + "not_assessed", + "in_progress", + "assessed" + ], + "example": "not_assessed" + }, + "inherentProbability": { + "type": "string", + "enum": [ + "very_unlikely", + "unlikely", + "possible", + "likely", + "very_likely" + ], + "example": "possible" + }, + "inherentImpact": { + "type": "string", + "enum": [ + "insignificant", + "minor", + "moderate", + "major", + "severe" + ], + "example": "moderate" + }, + "residualProbability": { + "type": "string", + "enum": [ + "very_unlikely", + "unlikely", + "possible", + "likely", + "very_likely" + ], + "example": "unlikely" + }, + "residualImpact": { + "type": "string", + "enum": [ + "insignificant", + "minor", + "moderate", + "major", + "severe" + ], + "example": "minor" + }, + "website": { + "type": "string", + "nullable": true, + "example": "https://www.cloudtechsolutions.com" + }, + "organizationId": { + "type": "string", + "example": "org_abc123def456" + }, + "assigneeId": { + "type": "string", + "nullable": true, + "description": "ID of the user assigned to manage this vendor", + "example": "mem_abc123def456" + }, + "createdAt": { + "type": "string", + "format": "date-time", + "description": "When the vendor was created" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "description": "When the vendor was last updated" + }, + "authType": { + "type": "string", + "enum": [ + "api-key", + "session" + ], + "description": "How the request was authenticated" + }, + "authenticatedUser": { + "type": "object", + "description": "User information (only for session auth)", + "properties": { + "id": { + "type": "string", + "example": "usr_def456ghi789" + }, + "email": { + "type": "string", + "example": "user@example.com" + } + } + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Invalid or expired API key" + } + } + } + } + } + }, + "404": { + "description": "Vendor not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Vendor with ID vnd_abc123def456 not found in organization org_abc123def456" + } + } + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Internal server error" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Get vendor by ID", + "tags": [ + "Vendors" + ] + }, + "patch": { + "description": "Partially updates a vendor. Only provided fields will be updated. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "VendorsController_updateVendor_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "id", + "required": true, + "in": "path", + "description": "Vendor ID", + "schema": { + "example": "vnd_abc123def456", + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "description": "Vendor update data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateVendorDto" + } + } + } + }, + "responses": { + "200": { + "description": "Vendor updated successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Vendor ID", + "example": "vnd_abc123def456" + }, + "name": { + "type": "string", + "description": "Vendor name", + "example": "CloudTech Solutions Inc." + }, + "description": { + "type": "string", + "description": "Vendor description", + "example": "Cloud infrastructure provider offering AWS-like services including compute, storage, and networking solutions for enterprise customers." + }, + "category": { + "type": "string", + "enum": [ + "cloud", + "infrastructure", + "software_as_a_service", + "finance", + "marketing", + "sales", + "hr", + "other" + ], + "example": "cloud" + }, + "status": { + "type": "string", + "enum": [ + "not_assessed", + "in_progress", + "assessed" + ], + "example": "assessed" + }, + "inherentProbability": { + "type": "string", + "enum": [ + "very_unlikely", + "unlikely", + "possible", + "likely", + "very_likely" + ], + "example": "possible" + }, + "inherentImpact": { + "type": "string", + "enum": [ + "insignificant", + "minor", + "moderate", + "major", + "severe" + ], + "example": "moderate" + }, + "residualProbability": { + "type": "string", + "enum": [ + "very_unlikely", + "unlikely", + "possible", + "likely", + "very_likely" + ], + "example": "unlikely" + }, + "residualImpact": { + "type": "string", + "enum": [ + "insignificant", + "minor", + "moderate", + "major", + "severe" + ], + "example": "minor" + }, + "website": { + "type": "string", + "nullable": true, + "example": "https://www.cloudtechsolutions.com" + }, + "organizationId": { + "type": "string", + "example": "org_abc123def456" + }, + "assigneeId": { + "type": "string", + "nullable": true, + "description": "ID of the user assigned to manage this vendor", + "example": "mem_abc123def456" + }, + "createdAt": { + "type": "string", + "format": "date-time", + "description": "When the vendor was created" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "description": "When the vendor was last updated" + }, + "authType": { + "type": "string", + "enum": [ + "api-key", + "session" + ], + "description": "How the request was authenticated" + }, + "authenticatedUser": { + "type": "object", + "description": "User information (only for session auth)", + "properties": { + "id": { + "type": "string", + "example": "usr_def456ghi789" + }, + "email": { + "type": "string", + "example": "user@example.com" + } + } + } + } + } + } + } + }, + "400": { + "description": "Bad request - Invalid input data", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "name should not be empty", + "category must be a valid enum value", + "status must be a valid enum value", + "website must be a URL address" + ] + }, + "error": { + "type": "string", + "example": "Bad Request" + }, + "statusCode": { + "type": "number", + "example": 400 + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Invalid or expired API key" + } + } + } + } + } + }, + "404": { + "description": "Vendor not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Vendor with ID vnd_abc123def456 not found in organization org_abc123def456" + } + } + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Internal server error" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Update vendor", + "tags": [ + "Vendors" + ] + }, + "delete": { + "description": "Permanently removes a vendor from the organization. This action cannot be undone. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "VendorsController_deleteVendor_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "id", + "required": true, + "in": "path", + "description": "Vendor ID", + "schema": { + "example": "vnd_abc123def456", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Vendor deleted successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Vendor deleted successfully" + }, + "deletedVendor": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Deleted vendor ID", + "example": "vnd_abc123def456" + }, + "name": { + "type": "string", + "description": "Deleted vendor name", + "example": "CloudTech Solutions Inc." + } + } + }, + "authType": { + "type": "string", + "enum": [ + "api-key", + "session" + ], + "description": "How the request was authenticated" + }, + "authenticatedUser": { + "type": "object", + "description": "User information (only for session auth)", + "properties": { + "id": { + "type": "string", + "example": "usr_def456ghi789" + }, + "email": { + "type": "string", + "example": "user@example.com" + } + } + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Invalid or expired API key" + } + } + } + } + } + }, + "404": { + "description": "Vendor not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Vendor with ID vnd_abc123def456 not found in organization org_abc123def456" + } + } + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Internal server error" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Delete vendor", + "tags": [ + "Vendors" + ] + } + }, + "/v1/context": { + "get": { + "description": "Returns all context entries for the authenticated organization. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "ContextController_getAllContext_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Context entries retrieved successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "question": { + "type": "string" + }, + "answer": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + } + } + } + }, + "count": { + "type": "number" + }, + "authType": { + "type": "string", + "enum": [ + "api-key", + "session" + ] + } + } + }, + "example": { + "data": [ + { + "id": "ctx_abc123def456", + "organizationId": "org_xyz789uvw012", + "question": "How do we handle user authentication in our application?", + "answer": "We use a hybrid authentication system supporting both API keys and session-based authentication.", + "tags": [ + "authentication", + "security", + "api", + "sessions" + ], + "createdAt": "2024-01-15T10:30:00.000Z", + "updatedAt": "2024-01-15T14:20:00.000Z" + }, + { + "id": "ctx_ghi789jkl012", + "organizationId": "org_xyz789uvw012", + "question": "What database do we use and why?", + "answer": "We use PostgreSQL as our primary database with Prisma as the ORM.", + "tags": [ + "database", + "postgresql", + "prisma", + "architecture" + ], + "createdAt": "2024-01-14T09:15:00.000Z", + "updatedAt": "2024-01-14T09:15:00.000Z" + } + ], + "count": 2, + "authType": "apikey" + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid or missing authentication", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "statusCode": { + "type": "number", + "example": 401 + } + } + }, + "example": { + "message": "Unauthorized", + "statusCode": 401 + } + } + } + }, + "404": { + "description": "Organization not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "statusCode": { + "type": "number", + "example": 404 + } + } + }, + "example": { + "message": "Organization not found", + "statusCode": 404 + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "statusCode": { + "type": "number", + "example": 500 + } + } + }, + "example": { + "message": "Internal server error", + "statusCode": 500 + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Get all context entries", + "tags": [ + "Context" + ] + }, + "post": { + "description": "Creates a new context entry for the authenticated organization. All required fields must be provided. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "ContextController_createContext_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "description": "Context entry data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateContextDto" + }, + "examples": { + "Authentication Context": { + "value": { + "question": "How do we handle user authentication in our application?", + "answer": "We use a hybrid authentication system supporting both API keys and session-based authentication. API keys are used for programmatic access while sessions are used for web interface interactions.", + "tags": [ + "authentication", + "security", + "api", + "sessions" + ] + } + }, + "Database Context": { + "value": { + "question": "What database do we use and why?", + "answer": "We use PostgreSQL as our primary database with Prisma as the ORM. PostgreSQL provides excellent performance, ACID compliance, and supports advanced features like JSON columns and full-text search.", + "tags": [ + "database", + "postgresql", + "prisma", + "architecture" + ] + } + } + } + } + } + }, + "responses": { + "201": { + "description": "Context entry created successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "question": { + "type": "string" + }, + "answer": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "authType": { + "type": "string", + "enum": [ + "api-key", + "session" + ] + } + } + }, + "example": { + "id": "ctx_abc123def456", + "organizationId": "org_xyz789uvw012", + "question": "How do we handle user authentication in our application?", + "answer": "We use a hybrid authentication system supporting both API keys and session-based authentication.", + "tags": [ + "authentication", + "security", + "api", + "sessions" + ], + "createdAt": "2024-01-15T10:30:00.000Z", + "updatedAt": "2024-01-15T10:30:00.000Z", + "authType": "apikey" + } + } + } + }, + "400": { + "description": "Bad request - Invalid input data", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "array", + "items": { + "type": "string" + } + }, + "error": { + "type": "string" + }, + "statusCode": { + "type": "number" + } + } + }, + "example": { + "message": [ + "question should not be empty", + "answer should not be empty" + ], + "error": "Bad Request", + "statusCode": 400 + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid or missing authentication", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "statusCode": { + "type": "number", + "example": 401 + } + } + }, + "example": { + "message": "Unauthorized", + "statusCode": 401 + } + } + } + }, + "404": { + "description": "Organization not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "statusCode": { + "type": "number", + "example": 404 + } + } + }, + "example": { + "message": "Organization not found", + "statusCode": 404 + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "statusCode": { + "type": "number", + "example": 500 + } + } + }, + "example": { + "message": "Internal server error", + "statusCode": 500 + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Create a new context entry", + "tags": [ + "Context" + ] + } + }, + "/v1/context/{id}": { + "get": { + "description": "Returns a specific context entry by ID for the authenticated organization. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "ContextController_getContextById_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "id", + "required": true, + "in": "path", + "description": "Context entry ID", + "schema": { + "example": "ctx_abc123def456", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Context entry retrieved successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "ctx_abc123def456" + }, + "organizationId": { + "type": "string", + "example": "org_xyz789uvw012" + }, + "question": { + "type": "string" + }, + "answer": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "authentication", + "security" + ] + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "authType": { + "type": "string", + "enum": [ + "api-key", + "session" + ] + } + } + }, + "example": { + "id": "ctx_abc123def456", + "organizationId": "org_xyz789uvw012", + "question": "How do we handle user authentication in our application?", + "answer": "We use a hybrid authentication system supporting both API keys and session-based authentication.", + "tags": [ + "authentication", + "security", + "api", + "sessions" + ], + "createdAt": "2024-01-15T10:30:00.000Z", + "updatedAt": "2024-01-15T14:20:00.000Z", + "authType": "apikey" + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid or missing authentication", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Unauthorized" + }, + "statusCode": { + "type": "number", + "example": 401 + } + } + }, + "example": { + "message": "Unauthorized", + "statusCode": 401 + } + } + } + }, + "404": { + "description": "Context entry not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "statusCode": { + "type": "number", + "example": 404 + } + } + }, + "example": { + "message": "Context entry with ID ctx_abc123def456 not found in organization org_xyz789uvw012", + "statusCode": 404 + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "statusCode": { + "type": "number", + "example": 500 + } + } + }, + "example": { + "message": "Internal server error", + "statusCode": 500 + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Get context entry by ID", + "tags": [ + "Context" + ] + }, + "patch": { + "description": "Partially updates a context entry. Only provided fields will be updated. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "ContextController_updateContext_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "id", + "required": true, + "in": "path", + "description": "Context entry ID", + "schema": { + "example": "ctx_abc123def456", + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "description": "Partial context entry data to update", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateContextDto" + }, + "examples": { + "Update Tags": { + "value": { + "tags": [ + "authentication", + "security", + "api", + "sessions", + "updated" + ] + } + }, + "Update Answer": { + "value": { + "answer": "Updated: We use a hybrid authentication system supporting both API keys and session-based authentication. Recent updates include support for OAuth2 providers." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Context entry updated successfully", + "content": { + "application/json": { + "example": { + "id": "ctx_abc123def456", + "organizationId": "org_xyz789uvw012", + "question": "How do we handle user authentication in our application?", + "answer": "Updated: We use a hybrid authentication system supporting both API keys and session-based authentication with OAuth2 support.", + "tags": [ + "authentication", + "security", + "api", + "sessions", + "oauth2" + ], + "createdAt": "2024-01-15T10:30:00.000Z", + "updatedAt": "2024-01-15T15:45:00.000Z", + "authType": "apikey" + } + } + } + }, + "400": { + "description": "Bad request - Invalid input data", + "content": { + "application/json": { + "example": { + "message": [ + "tags must be an array of strings" + ], + "error": "Bad Request", + "statusCode": 400 + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid or missing authentication", + "content": { + "application/json": { + "example": { + "message": "Unauthorized", + "statusCode": 401 + } + } + } + }, + "404": { + "description": "Context entry not found", + "content": { + "application/json": { + "example": { + "message": "Context entry with ID ctx_abc123def456 not found in organization org_xyz789uvw012", + "statusCode": 404 + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "example": { + "message": "Internal server error", + "statusCode": 500 + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Update context entry", + "tags": [ + "Context" + ] + }, + "delete": { + "description": "Permanently removes a context entry from the organization. This action cannot be undone. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "ContextController_deleteContext_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "id", + "required": true, + "in": "path", + "description": "Context entry ID", + "schema": { + "example": "ctx_abc123def456", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Context entry deleted successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "deletedContext": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "question": { + "type": "string" + } + } + }, + "authType": { + "type": "string", + "enum": [ + "api-key", + "session" + ] + } + } + }, + "example": { + "message": "Context entry deleted successfully", + "deletedContext": { + "id": "ctx_abc123def456", + "question": "How do we handle user authentication in our application?" + }, + "authType": "apikey" + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid or missing authentication", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "statusCode": { + "type": "number", + "example": 401 + } + } + }, + "example": { + "message": "Unauthorized", + "statusCode": 401 + } + } + } + }, + "404": { + "description": "Context entry not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "statusCode": { + "type": "number", + "example": 404 + } + } + }, + "example": { + "message": "Context entry with ID ctx_abc123def456 not found in organization org_xyz789uvw012", + "statusCode": 404 + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "statusCode": { + "type": "number", + "example": 500 + } + } + }, + "example": { + "message": "Internal server error", + "statusCode": 500 + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Delete context entry", + "tags": [ + "Context" + ] + } + }, + "/v1/devices": { + "get": { + "description": "Returns all devices for the authenticated organization from FleetDM. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "DevicesController_getAllDevices_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Devices retrieved successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DeviceResponseDto" + } + }, + "count": { + "type": "number", + "description": "Total number of devices", + "example": 25 + }, + "authType": { + "type": "string", + "enum": [ + "api-key", + "session" + ], + "description": "How the request was authenticated" + }, + "authenticatedUser": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "User ID", + "example": "usr_abc123def456" + }, + "email": { + "type": "string", + "description": "User email", + "example": "user@company.com" + } + } + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Invalid or expired API key" + } + } + } + } + } + }, + "404": { + "description": "Organization not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Organization with ID org_abc123def456 not found" + } + } + } + } + } + }, + "500": { + "description": "Internal server error - FleetDM integration issue", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Organization does not have FleetDM configured" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Get all devices", + "tags": [ + "Devices" + ] + } + }, + "/v1/devices/member/{memberId}": { + "get": { + "description": "Returns all devices assigned to a specific member within the authenticated organization. Devices are fetched from FleetDM using the member's dedicated fleetDmLabelId. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "DevicesController_getDevicesByMember_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "memberId", + "required": true, + "in": "path", + "description": "Member ID to get devices for", + "schema": { + "example": "mem_abc123def456", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Member devices retrieved successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DevicesByMemberResponseDto" + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Unauthorized" + } + } + } + } + } + }, + "404": { + "description": "Organization or member not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Member with ID mem_abc123def456 not found in organization org_abc123def456" + } + } + } + } + } + }, + "500": { + "description": "Internal server error - FleetDM integration issue" + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Get devices by member ID", + "tags": [ + "Devices" + ] + } + }, + "/v1/policies": { + "get": { + "description": "Returns all policies for the authenticated organization. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "PoliciesController_getAllPolicies_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Policies retrieved successfully", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PolicyResponseDto" + } + }, + "example": [ + { + "id": "pol_abc123def456", + "name": "Data Privacy Policy", + "status": "draft", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "..." + } + ] + } + ], + "isRequiredToSign": true, + "signedBy": [], + "createdAt": "2024-01-01T00:00:00.000Z", + "updatedAt": "2024-01-15T00:00:00.000Z", + "organizationId": "org_abc123def456" + } + ] + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Unauthorized" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Get all policies", + "tags": [ + "Policies" + ] + }, + "post": { + "description": "Creates a new policy for the authenticated organization. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "PoliciesController_createPolicy_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "description": "Policy creation data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreatePolicyDto" + } + } + } + }, + "responses": { + "201": { + "description": "Policy created successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PolicyResponseDto" + }, + "example": { + "id": "pol_abc123def456", + "name": "Data Privacy Policy", + "description": "This policy outlines how we handle and protect personal data", + "status": "draft", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Policy content here" + } + ] + } + ], + "frequency": "yearly", + "department": "it", + "isRequiredToSign": true, + "signedBy": [], + "reviewDate": "2024-12-31T00:00:00.000Z", + "isArchived": false, + "createdAt": "2024-01-01T00:00:00.000Z", + "updatedAt": "2024-01-15T00:00:00.000Z", + "organizationId": "org_abc123def456", + "assigneeId": "usr_abc123def456", + "approverId": "usr_xyz789abc123" + } + } + } + }, + "400": { + "description": "Bad Request - Invalid policy data", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Invalid policy content format" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Unauthorized" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Create a new policy", + "tags": [ + "Policies" + ] + } + }, + "/v1/policies/{id}": { + "get": { + "description": "Returns a specific policy by ID for the authenticated organization. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "PoliciesController_getPolicy_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "id", + "required": true, + "in": "path", + "description": "Policy ID", + "schema": { + "example": "pol_abc123def456", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Policy retrieved successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PolicyResponseDto" + }, + "example": { + "id": "pol_abc123def456", + "name": "Data Privacy Policy", + "status": "draft", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "..." + } + ] + } + ], + "isRequiredToSign": true, + "signedBy": [], + "createdAt": "2024-01-01T00:00:00.000Z", + "updatedAt": "2024-01-15T00:00:00.000Z", + "organizationId": "org_abc123def456" + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Unauthorized" + } + } + } + } + } + }, + "404": { + "description": "Policy not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Policy with ID pol_abc123def456 not found" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Get policy by ID", + "tags": [ + "Policies" + ] + }, + "patch": { + "description": "Partially updates a policy. Only provided fields will be updated. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "PoliciesController_updatePolicy_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "id", + "required": true, + "in": "path", + "description": "Policy ID", + "schema": { + "example": "pol_abc123def456", + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "description": "Policy update data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdatePolicyDto" + } + } + } + }, + "responses": { + "200": { + "description": "Policy updated successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PolicyResponseDto" + }, + "example": { + "id": "pol_abc123def456", + "name": "Data Privacy Policy", + "description": "This policy outlines how we handle and protect personal data", + "status": "published", + "content": [ + { + "type": "heading", + "attrs": { + "level": 2 + }, + "content": [ + { + "type": "text", + "text": "Purpose" + } + ] + } + ], + "frequency": "yearly", + "department": "it", + "isRequiredToSign": true, + "signedBy": [ + "usr_123" + ], + "reviewDate": "2024-12-31T00:00:00.000Z", + "isArchived": false, + "createdAt": "2024-01-01T00:00:00.000Z", + "updatedAt": "2024-01-15T00:00:00.000Z", + "organizationId": "org_abc123def456", + "assigneeId": "usr_abc123def456", + "approverId": "usr_xyz789abc123" + } + } + } + }, + "400": { + "description": "Bad Request - Invalid update data", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Validation failed" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Unauthorized" + } + } + } + } + } + }, + "404": { + "description": "Policy not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Policy with ID pol_abc123def456 not found" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Update policy", + "tags": [ + "Policies" + ] + }, + "delete": { + "description": "Permanently deletes a policy. This action cannot be undone. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "PoliciesController_deletePolicy_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "id", + "required": true, + "in": "path", + "description": "Policy ID", + "schema": { + "example": "pol_abc123def456", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Policy deleted successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "Indicates successful deletion", + "example": true + }, + "deletedPolicy": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The deleted policy ID", + "example": "pol_abc123def456" + }, + "name": { + "type": "string", + "description": "The deleted policy name", + "example": "Data Privacy Policy" + } + } + }, + "authType": { + "type": "string", + "enum": [ + "api-key", + "session" + ], + "description": "How the request was authenticated" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication or insufficient permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Unauthorized" + } + } + } + } + } + }, + "404": { + "description": "Policy not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Policy with ID pol_abc123def456 not found" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Delete policy", + "tags": [ + "Policies" + ] + } + }, + "/v1/device-agent/mac": { + "get": { + "description": "Downloads the Comp AI Device Agent installer for macOS as a DMG file. The agent helps monitor device compliance and security policies. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "DeviceAgentController_downloadMacAgent_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "default": { + "description": "macOS agent DMG file download", + "content": { + "application/x-apple-diskimage": { + "schema": { + "type": "string", + "format": "binary" + }, + "example": "Binary DMG file content" + } + }, + "headers": { + "Content-Disposition": { + "description": "Indicates file should be downloaded with specific filename", + "schema": { + "type": "string", + "example": "attachment; filename=\"Comp AI Agent-1.0.0-arm64.dmg\"" + } + }, + "Content-Type": { + "description": "MIME type for macOS disk image", + "schema": { + "type": "string", + "example": "application/x-apple-diskimage" + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Download macOS Device Agent", + "tags": [ + "Device Agent" + ] + } + }, + "/v1/device-agent/windows": { + "get": { + "description": "Downloads a ZIP package containing the Comp AI Device Agent installer for Windows, along with setup scripts and instructions. The package includes an MSI installer, setup batch script customized for the organization and user, and a README with installation instructions. Supports both API key authentication (X-API-Key header) and session authentication (cookies + X-Organization-Id header).", + "operationId": "DeviceAgentController_downloadWindowsAgent_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "default": { + "description": "Windows agent ZIP file download containing MSI installer and setup scripts", + "content": { + "application/zip": { + "schema": { + "type": "string", + "format": "binary" + }, + "example": "Binary ZIP file content" + } + }, + "headers": { + "Content-Disposition": { + "description": "Indicates file should be downloaded with specific filename", + "schema": { + "type": "string", + "example": "attachment; filename=\"compai-device-agent-windows.zip\"" + } + }, + "Content-Type": { + "description": "MIME type for ZIP archive", + "schema": { + "type": "string", + "example": "application/zip" + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Download Windows Device Agent ZIP", + "tags": [ + "Device Agent" + ] + } + }, + "/v1/attachments/{attachmentId}/download": { + "get": { + "description": "Generate a fresh signed URL for downloading any attachment", + "operationId": "AttachmentsController_getAttachmentDownloadUrl_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "attachmentId", + "required": true, + "in": "path", + "description": "Unique attachment identifier", + "schema": { + "example": "att_abc123def456", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Download URL generated successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "downloadUrl": { + "type": "string", + "description": "Signed URL for downloading the file", + "example": "https://bucket.s3.amazonaws.com/path/to/file.pdf?signature=..." + }, + "expiresIn": { + "type": "number", + "description": "URL expiration time in seconds", + "example": 900 + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Get attachment download URL", + "tags": [ + "Attachments" + ] + } + }, + "/v1/tasks": { + "get": { + "description": "Retrieve all tasks for the authenticated organization", + "operationId": "TasksController_getTasks_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Tasks retrieved successfully", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TaskResponseDto" + } + }, + "example": [ + { + "id": "tsk_abc123def456", + "title": "Implement user authentication", + "description": "Add OAuth 2.0 authentication to the platform", + "status": "in_progress", + "createdAt": "2024-01-15T10:30:00Z", + "updatedAt": "2024-01-15T10:30:00Z" + } + ] + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Unauthorized" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Get all tasks", + "tags": [ + "Tasks" + ] + } + }, + "/v1/tasks/{taskId}": { + "get": { + "description": "Retrieve a specific task by its ID", + "operationId": "TasksController_getTask_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "taskId", + "required": true, + "in": "path", + "description": "Unique task identifier", + "schema": { + "example": "tsk_abc123def456", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Task retrieved successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TaskResponseDto" + }, + "example": { + "id": "tsk_abc123def456", + "title": "Implement user authentication", + "description": "Add OAuth 2.0 authentication to the platform", + "status": "in_progress", + "createdAt": "2024-01-15T10:30:00Z", + "updatedAt": "2024-01-15T10:30:00Z" + } + } + } + }, + "404": { + "description": "Task not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Task with ID tsk_abc123def456 not found" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Get task by ID", + "tags": [ + "Tasks" + ] + } + }, + "/v1/tasks/{taskId}/attachments": { + "get": { + "description": "Retrieve all attachments for a specific task", + "operationId": "TasksController_getTaskAttachments_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "taskId", + "required": true, + "in": "path", + "description": "Unique task identifier", + "schema": { + "example": "tsk_abc123def456", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Attachments retrieved successfully", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AttachmentResponseDto" + } + }, + "example": [ + { + "id": "att_abc123def456", + "name": "evidence.pdf", + "type": "application/pdf", + "size": 123456, + "downloadUrl": "https://bucket.s3.amazonaws.com/path/to/file.pdf?signature=...", + "createdAt": "2024-01-15T10:30:00Z" + } + ] + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Unauthorized" + } + } + } + } + } + }, + "404": { + "description": "Task not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Task with ID tsk_abc123def456 not found" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Get task attachments", + "tags": [ + "Tasks" + ] + }, + "post": { + "description": "Upload a file attachment to a specific task", + "operationId": "TasksController_uploadTaskAttachment_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "taskId", + "required": true, + "in": "path", + "description": "Unique task identifier", + "schema": { + "example": "tsk_abc123def456", + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UploadAttachmentDto" + } + } + } + }, + "responses": { + "201": { + "description": "Attachment uploaded successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AttachmentResponseDto" + }, + "example": { + "id": "att_abc123def456", + "entityId": "tsk_abc123def456", + "entityType": "task", + "fileName": "evidence.pdf", + "fileType": "application/pdf", + "fileSize": 123456, + "createdAt": "2024-01-01T00:00:00Z", + "createdBy": "usr_abc123def456" + } + } + } + }, + "400": { + "description": "Invalid file data or file too large", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "File exceeds maximum allowed size" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Unauthorized" + } + } + } + } + } + }, + "404": { + "description": "Task not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Task with ID tsk_abc123def456 not found" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Upload attachment to task", + "tags": [ + "Tasks" + ] + } + }, + "/v1/tasks/{taskId}/attachments/{attachmentId}/download": { + "get": { + "description": "Generate a signed URL for downloading a task attachment", + "operationId": "TasksController_getTaskAttachmentDownloadUrl_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "taskId", + "required": true, + "in": "path", + "description": "Unique task identifier", + "schema": { + "example": "tsk_abc123def456", + "type": "string" + } + }, + { + "name": "attachmentId", + "required": true, + "in": "path", + "description": "Unique attachment identifier", + "schema": { + "example": "att_abc123def456", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Download URL generated successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "downloadUrl": { + "type": "string", + "description": "Signed URL for downloading the file", + "example": "https://bucket.s3.amazonaws.com/path/to/file.pdf?signature=..." + }, + "expiresIn": { + "type": "number", + "description": "URL expiration time in seconds", + "example": 900 + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Unauthorized" + } + } + } + } + } + }, + "404": { + "description": "Task or attachment not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Task or attachment not found" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Get attachment download URL", + "tags": [ + "Tasks" + ] + } + }, + "/v1/tasks/{taskId}/attachments/{attachmentId}": { + "delete": { + "description": "Delete a specific attachment from a task", + "operationId": "TasksController_deleteTaskAttachment_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "taskId", + "required": true, + "in": "path", + "description": "Unique task identifier", + "schema": { + "example": "tsk_abc123def456", + "type": "string" + } + }, + { + "name": "attachmentId", + "required": true, + "in": "path", + "description": "Unique attachment identifier", + "schema": { + "example": "att_abc123def456", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Attachment deleted successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "deletedAttachmentId": { + "type": "string", + "example": "att_abc123def456" + }, + "message": { + "type": "string", + "example": "Attachment deleted successfully" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Unauthorized" + } + } + } + } + } + }, + "404": { + "description": "Task or attachment not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Task or attachment not found" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Delete task attachment", + "tags": [ + "Tasks" + ] + } + }, + "/v1/comments": { + "get": { + "description": "Retrieve all comments for a specific entity (task, policy, vendor, etc.)", + "operationId": "CommentsController_getComments_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "entityId", + "required": true, + "in": "query", + "description": "ID of the entity to get comments for", + "schema": { + "example": "tsk_abc123def456", + "type": "string" + } + }, + { + "name": "entityType", + "required": true, + "in": "query", + "description": "Type of entity", + "schema": { + "enum": [ + "task", + "vendor", + "risk", + "policy" + ], + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Comments retrieved successfully", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CommentResponseDto" + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Get comments for an entity", + "tags": [ + "Comments" + ] + }, + "post": { + "description": "Create a comment on an entity with optional file attachments", + "operationId": "CommentsController_createComment_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateCommentDto" + } + } + } + }, + "responses": { + "201": { + "description": "Comment created successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CommentResponseDto" + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Create a new comment", + "tags": [ + "Comments" + ] + } + }, + "/v1/comments/{commentId}": { + "put": { + "description": "Update the content of an existing comment (author only)", + "operationId": "CommentsController_updateComment_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "commentId", + "required": true, + "in": "path", + "description": "Unique comment identifier", + "schema": { + "example": "cmt_abc123def456", + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateCommentDto" + } + } + } + }, + "responses": { + "200": { + "description": "Comment updated successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CommentResponseDto" + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Update a comment", + "tags": [ + "Comments" + ] + }, + "delete": { + "description": "Delete a comment and all its attachments (author only)", + "operationId": "CommentsController_deleteComment_v1", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID (required for session auth, optional for API key auth)", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "commentId", + "required": true, + "in": "path", + "description": "Unique comment identifier", + "schema": { + "example": "cmt_abc123def456", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Comment deleted successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "deletedCommentId": { + "type": "string", + "example": "cmt_abc123def456" + }, + "message": { + "type": "string", + "example": "Comment deleted successfully" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized - Invalid authentication", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Unauthorized" + } + } + } + } + } + }, + "404": { + "description": "Comment not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Comment with ID cmt_abc123def456 not found" + } + } + } + } + } + } + }, + "security": [ + { + "apikey": [] + } + ], + "summary": "Delete a comment", + "tags": [ + "Comments" + ] + } + }, + "/v1/health": { + "get": { + "description": "Returns the health status of the API", + "operationId": "HealthController_getHealth_v1", + "parameters": [], + "responses": { + "200": { + "description": "API is healthy", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "status": { + "type": "string", + "example": "ok" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "uptime": { + "type": "number", + "description": "Process uptime in seconds" + }, + "version": { + "type": "string", + "example": "1.0.0" + } + } + } + } + } + } + }, + "summary": "Health check", + "tags": [ + "Health" + ] + } + } + }, + "info": { + "title": "API Documentation", + "description": "The API documentation for this application", + "version": "1.0", + "contact": {} + }, + "tags": [], + "servers": [ + { + "url": "http://localhost:3333", + "description": "API Server" + } + ], + "components": { + "securitySchemes": { + "apikey": { + "type": "apiKey", + "in": "header", + "name": "X-API-Key", + "description": "API key for authentication" + } + }, + "schemas": { + "UserResponseDto": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "User ID", + "example": "usr_abc123def456" + }, + "name": { + "type": "string", + "description": "User name", + "example": "John Doe" + }, + "email": { + "type": "string", + "description": "User email", + "example": "john.doe@company.com" + }, + "emailVerified": { + "type": "boolean", + "description": "Whether email is verified", + "example": true + }, + "image": { + "type": "object", + "description": "User profile image URL", + "example": "https://example.com/avatar.jpg", + "nullable": true + }, + "createdAt": { + "format": "date-time", + "type": "string", + "description": "When the user was created", + "example": "2024-01-01T00:00:00Z" + }, + "updatedAt": { + "format": "date-time", + "type": "string", + "description": "When the user was last updated", + "example": "2024-01-15T00:00:00Z" + }, + "lastLogin": { + "type": "object", + "description": "Last login time", + "example": "2024-01-15T12:00:00Z", + "nullable": true + } + }, + "required": [ + "id", + "name", + "email", + "emailVerified", + "image", + "createdAt", + "updatedAt", + "lastLogin" + ] + }, + "PeopleResponseDto": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Member ID", + "example": "mem_abc123def456" + }, + "organizationId": { + "type": "string", + "description": "Organization ID this member belongs to", + "example": "org_abc123def456" + }, + "userId": { + "type": "string", + "description": "User ID associated with member", + "example": "usr_abc123def456" + }, + "role": { + "type": "string", + "description": "Member role", + "example": "admin" + }, + "createdAt": { + "format": "date-time", + "type": "string", + "description": "When the member was created", + "example": "2024-01-01T00:00:00Z" + }, + "department": { + "type": "string", + "description": "Member department", + "enum": [ + "none", + "admin", + "gov", + "hr", + "it", + "itsm", + "qms" + ], + "example": "it" + }, + "isActive": { + "type": "boolean", + "description": "Whether member is active", + "example": true + }, + "fleetDmLabelId": { + "type": "object", + "description": "FleetDM label ID for member devices", + "example": 123, + "nullable": true + }, + "user": { + "description": "User information", + "allOf": [ + { + "$ref": "#/components/schemas/UserResponseDto" + } + ] + } + }, + "required": [ + "id", + "organizationId", + "userId", + "role", + "createdAt", + "department", + "isActive", + "fleetDmLabelId", + "user" + ] + }, + "CreatePeopleDto": { + "type": "object", + "properties": { + "userId": { + "type": "string", + "description": "User ID to associate with this member", + "example": "usr_abc123def456" + }, + "role": { + "type": "string", + "description": "Role for the member", + "example": "admin" + }, + "department": { + "type": "string", + "description": "Member department", + "enum": [ + "none", + "admin", + "gov", + "hr", + "it", + "itsm", + "qms" + ], + "example": "it" + }, + "isActive": { + "type": "boolean", + "description": "Whether member is active", + "example": true + }, + "fleetDmLabelId": { + "type": "number", + "description": "FleetDM label ID for member devices", + "example": 123 + } + }, + "required": [ + "userId", + "role" + ] + }, + "BulkCreatePeopleDto": { + "type": "object", + "properties": { + "members": { + "description": "Array of members to create", + "example": [ + { + "userId": "usr_abc123def456", + "role": "admin", + "department": "it", + "isActive": true, + "fleetDmLabelId": 123 + }, + { + "userId": "usr_def456ghi789", + "role": "member", + "department": "hr", + "isActive": true + } + ], + "type": "array", + "items": { + "$ref": "#/components/schemas/CreatePeopleDto" + } + } + }, + "required": [ + "members" + ] + }, + "UpdatePeopleDto": { + "type": "object", + "properties": { + "userId": { + "type": "string", + "description": "User ID to associate with this member", + "example": "usr_abc123def456" + }, + "role": { + "type": "string", + "description": "Role for the member", + "example": "admin" + }, + "department": { + "type": "string", + "description": "Member department", + "enum": [ + "none", + "admin", + "gov", + "hr", + "it", + "itsm", + "qms" + ], + "example": "it" + }, + "isActive": { + "type": "boolean", + "description": "Whether to deactivate this member (soft delete)", + "example": false + }, + "fleetDmLabelId": { + "type": "number", + "description": "FleetDM label ID for member devices", + "example": 123 + } + } + }, + "CreateRiskDto": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "Risk title", + "example": "Data breach vulnerability in user authentication system" + }, + "description": { + "type": "string", + "description": "Detailed description of the risk", + "example": "Weak password requirements could lead to unauthorized access to user accounts" + }, + "category": { + "type": "string", + "description": "Risk category", + "enum": [ + "customer", + "governance", + "operations", + "other", + "people", + "regulatory", + "reporting", + "resilience", + "technology", + "vendor_management" + ], + "example": "technology" + }, + "department": { + "type": "string", + "description": "Department responsible for the risk", + "enum": [ + "none", + "admin", + "gov", + "hr", + "it", + "itsm", + "qms" + ], + "example": "it" + }, + "status": { + "type": "string", + "description": "Current status of the risk", + "enum": [ + "open", + "pending", + "closed", + "archived" + ], + "default": "open", + "example": "open" + }, + "likelihood": { + "type": "string", + "description": "Likelihood of the risk occurring", + "enum": [ + "very_unlikely", + "unlikely", + "possible", + "likely", + "very_likely" + ], + "default": "very_unlikely", + "example": "possible" + }, + "impact": { + "type": "string", + "description": "Impact if the risk materializes", + "enum": [ + "insignificant", + "minor", + "moderate", + "major", + "severe" + ], + "default": "insignificant", + "example": "major" + }, + "residualLikelihood": { + "type": "string", + "description": "Residual likelihood after treatment", + "enum": [ + "very_unlikely", + "unlikely", + "possible", + "likely", + "very_likely" + ], + "default": "very_unlikely", + "example": "unlikely" + }, + "residualImpact": { + "type": "string", + "description": "Residual impact after treatment", + "enum": [ + "insignificant", + "minor", + "moderate", + "major", + "severe" + ], + "default": "insignificant", + "example": "minor" + }, + "treatmentStrategyDescription": { + "type": "string", + "description": "Description of the treatment strategy", + "example": "Implement multi-factor authentication and strengthen password requirements" + }, + "treatmentStrategy": { + "type": "string", + "description": "Risk treatment strategy", + "enum": [ + "accept", + "avoid", + "mitigate", + "transfer" + ], + "default": "accept", + "example": "mitigate" + }, + "assigneeId": { + "type": "string", + "description": "ID of the user assigned to this risk", + "example": "mem_abc123def456" + } + }, + "required": [ + "title", + "description", + "category", + "status", + "likelihood", + "impact", + "residualLikelihood", + "residualImpact", + "treatmentStrategy" + ] + }, + "UpdateRiskDto": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "Risk title", + "example": "Data breach vulnerability in user authentication system" + }, + "description": { + "type": "string", + "description": "Detailed description of the risk", + "example": "Weak password requirements could lead to unauthorized access to user accounts" + }, + "category": { + "type": "string", + "description": "Risk category", + "enum": [ + "customer", + "governance", + "operations", + "other", + "people", + "regulatory", + "reporting", + "resilience", + "technology", + "vendor_management" + ], + "example": "technology" + }, + "department": { + "type": "string", + "description": "Department responsible for the risk", + "enum": [ + "none", + "admin", + "gov", + "hr", + "it", + "itsm", + "qms" + ], + "example": "it" + }, + "status": { + "type": "string", + "description": "Current status of the risk", + "enum": [ + "open", + "pending", + "closed", + "archived" + ], + "default": "open", + "example": "open" + }, + "likelihood": { + "type": "string", + "description": "Likelihood of the risk occurring", + "enum": [ + "very_unlikely", + "unlikely", + "possible", + "likely", + "very_likely" + ], + "default": "very_unlikely", + "example": "possible" + }, + "impact": { + "type": "string", + "description": "Impact if the risk materializes", + "enum": [ + "insignificant", + "minor", + "moderate", + "major", + "severe" + ], + "default": "insignificant", + "example": "major" + }, + "residualLikelihood": { + "type": "string", + "description": "Residual likelihood after treatment", + "enum": [ + "very_unlikely", + "unlikely", + "possible", + "likely", + "very_likely" + ], + "default": "very_unlikely", + "example": "unlikely" + }, + "residualImpact": { + "type": "string", + "description": "Residual impact after treatment", + "enum": [ + "insignificant", + "minor", + "moderate", + "major", + "severe" + ], + "default": "insignificant", + "example": "minor" + }, + "treatmentStrategyDescription": { + "type": "string", + "description": "Description of the treatment strategy", + "example": "Implement multi-factor authentication and strengthen password requirements" + }, + "treatmentStrategy": { + "type": "string", + "description": "Risk treatment strategy", + "enum": [ + "accept", + "avoid", + "mitigate", + "transfer" + ], + "default": "accept", + "example": "mitigate" + }, + "assigneeId": { + "type": "string", + "description": "ID of the user assigned to this risk", + "example": "mem_abc123def456" + } + } + }, + "CreateVendorDto": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Vendor name", + "example": "CloudTech Solutions Inc." + }, + "description": { + "type": "string", + "description": "Detailed description of the vendor and services provided", + "example": "Cloud infrastructure provider offering AWS-like services including compute, storage, and networking solutions for enterprise customers." + }, + "category": { + "type": "string", + "description": "Vendor category", + "enum": [ + "cloud", + "infrastructure", + "software_as_a_service", + "finance", + "marketing", + "sales", + "hr", + "other" + ], + "default": "other", + "example": "cloud" + }, + "status": { + "type": "string", + "description": "Assessment status of the vendor", + "enum": [ + "not_assessed", + "in_progress", + "assessed" + ], + "default": "not_assessed", + "example": "not_assessed" + }, + "inherentProbability": { + "type": "string", + "description": "Inherent probability of risk before controls", + "enum": [ + "very_unlikely", + "unlikely", + "possible", + "likely", + "very_likely" + ], + "default": "very_unlikely", + "example": "possible" + }, + "inherentImpact": { + "type": "string", + "description": "Inherent impact of risk before controls", + "enum": [ + "insignificant", + "minor", + "moderate", + "major", + "severe" + ], + "default": "insignificant", + "example": "moderate" + }, + "residualProbability": { + "type": "string", + "description": "Residual probability after controls are applied", + "enum": [ + "very_unlikely", + "unlikely", + "possible", + "likely", + "very_likely" + ], + "default": "very_unlikely", + "example": "unlikely" + }, + "residualImpact": { + "type": "string", + "description": "Residual impact after controls are applied", + "enum": [ + "insignificant", + "minor", + "moderate", + "major", + "severe" + ], + "default": "insignificant", + "example": "minor" + }, + "website": { + "type": "string", + "description": "Vendor website URL", + "example": "https://www.cloudtechsolutions.com" + }, + "assigneeId": { + "type": "string", + "description": "ID of the user assigned to manage this vendor", + "example": "mem_abc123def456" + } + }, + "required": [ + "name", + "description", + "category", + "status", + "inherentProbability", + "inherentImpact", + "residualProbability", + "residualImpact" + ] + }, + "UpdateVendorDto": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Vendor name", + "example": "CloudTech Solutions Inc." + }, + "description": { + "type": "string", + "description": "Detailed description of the vendor and services provided", + "example": "Cloud infrastructure provider offering AWS-like services including compute, storage, and networking solutions for enterprise customers." + }, + "category": { + "type": "string", + "description": "Vendor category", + "enum": [ + "cloud", + "infrastructure", + "software_as_a_service", + "finance", + "marketing", + "sales", + "hr", + "other" + ], + "default": "other", + "example": "cloud" + }, + "status": { + "type": "string", + "description": "Assessment status of the vendor", + "enum": [ + "not_assessed", + "in_progress", + "assessed" + ], + "default": "not_assessed", + "example": "not_assessed" + }, + "inherentProbability": { + "type": "string", + "description": "Inherent probability of risk before controls", + "enum": [ + "very_unlikely", + "unlikely", + "possible", + "likely", + "very_likely" + ], + "default": "very_unlikely", + "example": "possible" + }, + "inherentImpact": { + "type": "string", + "description": "Inherent impact of risk before controls", + "enum": [ + "insignificant", + "minor", + "moderate", + "major", + "severe" + ], + "default": "insignificant", + "example": "moderate" + }, + "residualProbability": { + "type": "string", + "description": "Residual probability after controls are applied", + "enum": [ + "very_unlikely", + "unlikely", + "possible", + "likely", + "very_likely" + ], + "default": "very_unlikely", + "example": "unlikely" + }, + "residualImpact": { + "type": "string", + "description": "Residual impact after controls are applied", + "enum": [ + "insignificant", + "minor", + "moderate", + "major", + "severe" + ], + "default": "insignificant", + "example": "minor" + }, + "website": { + "type": "string", + "description": "Vendor website URL", + "example": "https://www.cloudtechsolutions.com" + }, + "assigneeId": { + "type": "string", + "description": "ID of the user assigned to manage this vendor", + "example": "mem_abc123def456" + } + } + }, + "CreateContextDto": { + "type": "object", + "properties": { + "question": { + "type": "string", + "description": "The question or topic this context entry addresses", + "example": "How do we handle user authentication in our application?" + }, + "answer": { + "type": "string", + "description": "The answer or detailed explanation for the question", + "example": "We use a hybrid authentication system supporting both API keys and session-based authentication. API keys are used for programmatic access while sessions are used for web interface interactions." + }, + "tags": { + "description": "Tags to categorize and help search this context entry", + "example": [ + "authentication", + "security", + "api", + "sessions" + ], + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "question", + "answer" + ] + }, + "UpdateContextDto": { + "type": "object", + "properties": { + "question": { + "type": "string", + "description": "The question or topic this context entry addresses", + "example": "How do we handle user authentication in our application?" + }, + "answer": { + "type": "string", + "description": "The answer or detailed explanation for the question", + "example": "We use a hybrid authentication system supporting both API keys and session-based authentication. API keys are used for programmatic access while sessions are used for web interface interactions." + }, + "tags": { + "description": "Tags to categorize and help search this context entry", + "example": [ + "authentication", + "security", + "api", + "sessions" + ], + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "FleetPolicyDto": { + "type": "object", + "properties": { + "id": { + "type": "number", + "description": "Policy ID", + "example": 123 + }, + "name": { + "type": "string", + "description": "Policy name", + "example": "Password Policy" + }, + "query": { + "type": "string", + "description": "Policy query", + "example": "SELECT * FROM users;" + }, + "critical": { + "type": "boolean", + "description": "Whether policy is critical", + "example": true + }, + "description": { + "type": "string", + "description": "Policy description", + "example": "Ensures strong passwords" + }, + "author_id": { + "type": "number", + "description": "Author ID", + "example": 456 + }, + "author_name": { + "type": "string", + "description": "Author name", + "example": "John Doe" + }, + "author_email": { + "type": "string", + "description": "Author email", + "example": "john@example.com" + }, + "team_id": { + "type": "object", + "description": "Team ID", + "example": 789, + "nullable": true + }, + "resolution": { + "type": "string", + "description": "Policy resolution", + "example": "Update password settings" + }, + "platform": { + "type": "string", + "description": "Platform", + "example": "darwin" + }, + "calendar_events_enabled": { + "type": "boolean", + "description": "Calendar events enabled", + "example": false + }, + "created_at": { + "type": "string", + "description": "Created at", + "example": "2024-01-01T00:00:00Z" + }, + "updated_at": { + "type": "string", + "description": "Updated at", + "example": "2024-01-15T00:00:00Z" + }, + "response": { + "type": "string", + "description": "Policy response", + "example": "compliant" + } + }, + "required": [ + "id", + "name", + "query", + "critical", + "description", + "author_id", + "author_name", + "author_email", + "team_id", + "resolution", + "platform", + "calendar_events_enabled", + "created_at", + "updated_at", + "response" + ] + }, + "DeviceResponseDto": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "description": "Device created at", + "example": "2024-01-01T00:00:00Z" + }, + "updated_at": { + "type": "string", + "description": "Device updated at", + "example": "2024-01-15T00:00:00Z" + }, + "software": { + "type": "array", + "description": "Software list", + "items": { + "type": "object" + } + }, + "software_updated_at": { + "type": "string", + "description": "Software updated at", + "example": "2024-01-10T00:00:00Z" + }, + "id": { + "type": "number", + "description": "Device ID", + "example": 123 + }, + "detail_updated_at": { + "type": "string", + "description": "Detail updated at", + "example": "2024-01-10T00:00:00Z" + }, + "label_updated_at": { + "type": "string", + "description": "Label updated at", + "example": "2024-01-10T00:00:00Z" + }, + "policy_updated_at": { + "type": "string", + "description": "Policy updated at", + "example": "2024-01-10T00:00:00Z" + }, + "last_enrolled_at": { + "type": "string", + "description": "Last enrolled at", + "example": "2024-01-01T00:00:00Z" + }, + "seen_time": { + "type": "string", + "description": "Last seen time", + "example": "2024-01-15T12:00:00Z" + }, + "refetch_requested": { + "type": "boolean", + "description": "Refetch requested", + "example": false + }, + "hostname": { + "type": "string", + "description": "Hostname", + "example": "johns-macbook" + }, + "uuid": { + "type": "string", + "description": "Device UUID", + "example": "abc123def456" + }, + "platform": { + "type": "string", + "description": "Platform", + "example": "darwin" + }, + "osquery_version": { + "type": "string", + "description": "Osquery version", + "example": "5.10.2" + }, + "orbit_version": { + "type": "string", + "description": "Orbit version", + "example": "1.19.0" + }, + "fleet_desktop_version": { + "type": "string", + "description": "Fleet desktop version", + "example": "1.19.0" + }, + "scripts_enabled": { + "type": "boolean", + "description": "Scripts enabled", + "example": true + }, + "os_version": { + "type": "string", + "description": "OS version", + "example": "macOS 14.2.1" + }, + "build": { + "type": "string", + "description": "Build", + "example": "23C71" + }, + "platform_like": { + "type": "string", + "description": "Platform like", + "example": "darwin" + }, + "code_name": { + "type": "string", + "description": "Code name", + "example": "sonoma" + }, + "uptime": { + "type": "number", + "description": "Uptime in seconds", + "example": 86400 + }, + "memory": { + "type": "number", + "description": "Memory in bytes", + "example": 17179869184 + }, + "cpu_type": { + "type": "string", + "description": "CPU type", + "example": "x86_64" + }, + "cpu_subtype": { + "type": "string", + "description": "CPU subtype", + "example": "x86_64h" + }, + "cpu_brand": { + "type": "string", + "description": "CPU brand", + "example": "Intel(R) Core(TM) i7-9750H" + }, + "cpu_physical_cores": { + "type": "number", + "description": "CPU physical cores", + "example": 6 + }, + "cpu_logical_cores": { + "type": "number", + "description": "CPU logical cores", + "example": 12 + }, + "hardware_vendor": { + "type": "string", + "description": "Hardware vendor", + "example": "Apple Inc." + }, + "hardware_model": { + "type": "string", + "description": "Hardware model", + "example": "MacBookPro16,1" + }, + "hardware_version": { + "type": "string", + "description": "Hardware version", + "example": "1.0" + }, + "hardware_serial": { + "type": "string", + "description": "Hardware serial", + "example": "C02XW0AAJGH6" + }, + "computer_name": { + "type": "string", + "description": "Computer name", + "example": "John's MacBook Pro" + }, + "public_ip": { + "type": "string", + "description": "Public IP", + "example": "203.0.113.1" + }, + "primary_ip": { + "type": "string", + "description": "Primary IP", + "example": "192.168.1.100" + }, + "primary_mac": { + "type": "string", + "description": "Primary MAC", + "example": "00:11:22:33:44:55" + }, + "distributed_interval": { + "type": "number", + "description": "Distributed interval", + "example": 10 + }, + "config_tls_refresh": { + "type": "number", + "description": "Config TLS refresh", + "example": 3600 + }, + "logger_tls_period": { + "type": "number", + "description": "Logger TLS period", + "example": 300 + }, + "team_id": { + "type": "object", + "description": "Team ID", + "example": 1, + "nullable": true + }, + "pack_stats": { + "type": "array", + "description": "Pack stats", + "items": { + "type": "object" + } + }, + "team_name": { + "type": "object", + "description": "Team name", + "example": "Engineering", + "nullable": true + }, + "users": { + "type": "array", + "description": "Users", + "items": { + "type": "object" + } + }, + "gigs_disk_space_available": { + "type": "number", + "description": "Disk space available in GB", + "example": 250.5 + }, + "percent_disk_space_available": { + "type": "number", + "description": "Percent disk space available", + "example": 75.2 + }, + "gigs_total_disk_space": { + "type": "number", + "description": "Total disk space in GB", + "example": 500 + }, + "disk_encryption_enabled": { + "type": "boolean", + "description": "Disk encryption enabled", + "example": true + }, + "issues": { + "type": "object", + "description": "Issues", + "additionalProperties": true + }, + "mdm": { + "type": "object", + "description": "MDM info", + "additionalProperties": true + }, + "refetch_critical_queries_until": { + "type": "object", + "description": "Refetch critical queries until", + "example": "2024-01-20T00:00:00Z", + "nullable": true + }, + "last_restarted_at": { + "type": "string", + "description": "Last restarted at", + "example": "2024-01-10T08:00:00Z" + }, + "policies": { + "description": "Policies", + "type": "array", + "items": { + "$ref": "#/components/schemas/FleetPolicyDto" + } + }, + "labels": { + "type": "array", + "description": "Labels", + "items": { + "type": "object" + } + }, + "packs": { + "type": "array", + "description": "Packs", + "items": { + "type": "object" + } + }, + "batteries": { + "type": "array", + "description": "Batteries", + "items": { + "type": "object" + } + }, + "end_users": { + "type": "array", + "description": "End users", + "items": { + "type": "object" + } + }, + "last_mdm_enrolled_at": { + "type": "string", + "description": "Last MDM enrolled at", + "example": "2024-01-01T00:00:00Z" + }, + "last_mdm_checked_in_at": { + "type": "string", + "description": "Last MDM checked in at", + "example": "2024-01-15T12:00:00Z" + }, + "status": { + "type": "string", + "description": "Device status", + "example": "online" + }, + "display_text": { + "type": "string", + "description": "Display text", + "example": "Johns MacBook Pro" + }, + "display_name": { + "type": "string", + "description": "Display name", + "example": "John's MacBook Pro" + } + }, + "required": [ + "created_at", + "updated_at", + "software", + "software_updated_at", + "id", + "detail_updated_at", + "label_updated_at", + "policy_updated_at", + "last_enrolled_at", + "seen_time", + "refetch_requested", + "hostname", + "uuid", + "platform", + "osquery_version", + "orbit_version", + "fleet_desktop_version", + "scripts_enabled", + "os_version", + "build", + "platform_like", + "code_name", + "uptime", + "memory", + "cpu_type", + "cpu_subtype", + "cpu_brand", + "cpu_physical_cores", + "cpu_logical_cores", + "hardware_vendor", + "hardware_model", + "hardware_version", + "hardware_serial", + "computer_name", + "public_ip", + "primary_ip", + "primary_mac", + "distributed_interval", + "config_tls_refresh", + "logger_tls_period", + "team_id", + "pack_stats", + "team_name", + "users", + "gigs_disk_space_available", + "percent_disk_space_available", + "gigs_total_disk_space", + "disk_encryption_enabled", + "issues", + "mdm", + "refetch_critical_queries_until", + "last_restarted_at", + "policies", + "labels", + "packs", + "batteries", + "end_users", + "last_mdm_enrolled_at", + "last_mdm_checked_in_at", + "status", + "display_text", + "display_name" + ] + }, + "MemberResponseDto": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Member ID", + "example": "mem_abc123def456" + }, + "userId": { + "type": "string", + "description": "User ID associated with member", + "example": "usr_abc123def456" + }, + "role": { + "type": "string", + "description": "Member role", + "example": "admin" + }, + "department": { + "type": "object", + "description": "Member department", + "example": "engineering", + "nullable": true + }, + "isActive": { + "type": "boolean", + "description": "Whether member is active", + "example": true + }, + "fleetDmLabelId": { + "type": "object", + "description": "FleetDM label ID for member devices", + "example": 123, + "nullable": true + }, + "organizationId": { + "type": "string", + "description": "Organization ID this member belongs to", + "example": "org_abc123def456" + }, + "createdAt": { + "format": "date-time", + "type": "string", + "description": "When the member was created", + "example": "2024-01-01T00:00:00Z" + } + }, + "required": [ + "id", + "userId", + "role", + "department", + "isActive", + "fleetDmLabelId", + "organizationId", + "createdAt" + ] + }, + "DevicesByMemberResponseDto": { + "type": "object", + "properties": { + "data": { + "description": "Array of devices assigned to the member", + "type": "array", + "items": { + "$ref": "#/components/schemas/DeviceResponseDto" + } + }, + "count": { + "type": "number", + "description": "Total number of devices for this member", + "example": 3 + }, + "member": { + "description": "Member information", + "allOf": [ + { + "$ref": "#/components/schemas/MemberResponseDto" + } + ] + }, + "authType": { + "type": "string", + "description": "How the request was authenticated", + "enum": [ + "api-key", + "session" + ], + "example": "api-key" + }, + "authenticatedUser": { + "type": "object", + "description": "Authenticated user information (present for session auth)", + "example": { + "id": "usr_abc123def456", + "email": "user@company.com" + } + } + }, + "required": [ + "data", + "count", + "member", + "authType" + ] + }, + "PolicyResponseDto": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The policy ID", + "example": "pol_abc123def456" + }, + "name": { + "type": "string", + "description": "Name of the policy", + "example": "Data Privacy Policy" + }, + "description": { + "type": "string", + "description": "Description of the policy", + "example": "This policy outlines how we handle and protect personal data", + "nullable": true + }, + "status": { + "type": "string", + "description": "Status of the policy", + "enum": [ + "draft", + "published", + "needs_review" + ], + "example": "draft" + }, + "content": { + "type": "array", + "description": "Content of the policy as TipTap JSON (array of nodes)", + "example": [ + { + "type": "heading", + "attrs": { + "level": 2, + "textAlign": null + }, + "content": [ + { + "type": "text", + "text": "Purpose" + } + ] + }, + { + "type": "paragraph", + "attrs": { + "textAlign": null + }, + "content": [ + { + "type": "text", + "text": "Verify workforce integrity and grant the right access at start, revoke at end." + } + ] + } + ], + "items": { + "type": "object", + "additionalProperties": true + } + }, + "frequency": { + "type": "string", + "description": "Review frequency of the policy", + "enum": [ + "monthly", + "quarterly", + "yearly" + ], + "example": "yearly", + "nullable": true + }, + "department": { + "type": "string", + "description": "Department this policy applies to", + "enum": [ + "none", + "admin", + "gov", + "hr", + "it", + "itsm", + "qms" + ], + "example": "it", + "nullable": true + }, + "isRequiredToSign": { + "type": "boolean", + "description": "Whether this policy requires a signature", + "example": true + }, + "signedBy": { + "type": "array", + "description": "List of user IDs who have signed this policy", + "example": [ + "usr_123", + "usr_456" + ], + "items": { + "type": "string" + } + }, + "reviewDate": { + "format": "date-time", + "type": "string", + "description": "Review date for the policy", + "example": "2024-12-31T00:00:00.000Z", + "nullable": true + }, + "isArchived": { + "type": "boolean", + "description": "Whether this policy is archived", + "example": false + }, + "createdAt": { + "format": "date-time", + "type": "string", + "description": "When the policy was created", + "example": "2024-01-01T00:00:00.000Z" + }, + "updatedAt": { + "format": "date-time", + "type": "string", + "description": "When the policy was last updated", + "example": "2024-01-15T00:00:00.000Z" + }, + "lastArchivedAt": { + "format": "date-time", + "type": "string", + "description": "When the policy was last archived", + "example": "2024-02-01T00:00:00.000Z", + "nullable": true + }, + "lastPublishedAt": { + "format": "date-time", + "type": "string", + "description": "When the policy was last published", + "example": "2024-01-10T00:00:00.000Z", + "nullable": true + }, + "organizationId": { + "type": "string", + "description": "Organization ID this policy belongs to", + "example": "org_abc123def456" + }, + "assigneeId": { + "type": "string", + "description": "ID of the user assigned to this policy", + "example": "usr_abc123def456", + "nullable": true + }, + "approverId": { + "type": "string", + "description": "ID of the user who approved this policy", + "example": "usr_xyz789abc123", + "nullable": true + }, + "policyTemplateId": { + "type": "string", + "description": "ID of the policy template this policy is based on", + "example": "plt_template123", + "nullable": true + } + }, + "required": [ + "id", + "name", + "description", + "status", + "content", + "frequency", + "department", + "isRequiredToSign", + "signedBy", + "reviewDate", + "isArchived", + "createdAt", + "updatedAt", + "lastArchivedAt", + "lastPublishedAt", + "organizationId", + "assigneeId", + "approverId", + "policyTemplateId" + ] + }, + "CreatePolicyDto": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Name of the policy", + "example": "Data Privacy Policy" + }, + "description": { + "type": "string", + "description": "Description of the policy", + "example": "This policy outlines how we handle and protect personal data" + }, + "status": { + "type": "string", + "description": "Status of the policy", + "enum": [ + "draft", + "published", + "needs_review" + ], + "example": "draft" + }, + "content": { + "type": "array", + "description": "Content of the policy as TipTap JSON (array of nodes)", + "example": [ + { + "type": "heading", + "attrs": { + "level": 2, + "textAlign": null + }, + "content": [ + { + "type": "text", + "text": "Purpose" + } + ] + }, + { + "type": "paragraph", + "attrs": { + "textAlign": null + }, + "content": [ + { + "type": "text", + "text": "Verify workforce integrity and grant the right access at start, revoke at end." + } + ] + } + ], + "items": { + "type": "object", + "additionalProperties": true + } + }, + "frequency": { + "type": "string", + "description": "Review frequency of the policy", + "enum": [ + "monthly", + "quarterly", + "yearly" + ], + "example": "yearly" + }, + "department": { + "type": "string", + "description": "Department this policy applies to", + "enum": [ + "none", + "admin", + "gov", + "hr", + "it", + "itsm", + "qms" + ], + "example": "it" + }, + "isRequiredToSign": { + "type": "boolean", + "description": "Whether this policy requires a signature", + "example": true + }, + "reviewDate": { + "type": "string", + "description": "Review date for the policy", + "example": "2024-12-31T00:00:00.000Z" + }, + "assigneeId": { + "type": "string", + "description": "ID of the user assigned to this policy", + "example": "usr_abc123def456" + }, + "approverId": { + "type": "string", + "description": "ID of the user who approved this policy", + "example": "usr_xyz789abc123" + }, + "policyTemplateId": { + "type": "string", + "description": "ID of the policy template this policy is based on", + "example": "plt_template123" + }, + "signedBy": { + "type": "array", + "description": "List of user IDs who have signed this policy", + "example": [ + "usr_123", + "usr_456" + ], + "items": { + "type": "string" + } + } + }, + "required": [ + "name", + "content" + ] + }, + "UpdatePolicyDto": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Name of the policy", + "example": "Data Privacy Policy" + }, + "description": { + "type": "string", + "description": "Description of the policy", + "example": "This policy outlines how we handle and protect personal data" + }, + "status": { + "type": "string", + "description": "Status of the policy", + "enum": [ + "draft", + "published", + "needs_review" + ], + "example": "draft" + }, + "content": { + "type": "array", + "description": "Content of the policy as TipTap JSON (array of nodes)", + "example": [ + { + "type": "heading", + "attrs": { + "level": 2, + "textAlign": null + }, + "content": [ + { + "type": "text", + "text": "Purpose" + } + ] + }, + { + "type": "paragraph", + "attrs": { + "textAlign": null + }, + "content": [ + { + "type": "text", + "text": "Verify workforce integrity and grant the right access at start, revoke at end." + } + ] + } + ], + "items": { + "type": "object", + "additionalProperties": true + } + }, + "frequency": { + "type": "string", + "description": "Review frequency of the policy", + "enum": [ + "monthly", + "quarterly", + "yearly" + ], + "example": "yearly" + }, + "department": { + "type": "string", + "description": "Department this policy applies to", + "enum": [ + "none", + "admin", + "gov", + "hr", + "it", + "itsm", + "qms" + ], + "example": "it" + }, + "isRequiredToSign": { + "type": "boolean", + "description": "Whether this policy requires a signature", + "example": true + }, + "reviewDate": { + "type": "string", + "description": "Review date for the policy", + "example": "2024-12-31T00:00:00.000Z" + }, + "assigneeId": { + "type": "string", + "description": "ID of the user assigned to this policy", + "example": "usr_abc123def456" + }, + "approverId": { + "type": "string", + "description": "ID of the user who approved this policy", + "example": "usr_xyz789abc123" + }, + "policyTemplateId": { + "type": "string", + "description": "ID of the policy template this policy is based on", + "example": "plt_template123" + }, + "signedBy": { + "type": "array", + "description": "List of user IDs who have signed this policy", + "example": [ + "usr_123", + "usr_456" + ], + "items": { + "type": "string" + } + }, + "isArchived": { + "type": "boolean", + "description": "Whether to archive this policy", + "example": false + } + } + }, + "TaskResponseDto": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for the task", + "example": "tsk_abc123def456" + }, + "title": { + "type": "string", + "description": "Task title", + "example": "Implement user authentication" + }, + "description": { + "type": "string", + "description": "Task description", + "example": "Add OAuth 2.0 authentication to the platform" + }, + "status": { + "type": "string", + "description": "Task status", + "example": "in_progress", + "enum": [ + "todo", + "in_progress", + "done", + "blocked" + ] + }, + "createdAt": { + "format": "date-time", + "type": "string", + "description": "Task creation timestamp", + "example": "2024-01-15T10:30:00Z" + }, + "updatedAt": { + "format": "date-time", + "type": "string", + "description": "Task last update timestamp", + "example": "2024-01-15T10:30:00Z" + } + }, + "required": [ + "id", + "title", + "status", + "createdAt", + "updatedAt" + ] + }, + "AttachmentResponseDto": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for the attachment", + "example": "att_abc123def456" + }, + "name": { + "type": "string", + "description": "Original filename", + "example": "document.pdf" + }, + "type": { + "type": "string", + "description": "File type/MIME type", + "example": "application/pdf" + }, + "size": { + "type": "number", + "description": "File size in bytes", + "example": 1024000 + }, + "downloadUrl": { + "type": "string", + "description": "Signed URL for downloading the file (temporary)", + "example": "https://bucket.s3.amazonaws.com/path/to/file.pdf?signature=..." + }, + "createdAt": { + "format": "date-time", + "type": "string", + "description": "Upload timestamp", + "example": "2024-01-15T10:30:00Z" + } + }, + "required": [ + "id", + "name", + "type", + "size", + "downloadUrl", + "createdAt" + ] + }, + "UploadAttachmentDto": { + "type": "object", + "properties": { + "fileName": { + "type": "string", + "description": "Name of the file", + "example": "document.pdf", + "maxLength": 255 + }, + "fileType": { + "type": "string", + "description": "MIME type of the file", + "example": "application/pdf", + "enum": [ + "image/jpeg", + "image/png", + "image/gif", + "image/webp", + "application/pdf", + "text/plain", + "application/msword", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document" + ] + }, + "fileData": { + "type": "string", + "description": "Base64 encoded file data", + "example": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==" + }, + "description": { + "type": "string", + "description": "Description of the attachment", + "example": "Meeting notes from Q4 planning session", + "maxLength": 500 + } + }, + "required": [ + "fileName", + "fileType", + "fileData" + ] + }, + "AuthorResponseDto": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "User ID", + "example": "usr_abc123def456" + }, + "name": { + "type": "string", + "description": "User name", + "example": "John Doe" + }, + "email": { + "type": "string", + "description": "User email", + "example": "john.doe@company.com" + } + }, + "required": [ + "id", + "name", + "email" + ] + }, + "AttachmentMetadataDto": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for the attachment", + "example": "att_abc123def456" + }, + "name": { + "type": "string", + "description": "Original filename", + "example": "document.pdf" + }, + "type": { + "type": "string", + "description": "File type/MIME type", + "example": "application/pdf" + }, + "createdAt": { + "format": "date-time", + "type": "string", + "description": "Upload timestamp", + "example": "2024-01-15T10:30:00Z" + } + }, + "required": [ + "id", + "name", + "type", + "createdAt" + ] + }, + "CommentResponseDto": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for the comment", + "example": "cmt_abc123def456" + }, + "content": { + "type": "string", + "description": "Comment content", + "example": "This task needs to be completed by end of week" + }, + "author": { + "description": "Comment author information", + "allOf": [ + { + "$ref": "#/components/schemas/AuthorResponseDto" + } + ] + }, + "attachments": { + "description": "Attachment metadata (URLs generated on-demand)", + "type": "array", + "items": { + "$ref": "#/components/schemas/AttachmentMetadataDto" + } + }, + "createdAt": { + "format": "date-time", + "type": "string", + "description": "Comment creation timestamp", + "example": "2024-01-15T10:30:00Z" + } + }, + "required": [ + "id", + "content", + "author", + "attachments", + "createdAt" + ] + }, + "CreateCommentDto": { + "type": "object", + "properties": { + "content": { + "type": "string", + "description": "Content of the comment", + "example": "This task needs to be completed by end of week", + "maxLength": 2000 + }, + "entityId": { + "type": "string", + "description": "ID of the entity to comment on", + "example": "tsk_abc123def456" + }, + "entityType": { + "type": "string", + "description": "Type of entity being commented on", + "enum": [ + "task", + "vendor", + "risk", + "policy" + ], + "example": "task" + }, + "attachments": { + "description": "Optional attachments to include with the comment", + "type": "array", + "items": { + "$ref": "#/components/schemas/UploadAttachmentDto" + } + } + }, + "required": [ + "content", + "entityId", + "entityType" + ] + }, + "UpdateCommentDto": { + "type": "object", + "properties": { + "content": { + "type": "string", + "description": "Updated content of the comment", + "example": "This task needs to be completed by end of week (updated)", + "maxLength": 2000 + } + }, + "required": [ + "content" + ] + } + } + } +} \ No newline at end of file