From e0ff9ef4420e3170d4a5c8081ef184843fae2dfe Mon Sep 17 00:00:00 2001 From: Mariano Fuentes Date: Wed, 5 Mar 2025 11:42:07 -0500 Subject: [PATCH 1/3] added risk endpoints --- apps/app/src/app/api/v1/risks/[id]/route.ts | 218 ++++++++++++ apps/app/src/app/api/v1/risks/route.ts | 298 ++++++++++++++++ packages/docs/api-reference/v1/index.mdx | 30 +- packages/docs/api-reference/v1/risks.mdx | 367 ++++++++++++++++++++ 4 files changed, 902 insertions(+), 11 deletions(-) create mode 100644 apps/app/src/app/api/v1/risks/[id]/route.ts create mode 100644 apps/app/src/app/api/v1/risks/route.ts create mode 100644 packages/docs/api-reference/v1/risks.mdx diff --git a/apps/app/src/app/api/v1/risks/[id]/route.ts b/apps/app/src/app/api/v1/risks/[id]/route.ts new file mode 100644 index 0000000000..c2ca8c23c4 --- /dev/null +++ b/apps/app/src/app/api/v1/risks/[id]/route.ts @@ -0,0 +1,218 @@ +import { db } from "@bubba/db"; +import { NextResponse, type NextRequest } from "next/server"; +import { getOrganizationFromApiKey } from "@/lib/api-key"; + +// Configure this route to use Node.js runtime instead of Edge +export const runtime = "nodejs"; + +/** + * GET /api/v1/risks/:id + * + * Get a single risk by ID for the organization associated with the API key + * + * Headers: + * - Authorization: Bearer {api_key} or X-API-Key: {api_key} + * + * Path Parameters: + * - id: string - The ID of the risk to fetch + * + * Returns: + * - 200: { success: true, data: Risk } + * - 401: { success: false, error: "Invalid or missing API key" } + * - 404: { success: false, error: "Risk not found" } + * - 500: { success: false, error: "Failed to fetch risk" } + */ +export async function GET( + request: NextRequest, + { params }: { params: { id: string } } +) { + // Get the organization ID from the API key + const { organizationId, errorResponse } = + await getOrganizationFromApiKey(request); + + // If there's an error response, return it + if (errorResponse) { + return errorResponse; + } + + try { + const riskId = params.id; + + // Fetch the risk + const risk = await db.risk.findFirst({ + where: { + id: riskId, + organizationId: organizationId!, + }, + select: { + id: true, + title: true, + description: true, + category: true, + department: true, + status: true, + probability: true, + impact: true, + residual_probability: true, + residual_impact: true, + createdAt: true, + updatedAt: true, + ownerId: true, + owner: { + select: { + id: true, + name: true, + email: true, + }, + }, + treatmentStrategy: { + select: { + id: true, + type: true, + description: true, + createdAt: true, + updatedAt: true, + }, + }, + mitigationTasks: { + select: { + id: true, + title: true, + description: true, + status: true, + dueDate: true, + completedAt: true, + createdAt: true, + updatedAt: true, + ownerId: true, + }, + orderBy: { + createdAt: "desc", + }, + }, + }, + }); + + // If risk not found, return 404 + if (!risk) { + return NextResponse.json( + { + success: false, + error: "Risk not found", + }, + { status: 404 } + ); + } + + // Format dates for JSON response + const formattedRisk = { + ...risk, + createdAt: risk.createdAt.toISOString(), + updatedAt: risk.updatedAt.toISOString(), + treatmentStrategy: risk.treatmentStrategy + ? { + ...risk.treatmentStrategy, + createdAt: risk.treatmentStrategy.createdAt.toISOString(), + updatedAt: risk.treatmentStrategy.updatedAt.toISOString(), + } + : null, + mitigationTasks: risk.mitigationTasks.map((task) => ({ + ...task, + createdAt: task.createdAt.toISOString(), + updatedAt: task.updatedAt.toISOString(), + dueDate: task.dueDate ? task.dueDate.toISOString() : null, + completedAt: task.completedAt ? task.completedAt.toISOString() : null, + })), + }; + + return NextResponse.json({ + success: true, + data: formattedRisk, + }); + } catch (error) { + console.error("Error fetching risk:", error); + return NextResponse.json( + { + success: false, + error: "Failed to fetch risk", + }, + { status: 500 } + ); + } +} + +/** + * DELETE /api/v1/risks/:id + * + * Delete a risk by ID for the organization associated with the API key + * + * Headers: + * - Authorization: Bearer {api_key} or X-API-Key: {api_key} + * + * Path Parameters: + * - id: string - The ID of the risk to delete + * + * Returns: + * - 200: { success: true, data: { message: string } } + * - 401: { success: false, error: string } + * - 404: { success: false, error: string } + * - 500: { success: false, error: string } + */ +export async function DELETE( + request: NextRequest, + { params }: { params: { id: string } } +) { + // Get the organization ID from the API key + const { organizationId, errorResponse } = + await getOrganizationFromApiKey(request); + + // If there's an error response, return it + if (errorResponse) { + return errorResponse; + } + + try { + const riskId = params.id; + + // Check if the risk exists and belongs to the organization + const existingRisk = await db.risk.findFirst({ + where: { + id: riskId, + organizationId: organizationId!, + }, + }); + + if (!existingRisk) { + return NextResponse.json( + { + success: false, + error: "Risk not found", + }, + { status: 404 } + ); + } + + // Delete the risk + await db.risk.delete({ + where: { + id: riskId, + }, + }); + + return NextResponse.json({ + success: true, + data: { + message: "Risk deleted successfully", + }, + }); + } catch (error) { + console.error("Error deleting risk:", error); + return NextResponse.json( + { + success: false, + error: "Failed to delete risk", + }, + { status: 500 } + ); + } +} diff --git a/apps/app/src/app/api/v1/risks/route.ts b/apps/app/src/app/api/v1/risks/route.ts new file mode 100644 index 0000000000..728958b4f2 --- /dev/null +++ b/apps/app/src/app/api/v1/risks/route.ts @@ -0,0 +1,298 @@ +import { db } from "@bubba/db"; +import { NextResponse, type NextRequest } from "next/server"; +import { getOrganizationFromApiKey } from "@/lib/api-key"; +import { z } from "zod"; + +// Configure this route to use Node.js runtime instead of Edge +export const runtime = "nodejs"; + +// Define the schema for query parameters +const queryParamsSchema = z.object({ + status: z.enum(["open", "pending", "closed", "archived"]).optional(), + category: z + .enum([ + "customer", + "governance", + "operations", + "other", + "people", + "regulatory", + "reporting", + "resilience", + "technology", + "vendor_management", + ]) + .optional(), + department: z + .enum(["none", "admin", "gov", "hr", "it", "itsm", "qms"]) + .optional(), + search: z.string().optional(), +}); + +// Define the schema for risk creation +const riskCreateSchema = z.object({ + title: z.string().min(1, { message: "Title is required" }), + description: z.string().min(1, { message: "Description is required" }), + category: z.enum([ + "customer", + "governance", + "operations", + "other", + "people", + "regulatory", + "reporting", + "resilience", + "technology", + "vendor_management", + ]), + department: z + .enum(["none", "admin", "gov", "hr", "it", "itsm", "qms"]) + .optional(), + status: z + .enum(["open", "pending", "closed", "archived"]) + .optional() + .default("open"), + probability: z.number().min(0).max(10).optional().default(0), + impact: z.number().min(0).max(10).optional().default(0), + residual_probability: z.number().min(0).max(10).optional().default(0), + residual_impact: z.number().min(0).max(10).optional().default(0), + ownerId: z.string().optional().nullable(), +}); + +// Type for the validated query parameters +type QueryParams = z.infer; + +// Type for the validated risk creation data +type RiskCreateInput = z.infer; + +/** + * GET /api/v1/risks + * + * Get all risks for the organization associated with the API key + * + * Headers: + * - Authorization: Bearer {api_key} or X-API-Key: {api_key} + * + * Query Parameters: + * - status: string - Filter by risk status (optional) + * - category: string - Filter by risk category (optional) + * - department: string - Filter by department (optional) + * - search: string - Search by title (optional) + * + * Returns: + * - 200: { success: true, data: Risk[] } + * - 401: { error: "Invalid or missing API key" } + * - 400: { error: "Validation failed", details: {...} } + * - 500: { error: "Failed to fetch risks" } + */ +export async function GET(request: NextRequest) { + // Get the organization ID from the API key + const { organizationId, errorResponse } = + await getOrganizationFromApiKey(request); + + // If there's an error response, return it + if (errorResponse) { + return errorResponse; + } + + try { + // Get query parameters + const searchParams = request.nextUrl.searchParams; + + // Create an object from the search params + const queryParamsObj = { + status: searchParams.get("status") || undefined, + category: searchParams.get("category") || undefined, + department: searchParams.get("department") || undefined, + search: searchParams.get("search") || undefined, + }; + + // Validate query parameters + const validationResult = queryParamsSchema.safeParse(queryParamsObj); + + if (!validationResult.success) { + return NextResponse.json( + { + success: false, + error: "Validation failed", + details: validationResult.error.format(), + }, + { status: 400 } + ); + } + + // Extract validated query parameters + const { status, category, department, search } = validationResult.data; + + // Build the where clause + const where: any = { + organizationId: organizationId!, + }; + + // Add status filter if provided + if (status) { + where.status = status; + } + + // Add category filter if provided + if (category) { + where.category = category; + } + + // Add department filter if provided + if (department) { + where.department = department; + } + + // Add search filter if provided + if (search) { + where.OR = [ + { + title: { + contains: search, + mode: "insensitive", + }, + }, + { + description: { + contains: search, + mode: "insensitive", + }, + }, + ]; + } + + // Fetch risks + const risks = await db.risk.findMany({ + where, + select: { + id: true, + title: true, + description: true, + category: true, + department: true, + status: true, + probability: true, + impact: true, + residual_probability: true, + residual_impact: true, + createdAt: true, + updatedAt: true, + ownerId: true, + owner: { + select: { + id: true, + name: true, + email: true, + }, + }, + }, + orderBy: { + updatedAt: "desc", + }, + }); + + // Format dates for JSON response + const formattedRisks = risks.map((risk) => ({ + ...risk, + createdAt: risk.createdAt.toISOString(), + updatedAt: risk.updatedAt.toISOString(), + })); + + return NextResponse.json({ success: true, data: formattedRisks }); + } catch (error) { + console.error("Error fetching risks:", error); + return NextResponse.json( + { success: false, error: "Failed to fetch risks" }, + { status: 500 } + ); + } +} + +/** + * POST /api/v1/risks + * + * Create a new risk for the organization associated with the API key + * + * Headers: + * - Authorization: Bearer {api_key} or X-API-Key: {api_key} + * + * Body: + * - title: string - The title of the risk (required) + * - description: string - The description of the risk (required) + * - category: RiskCategory - The category of the risk (required) + * - department: Departments - The department associated with the risk (optional) + * - status: RiskStatus - The status of the risk (optional, defaults to "open") + * - probability: number - The probability score (0-10) (optional, defaults to 0) + * - impact: number - The impact score (0-10) (optional, defaults to 0) + * - residual_probability: number - The residual probability score (0-10) (optional, defaults to 0) + * - residual_impact: number - The residual impact score (0-10) (optional, defaults to 0) + * - ownerId: string - The ID of the user who owns the risk (optional) + * + * Returns: + * - 200: { success: true, data: Risk } + * - 400: { success: false, error: "Validation failed", details: {...} } + * - 401: { success: false, error: "Invalid or missing API key" } + * - 500: { success: false, error: "Failed to create risk" } + */ +export async function POST(request: NextRequest) { + // Get the organization ID from the API key + const { organizationId, errorResponse } = + await getOrganizationFromApiKey(request); + + // If there's an error response, return it + if (errorResponse) { + return errorResponse; + } + + try { + const body = await request.json(); + + // Validate the request body against the schema + const validationResult = riskCreateSchema.safeParse(body); + + if (!validationResult.success) { + // Return validation errors + return NextResponse.json( + { + success: false, + error: "Validation failed", + details: validationResult.error.format(), + }, + { status: 400 } + ); + } + + // Extract validated data + const validatedData: RiskCreateInput = validationResult.data; + + // Create the risk using the organization ID from the API key + const risk = await db.risk.create({ + data: { + ...validatedData, + organizationId: organizationId!, + }, + }); + + // Format dates for JSON response + const formattedRisk = { + ...risk, + createdAt: risk.createdAt.toISOString(), + updatedAt: risk.updatedAt.toISOString(), + }; + + return NextResponse.json({ + success: true, + data: formattedRisk, + }); + } catch (error) { + console.error("Error creating risk:", error); + return NextResponse.json( + { + success: false, + error: "Failed to create risk", + }, + { status: 500 } + ); + } +} diff --git a/packages/docs/api-reference/v1/index.mdx b/packages/docs/api-reference/v1/index.mdx index 1772fd4bdc..3c18d4bec7 100644 --- a/packages/docs/api-reference/v1/index.mdx +++ b/packages/docs/api-reference/v1/index.mdx @@ -1,6 +1,6 @@ --- -title: 'Overview' -description: 'Comp AI API v1 Reference' +title: "Overview" +description: "Comp AI API v1 Reference" --- # Comp AI API v1 @@ -12,6 +12,7 @@ Welcome to the Comp AI API v1 reference documentation. This section provides det Most API endpoints require authentication using an API key. You can manage your API keys in the [Settings > API Keys](https://app.trycomp.ai/settings/api-keys) section of the dashboard. API keys can be passed in one of two ways: + - In the `Authorization` header as a Bearer token: `Authorization: Bearer {api_key}` - In the `X-API-Key` header: `X-API-Key: {api_key}` @@ -29,6 +30,13 @@ or, if you self-host: https://app.yourdomain.com/v1/employees ``` +## Available Endpoints + +The API provides access to the following resources: + +- [Employees](/api-reference/v1/employees) - Manage employees in your organization +- [Risks](/api-reference/v1/risks) - Manage risks and their mitigations + ## Response Format All API responses follow a consistent JSON format: @@ -46,15 +54,15 @@ All API responses follow a consistent JSON format: Common error codes you might encounter: -| Status Code | Description | -|-------------|-------------| -| 400 | Bad Request - Often due to missing or invalid parameters | -| 401 | Unauthorized - Invalid or missing API key | -| 403 | Forbidden - Valid API key but insufficient permissions | -| 404 | Not Found - The requested resource doesn't exist | -| 429 | Too Many Requests - Rate limit exceeded | -| 500 | Internal Server Error - Something went wrong on our end | +| Status Code | Description | +| ----------- | -------------------------------------------------------- | +| 400 | Bad Request - Often due to missing or invalid parameters | +| 401 | Unauthorized - Invalid or missing API key | +| 403 | Forbidden - Valid API key but insufficient permissions | +| 404 | Not Found - The requested resource doesn't exist | +| 429 | Too Many Requests - Rate limit exceeded | +| 500 | Internal Server Error - Something went wrong on our end | ## Need Help? -If you have questions or need assistance with the API, please contact our support team at [support@trycomp.ai](mailto:hello@trycomp.ai) or join our [Discord community](https://discord.gg/compai). \ No newline at end of file +If you have questions or need assistance with the API, please contact our support team at [support@trycomp.ai](mailto:hello@trycomp.ai) or join our [Discord community](https://discord.gg/compai). diff --git a/packages/docs/api-reference/v1/risks.mdx b/packages/docs/api-reference/v1/risks.mdx new file mode 100644 index 0000000000..6a90ecf26e --- /dev/null +++ b/packages/docs/api-reference/v1/risks.mdx @@ -0,0 +1,367 @@ +--- +title: "Risks" +--- + +# Risks + +The Risks endpoints allow you to list risks, create new risks, retrieve individual risks, and delete existing risks. Risks represent potential threats to your organization that need to be tracked, assessed, and mitigated. + +## Authentication + +All endpoints require authentication using an API key. You can provide the API key in one of two ways: + +1. **Bearer token** in the `Authorization` header: + +``` +Authorization: Bearer your_api_key +``` + +2. Using the `X-API-Key` header: + +``` +X-API-Key: your_api_key +``` + +## Endpoints + +- `GET /api/v1/risks` - List all risks with optional filtering +- `POST /api/v1/risks` - Create a new risk +- `GET /api/v1/risks/:id` - Get a risk by ID +- `DELETE /api/v1/risks/:id` - Delete a risk by ID + +## List Risks + +``` +GET /api/v1/risks +``` + +Get all risks for the organization associated with the API key. + +### Query Parameters + +| Parameter | Type | Description | +| ---------- | ------ | ---------------------------------------------------------------------------------------- | +| status | string | Filter by risk status. Possible values: `open`, `pending`, `closed`, `archived` | +| category | string | Filter by risk category (e.g., `technology`, `operations`, `regulatory`, etc.) | +| department | string | Filter by department. Possible values: `none`, `admin`, `gov`, `hr`, `it`, `itsm`, `qms` | +| search | string | Search by title or description | + +### Response + +```json +{ + "success": true, + "data": [ + { + "id": "clc123abc456", + "title": "Server vulnerability", + "description": "Outdated software on production servers", + "category": "technology", + "department": "it", + "status": "open", + "probability": 7, + "impact": 8, + "residual_probability": 3, + "residual_impact": 5, + "createdAt": "2023-01-15T12:00:00.000Z", + "updatedAt": "2023-01-16T09:30:00.000Z", + "ownerId": "u123abc456", + "owner": { + "id": "u123abc456", + "name": "John Doe", + "email": "john.doe@example.com" + } + } + // More risks... + ] +} +``` + +### Error Responses + +**Invalid API key (401):** + +```json +{ + "success": false, + "error": "Invalid or missing API key" +} +``` + +**Validation Error (400):** + +```json +{ + "success": false, + "error": "Validation failed", + "details": { + "status": { + "_errors": [ + "Invalid enum value. Expected 'open' | 'pending' | 'closed' | 'archived'" + ] + } + } +} +``` + +**Internal Error (500):** + +```json +{ + "success": false, + "error": "Failed to fetch risks" +} +``` + +## Create Risk + +``` +POST /api/v1/risks +``` + +Create a new risk for the organization associated with the API key. + +### Request Body + +| Field | Type | Description | Required | +| -------------------- | ------ | ------------------------------------------------------------------------ | -------- | +| title | string | The title of the risk | Yes | +| description | string | A detailed description of the risk | Yes | +| category | string | The risk category (e.g., `technology`, `operations`, `regulatory`, etc.) | Yes | +| department | string | The department associated with the risk | No | +| status | string | The risk status. Default: `open` | No | +| probability | number | The initial probability score (0-10). Default: 0 | No | +| impact | number | The initial impact score (0-10). Default: 0 | No | +| residual_probability | number | The residual probability score after mitigation (0-10). Default: 0 | No | +| residual_impact | number | The residual impact score after mitigation (0-10). Default: 0 | No | +| ownerId | string | The ID of the user who owns the risk | No | + +#### Example Request + +```json +{ + "title": "Server vulnerability", + "description": "Outdated software on production servers", + "category": "technology", + "department": "it", + "probability": 7, + "impact": 8 +} +``` + +### Response + +```json +{ + "success": true, + "data": { + "id": "clc123abc456", + "title": "Server vulnerability", + "description": "Outdated software on production servers", + "category": "technology", + "department": "it", + "status": "open", + "probability": 7, + "impact": 8, + "residual_probability": 0, + "residual_impact": 0, + "createdAt": "2023-01-15T12:00:00.000Z", + "updatedAt": "2023-01-15T12:00:00.000Z", + "ownerId": null, + "organizationId": "org123abc456" + } +} +``` + +### Error Responses + +**Invalid API key (401):** + +```json +{ + "success": false, + "error": "Invalid or missing API key" +} +``` + +**Validation Error (400):** + +```json +{ + "success": false, + "error": "Validation failed", + "details": { + "title": { + "_errors": ["Title is required"] + }, + "category": { + "_errors": ["Invalid enum value"] + } + } +} +``` + +**Internal Error (500):** + +```json +{ + "success": false, + "error": "Failed to create risk" +} +``` + +## Get Risk + +``` +GET /api/v1/risks/:id +``` + +Get a single risk by ID for the organization associated with the API key. + +### Path Parameters + +| Parameter | Type | Description | +| --------- | ------ | ------------------------- | +| id | string | The ID of the risk to get | + +### Response + +```json +{ + "success": true, + "data": { + "id": "clc123abc456", + "title": "Server vulnerability", + "description": "Outdated software on production servers", + "category": "technology", + "department": "it", + "status": "open", + "probability": 7, + "impact": 8, + "residual_probability": 3, + "residual_impact": 5, + "createdAt": "2023-01-15T12:00:00.000Z", + "updatedAt": "2023-01-16T09:30:00.000Z", + "ownerId": "u123abc456", + "owner": { + "id": "u123abc456", + "name": "John Doe", + "email": "john.doe@example.com" + }, + "treatmentStrategy": { + "id": "trt123abc456", + "type": "mitigate", + "description": "Update software and implement regular updates", + "createdAt": "2023-01-15T14:00:00.000Z", + "updatedAt": "2023-01-15T14:00:00.000Z" + }, + "mitigationTasks": [ + { + "id": "tsk123abc456", + "title": "Update server software", + "description": "Update all production servers to the latest stable version", + "status": "open", + "dueDate": "2023-02-15T00:00:00.000Z", + "completedAt": null, + "createdAt": "2023-01-15T14:10:00.000Z", + "updatedAt": "2023-01-15T14:10:00.000Z", + "ownerId": "u123abc456" + }, + { + "id": "tsk789xyz123", + "title": "Implement automatic updates", + "description": "Set up automatic security updates for all servers", + "status": "open", + "dueDate": "2023-03-01T00:00:00.000Z", + "completedAt": null, + "createdAt": "2023-01-15T14:15:00.000Z", + "updatedAt": "2023-01-15T14:15:00.000Z", + "ownerId": "u123abc456" + } + ] + } +} +``` + +### Error Responses + +**Invalid API key (401):** + +```json +{ + "success": false, + "error": "Invalid or missing API key" +} +``` + +**Risk Not Found (404):** + +```json +{ + "success": false, + "error": "Risk not found" +} +``` + +**Internal Error (500):** + +```json +{ + "success": false, + "error": "Failed to fetch risk" +} +``` + +## Delete Risk + +``` +DELETE /api/v1/risks/:id +``` + +Delete a risk by ID for the organization associated with the API key. + +### Path Parameters + +| Parameter | Type | Description | +| --------- | ------ | ---------------------------- | +| id | string | The ID of the risk to delete | + +### Response + +```json +{ + "success": true, + "data": { + "message": "Risk deleted successfully" + } +} +``` + +### Error Responses + +**Invalid API key (401):** + +```json +{ + "success": false, + "error": "Invalid or missing API key" +} +``` + +**Risk Not Found (404):** + +```json +{ + "success": false, + "error": "Risk not found" +} +``` + +**Internal Error (500):** + +```json +{ + "success": false, + "error": "Failed to delete risk" +} +``` From 86b1a3b2a9bc89d85c48224dd156ea7bc5678fa1 Mon Sep 17 00:00:00 2001 From: Mariano Fuentes Date: Wed, 5 Mar 2025 11:46:46 -0500 Subject: [PATCH 2/3] use native enums instead of hardcoded --- apps/app/src/app/api/v1/risks/route.ts | 48 ++++---------------------- 1 file changed, 7 insertions(+), 41 deletions(-) diff --git a/apps/app/src/app/api/v1/risks/route.ts b/apps/app/src/app/api/v1/risks/route.ts index 728958b4f2..a594f5ca48 100644 --- a/apps/app/src/app/api/v1/risks/route.ts +++ b/apps/app/src/app/api/v1/risks/route.ts @@ -1,4 +1,4 @@ -import { db } from "@bubba/db"; +import { db, Departments, RiskCategory, RiskStatus } from "@bubba/db"; import { NextResponse, type NextRequest } from "next/server"; import { getOrganizationFromApiKey } from "@/lib/api-key"; import { z } from "zod"; @@ -8,24 +8,9 @@ export const runtime = "nodejs"; // Define the schema for query parameters const queryParamsSchema = z.object({ - status: z.enum(["open", "pending", "closed", "archived"]).optional(), - category: z - .enum([ - "customer", - "governance", - "operations", - "other", - "people", - "regulatory", - "reporting", - "resilience", - "technology", - "vendor_management", - ]) - .optional(), - department: z - .enum(["none", "admin", "gov", "hr", "it", "itsm", "qms"]) - .optional(), + status: z.nativeEnum(RiskStatus).optional(), + category: z.nativeEnum(RiskCategory).optional(), + department: z.nativeEnum(Departments).optional(), search: z.string().optional(), }); @@ -33,25 +18,9 @@ const queryParamsSchema = z.object({ const riskCreateSchema = z.object({ title: z.string().min(1, { message: "Title is required" }), description: z.string().min(1, { message: "Description is required" }), - category: z.enum([ - "customer", - "governance", - "operations", - "other", - "people", - "regulatory", - "reporting", - "resilience", - "technology", - "vendor_management", - ]), - department: z - .enum(["none", "admin", "gov", "hr", "it", "itsm", "qms"]) - .optional(), - status: z - .enum(["open", "pending", "closed", "archived"]) - .optional() - .default("open"), + category: z.nativeEnum(RiskCategory), + department: z.nativeEnum(Departments).optional(), + status: z.nativeEnum(RiskStatus).optional().default(RiskStatus.open), probability: z.number().min(0).max(10).optional().default(0), impact: z.number().min(0).max(10).optional().default(0), residual_probability: z.number().min(0).max(10).optional().default(0), @@ -59,9 +28,6 @@ const riskCreateSchema = z.object({ ownerId: z.string().optional().nullable(), }); -// Type for the validated query parameters -type QueryParams = z.infer; - // Type for the validated risk creation data type RiskCreateInput = z.infer; From 0051a4e9ff397927ca5bda4137adee3f66739734 Mon Sep 17 00:00:00 2001 From: Mariano Fuentes Date: Wed, 5 Mar 2025 11:52:25 -0500 Subject: [PATCH 3/3] fix: Update risk API route params type to support async params --- apps/app/src/app/api/v1/risks/[id]/route.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/app/src/app/api/v1/risks/[id]/route.ts b/apps/app/src/app/api/v1/risks/[id]/route.ts index c2ca8c23c4..35da9e6983 100644 --- a/apps/app/src/app/api/v1/risks/[id]/route.ts +++ b/apps/app/src/app/api/v1/risks/[id]/route.ts @@ -24,7 +24,7 @@ export const runtime = "nodejs"; */ export async function GET( request: NextRequest, - { params }: { params: { id: string } } + { params }: { params: Promise<{ id: string }> } ) { // Get the organization ID from the API key const { organizationId, errorResponse } = @@ -36,7 +36,7 @@ export async function GET( } try { - const riskId = params.id; + const riskId = (await params).id; // Fetch the risk const risk = await db.risk.findFirst({ @@ -160,7 +160,7 @@ export async function GET( */ export async function DELETE( request: NextRequest, - { params }: { params: { id: string } } + { params }: { params: Promise<{ id: string }> } ) { // Get the organization ID from the API key const { organizationId, errorResponse } = @@ -172,7 +172,7 @@ export async function DELETE( } try { - const riskId = params.id; + const riskId = (await params).id; // Check if the risk exists and belongs to the organization const existingRisk = await db.risk.findFirst({