Skip to content

feat(reminders): specify recipients #3453

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

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open

Conversation

DanielHougaard
Copy link
Member

@DanielHougaard DanielHougaard commented Apr 19, 2025

Description 📣

This PR adds support for specifying recipients for secret reminders. The previous behavior was that all members of the project would receive the reminder. To keep things inline with the existing implementation, the default behavior remains that all project members receive the reminder unless one or more recipients are specified.

Additionally I also realized that secret reminders have been broken for Secrets V2 since we rolled out Secrets V2 due to column renaming in the new secrets table. This has also been patched in this PR.

Type ✨

  • Bug fix
  • New feature
  • Improvement
  • Breaking change
  • Documentation

Summary by CodeRabbit

  • New Features

    • Added the ability to specify custom recipients for secret reminder emails. Users can now select which project members will receive reminder notifications for secrets.
    • Updated secret reminder forms and APIs to support selecting and managing reminder recipients.
    • The reminder email system now sends notifications only to selected recipients, or to all project members if none are specified.
    • Introduced a new database table to store secret reminder recipients and integrated it across relevant services and APIs.
  • Bug Fixes

    • Improved handling of optional and nullable fields in certificate schemas.
  • Documentation

    • Enhanced API documentation to describe the new secret reminder recipients feature.

@DanielHougaard DanielHougaard self-assigned this Apr 19, 2025
Copy link

coderabbitai bot commented Apr 19, 2025

"""

Walkthrough

This change introduces support for specifying and managing reminder recipients for secrets within the application. On the backend, a new table SecretReminderRecipients is added via migration along with corresponding TypeScript types and Zod schemas. A data access layer (DAL) for this table is implemented, providing methods for managing and querying recipients. Backend services and API endpoints are updated to accept, persist, and process the secretReminderRecipients field when secrets are updated, including integration into secret reminder scheduling and notification logic. When project memberships are removed, associated reminder recipient records are also deleted. On the frontend, types and form schemas are extended to include the reminderRecipients field. The secret reminder creation form is enhanced to allow selection of specific users as recipients, and this data is propagated through the UI and API calls. The update enables secret reminders to be targeted to specific users rather than all project members.

Possibly related PRs

  • Feature: Secret Rotations v2 #3343: Implements secret rotations version 2 with extensive additions to types, schemas, services, routes, and frontend components related to secret rotations and app connections; overlaps in domain and code areas related to secret management.

Suggested reviewers

  • maidul98
    """

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

backend/src/services/secret-v2-bridge/secret-v2-bridge-fns.ts

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the plugin "@typescript-eslint/eslint-plugin".

(The package "@typescript-eslint/eslint-plugin" was not found when loaded as a Node module from the directory "/backend".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install @typescript-eslint/eslint-plugin@latest --save-dev

The plugin "@typescript-eslint/eslint-plugin" was referenced from the config file in "backend/.eslintrc.js".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.

backend/src/services/secret/secret-fns.ts

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the plugin "@typescript-eslint/eslint-plugin".

(The package "@typescript-eslint/eslint-plugin" was not found when loaded as a Node module from the directory "/backend".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install @typescript-eslint/eslint-plugin@latest --save-dev

The plugin "@typescript-eslint/eslint-plugin" was referenced from the config file in "backend/.eslintrc.js".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.

backend/src/services/secret-reminder-recipients/secret-reminder-recipients-dal.ts

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the plugin "@typescript-eslint/eslint-plugin".

(The package "@typescript-eslint/eslint-plugin" was not found when loaded as a Node module from the directory "/backend".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install @typescript-eslint/eslint-plugin@latest --save-dev

The plugin "@typescript-eslint/eslint-plugin" was referenced from the config file in "backend/.eslintrc.js".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.

  • 4 others

📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e7783fe and 0a5f627.

📒 Files selected for processing (8)
  • backend/src/services/secret-reminder-recipients/secret-reminder-recipients-dal.ts (1 hunks)
  • backend/src/services/secret-v2-bridge/secret-v2-bridge-fns.ts (4 hunks)
  • backend/src/services/secret-v2-bridge/secret-v2-bridge-types.ts (2 hunks)
  • backend/src/services/secret/secret-fns.ts (2 hunks)
  • backend/src/services/secret/secret-queue.ts (10 hunks)
  • backend/src/services/secret/secret-service.ts (6 hunks)
  • backend/src/services/secret/secret-types.ts (4 hunks)
  • frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/CreateReminderForm.tsx (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • backend/src/services/secret-v2-bridge/secret-v2-bridge-types.ts
  • backend/src/services/secret-v2-bridge/secret-v2-bridge-fns.ts
  • backend/src/services/secret/secret-service.ts
  • backend/src/services/secret-reminder-recipients/secret-reminder-recipients-dal.ts
  • backend/src/services/secret/secret-types.ts
  • frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/CreateReminderForm.tsx
  • backend/src/services/secret/secret-queue.ts
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Check TS and Lint
  • GitHub Check: Run integration test
  • GitHub Check: Check Frontend Type and Lint check
  • GitHub Check: Check API Changes
🔇 Additional comments (2)
backend/src/services/secret/secret-fns.ts (2)

399-399: Addition of secretReminderRecipients property looks good.

Adding the secretReminderRecipients property initialized as an empty array supports the new feature for specifying recipients for secret reminders.


740-740: Good improvement to maintain transaction consistency.

Passing the transaction object tx to secretQueueService.removeSecretReminder ensures that if the removal of secrets fails, the removal of reminders is also rolled back, maintaining database integrity.

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (1)
frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretDetailSidebar.tsx (1)

106-106: ⚠️ Potential issue

Fix the TypeScript error in form values type

The pipeline failure indicates a type error where SecretV3RawSanitized is not assignable to the expected type. This suggests that the type definition for the form values needs to be updated to include the new reminderRecipients field.

#!/bin/bash
# Find the TypeScript type definition for SecretV3RawSanitized
rg -l "SecretV3RawSanitized" --type ts | grep -v node_modules | xargs grep -l "interface\|type" | head -n 5
🧰 Tools
🪛 GitHub Actions: Check Frontend Type and Lint check

[error] 106-106: TypeScript error TS2322: Type 'SecretV3RawSanitized' is not assignable to the expected type at this location.

🧹 Nitpick comments (2)
backend/src/services/secret-reminder-recipients/secret-reminder-recipients-dal.ts (1)

10-32: Improve type safety in the findUsersBySecretId method

The findUsersBySecretId query is well-structured with appropriate joins and filtering, but it returns Promise<any[]> which lacks type safety. Consider defining an interface for the return type to make the code more robust and easier to maintain.

- const findUsersBySecretId = async (secretId: string) => {
+ interface SecretReminderRecipientWithUser {
+   id: string;
+   secretId: string;
+   userId: string;
+   projectId: string;
+   email: string;
+   username: string;
+   firstName: string | null;
+   lastName: string | null;
+ }
+ 
+ const findUsersBySecretId = async (secretId: string): Promise<SecretReminderRecipientWithUser[]> => {
frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/CreateReminderForm.tsx (1)

197-197: Consider resetting recipients when deleting a reminder.

When deleting a reminder, the code sets days and note to null but doesn't explicitly set recipients to null. This might lead to unexpected behavior if the form is later used to create a new reminder.

Consider updating the delete handler to also reset recipients:

- onClick={() => onOpenChange(false, { days: null, note: null })}
+ onClick={() => onOpenChange(false, { days: null, note: null, recipients: [] })}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f1b1d6f and 9982ade.

📒 Files selected for processing (25)
  • backend/src/@types/knex.d.ts (2 hunks)
  • backend/src/db/migrations/20250419004044_secret-reminder-recipients.ts (1 hunks)
  • backend/src/db/schemas/certificates.ts (1 hunks)
  • backend/src/db/schemas/kmip-org-server-certificates.ts (1 hunks)
  • backend/src/db/schemas/models.ts (1 hunks)
  • backend/src/db/schemas/oidc-configs.ts (1 hunks)
  • backend/src/db/schemas/organizations.ts (1 hunks)
  • backend/src/db/schemas/secret-reminder-recipients.ts (1 hunks)
  • backend/src/ee/services/secret-approval-request/secret-approval-request-types.ts (1 hunks)
  • backend/src/lib/api-docs/constants.ts (1 hunks)
  • backend/src/server/routes/index.ts (4 hunks)
  • backend/src/server/routes/v3/secret-router.ts (2 hunks)
  • backend/src/services/project-membership/project-membership-service.ts (6 hunks)
  • backend/src/services/secret-reminder-recipients/secret-reminder-recipients-dal.ts (1 hunks)
  • backend/src/services/secret-v2-bridge/secret-v2-bridge-service.ts (1 hunks)
  • backend/src/services/secret-v2-bridge/secret-v2-bridge-types.ts (1 hunks)
  • backend/src/services/secret/secret-queue.ts (10 hunks)
  • backend/src/services/secret/secret-service.ts (3 hunks)
  • backend/src/services/secret/secret-types.ts (3 hunks)
  • frontend/src/hooks/api/secrets/mutations.tsx (2 hunks)
  • frontend/src/hooks/api/secrets/types.ts (2 hunks)
  • frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/CreateReminderForm.tsx (5 hunks)
  • frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretDetailSidebar.tsx (3 hunks)
  • frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretListView.tsx (6 hunks)
  • frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretListView.utils.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (7)
backend/src/server/routes/index.ts (1)
backend/src/services/secret-reminder-recipients/secret-reminder-recipients-dal.ts (1)
  • secretReminderRecipientsDALFactory (7-35)
backend/src/services/project-membership/project-membership-service.ts (1)
backend/src/services/secret-reminder-recipients/secret-reminder-recipients-dal.ts (1)
  • TSecretReminderRecipientsDALFactory (5-5)
backend/src/@types/knex.d.ts (1)
backend/src/db/schemas/secret-reminder-recipients.ts (3)
  • TSecretReminderRecipients (19-19)
  • TSecretReminderRecipientsInsert (20-20)
  • TSecretReminderRecipientsUpdate (21-23)
backend/src/services/secret/secret-queue.ts (3)
backend/src/services/secret-reminder-recipients/secret-reminder-recipients-dal.ts (1)
  • TSecretReminderRecipientsDALFactory (5-5)
backend/src/services/secret-v2-bridge/secret-v2-bridge-types.ts (1)
  • TCreateSecretReminderDTO (234-238)
backend/src/services/secret/secret-types.ts (1)
  • TCreateSecretReminderDTO (409-414)
backend/src/db/schemas/secret-reminder-recipients.ts (1)
backend/src/db/schemas/models.ts (1)
  • TImmutableDBKeys (153-153)
backend/src/services/secret/secret-types.ts (1)
backend/src/db/schemas/secrets.ts (1)
  • TSecrets (36-36)
backend/src/server/routes/v3/secret-router.ts (1)
backend/src/lib/api-docs/constants.ts (1)
  • RAW_SECRETS (709-789)
🪛 GitHub Actions: Check Frontend Type and Lint check
frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretDetailSidebar.tsx

[error] 106-106: TypeScript error TS2322: Type 'SecretV3RawSanitized' is not assignable to the expected type at this location.

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Check TS and Lint
  • GitHub Check: Check API Changes
  • GitHub Check: Run integration test
🔇 Additional comments (69)
backend/src/db/schemas/organizations.ts (1)

26-26: New organization-level setting for secret sharing

This new boolean field secretShareSendToAnyone controls whether secrets can be shared with anyone or only specific recipients. It defaults to true (allowing sharing with anyone) and is properly defined as nullable and optional to maintain backward compatibility with existing database records.

backend/src/db/schemas/models.ts (1)

149-150: New table entries for secret rotation and reminder recipients

The addition of SecretReminderRecipients to the TableName enum properly registers the new database table for storing recipients of secret reminders. This is a key component of the new feature to specify recipients for secret reminders.

frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretListView.utils.ts (1)

50-50: Added support for recipient selection in form schema

The new reminderRecipients field in the form schema correctly defines the expected data structure for secret reminder recipients as an array of UUIDs. This validates user input when selecting specific recipients for secret reminders.

backend/src/ee/services/secret-approval-request/secret-approval-request-types.ts (1)

36-36: Looks good - new field integrates properly with approval workflow types.

The new secretReminderRecipients field is appropriately typed as an optional string array or null, allowing for specification of specific user IDs to receive reminders, while maintaining backward compatibility.

frontend/src/hooks/api/secrets/mutations.tsx (1)

86-86: Implementation correctly handles sending reminder recipients through the API.

The mutation hook parameter and request payload are properly extended to include the new secretReminderRecipients field, enabling the frontend to specify which users should receive secret reminders.

Also applies to: 97-97

backend/src/lib/api-docs/constants.ts (1)

765-766: Documentation clearly explains the new parameter's purpose and default behavior.

The API documentation for secretReminderRecipients accurately describes that this parameter accepts an array of user IDs and that the default behavior (when not specified) is to send reminders to all project members, which aligns with the PR objectives.

backend/src/services/secret-v2-bridge/secret-v2-bridge-service.ts (1)

539-543: Good optimization to reduce the data passed to the queue service.

This change narrows down the data passed to secretQueueService.handleSecretReminder() by only including the specific fields needed (id, secretReminderNote, and secretReminderRepeatDays) instead of the entire secret object. This is more efficient and creates a cleaner contract between the services.

backend/src/server/routes/index.ts (4)

217-217: Integration of new DAL for secret reminder recipients.

Correctly importing the newly created DAL factory for the secret reminder recipients feature.


421-421: Proper instantiation of the secret reminder recipients DAL.

The DAL is properly instantiated with the database connection, following the established pattern in the codebase.


726-726: Injecting DAL into project membership service for recipient cleanup.

Adding the secretReminderRecipientsDAL to the project membership service enables the cleanup of reminder recipient records when project memberships are removed, ensuring referential integrity.


960-960: Injecting DAL into secret queue service for targeted reminders.

This injection allows the secret queue service to access and use the recipient information when processing secret reminders, enabling targeted notifications instead of sending to all project members.

frontend/src/hooks/api/secrets/types.ts (2)

45-45: Added support for reminder recipients in the secret model.

The reminderRecipients property has been added to the SecretV3RawSanitized type to store the IDs of users who should receive reminders, aligning with the backend changes.


181-181: Extended DTO to support updating reminder recipients.

The secretReminderRecipients field has been added to the update DTO, allowing the frontend to specify recipient IDs when updating a secret. This enables the new feature to target reminders to specific users.

backend/src/server/routes/v3/secret-router.ts (2)

651-651: Schema addition for recipient management looks good

This new optional field properly implements the ability to specify which users should receive secret reminder emails, aligning with the API documentation constants.


682-682: Handler correctly passes the new field to the service

The handler properly extracts and passes the secretReminderRecipients from the request body to the updateSecretRaw service method, ensuring the data flows correctly to backend services.

backend/src/@types/knex.d.ts (2)

426-430: New types imported correctly

The necessary TypeScript types for the new SecretReminderRecipients table are properly imported from the schema file.


1002-1006: Table declaration for new SecretReminderRecipients table

The Knex table declaration is correctly defined using the appropriate CompositeTableType, properly integrating the new table into the TypeScript type system.

backend/src/services/secret/secret-service.ts (1)

1789-1790: Integration of secretReminderRecipients parameter looks good.

The parameter is correctly integrated into the interface and properly passed to secretV2BridgeService.updateSecret at line 1843, maintaining consistent parameter ordering.

backend/src/services/secret/secret-types.ts (4)

25-27: Appropriate extension of TPartialSecret type.

The secretReminderRecipients field is properly typed as an optional string array or null, which aligns with the pattern used for other optional fields in this type.


29-31: Appropriate extension of TPartialInputSecret type.

The type definition follows the same pattern as the TPartialSecret type, ensuring consistency across related types.


265-265: Appropriate addition to TUpdateSecretRawDTO.

The secretReminderRecipients field is correctly added to the update DTO type, enabling proper validation and type checking when updating secrets.


413-413: Consistent typing for TCreateSecretReminderDTO recipients field.

The field is named "recipients" here rather than "secretReminderRecipients", maintaining naming consistency with other properties in this specific DTO.

backend/src/db/schemas/secret-reminder-recipients.ts (2)

10-17: Well-structured schema definition for SecretReminderRecipients.

The schema properly defines all necessary fields with appropriate validation:

  • UUID validation for id, secretId, and userId
  • Required date fields for timestamps
  • All foreign key fields are marked as required

This aligns with the database schema created in the migration file.


19-23: Clean type exports following project conventions.

The types follow the established pattern in the project:

  • Full type (TSecretReminderRecipients)
  • Insert type omitting immutable fields
  • Update type making fields optional and omitting immutable fields

This ensures type safety across database operations.

backend/src/db/migrations/20250419004044_secret-reminder-recipients.ts (2)

10-26: Well-designed table schema with appropriate constraints.

The table design includes:

  • Primary key with UUID generation
  • Proper foreign key relationships with CASCADE on delete
  • Index on secretId for query performance
  • Unique constraint on the combination of secretId, userId, and projectId to prevent duplicates

Good comment explaining the decision to use userId instead of project membership ID for future extensibility.


17-19:

✅ Verification successful

Consider implementing a cleanup mechanism for project membership removal.

The comment acknowledges the need for manual cleanup when users are removed from projects. Ensure this cleanup is implemented in the project membership service to prevent dangling recipient records.


🏁 Script executed:

#!/bin/bash
# Check if cleanup is implemented in project-membership service
echo "Searching for code that handles cleanup of SecretReminderRecipients when users are removed from projects..."
rg -A 5 -B 5 "SecretReminderRecipients.*delete" --glob "**/*project-membership*"

Length of output: 1752


🏁 Script executed:

#!/bin/bash
echo "Locating usage of secretReminderRecipientsDAL in project-membership-service.ts..."
rg -n "secretReminderRecipientsDAL" -A 10 -B 10 backend/src/services/project-membership/project-membership-service.ts

Length of output: 3945


Cleanup Already Implemented in Project Membership Service

The service calls secretReminderRecipientsDAL.delete whenever project memberships are removed:

  • In single‐membership deletion (lines 391–399)
  • In bulk‐membership deletion (lines 476–484)
  • In self‐removal flow (lines 548–555)

No further action is needed here.

frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretDetailSidebar.tsx (5)

223-225: Support for reminderRecipients added to function parameter

The function signature has been properly extended to accept reminderRecipients as an optional parameter, allowing for specific users to be selected as recipients for secret reminders.


228-228: Reminder recipients included in secret payload

The reminderRecipients field is now properly included in the payload sent to onSaveSecret, which ensures the selected recipients are saved with the rest of the reminder data.


237-237: Added form state tracking for reminderRecipients

The component now watches the reminderRecipients field in the form state, which allows it to respond to changes in the recipients selection.


294-294: Passing recipients to CreateReminderForm component

The CreateReminderForm component now receives the watched secretReminderRecipients value as a prop, enabling it to display and manage the selected recipients.


300-307: Processing and passing reminder recipients from form data

This logic correctly extracts recipient data from the form, transforms it to the expected format (array of string IDs), updates the form state, and passes it to the submit handler. The conditional check ensures that empty selections are handled properly.

backend/src/services/secret-reminder-recipients/secret-reminder-recipients-dal.ts (4)

5-5: Good type definition for the DAL factory

Properly exporting the type of the DAL factory's return value makes it easier to use this module in other parts of the codebase with strong typing support.


7-9: DAL factory initialization follows project patterns

The factory pattern used here is consistent with other DAL implementations in the project, taking a database client and initializing an ORM for the SecretReminderRecipients table.


21-23: Confirm intended behavior for active org memberships filter

The query filters for active organization memberships (where('${TableName.OrgMembership}.isActive', true)). This means users who have been deactivated in the organization won't receive reminders, even if they're still listed as recipients. Please confirm this is the intended behavior.


34-35: DAL exports both ORM methods and custom query

The DAL correctly returns both the auto-generated ORM methods and the custom findUsersBySecretId query method, providing a complete interface for interacting with reminder recipients data.

frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretListView.tsx (6)

88-89: Reminder recipients added to function parameters

The handleSecretOperation function signature has been properly extended to include the new reminderRecipients parameter.


100-101: Type definition updated for reminderRecipients

The type definition properly specifies that reminderRecipients is an optional parameter that can be either an array of strings or null.


136-137: Reminder recipients included in API call payload

The new secretReminderRecipients field is now included in the payload for the updateSecretV3 API call, ensuring the backend receives the recipient information.


178-179: Destructuring reminderRecipients from secret data

The component now correctly extracts the reminderRecipients field from the modified secret for further processing.


196-197: Added reminderRecipients to equality check

The reminderRecipients field is now included in the list of fields used to determine if a shared secret has been changed, ensuring updates to recipients trigger the appropriate save actions.


232-233: Passing reminderRecipients to handleSecretOperation

The reminderRecipients field is correctly passed to the handleSecretOperation function when updating a shared secret.

backend/src/services/project-membership/project-membership-service.ts (6)

26-27: Import for SecretReminderRecipientsDAL type

The import of the TSecretReminderRecipientsDALFactory type is correctly added to support the new dependency.


57-58: Added secretReminderRecipientsDAL to service dependencies

The service factory dependencies have been properly extended to include the secretReminderRecipientsDAL, specifying that only the delete method is needed.


76-77: Included secretReminderRecipientsDAL in factory parameters

The parameter list for the service factory function now includes the secretReminderRecipientsDAL, allowing it to be used within the service.


394-400: Clean up reminder recipients when deleting a project membership

This code properly deletes reminder recipients associated with a user when their project membership is deleted, ensuring data integrity. The deletion is performed within the same transaction as the membership deletion.


478-486: Clean up reminder recipients when deleting multiple project memberships

When deleting multiple project memberships, this code correctly cleans up all associated reminder recipients in the same transaction, using the appropriate filter to match the memberships being deleted.


549-555: Clean up reminder recipients when a user leaves a project

This code ensures that when a user leaves a project, any reminder recipients associated with that user in the project are deleted, maintaining data consistency.

backend/src/services/secret/secret-queue.ts (12)

9-9: Improved type safety by using enum value instead of string literal.

Using SecretType enum instead of string literals enhances code readability and type safety.


57-57: Good addition of dependency for recipient management.

Adding the import for TSecretReminderRecipientsDALFactory properly sets up the type structure needed for the new recipient functionality.


114-117: Clean interface design for recipient data access.

The interface properly specifies the required methods needed for recipient management: delete, find, insert, and transaction support.


179-179: Properly injected the new DAL dependency.

The dependency is correctly injected into the factory function, making it available throughout the service.


189-190: Good cleanup implementation for recipients.

Deleting recipients before stopping the reminder job ensures there are no orphaned records in the database.


236-236: Signature update maintains backward compatibility.

The function signature has been updated to support recipients while maintaining backward compatibility with existing code.


262-274: Well-implemented transaction for atomicity.

Using a transaction ensures that the deletion and insertion of recipients is atomic, preventing partial updates in case of errors.


311-311: Improved type safety with enum usage.

Replacing the string literal "personal" with SecretType.Personal enum provides better type safety and code maintainability.


320-320: Properly passing recipients to the reminder creation function.

The code correctly passes the recipients from the new secret to the addSecretReminder function.


1099-1099: Implemented recipient lookup for email notifications.

Added code to fetch users by secret ID using the new DAL, supporting the targeted notification feature.


1118-1120: Smart fallback behavior for email recipients.

This implementation correctly handles the core functionality - sending emails to specific recipients if available, otherwise defaulting to all project members as described in the PR objectives.


1125-1125: Email notification using selected recipients.

The code correctly uses the selected recipients for email notifications, completing the feature implementation.

frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/CreateReminderForm.tsx (8)

9-19: Added necessary imports for recipient selection functionality.

The imports correctly include the FilterableSelect component and the hooks needed to access workspace data and users.


23-30: Well-structured form schema for recipients.

The schema properly defines recipients as an optional array of objects with the expected structure (label and UUID value).


43-43: Updated props interface for recipients.

The ReminderFormProps interface has been updated to accept the optional recipients prop.


52-52: Added destructuring for recipients prop.

The recipients prop is now correctly destructured from the component props.


54-57: Fetching required data for recipient selection.

The component now fetches the current workspace and its users, which are needed for the recipient selection functionality.


86-97: Recipients initialization has a potential issue.

The effect correctly initializes recipients based on the members and passed props, but it doesn't include recipients in its dependency array. If recipients change externally without the modal being reopened, the effect won't run.

Consider updating the effect to include recipients in its dependency array:

- }, [members, isOpen]);
+ }, [members, isOpen, recipients]);

114-114: Good UX improvement by marking reminder interval as required.

Adding the isRequired prop to the form control clearly indicates to users that this field must be filled.


152-181: Well-implemented recipient selector with helpful tooltip.

The FilterableSelect component is correctly integrated for recipient selection with a helpful tooltip explaining the default behavior when no recipients are selected. The component is properly wired to the form control.

backend/src/db/schemas/certificates.ts (1)

23-23:

❓ Verification inconclusive

Check if default value removal is intentional

The .default("") clause was removed from the altNames field schema. This changes the behavior from having an empty string as default to potentially being undefined or null, which could impact code that expects this field to always have at least an empty string value.

This change appears unrelated to the main PR objective of adding secret reminder recipients functionality. Is this change intentional or did it happen as part of the schema auto-generation process?


🏁 Script executed:

#!/bin/bash
# Check for usage of certificates.altNames to identify potential impacts
rg -A 2 -B 2 "certificates.*altNames" --type ts

Length of output: 49


🏁 Script executed:

#!/bin/bash
# Search for all occurrences of altNames in the TypeScript codebase
rg -n "altNames" --type ts

Length of output: 6880


🏁 Script executed:

#!/bin/bash
# Search for .default() usage in DB schema files
rg -n '\.default\(' --type ts backend/src/db/schemas

Length of output: 15788


Confirm intentional removal of the Zod default("") on altNames

We’ve dropped the .default("") from the altNames field in your Zod schemas, which means:

  • Incoming payloads no longer get coerced to "" when altNames is absent.
  • Consumers of the parsed data may now see undefined (or null) instead of "".
  • Database migrations still define a column‐level default of "" on insert, but updates or explicit null inputs could diverge.

Please verify that this was intentional and that higher‐level logic, API clients, and downstream code can handle undefined/null here.

Files affected:

  • backend/src/db/schemas/certificates.ts:23
  • backend/src/db/schemas/kmip-org-server-certificates.ts:16
backend/src/db/schemas/kmip-org-server-certificates.ts (1)

16-16:

✅ Verification successful

Potential breaking change in field requirement

The altNames field has been changed from a required non-nullable string to an optional nullable string. This is a significant change that might affect validation rules and type checking throughout the codebase.

Since this change appears unrelated to the main PR objective (adding secret reminder recipients), please verify this change is intentional. If so, ensure:

  1. Any code that relies on altNames being present is updated
  2. Database migrations properly handle this schema change

🏁 Script executed:

#!/bin/bash
# Search for usage of KmipOrgServerCertificates.altNames to identify potential impacts
rg -A 2 -B 2 "KmipOrgServerCertificates.*altNames" --type ts

Length of output: 62


🏁 Script executed:

#!/bin/bash
# Search for all occurrences of the `altNames` field across the codebase
rg -n "altNames" -C 2 .

Length of output: 28946


No action needed: altNames optional/nullable change is safe and intentional

We’ve confirmed the Zod schema update for kmip certificates aligns with the existing generic certificates schema (altNames: z.string().nullable().optional()), and:

  • The database column remains non‑nullable with a default of "", so all existing rows continue to return a string.
  • Code paths that consume altNames guard against falsy values (if (altNames) …) and default to "" in the UI and service layers.
  • Input validation for API routes uses validateAltNamesField, not the DB schema, and already accepts optional values.
  • Migrations for length and column addition do not conflict with this schema change.

No further updates or migrations are required here.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Summary

This PR adds functionality to specify recipients for secret reminders and fixes broken reminder functionality in Secrets V2, with key database and service layer changes to support targeted notifications.

  • Added new secret_reminder_recipients table with appropriate foreign key constraints and unique composite index in /backend/src/db/migrations/20250419004044_secret-reminder-recipients.ts
  • Added secretReminderRecipients field (string[] | null) to secret update DTOs and form schemas to support recipient selection
  • Fixed broken Secrets V2 reminders by updating column references and adding cleanup logic when project members are removed
  • Added FilterableSelect component in frontend to allow selecting specific recipients from workspace members
  • Modified email sending logic to target specified recipients while maintaining backward compatibility by defaulting to all project members

23 file(s) reviewed, 5 comment(s)
Edit PR Review Bot Settings | Greptile

Copy link
Member

@akhilmhdh akhilmhdh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now! discard changes in db schema that doesn't belong to this PR

@akhilmhdh
Copy link
Member

Pending: Application testing

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Summary

(updates since last review)

This PR enhances the secret reminder functionality by adding recipient specification and fixes Secrets V2 reminder issues. Here are the key changes:

  • Added new secret_reminder_recipients table with proper foreign key constraints and unique composite index on (secretId, userId) in /backend/src/db/migrations/20250419004044_secret-reminder-recipients.ts
  • Implemented secretReminderRecipientsDAL with proper type safety and active membership filtering in /backend/src/services/secret-reminder-recipients/secret-reminder-recipients-dal.ts
  • Added cleanup logic in project membership service to remove reminder recipients when users leave projects
  • Modified email sending logic to target specified recipients while maintaining backward compatibility
  • Fixed broken Secrets V2 reminders by updating column references in the queue service

The changes look well-structured with proper type safety and data integrity considerations. The implementation follows existing patterns and maintains backward compatibility.

2 file(s) reviewed, 1 comment(s)
Edit PR Review Bot Settings | Greptile

@maidul98
Copy link
Collaborator

maidul98 commented Apr 26, 2025

🎉 Snyk checks have passed. No issues have been found so far.

security/snyk check is complete. No issues have been found. (View Details)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🔭 Outside diff range comments (1)
backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts (1)

187-400: ⚠️ Potential issue

Missing recipients support in the find method.

Unlike findOne and findByFolderIds, the find method doesn't include JOIN operations and field selections for reminder recipients. This inconsistency may cause recipients data to be missing when using this method.

Consider adding similar JOIN operations and field selections to the find method:

const query = (tx || db)(TableName.SecretV2)
  // eslint-disable-next-line @typescript-eslint/no-misused-promises
  .where(buildFindFilter(filter))
  .leftJoin(
    TableName.SecretV2JnTag,
    `${TableName.SecretV2}.id`,
    `${TableName.SecretV2JnTag}.${TableName.SecretV2}Id`
  )
  .leftJoin(
    TableName.SecretTag,
    `${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`,
    `${TableName.SecretTag}.id`
  )
  .leftJoin(TableName.ResourceMetadata, `${TableName.SecretV2}.id`, `${TableName.ResourceMetadata}.secretId`)
  .leftJoin(
    TableName.SecretRotationV2SecretMapping,
    `${TableName.SecretV2}.id`,
    `${TableName.SecretRotationV2SecretMapping}.secretId`
  )
+ .leftJoin(
+   TableName.SecretReminderRecipients,
+   `${TableName.SecretV2}.id`,
+   `${TableName.SecretReminderRecipients}.secretId`
+ )
+ .leftJoin(TableName.Users, `${TableName.SecretReminderRecipients}.userId`, `${TableName.Users}.id`)

And add selections for the recipient fields:

.select(
  db.ref("id").withSchema(TableName.ResourceMetadata).as("metadataId"),
  db.ref("key").withSchema(TableName.ResourceMetadata).as("metadataKey"),
  db.ref("value").withSchema(TableName.ResourceMetadata).as("metadataValue")
)
.select(selectAllTableCols(TableName.SecretV2))
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
.select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug"))
.select(db.ref("rotationId").withSchema(TableName.SecretRotationV2SecretMapping))
+ .select(db.ref("id").withSchema(TableName.SecretReminderRecipients).as("reminderRecipientId"))
+ .select(db.ref("username").withSchema(TableName.Users).as("reminderRecipientUsername"))
+ .select(db.ref("email").withSchema(TableName.Users).as("reminderRecipientEmail"))
+ .select(db.ref("id").withSchema(TableName.Users).as("reminderRecipientUserId"))

Also add a child mapper for recipients:

childrenMapper: [
  {
    key: "tagId",
    label: "tags" as const,
    mapper: ({ tagId: id, tagColor: color, tagSlug: slug }) => ({
      id,
      color,
      slug,
      name: slug
    })
  },
  {
    key: "metadataId",
    label: "secretMetadata" as const,
    mapper: ({ metadataKey, metadataValue, metadataId }) => ({
      id: metadataId,
      key: metadataKey,
      value: metadataValue
    })
  },
+ {
+   key: "reminderRecipientId",
+   label: "secretReminderRecipients" as const,
+   mapper: ({
+     reminderRecipientId,
+     reminderRecipientUsername,
+     reminderRecipientEmail,
+     reminderRecipientUserId
+   }) => ({
+     user: {
+       id: reminderRecipientUserId,
+       username: reminderRecipientUsername,
+       email: reminderRecipientEmail
+     },
+     id: reminderRecipientId
+   })
+ }
]
♻️ Duplicate comments (1)
backend/src/services/secret/secret-queue.ts (1)

264-278: ⚠️ Potential issue

Handle edge case with empty recipients array.

The insertMany operation may fail if recipients is an empty array. An explicit check should be added to avoid this potential issue.

if (recipients?.length) {
  await secretReminderRecipientsDAL.transaction(async (tx) => {
    await secretReminderRecipientsDAL.delete({ secretId: newSecret.id }, tx);
    await secretReminderRecipientsDAL.insertMany(
      recipients.map((r) => ({
        secretId: newSecret.id,
        userId: r,
        projectId
      })),
      tx
    );
  });
} else if (existingRecipients) {
  await secretReminderRecipientsDAL.delete({ secretId: newSecret.id });
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9ea6eca and e7783fe.

📒 Files selected for processing (13)
  • backend/src/server/routes/v1/dashboard-router.ts (2 hunks)
  • backend/src/services/secret-reminder-recipients/secret-reminder-recipients-types.ts (1 hunks)
  • backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts (5 hunks)
  • backend/src/services/secret-v2-bridge/secret-v2-bridge-fns.ts (3 hunks)
  • backend/src/services/secret-v2-bridge/secret-v2-bridge-service.ts (1 hunks)
  • backend/src/services/secret/secret-fns.ts (1 hunks)
  • backend/src/services/secret/secret-queue.ts (10 hunks)
  • backend/src/services/secret/secret-service.ts (4 hunks)
  • frontend/src/hooks/api/secrets/queries.tsx (1 hunks)
  • frontend/src/hooks/api/secrets/types.ts (5 hunks)
  • frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/CreateReminderForm.tsx (6 hunks)
  • frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretDetailSidebar.tsx (4 hunks)
  • frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretListView.tsx (8 hunks)
✅ Files skipped from review due to trivial changes (3)
  • backend/src/services/secret-reminder-recipients/secret-reminder-recipients-types.ts
  • frontend/src/hooks/api/secrets/queries.tsx
  • backend/src/services/secret/secret-fns.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • backend/src/services/secret-v2-bridge/secret-v2-bridge-service.ts
  • backend/src/services/secret/secret-service.ts
  • frontend/src/hooks/api/secrets/types.ts
  • frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretListView.tsx
  • frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/CreateReminderForm.tsx
  • frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretDetailSidebar.tsx
🧰 Additional context used
🧬 Code Graph Analysis (3)
backend/src/services/secret-v2-bridge/secret-v2-bridge-fns.ts (1)
backend/src/services/secret-reminder-recipients/secret-reminder-recipients-types.ts (1)
  • TSecretReminderRecipient (1-8)
backend/src/services/secret/secret-queue.ts (3)
backend/src/services/secret-reminder-recipients/secret-reminder-recipients-dal.ts (1)
  • TSecretReminderRecipientsDALFactory (5-5)
backend/src/services/secret-v2-bridge/secret-v2-bridge-types.ts (1)
  • TCreateSecretReminderDTO (234-238)
backend/src/services/secret/secret-types.ts (1)
  • TCreateSecretReminderDTO (409-414)
backend/src/server/routes/v1/dashboard-router.ts (1)
backend/src/db/schemas/users.ts (1)
  • UsersSchema (10-31)
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Check API Changes
  • GitHub Check: Run integration test
  • GitHub Check: Check Frontend Type and Lint check
  • GitHub Check: Check TS and Lint
🔇 Additional comments (17)
backend/src/services/secret-v2-bridge/secret-v2-bridge-fns.ts (3)

13-13: Clean and appropriate import addition.

The addition of TSecretReminderRecipient type import is necessary for the new parameter in the reshapeBridgeSecret function.


672-673: Appropriate type extension for the secret object parameter.

The secretReminderRecipients optional property is correctly added to the parameter type, allowing for this data to be passed to the reshaping function.


704-704: Well implemented default value for secretReminderRecipients.

The implementation correctly handles the case where secretReminderRecipients is undefined by defaulting to an empty array, ensuring consistent output structure regardless of input.

backend/src/server/routes/v1/dashboard-router.ts (1)

597-602: Well-structured schema for secretReminderRecipients.

The schema correctly defines the structure of secret reminder recipients in the API response, including only the necessary user information (id, email, username) and the recipient ID.

backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts (6)

98-103: Correct LEFT JOIN implementation for reminder recipients.

The LEFT JOIN operations correctly connect the SecretReminderRecipients and Users tables, allowing for the retrieval of recipient data along with secrets.


105-108: Comprehensive selection of required recipient fields.

The code properly selects all necessary fields from the joined tables with appropriate aliases to avoid column name conflicts.


133-149: Well-structured child mapper for secretReminderRecipients.

The mapper correctly transforms the raw database results into a structured object with nested user information, making the data easier to work with in the application.


637-643: Consistent JOIN implementation across query methods.

The JOIN operations in findByFolderIds match those in findOne, ensuring consistent data retrieval across different query paths.


671-674: Consistent field selection across query methods.

The selected fields match those in the findOne method, maintaining consistency in the returned data structure.


719-735: Consistent child mapper implementation.

The mapper structure is consistent with the one in findOne, ensuring the same data transformation logic is applied across different query methods.

backend/src/services/secret/secret-queue.ts (7)

9-9: Good type safety improvement.

Replacing string literals with the SecretType enum improves type safety and makes the code more maintainable.


57-57: Proper dependency integration.

The TSecretReminderRecipientsDALFactory is correctly imported and added to the factory dependencies with appropriate method selections.

Also applies to: 114-117


189-190: Improved cleanup in removeSecretReminder.

Deleting recipient records before stopping the reminder job ensures complete cleanup of all related data.


236-236: Appropriate function signature update.

Adding the optional recipients parameter allows for specifying which users should receive the reminder.


315-320: Well-implemented recipients change detection.

The code properly detects when recipients have been added or removed, triggering reminder updates only when necessary.


322-334: Improved type safety with SecretType enum.

Using SecretType.Personal instead of string literals improves type safety and clarity.


1111-1112: Proper recipient data retrieval.

Fetching recipients by secretId is correctly implemented, providing the necessary data for the email sending logic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants