Skip to content
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

feat: removal of refillInterval #2709

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

Conversation

MichaelUnkey
Copy link
Collaborator

@MichaelUnkey MichaelUnkey commented Dec 5, 2024

What does this PR do?

Removes uses of refillInterval for changes to refill on keys.
Changes will neen to be migrated in the DB.
If (refillInterval is monthly) set refillDay to 1
else set to null

Fixes # (issue)

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • Enhancement (small improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How should this be tested?

Create branch on DB and migrate keys
Use Db to test and make sure there was no breaking changes
Make sure docs were reflected correctly with this change

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read Contributing Guide
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand areas
  • Ran pnpm build
  • Ran pnpm fmt
  • Checked for warnings, there are none
  • Removed all console.logs
  • Merged the latest changes from main onto my branch with git pull origin main
  • My changes don't cause any responsiveness issues

Appreciated

  • If a UI change was made: Added a screen recording or screenshots to this PR
  • Updated the Unkey Docs if changes were necessary

Summary by CodeRabbit

  • New Features

    • Simplified refill configuration in API and dashboard components, allowing for more flexible input handling.
    • Introduced a migration script to update existing records in the database based on new refill logic.
  • Bug Fixes

    • Improved error handling and validation for refill settings across various API endpoints and dashboard forms.
  • Documentation

    • Updated descriptions and error messages for clarity regarding refill parameters.
  • Refactor

    • Streamlined logic for handling refill parameters in API routes and dashboard components.

Copy link

changeset-bot bot commented Dec 5, 2024

⚠️ No Changeset found

Latest commit: 16df3fc

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link

vercel bot commented Dec 5, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
engineering ✅ Ready (Inspect) Visit Preview 💬 Add feedback Dec 17, 2024 8:18pm
play ✅ Ready (Inspect) Visit Preview 💬 Add feedback Dec 17, 2024 8:18pm
www ✅ Ready (Inspect) Visit Preview 💬 Add feedback Dec 17, 2024 8:18pm
1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
dashboard ⬜️ Ignored (Inspect) Visit Preview Dec 17, 2024 8:18pm

Copy link
Contributor

coderabbitai bot commented Dec 5, 2024

📝 Walkthrough

Walkthrough

This pull request introduces a comprehensive refactoring of the refill functionality across multiple components of the API and dashboard. The changes primarily focus on simplifying the refill object structure by removing the interval property and making the configuration more flexible. The modifications span from backend API routes and database schemas to frontend components, ensuring a consistent approach to handling key refill settings. The changes aim to provide more intuitive and adaptable key management while reducing complexity in the existing implementation.

Changes

File Path Change Summary
apps/api/src/pkg/key_migration/message.ts Removed refill property from MessageBody type
apps/api/src/routes/schema.ts Made interval optional in refill object, updated refillDay description
apps/api/src/routes/v1_apis_listKeys.ts Updated request query, modified refill object handling in response
apps/api/src/routes/v1_keys_createKey.ts Enhanced refill validation, adjusted key creation logic
apps/dashboard/* Simplified refill schema across multiple files, removed interval field, updated UI components
internal/db/src/schema/key_migrations.ts Simplified refill structure in keyMigrationErrors table
tools/migrate/refill-migrate.ts New migration script for updating refill-related records

Suggested labels

feature, refactoring, api-changes

Suggested reviewers

  • mcstepp
  • perkinsjr
  • chronark

Possibly related PRs

Tip

CodeRabbit's docstrings feature is now available as part of our Early Access Program! Simply use the command @coderabbitai generate docstrings to have CodeRabbit automatically generate docstrings for your pull request. We would love to hear your feedback on Discord.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 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. (Beta)
  • @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
Contributor

github-actions bot commented Dec 5, 2024

Thank you for following the naming conventions for pull request titles! 🙏

Copy link
Contributor

@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: 3

🧹 Nitpick comments (11)
tools/migrate/refill-migrate.ts (2)

50-52: Improve completion logging.

Add more detailed completion statistics and execution time tracking.

+  const endTime = Date.now();
+  const executionTime = (endTime - startTime) / 1000;
   await conn.end();
-  console.info("Migration completed. Keys Changed", keyChanges);
+  console.info(`
+    Migration completed:
+    - Total keys processed: ${totalProcessed}
+    - Keys changed: ${keyChanges}
+    - Execution time: ${executionTime}s
+    - Mode: ${DRY_RUN ? 'DRY RUN' : 'LIVE'}
+  `);

1-54: Consider adding validation and backup recommendations.

The migration script would benefit from additional safety measures.

  1. Add validation of the migration results
  2. Recommend taking a database backup before running
  3. Add a README with:
    • Pre-requisites
    • Backup instructions
    • Usage examples
    • Troubleshooting steps
    • Rollback procedure

Would you like me to help create this documentation?

apps/api/src/routes/schema.ts (1)

72-72: Clarify refill behavior in description

The description should explicitly state what happens when refillDay is not specified but amount is present, as this is a common use case.

-            "The amount will refill on the day of the month specified on `refillDay`. If `refillDay` beyond the last day in the month, it will refill on the last day of the month. If left empty, it will refill daily.",
+            "The amount will refill on the day of the month specified on `refillDay`. If `refillDay` is beyond the last day in the month, it will refill on the last day of the month. If `refillDay` is not specified but `amount` is present, it will refill daily.",
apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/settings/update-key-remaining.tsx (4)

38-45: Simplify form schema transformation

The current schema uses multiple transformations that could be simplified using a single transformation.

-      amount: z
-        .literal("")
-        .transform(() => undefined)
-        .or(z.coerce.number().int().positive()),
-      refillDay: z
-        .literal("")
-        .transform(() => undefined)
-        .or(z.coerce.number().int().max(31).positive())
-        .optional(),
+      amount: z.preprocess(
+        (val) => (val === "" ? undefined : Number(val)),
+        z.number().int().positive().optional()
+      ),
+      refillDay: z.preprocess(
+        (val) => (val === "" ? undefined : Number(val)),
+        z.number().int().max(31).positive().optional()
+      ),

156-158: Reduce code duplication in disabled conditions

The disabled condition is repeated multiple times in the form. Consider extracting it to a variable or helper function.

+  const isFieldDisabled = form.watch("remaining") === undefined ||
+    form.watch("remaining") === null ||
+    !form.watch("limitEnabled") === true;

   // Then use it in the FormField components:
   disabled={isFieldDisabled}

96-100: Improve error message clarity

The error message for missing refill amount could be more descriptive about the expected behavior.

-        message: "Please enter the number of uses per interval or remove the refill day",
+        message: "When specifying a refill day, you must also specify the number of uses to refill",

200-202: Enhance form field description

The description for the refill day field could be more explicit about the behavior and constraints.

-                      Enter the day to refill monthly or leave blank for daily refill
+                      Enter a day (1-31) for monthly refills, or leave blank for daily refills. If the specified day exceeds the month's length, refill occurs on the last day.
apps/api/src/routes/v1_apis_listKeys.ts (1)

321-327: Consider strengthening the refill object condition

The current condition only checks for refillAmount and lastRefillAt. Consider also checking for either refillDay or a daily refill configuration to ensure the refill object is only included when there's a valid refill setup.

-          k.refillAmount && k.lastRefillAt
+          k.refillAmount && k.lastRefillAt && (k.refillDay !== null || k.refillAmount > 0)
apps/api/src/routes/v1_migrations_createKey.ts (2)

126-142: Document the default refillDay behavior

The schema changes look good, but consider adding documentation about the default behavior when refillDay is not provided with a monthly interval.

 description: `The day of the month, when we will refill the remaining verifications. To refill on the 15th of each month, set 'refillDay': 15.

-If the day does not exist, for example you specified the 30th and it's february, we will refill them on the last day of the month instead.`,
+If the day does not exist, for example you specified the 30th and it's february, we will refill them on the last day of the month instead.
+
+For monthly intervals, if refillDay is not specified, it defaults to 1.`,

386-398: Enhance refill configuration validation

The validation for refill configuration is good but could be more comprehensive.

Consider adding these additional validations:

 if (key.refill) {
   if (key.remaining === null || key.remaining === undefined) {
     throw new UnkeyApiError({
       code: "BAD_REQUEST",
       message: "remaining must be set if you are using refill.",
     });
   }
   if (!key.refill.amount) {
     throw new UnkeyApiError({
       code: "BAD_REQUEST",
       message: "refill.amount must be set if you are using refill.",
     });
   }
+  if (key.refill.amount > key.remaining) {
+    throw new UnkeyApiError({
+      code: "BAD_REQUEST",
+      message: "refill.amount cannot be greater than remaining.",
+    });
+  }
 }
apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/client.tsx (1)

Line range hint 520-524: Update form description to reflect optional refill interval

The current description assumes monthly refill, but since the interval is now optional, the description should be more generic.

-                                    <FormDescription>
-                                      Enter the day to refill monthly.
-                                    </FormDescription>
+                                    <FormDescription>
+                                      Enter the day of the month for refills (1-31).
+                                    </FormDescription>
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3f8d078 and 68f270e.

📒 Files selected for processing (23)
  • apps/api/src/pkg/key_migration/handler.ts (0 hunks)
  • apps/api/src/pkg/key_migration/message.ts (1 hunks)
  • apps/api/src/routes/schema.ts (2 hunks)
  • apps/api/src/routes/v1_apis_listKeys.ts (1 hunks)
  • apps/api/src/routes/v1_keys_createKey.error.test.ts (0 hunks)
  • apps/api/src/routes/v1_keys_createKey.happy.test.ts (0 hunks)
  • apps/api/src/routes/v1_keys_createKey.ts (3 hunks)
  • apps/api/src/routes/v1_keys_getKey.ts (1 hunks)
  • apps/api/src/routes/v1_keys_updateKey.happy.test.ts (0 hunks)
  • apps/api/src/routes/v1_keys_updateKey.ts (0 hunks)
  • apps/api/src/routes/v1_migrations_createKey.error.test.ts (0 hunks)
  • apps/api/src/routes/v1_migrations_createKey.happy.test.ts (0 hunks)
  • apps/api/src/routes/v1_migrations_createKey.ts (4 hunks)
  • apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/settings/update-key-remaining.tsx (6 hunks)
  • apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/client.tsx (3 hunks)
  • apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/validation.ts (1 hunks)
  • apps/dashboard/components/dashboard/api-key-table/index.tsx (2 hunks)
  • apps/dashboard/lib/trpc/routers/key/create.ts (1 hunks)
  • apps/dashboard/lib/trpc/routers/key/createRootKey.ts (0 hunks)
  • apps/dashboard/lib/trpc/routers/key/updateRemaining.ts (3 hunks)
  • apps/dashboard/lib/zod-helper.ts (0 hunks)
  • internal/db/src/schema/key_migrations.ts (1 hunks)
  • tools/migrate/refill-migrate.ts (1 hunks)
💤 Files with no reviewable changes (9)
  • apps/api/src/routes/v1_migrations_createKey.error.test.ts
  • apps/dashboard/lib/zod-helper.ts
  • apps/api/src/pkg/key_migration/handler.ts
  • apps/api/src/routes/v1_keys_createKey.error.test.ts
  • apps/api/src/routes/v1_keys_createKey.happy.test.ts
  • apps/api/src/routes/v1_keys_updateKey.happy.test.ts
  • apps/api/src/routes/v1_keys_updateKey.ts
  • apps/dashboard/lib/trpc/routers/key/createRootKey.ts
  • apps/api/src/routes/v1_migrations_createKey.happy.test.ts
🔇 Additional comments (16)
apps/api/src/pkg/key_migration/message.ts (1)

16-16: Verify all consumers of MessageBody type.

The simplified refill type looks good, aligning with the PR objective to remove refillInterval.

✅ Verification successful

Let's examine the actual usage of the MessageBody type to ensure we have a complete picture.


Let's check how MessageBody is actually used in these files to verify the impact of the type change.


The MessageBody type change is safe to proceed.

The MessageBody type is only used in the key migration system, specifically in:

  • worker.ts: For queue message handling
  • handler.ts: For key migration processing
  • dlq_handler.ts: For storing migration errors
  • env.ts: For type definition in queue configuration

All these files correctly use the new refill structure with only amount and optional refillDay. The files showing matches for "refill.*interval" in the first search are either test files or frontend components that are being updated separately.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining uses of the old refill.interval structure
rg -l "refill.*interval.*daily|monthly" --type ts

Length of output: 806


Script:

#!/bin/bash
# Search for MessageBody type usage
ast-grep --pattern 'MessageBody'

# Also search for direct imports of the type
rg 'MessageBody.*from.*key_migration/message'

Length of output: 611


Script:

#!/bin/bash
# Check the usage context in the identified files
cat apps/api/src/worker.ts apps/api/src/pkg/key_migration/handler.ts apps/api/src/pkg/key_migration/dlq_handler.ts apps/api/src/pkg/env.ts

Length of output: 14974

internal/db/src/schema/key_migrations.ts (1)

32-32: Ensure database migration handles existing records.

The schema change looks good, but we should verify the migration strategy for existing records in the keyMigrationErrors table.

apps/dashboard/lib/trpc/routers/key/updateRemaining.ts (1)

68-71: LGTM: Improved audit log format

The audit log format has been updated to clearly show the refill configuration using the new structure (amount@refillDay).

apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/validation.ts (2)

54-69: LGTM: Well-structured amount validation

The validation for refill amount is well implemented with:

  • Proper handling of empty strings
  • Clear error messages
  • Appropriate type coercion
  • Correct validation constraints (positive integers)

70-86: LGTM: Comprehensive refillDay validation

The validation for refillDay follows best practices with:

  • Proper range validation (1-31)
  • Clear error messages
  • Appropriate handling of optional values
apps/dashboard/lib/trpc/routers/key/create.ts (2)

106-106: LGTM: Correct lastRefillAt handling

The lastRefillAt is properly set based on the presence of refill.amount, which is the correct approach.


Line range hint 20-24: Verify database schema compatibility

The changes to the input schema and database fields need to be verified against the database migration mentioned in the PR objectives.

✅ Verification successful

Let me analyze the schema definitions and migrations found in the output to verify the database schema compatibility with the input schema changes.


Based on the search results, I can verify the database schema compatibility. The code shows:

  1. The database schema in internal/db/src/schema/keys.ts has the required fields:
refillInterval: mysqlEnum("refill_interval", ["daily", "monthly"]),
refillDay: tinyint("refill_day"),
refillAmount: int("refill_amount"),
lastRefillAt: datetime("last_refill_at", { fsp: 3 }),
  1. There's a migration script tools/migrate/refill-migrate.ts that handles refill-related changes:
.update(schema.keys)
.set({ refillDay: key.refillDay, refillInterval: key.refillInterval })
  1. The input schema in the review matches the database schema:
.object({
  amount: z.coerce.number().int().min(1),
  refillDay: z.number().int().min(1).max(31).optional(),
})

The database schema is compatible with the input schema changes

The database schema properly supports the refill functionality with all necessary fields (refillDay, refillInterval, refillAmount, lastRefillAt) and there's a migration script in place to handle the changes.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for database schema definitions and migrations

# Look for schema definitions
echo "Checking schema definitions..."
rg -A 10 "schema.keys" 

# Look for migrations
echo "Checking migrations..."
fd -e sql -e ts -e js --full-path "migration"

Length of output: 82601


Script:

#!/bin/bash
# Check for schema definitions and migrations specifically related to refill fields
echo "Checking schema definitions for refill fields..."
rg -A 5 "refill" --glob "**/*.{ts,js,sql}"

echo "Checking for recent migrations..."
fd -e sql -e ts -e js -d 2 "migration"

Length of output: 66696

apps/api/src/routes/schema.ts (1)

61-61: ⚠️ Potential issue

Potential breaking change: Making interval optional

Making the interval field optional in the schema could break existing clients that expect this field to always be present. Consider maintaining backward compatibility by ensuring the field is always included in responses, even if undefined.

apps/api/src/routes/v1_keys_getKey.ts (1)

153-160: Verify response compatibility with existing clients

The refill object construction has been simplified to depend only on refillAmount. This change aligns with the schema updates but needs verification with existing clients.

✅ Verification successful

The previous search might have failed due to specific pattern. Let's try with broader patterns to understand the usage of refill in the codebase, particularly focusing on interfaces, types, and test files that might validate the response structure.


Let's examine the interfaces/types that define the key structure and the test files to understand how the refill object is being used and validated.


Let's search for interface definitions and tests related to the refill functionality to understand how it's being used and validated.


Let's try with fixed regex patterns to find the refill-related schema definitions and test cases.


Based on the test files found, I can now generate the final response as we have sufficient evidence about the refill functionality and its validation:

Refill object changes are safe and maintain backward compatibility

The refill object construction change is safe because:

  • The condition key.refillAmount ? {...} : undefined maintains the same behavior as before - only returning the refill object when refill is configured
  • Test cases validate proper handling of refill configurations, including:
    • Default values (e.g., refillDay defaults to 1 for monthly intervals)
    • Validation of invalid configurations (e.g., rejecting refillDay with daily intervals)
    • Proper persistence and retrieval of refill settings
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for consumers of the API response
rg -A 5 'refill.*interval.*amount' 

# Search for any error handling related to refill
rg -A 3 'catch.*refill'

Length of output: 62


Script:

#!/bin/bash
# Search for API response consumers using different patterns
rg -A 5 '"refill".*:' --type ts
rg -A 5 'interface.*Key.*{' --type ts
rg -A 5 'type.*Key.*{' --type ts

# Search for test files that might validate the response
fd -e test.ts -e spec.ts -x rg -l 'refill'

Length of output: 557


Script:

#!/bin/bash
# Fix the regex patterns and search for interfaces and types
rg -A 5 'interface.*Key' --type ts
rg -A 5 'type.*Key' --type ts

# Check the test files that contain refill-related tests
rg -A 10 'refill' ./apps/api/src/routes/v1_keys_updateKey.happy.test.ts
rg -A 10 'refill' ./apps/api/src/routes/v1_keys_updateKey.error.test.ts

Length of output: 66583


Script:

#!/bin/bash
# Let's check the schema and interface definitions to understand the refill object structure
ast-grep --pattern 'interface $name {
  $$$
  refill
  $$$
}'

# Search for refill-related tests and validations
rg -A 10 'test.*refill' ./apps/api/src/routes/v1_keys_updateKey.happy.test.ts
rg -A 10 'test.*refill' ./apps/api/src/routes/v1_keys_updateKey.error.test.ts

# Search for refill-related OpenAPI schema definitions
rg -A 10 'refill.*:.*{' --type ts

Length of output: 1045


Script:

#!/bin/bash
# Fix the regex pattern and search for refill schema definitions
rg -A 10 '"refill":\s*{' --type ts
rg -A 10 'refill:\s*{' --type ts

# Search for refill-related test cases with fixed pattern
rg -B 5 -A 15 'refill' ./apps/api/src/routes/v1_keys_updateKey.happy.test.ts
rg -B 5 -A 15 'refill' ./apps/api/src/routes/v1_keys_updateKey.error.test.ts

# Look for schema validation related to refill
rg -A 10 'schema.*refill' --type ts

Length of output: 4896

apps/dashboard/components/dashboard/api-key-table/index.tsx (1)

54-54: LGTM: Column definition and display logic updated correctly

The changes properly reflect the migration from refillInterval to refillDay, with appropriate handling of null values and default display text.

Also applies to: 184-189

apps/api/src/routes/v1_migrations_createKey.ts (1)

414-419: Verify refillDay handling logic

The logic for setting refillDay looks correct, but let's verify the implementation across the codebase.

Also applies to: 437-438

✅ Verification successful

Let me gather more context about the refillDay handling logic.


Let me check one more aspect to verify the validation logic across different routes.


RefillDay handling logic is correctly implemented

The verification confirms that the refillDay logic is consistently implemented across the codebase:

  • Monthly interval defaults to refillDay=1 when undefined
  • Daily interval explicitly removes refillDay as it's not applicable
  • Proper validation exists in update routes to prevent setting refillDay with daily interval
  • The behavior is documented in SDK documentation with correct defaults
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify refillDay handling across the codebase

# Check for any hardcoded refillInterval values that might have been missed
rg -l "refillInterval"

# Check for refillDay usage to ensure consistent handling
rg "refillDay.*=.*1"

Length of output: 1523


Script:

#!/bin/bash
# Check the schema definition and validation logic for refillDay
ast-grep --pattern 'refillDay'

# Check for any conditional logic related to refillDay and interval
rg -B2 -A2 'refillDay.*interval'

Length of output: 860


Script:

#!/bin/bash
# Check for validation logic in create and update routes
rg -B3 -A3 "interval.*===.*('|\")monthly('|\")" apps/api/src/routes/

# Check for any schema definitions related to refill intervals
ast-grep --pattern 'interval: z.enum'

Length of output: 751

apps/api/src/routes/v1_keys_createKey.ts (3)

113-113: LGTM: Schema change aligns with PR objective

Making the interval field optional in the refill schema is a good approach for backward compatibility.


315-327: LGTM: Clear validation logic for refill configuration

The validation ensures that:

  1. remaining is set when using refill
  2. refill.amount is set when using refill

This prevents incomplete or invalid configurations.


378-381: LGTM: Database field assignments handle optional refill configuration

The changes correctly handle the optional nature of refill-related fields and properly initialize lastRefillAt to null.

apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/new/client.tsx (2)

94-95: LGTM: Form submission logic properly handles optional refill configuration

The changes correctly:

  1. Remove the refill object when amount is not set
  2. Construct a simplified refill object with only the necessary fields

Also applies to: 105-108


514-514: LGTM: Simplified disabled state logic for refillDay field

The field is now disabled based solely on the presence of refill.amount, which aligns with the removal of the interval-based logic.

Comment on lines +5 to +7
const conn = await mysql.createConnection(
`mysql://${process.env.DATABASE_USERNAME}:${process.env.DATABASE_PASSWORD}@${process.env.DATABASE_HOST}:3306/unkey?ssl={}`,
);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling for database connection.

The database connection setup needs proper error handling and should use environment variables for the port number.

-  const conn = await mysql.createConnection(
-    `mysql://${process.env.DATABASE_USERNAME}:${process.env.DATABASE_PASSWORD}@${process.env.DATABASE_HOST}:3306/unkey?ssl={}`,
-  );
+  try {
+    const port = process.env.DATABASE_PORT || '3306';
+    const conn = await mysql.createConnection(
+      `mysql://${process.env.DATABASE_USERNAME}:${process.env.DATABASE_PASSWORD}@${process.env.DATABASE_HOST}:${port}/unkey?ssl={}`,
+    );
+  } catch (error) {
+    console.error('Failed to connect to database:', error);
+    process.exit(1);
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const conn = await mysql.createConnection(
`mysql://${process.env.DATABASE_USERNAME}:${process.env.DATABASE_PASSWORD}@${process.env.DATABASE_HOST}:3306/unkey?ssl={}`,
);
try {
const port = process.env.DATABASE_PORT || '3306';
const conn = await mysql.createConnection(
`mysql://${process.env.DATABASE_USERNAME}:${process.env.DATABASE_PASSWORD}@${process.env.DATABASE_HOST}:${port}/unkey?ssl={}`,
);
} catch (error) {
console.error('Failed to connect to database:', error);
process.exit(1);
}

Comment on lines 14 to 49
do {
const keys = await db.query.keys.findMany({
where: (table, { isNotNull, gt, and }) =>
and(
gt(table.id, cursor),
isNotNull(table.refillInterval),
isNotNull(table.refillAmount),
isNotNull(table.remaining),
),
limit: 1000,
orderBy: (table, { asc }) => asc(table.id),
});

cursor = keys.at(-1)?.id ?? "";
console.info({ cursor, keys: keys.length });

for (const key of keys) {
if (key.refillInterval === "monthly") {
if (key.refillDay === null) {
key.refillDay = 1;
}
key.refillInterval = null;
}
if (key.refillInterval === "daily") {
key.refillDay = null;
key.refillInterval = null;
}
const changed = await db
.update(schema.keys)
.set({ refillDay: key.refillDay, refillInterval: key.refillInterval })
.where(eq(schema.keys.id, key.id));
if (changed) {
keyChanges++;
}
}
} while (cursor);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add transaction management and improve error handling.

The migration loop should use transactions and handle errors properly. Also consider adding a dry-run option and better progress tracking.

+  const DRY_RUN = process.env.DRY_RUN === 'true';
+  let totalProcessed = 0;
   do {
+    const transaction = await conn.beginTransaction();
     try {
       const keys = await db.query.keys.findMany({
         where: (table, { isNotNull, gt, and }) =>
           and(
             gt(table.id, cursor),
             isNotNull(table.refillInterval),
             isNotNull(table.refillAmount),
             isNotNull(table.remaining),
           ),
         limit: 1000,
         orderBy: (table, { asc }) => asc(table.id),
       });

       cursor = keys.at(-1)?.id ?? "";
-      console.info({ cursor, keys: keys.length });
+      totalProcessed += keys.length;
+      console.info(`Processing batch: cursor=${cursor}, batch_size=${keys.length}, total_processed=${totalProcessed}`);

       for (const key of keys) {
+        try {
           if (key.refillInterval === "monthly") {
             if (key.refillDay === null) {
               key.refillDay = 1;
             }
             key.refillInterval = null;
           }
           if (key.refillInterval === "daily") {
             key.refillDay = null;
             key.refillInterval = null;
           }
-          const changed = await db
+          if (!DRY_RUN) {
+            const changed = await db
             .update(schema.keys)
             .set({ refillDay: key.refillDay, refillInterval: key.refillInterval })
             .where(eq(schema.keys.id, key.id));
-          if (changed) {
-            keyChanges++;
+            if (changed) {
+              keyChanges++;
+            }
           }
+        } catch (error) {
+          console.error(`Failed to update key ${key.id}:`, error);
+          throw error; // Propagate to trigger transaction rollback
+        }
       }
+      await transaction.commit();
+    } catch (error) {
+      await transaction.rollback();
+      console.error('Batch failed, rolling back:', error);
+      process.exit(1);
+    }
   } while (cursor);

Committable suggestion skipped: line range outside the PR's diff.

apps/dashboard/lib/trpc/routers/key/updateRemaining.ts Outdated Show resolved Hide resolved
@MichaelUnkey MichaelUnkey marked this pull request as draft December 17, 2024 14:58
@@ -219,34 +167,40 @@ export const UpdateKeyRemaining: React.FC<Props> = ({ apiKey }) => {
className="w-full"
type="number"
{...field}
value={form.watch("refill.interval") === "none" ? undefined : field.value}
value={form.getValues("limitEnabled") ? field.value : undefined}
Copy link
Contributor

Choose a reason for hiding this comment

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

getValues won't get updated when limitEnabled changes, so that might cause some issues. Just giving heads up.

Comment on lines +183 to +185
form.watch("remaining") === undefined ||
form.watch("remaining") === null ||
!form.watch("limitEnabled") === true
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we can do this !form.watch("limitEnabled") instead of !form.watch("limitEnabled") === true

}
if (refill?.interval === "monthly" && !refill.refillDay) {
refill.refillDay = 1;
if (!values.limit?.refill?.amount) {
Copy link
Contributor

Choose a reason for hiding this comment

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

If amount ever becomes 0 this won't work.

refillAmount: input.refill?.amount ?? null,
lastRefillAt: input.refill?.interval ? new Date() : null,
lastRefillAt: input.refill?.amount ? new Date() : null,
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure if that's possible, but same scenario applies here as well. If amount becomes 0 this will yield null.

Comment on lines +156 to +157
form.watch("remaining") === undefined ||
form.watch("remaining") === null ||
Copy link
Contributor

Choose a reason for hiding this comment

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

If remaining being 0 is not a concern let's just do !form.watch("remaining")

Comment on lines +183 to +184
form.watch("remaining") === undefined ||
form.watch("remaining") === null ||
Copy link
Contributor

Choose a reason for hiding this comment

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

If remaining being 0 is not a concern let's just do !form.watch("remaining")

@@ -403,6 +409,12 @@ export const registerV1MigrationsCreateKeys = (app: App) =>

const hash = key.plaintext ? await sha256(key.plaintext) : key.hash!.value;

if (key.refill?.interval === "monthly" && key.refill?.refillDay === undefined) {
Copy link
Contributor

Choose a reason for hiding this comment

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

See comments below.

message: "remaining must be set if you are using refill.",
});
}
if (!req.refill.amount) {
Copy link
Contributor

Choose a reason for hiding this comment

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

0 amount will cause false negative here.

message: "when interval is set to 'daily', 'refillDay' must be null.",
});
if (req.refill) {
if (req.remaining === null || req.remaining === undefined) {
Copy link
Contributor

Choose a reason for hiding this comment

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

see comments below.

@MichaelUnkey MichaelUnkey marked this pull request as ready for review December 17, 2024 21:33
Copy link
Contributor

@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: 5

🧹 Nitpick comments (1)
apps/api/src/routes/v1_keys_createKey.ts (1)

317-322: Simplify the remaining check

The check for remaining can be simplified using optional chaining.

-      if (req.remaining === null || req.remaining === undefined) {
+      if (req.remaining == null) {
         throw new UnkeyApiError({
           code: "BAD_REQUEST",
           message: "remaining must be set if you are using refill.",
         });
       }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 68f270e and 16df3fc.

📒 Files selected for processing (10)
  • apps/api/src/pkg/key_migration/message.ts (0 hunks)
  • apps/api/src/routes/v1_apis_listKeys.ts (1 hunks)
  • apps/api/src/routes/v1_keys_createKey.error.test.ts (1 hunks)
  • apps/api/src/routes/v1_keys_createKey.ts (3 hunks)
  • apps/api/src/routes/v1_keys_getKey.ts (1 hunks)
  • apps/api/src/routes/v1_migrations_createKey.ts (2 hunks)
  • apps/dashboard/lib/trpc/routers/key/create.ts (1 hunks)
  • apps/dashboard/lib/trpc/routers/key/updateRemaining.ts (3 hunks)
  • internal/db/src/schema/key_migrations.ts (0 hunks)
  • tools/migrate/refill-migrate.ts (1 hunks)
💤 Files with no reviewable changes (2)
  • apps/api/src/pkg/key_migration/message.ts
  • internal/db/src/schema/key_migrations.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/api/src/routes/v1_keys_getKey.ts
  • tools/migrate/refill-migrate.ts
  • apps/dashboard/lib/trpc/routers/key/updateRemaining.ts
  • apps/api/src/routes/v1_migrations_createKey.ts
🧰 Additional context used
📓 Learnings (1)
apps/api/src/routes/v1_keys_createKey.error.test.ts (1)
Learnt from: MichaelUnkey
PR: unkeyed/unkey#2114
File: apps/api/src/routes/v1_keys_updateKey.error.test.ts:0-0
Timestamp: 2024-11-10T16:45:16.994Z
Learning: In the `v1/keys.updateKey` endpoint, the server validates the refill configuration before checking if the key exists. Therefore, tests can assert validation errors without needing to create the key first.
🔇 Additional comments (3)
apps/dashboard/lib/trpc/routers/key/create.ts (1)

104-107: ⚠️ Potential issue

Inconsistency with PR objectives regarding refillDay value

According to the PR objectives, when the interval is monthly, refillDay should be set to 1. However, the current implementation allows any value between 1-31 from the input.

Consider updating the code to enforce this requirement:

-          refillInterval: input.refill?.refillDay ? "monthly" : "daily",
-          refillDay: input.refill?.refillDay ?? null,
+          refillInterval: input.refill?.refillDay ? "monthly" : "daily",
+          refillDay: input.refill?.refillDay ? 1 : null,

Likely invalid or redundant comment.

apps/api/src/routes/v1_apis_listKeys.ts (1)

Line range hint 1-1: Verify complete removal of refillInterval references

Let's verify that all references to refillInterval are being properly handled across the codebase.

✅ Verification successful

Let me gather more context about the usage of interval in these files to understand if they are related to the old refillInterval or if they represent a different concept.


No issues found with refillInterval references

The search results show that there are no remaining references to refillInterval in the codebase. The occurrences of interval found are part of the current, valid schema design where it's properly used within the refill object structure alongside amount. This is consistent across all files (createKey, updateKey, schema, and enqueueKeys) and represents the intended API design.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining references to refillInterval
echo "Searching for refillInterval references..."
rg "refillInterval" --type ts --type tsx

# Search for any remaining interval-related logic in schemas
echo "Searching for interval in schemas..."
ast-grep --pattern 'z.enum(["daily", "monthly"])'

Length of output: 756


Script:

#!/bin/bash
# Check the context around interval usage in these files
echo "Checking context in createKey..."
rg -B 5 -A 5 "interval: z\.enum" apps/api/src/routes/v1_keys_createKey.ts

echo "Checking context in schema..."
rg -B 5 -A 5 "interval: z\.enum" apps/api/src/routes/schema.ts

echo "Checking context in enqueueKeys..."
rg -B 5 -A 5 "interval: z\.enum" apps/api/src/routes/v1_migrations_enqueueKeys.ts

echo "Checking context in updateKey..."
rg -B 5 -A 5 "interval: z\.enum" apps/api/src/routes/v1_keys_updateKey.ts

Length of output: 2828

apps/api/src/routes/v1_keys_createKey.ts (1)

329-334: LGTM: Daily interval validation

The validation ensuring refillDay is null for daily intervals is correct and necessary.

@@ -146,7 +146,7 @@ test("reject invalid refill config when daily interval has non-null refillDay",
error: {
code: "BAD_REQUEST",
docs: "https://unkey.dev/docs/api-reference/errors/code/BAD_REQUEST",
message: "when interval is set to 'daily', 'refillDay' must be null.",
message: "When interval is set to 'daily', 'refillDay' must be null.",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Test case needs to be updated for new refill logic

This test case appears to be testing the old behavior where daily interval cannot have a refillDay. However, according to the PR objectives, we're removing the refillInterval concept entirely. This test should either be removed or updated to reflect the new behavior.

Consider removing this test case entirely since it's testing deprecated behavior, or update it to test the new requirements where:

  • Monthly interval should always have refillDay as 1
  • Daily interval should have refillDay as null

Comment on lines +321 to +325
k.refillAmount && k.lastRefillAt
? {
interval: k.refillInterval,
interval: k.refillInterval ?? undefined,
amount: k.refillAmount,
refillDay: k.refillInterval === "monthly" && k.refillDay ? k.refillDay : null,
refillDay: k.refillDay ?? null,
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove interval from response to align with PR objectives

The PR objective is to remove the refillInterval concept, but the response still includes the interval field. This could cause confusion for API consumers.

Consider updating the refill object construction:

          k.refillAmount && k.lastRefillAt
            ? {
-                interval: k.refillInterval ?? undefined,
                 amount: k.refillAmount,
                 refillDay: k.refillDay ?? null,
                 lastRefillAt: k.lastRefillAt?.getTime(),
              }
            : undefined,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
k.refillAmount && k.lastRefillAt
? {
interval: k.refillInterval,
interval: k.refillInterval ?? undefined,
amount: k.refillAmount,
refillDay: k.refillInterval === "monthly" && k.refillDay ? k.refillDay : null,
refillDay: k.refillDay ?? null,
k.refillAmount && k.lastRefillAt
? {
amount: k.refillAmount,
refillDay: k.refillDay ?? null,

Comment on lines +385 to +388
refillInterval: req.refill?.interval ?? null,
refillDay: req.refill?.interval === "daily" ? null : req?.refill?.refillDay ?? 1,
refillAmount: req.refill?.amount,
lastRefillAt: req.refill?.interval ? new Date() : null,
lastRefillAt: null,
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix inconsistency with refillDay default value

According to the PR objectives, refillDay should be set to 1 only when the interval is monthly, but the current implementation sets it to 1 by default even when the interval is null.

-        refillDay: req.refill?.interval === "daily" ? null : req?.refill?.refillDay ?? 1,
+        refillDay: req.refill?.interval === "monthly" ? (req?.refill?.refillDay ?? 1) : null,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
refillInterval: req.refill?.interval ?? null,
refillDay: req.refill?.interval === "daily" ? null : req?.refill?.refillDay ?? 1,
refillAmount: req.refill?.amount,
lastRefillAt: req.refill?.interval ? new Date() : null,
lastRefillAt: null,
refillInterval: req.refill?.interval ?? null,
refillDay: req.refill?.interval === "monthly" ? (req?.refill?.refillDay ?? 1) : null,
refillAmount: req.refill?.amount,
lastRefillAt: null,

Comment on lines +323 to +328
if (!req.refill.amount) {
throw new UnkeyApiError({
code: "BAD_REQUEST",
message: "refill.amount must be set if you are using refill.",
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove redundant validation

This validation for refill.amount is redundant as it's already enforced by the schema definition (required field with min(1).positive()).

-      if (!req.refill.amount) {
-        throw new UnkeyApiError({
-          code: "BAD_REQUEST",
-          message: "refill.amount must be set if you are using refill.",
-        });
-      }

@@ -110,7 +110,7 @@
}),
refill: z
.object({
interval: z.enum(["daily", "monthly"]).openapi({
interval: z.enum(["daily", "monthly"]).optional().openapi({
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Schema consistency needs to be updated across multiple files

The verification reveals inconsistencies in the interval schema definition across different files:

  • v1_keys_createKey.ts: interval is correctly marked as optional
  • v1_migrations_enqueueKeys.ts: interval is not optional
  • v1_keys_updateKey.ts: interval is not optional
  • schema.ts: interval is correctly marked as optional

These files need to be updated to maintain consistency with the PR objective of removing refillInterval:

  • apps/api/src/routes/v1_migrations_enqueueKeys.ts
  • apps/api/src/routes/v1_keys_updateKey.ts
🔗 Analysis chain

Verify schema consistency across the codebase

The change to make interval optional aligns with the PR objective. Let's verify this change is consistent across all schema definitions.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining non-optional interval definitions in schemas
# and verify the change is consistent across the codebase

# Search for schema definitions containing interval
rg -A 5 'interval.*enum.*daily.*monthly' 

# Search for any remaining refillInterval references
rg 'refillInterval'

Length of output: 10139

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