-
Notifications
You must be signed in to change notification settings - Fork 426
Add statuses and keep track of policies across controls #64
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e338048
b7d4a76
92cfe09
0250fad
271aa61
2fbc3f6
f6d986e
117c81b
eaf519b
0686504
f0e8804
197f0a7
1399996
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| "use server"; | ||
|
|
||
| import { authActionClient } from "@/actions/safe-action"; | ||
| import { db } from "@bubba/db"; | ||
| import { z } from "zod"; | ||
|
|
||
| export interface ControlProgressResponse { | ||
| total: number; | ||
| completed: number; | ||
| progress: number; | ||
| byType: { | ||
| [key: string]: { | ||
| total: number; | ||
| completed: number; | ||
| }; | ||
| }; | ||
| } | ||
|
|
||
| export const getOrganizationControlProgress = authActionClient | ||
| .schema(z.object({ controlId: z.string() })) | ||
| .metadata({ | ||
| name: "getOrganizationControlProgress", | ||
| track: { | ||
| event: "get-organization-control-progress", | ||
| channel: "server", | ||
| }, | ||
| }) | ||
| .action(async ({ ctx, parsedInput }) => { | ||
| const { user } = ctx; | ||
| const { controlId } = parsedInput; | ||
|
|
||
| if (!user.organizationId) { | ||
| return { | ||
| error: "Not authorized - no organization found", | ||
| }; | ||
| } | ||
|
|
||
| try { | ||
| const requirements = await db.organizationControlRequirement.findMany({ | ||
| where: { | ||
| organizationControlId: controlId, | ||
| }, | ||
| include: { | ||
| organizationPolicy: true, | ||
| }, | ||
| }); | ||
|
|
||
| const progress: ControlProgressResponse = { | ||
| total: requirements.length, | ||
| completed: 0, | ||
| progress: 0, | ||
| byType: {}, | ||
| }; | ||
|
|
||
| for (const requirement of requirements) { | ||
| // Initialize type counters if not exists | ||
| if (!progress.byType[requirement.type]) { | ||
| progress.byType[requirement.type] = { | ||
| total: 0, | ||
| completed: 0, | ||
| }; | ||
| } | ||
|
|
||
| progress.byType[requirement.type].total++; | ||
|
|
||
| // Check completion based on requirement type | ||
| let isCompleted = false; | ||
| switch (requirement.type) { | ||
| case "policy": | ||
| isCompleted = | ||
| requirement.organizationPolicy?.status === "published"; | ||
| break; | ||
| case "file": | ||
| isCompleted = !!requirement.fileUrl; | ||
| break; | ||
| case "evidence": | ||
| isCompleted = !!requirement.content; | ||
| break; | ||
| default: | ||
| isCompleted = requirement.published; | ||
| } | ||
|
|
||
| if (isCompleted) { | ||
| progress.completed++; | ||
| progress.byType[requirement.type].completed++; | ||
| } | ||
| } | ||
|
|
||
| // Calculate overall progress percentage | ||
| progress.progress = | ||
| progress.total > 0 | ||
| ? Math.round((progress.completed / progress.total) * 100) | ||
| : 0; | ||
|
|
||
| return { | ||
| data: { | ||
| progress, | ||
| }, | ||
| }; | ||
| } catch (error) { | ||
| console.error("Error fetching control progress:", error); | ||
| return { | ||
| error: "Failed to fetch control progress", | ||
| }; | ||
| } | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| "use server"; | ||
|
|
||
| import { authActionClient } from "@/actions/safe-action"; | ||
| import type { | ||
| ControlRequirement, | ||
| OrganizationControlRequirement, | ||
| Policy, | ||
| } from "@bubba/db"; | ||
| import { db } from "@bubba/db"; | ||
| import { z } from "zod"; | ||
|
|
||
| export const getOrganizationControlRequirements = authActionClient | ||
| .schema(z.object({ controlId: z.string() })) | ||
| .metadata({ | ||
| name: "getOrganizationControlRequirements", | ||
| track: { | ||
| event: "get-organization-control-requirements", | ||
| channel: "server", | ||
| }, | ||
| }) | ||
| .action(async ({ ctx, parsedInput }) => { | ||
| const { user } = ctx; | ||
| const { controlId } = parsedInput; | ||
|
|
||
| if (!user.organizationId) { | ||
| return { | ||
| error: "Not authorized - no organization found", | ||
| }; | ||
| } | ||
|
Comment on lines
+26
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Standardize error response structure. The error response structure is inconsistent with other functions in the codebase which use Apply this diff to standardize error responses: return {
+ success: false,
error: "Not authorized - no organization found",
};
// ... and in catch block:
return {
+ success: false,
- error: "Failed to fetch organization control",
+ error: "Failed to fetch organization control requirements",
};Also applies to: 58-62 |
||
|
|
||
| try { | ||
| const organizationControlRequirements = | ||
| await db.organizationControlRequirement.findMany({ | ||
| where: { | ||
| organizationControlId: controlId, | ||
| }, | ||
| include: { | ||
| organizationPolicy: { | ||
| include: { | ||
| policy: true, | ||
| }, | ||
| }, | ||
| }, | ||
| }); | ||
|
|
||
| if (!organizationControlRequirements) { | ||
| return { | ||
| error: "Organization control requirements not found", | ||
| }; | ||
| } | ||
|
|
||
| return { | ||
| data: { | ||
| organizationControlRequirements, | ||
| }, | ||
| }; | ||
| } catch (error) { | ||
| console.error("Error fetching organization control:", error); | ||
| return { | ||
| error: "Failed to fetch organization control", | ||
| }; | ||
| } | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Enhance error handling and type safety.
Consider adding error handling for database operations and improving type safety.
const createOrganizationControlRequirements = async ( user: User, organizationFrameworkIds: string[] ) => { if (!user.organizationId) { throw new Error("Not authorized - no organization found"); } + try { const controls = await db.organizationControl.findMany({ where: { organizationId: user.organizationId!, organizationFrameworkId: { in: organizationFrameworkIds, }, }, include: { control: true, }, }); + if (controls.length === 0) { + console.warn("No controls found for the given organization frameworks"); + return []; + } const controlRequirements = await db.controlRequirement.findMany({ where: { controlId: { in: controls.map((control) => control.controlId) }, }, include: { policy: true, }, }); + if (controlRequirements.length === 0) { + console.warn("No control requirements found for the given controls"); + return []; + } const organizationPolicies = await db.organizationPolicy.findMany({ where: { organizationId: user.organizationId, }, }); for (const control of controls) { const requirements = controlRequirements.filter( (req) => req.controlId === control.controlId ); + if (requirements.length === 0) { + console.warn(`No requirements found for control ${control.controlId}`); + continue; + } await db.organizationControlRequirement.createMany({ data: requirements.map((requirement) => { const policyId = requirement.type === "policy" ? requirement.policy?.id : null; const organizationPolicy = policyId ? organizationPolicies.find((op) => op.policyId === policyId) : null; return { organizationControlId: control.id, controlRequirementId: requirement.id, type: requirement.type, description: requirement.description, organizationPolicyId: organizationPolicy?.id || null, }; }), }); } return controlRequirements; + } catch (error) { + console.error("Error creating organization control requirements:", error); + throw new Error("Failed to create organization control requirements"); + } };📝 Committable suggestion