Skip to content
This repository was archived by the owner on Feb 8, 2026. It is now read-only.

feat: add schedule validation and time trigger creation logic#82

Merged
johnnyluo merged 4 commits into
mainfrom
validate-schedule
Jun 4, 2025
Merged

feat: add schedule validation and time trigger creation logic#82
johnnyluo merged 4 commits into
mainfrom
validate-schedule

Conversation

@johnnyluo

@johnnyluo johnnyluo commented Jun 3, 2025

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features
    • Added support for a biweekly schedule frequency.
    • Introduced validation for schedule configurations in payroll policies.
  • Improvements
    • Scheduling now uses more precise types for frequency, improving reliability and validation.
    • Enhanced error handling during scheduler service initialization.
    • Unified context validation in database operations for better error handling.
  • Bug Fixes
    • Improved validation to prevent invalid schedule configurations.
  • Database
    • Updated the frequency column in the time triggers table from text to integer for better data consistency.
  • Tests
    • Added tests for time trigger lifecycle operations in the database.
  • Dependencies
    • Updated an external dependency to a newer version.

Copilot AI review requested due to automatic review settings June 3, 2025 23:25
@coderabbitai

coderabbitai Bot commented Jun 3, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

The changes introduce protobuf-based scheduling types and enums, refactor scheduler logic to use these types, and enhance validation for policy schedules. Error handling is improved in the scheduler service initialization. The frequency field for time triggers is migrated from string to integer in both code and database schema. A new test validates time trigger operations. Minor formatting and dependency version updates are also included.

Changes

File(s) Change Summary
api/server.go Scheduler service initialization now handles errors; removed explicit service start and log.
internal/scheduler/scheduler.go, internal/types/time_trigger.go Refactored to use protobuf-based types/enums for scheduling; improved error handling; added biweekly.
plugin/payroll/policy.go Added schedule validation method; renamed rule check method; minor validation rearrangements.
storage/postgres/migrations/plugin/20241211182120_add_time_triggers.sql Changed frequency column type in time_triggers table from TEXT to INT.
storage/postgres/time_trigger_test.go Added test for time trigger lifecycle in Postgres backend.
storage/postgres/time_trigger.go Added context validation helper; replaced pool nil checks with context checks in Postgres methods.
go.mod Updated github.com/vultisig/recipes dependency version.
storage/postgres/db.go Renamed receiver variable in methods; minor formatting adjustments.
cmd/payroll/worker/main.go Added a blank line after redisOptions initialization; no logic changes.

Sequence Diagram(s)

sequenceDiagram
    participant Server
    participant SchedulerService
    participant Logger

    Server->>SchedulerService: NewSchedulerService(...)
    alt Error
        SchedulerService-->>Server: error
        Server->>Logger: Log fatal error
        Server-->>Caller: return nil
    else Success
        SchedulerService-->>Server: SchedulerService instance, nil
        Server->>Caller: return Server instance
    end
Loading
sequenceDiagram
    participant Plugin
    participant Schedule
    participant Validator

    Plugin->>Validator: validateSchedule(schedule)
    alt schedule is nil or frequency unspecified or start time missing
        Validator-->>Plugin: return error
    else end time before start time
        Validator-->>Plugin: return error
    else
        Validator-->>Plugin: success
    end
Loading

Poem

🐇
Hopping through the code so neat,
Schedules now with enums beat!
Triggers ticking, tests in tow,
Migrations help the schema grow.
With protobuf types, we leap ahead—
Robust and tidy, just as said!
—Your friendly coding rabbit

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.

🔧 golangci-lint (1.64.8)

level=warning msg="[runner] Can't run linter goanalysis_metalinter: buildir: failed to load package session: could not load export data: no export data for "github.com/vultisig/go-wrappers/go-dkls/sessions""
level=error msg="Running error: can't run linter goanalysis_metalinter\nbuildir: failed to load package session: could not load export data: no export data for "github.com/vultisig/go-wrappers/go-dkls/sessions""


📜 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 ab533ef and e049925.

📒 Files selected for processing (1)
  • storage/postgres/time_trigger.go (7 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
storage/postgres/time_trigger.go (2)
storage/postgres/db.go (1)
  • PostgresBackend (19-21)
internal/types/time_trigger.go (1)
  • TimeTrigger (17-26)
🔇 Additional comments (3)
storage/postgres/time_trigger.go (3)

15-21: LGTM! Clean context validation helper.

The implementation correctly validates context state and follows Go best practices for context handling.


66-66: Frequency field inclusion aligns with schema migration.

Good addition of the frequency field to the SELECT statement, consistent with the frequency field migration from string to integer mentioned in the PR summary.


24-26:

Details

✅ Verification successful

Verify that removing pool nil checks is safe.

While centralizing context validation is good, ensure that replacing p.pool == nil checks with context validation doesn't introduce potential panics. Context validation serves request lifecycle management, while pool validation prevents panics from uninitialized database connections.

Also applies to: 48-50, 59-61, 107-109, 122-124, 145-147, 168-170


🏁 Script executed:

#!/bin/bash
# Description: Verify if PostgresBackend.pool can be nil during normal operation
# Expected: Find initialization patterns and usage to confirm pool is never nil after proper setup

# Search for PostgresBackend initialization and pool assignment
ast-grep --pattern 'func $_($$$) *PostgresBackend {
  $$$
}'

# Search for any code that might set pool to nil
rg -A 3 -B 3 "\.pool\s*=\s*nil"

# Search for defensive nil checks in other PostgresBackend methods
rg -A 2 -B 2 "pool.*==.*nil|nil.*==.*pool" --type go

Length of output: 858


🏁 Script executed:

#!/bin/bash
set -eo pipefail

echo "===== type PostgresBackend ====="
rg -n "type PostgresBackend" storage/postgres || echo "No type declaration found"

echo "===== struct literal &PostgresBackend ====="
rg -n "&PostgresBackend" storage/postgres || echo "No struct literals found"

echo "===== PostgresBackend{ usage ====="
rg -n "PostgresBackend{" storage/postgres || echo "No inline struct literals found"

echo "===== pool: field in literals ====="
rg -n "pool:" storage/postgres || echo "No pool field usage in literals"

echo "===== .pool = assignment ====="
rg -n "\.pool\s*=" storage/postgres || echo "No direct pool assignments found"

echo "===== func New* ====="
rg -n "func New" storage/postgres || echo "No factory functions starting with New found"

echo "===== func .*Backend ====="
rg -n "func .*Backend" storage/postgres || echo "No functions returning or named Backend found"

Length of output: 5133


PostgresBackend.pool is always initialized by NewPostgresBackend; nil checks are redundant and safe to remove

✔ NewPostgresBackend (db.go:28–35) always assigns a non-nil pgxpool.Pool or returns an error.
✔ There are no assignments of p.pool = nil, nor any other constructors for PostgresBackend.
✔ Removing p.pool == nil guards in time_trigger.go will not introduce panics.

Optional: you may clean up the remaining p.pool == nil checks in policy.go for consistency, but they’re no longer functionally needed.

✨ 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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull Request Overview

This PR introduces schedule validation and time trigger creation logic for the plugin. Key changes include adding tests for time triggers, updating the scheduling logic in the scheduler and policy components, and adjusting the database migration for time trigger frequency storage.

Reviewed Changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
storage/postgres/time_trigger_test.go Adds tests for creating, retrieving, and deleting time triggers.
storage/postgres/migrations/plugin/20241211182120_add_time_triggers.sql Updates the frequency column type from TEXT to INT.
storage/postgres/db.go Refactors receiver names and updates migration function usage.
plugin/payroll/policy.go Introduces schedule validation and renames rule validation functions.
internal/types/time_trigger.go Changes Frequency field type to use the enum rtypes.ScheduleFrequency.
internal/scheduler/scheduler.go Refactors scheduler service and schedule creation logic to use updated types.
go.mod Upgrades package dependency for recipes.
cmd/payroll/worker/main.go Minor formatting change.
api/server.go Adjusts scheduler service initialization to handle errors.
Comments suppressed due to low confidence (1)

storage/postgres/time_trigger_test.go:16

  • The test is being skipped unconditionally, which reduces test coverage for the time trigger functionality. If intentional, consider adding a comment explaining the rationale.
t.SkipNow()

Comment thread internal/scheduler/scheduler.go Outdated
Comment thread plugin/payroll/policy.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🔭 Outside diff range comments (1)
plugin/payroll/policy.go (1)

215-217: 🛠️ Refactor suggestion

Consider utilizing the new validateSchedule method.

After unmarshalling the policy, you check if the schedule is nil but don't call the validateSchedule method that was just added. Consider using it for comprehensive validation.

 if rPolicy.Schedule == nil {
     return fmt.Errorf("policy schedule is nil")
 }
+if err := p.validateSchedule(rPolicy.Schedule); err != nil {
+    return fmt.Errorf("invalid schedule: %w", err)
+}

Also applies to: 155-172

🧹 Nitpick comments (3)
storage/postgres/time_trigger_test.go (2)

16-16: Consider removing or documenting the reason for skipping this test.

The test is currently skipped by default, which means it won't run in CI/CD pipelines. If this is intentional (e.g., requires specific database setup), please add a comment explaining why and when it should be enabled.

+// Skip this test by default as it requires a running Postgres instance.
+// To run: go test -v -run TestTimeTrigger ./storage/postgres
 t.SkipNow()

41-53: Consider using a cleaner approach for transaction rollback.

The transaction rollback pattern could be simplified using defer to ensure cleanup even if the test panics.

 ctx := context.Background()
 tx, err := backend.pool.Begin(ctx)
 if err != nil {
     t.Fatalf("Failed to begin transaction: %v", err)
 }
+defer func() {
+    if err != nil {
+        tx.Rollback(ctx)
+    }
+}()
+
 if _, err := backend.InsertPluginPolicyTx(ctx, tx, vtypes.PluginPolicy{
     ID:            policyID,
     PublicKey:     "4a7b9c2f8e1d3a5b6c4f9e2d7a1b3c5f8d4e6a2b9c1f7e3d5a6b4c2f8e1d3a5b",
     PluginID:      "vultisig-payroll-0000",
     PluginVersion: "1",
     PolicyVersion: "1",
     Signature:     "whatever",
     Recipe:        "k3uaL47TpWtsT54tfhO7K6Gbyxf4H71jWma0zC2e3kQ=",
     Active:        true,
 }); err != nil {
-    tx.Rollback(ctx)
     t.Fatalf("Failed to insert plugin policy: %v", err)
 }
internal/scheduler/scheduler.go (1)

185-193: Remove unused struct definition.

The policySchedule struct defined at the beginning of GetTriggerFromPolicy is no longer used after the refactoring to protobuf types.

 func (s *SchedulerService) GetTriggerFromPolicy(policy vtypes.PluginPolicy) (*types.TimeTrigger, error) {
-	var policySchedule struct {
-		Schedule struct {
-			Frequency string     `json:"frequency"`
-			StartTime time.Time  `json:"start_time"`
-			Interval  string     `json:"interval"`
-			EndTime   *time.Time `json:"end_time,omitempty"`
-		} `json:"schedule"`
-	}
 	var p rtypes.Policy
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 02bc3d8 and a0ba6ab.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (9)
  • api/server.go (1 hunks)
  • cmd/payroll/worker/main.go (1 hunks)
  • go.mod (1 hunks)
  • internal/scheduler/scheduler.go (6 hunks)
  • internal/types/time_trigger.go (2 hunks)
  • plugin/payroll/policy.go (3 hunks)
  • storage/postgres/db.go (3 hunks)
  • storage/postgres/migrations/plugin/20241211182120_add_time_triggers.sql (1 hunks)
  • storage/postgres/time_trigger_test.go (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
storage/postgres/db.go (2)
storage/postgres/db_system.go (1)
  • NewSystemMigrationManager (18-20)
storage/postgres/db_plugin.go (1)
  • NewPluginMigrationManager (21-23)
storage/postgres/time_trigger_test.go (2)
storage/postgres/db.go (1)
  • NewPostgresBackend (28-52)
internal/types/time_trigger.go (1)
  • TimeTrigger (17-26)
api/server.go (1)
internal/scheduler/scheduler.go (1)
  • NewSchedulerService (34-50)
🔇 Additional comments (11)
cmd/payroll/worker/main.go (1)

35-35: LGTM - Minor formatting improvement.

The added blank line improves code readability by providing visual separation after the redisOptions initialization.

go.mod (1)

21-21: LGTM - Dependency update supports protobuf enum migration.

The updated version of github.com/vultisig/recipes provides the rtypes.ScheduleFrequency enum type needed for the frequency field migration from string to protobuf enum.

storage/postgres/migrations/plugin/20241211182120_add_time_triggers.sql (1)

14-14:

Details

✅ Verification successful

Verify data migration strategy for existing frequency values.

The change from TEXT to INT for the frequency column correctly supports the migration to protobuf enum values. However, ensure that any existing data in the frequency column is properly migrated or that this is a new table without existing data.


🏁 Script executed:

#!/bin/bash
# Description: Check if this is a new table or if there's existing data that needs migration
# Expected: Either no existing migration files or a data migration strategy

# Check for existing time_triggers table usage
rg -A 5 -B 5 "time_triggers" --type sql

Length of output: 2498


New time_triggers table: no data migration needed.

This migration creates the time_triggers table from scratch—there’s no preexisting time_triggers to convert, so you don’t need a data‐migration strategy for the frequency (now interval) column.

storage/postgres/db.go (1)

54-81: LGTM - Improved naming consistency.

Renaming the receiver variable from d to p in the Close and Migrate methods improves consistency with the PostgresBackend struct name. All references are updated correctly, and the minor formatting improvements enhance readability.

internal/types/time_trigger.go (1)

7-7: LGTM - Required import for protobuf enum type.

The import for rtypes is correctly added to support the ScheduleFrequency enum type.

storage/postgres/time_trigger_test.go (1)

31-31:

Details

❌ Incorrect review comment

Verify that the frequency enum value is correctly represented in the database.

The test uses rtypes.ScheduleFrequency_SCHEDULE_FREQUENCY_DAILY which should be stored as an integer in the database according to the migration. Ensure this aligns with the database schema change from TEXT to INT.

Run the following script to verify the enum value mapping:


🏁 Script executed:

#!/bin/bash
# Description: Verify the protobuf enum values for ScheduleFrequency

# Search for the ScheduleFrequency enum definition
ast-grep --pattern 'enum ScheduleFrequency {
  $$$
}'

# Also check for any usage of the enum values to understand the mapping
rg -A 3 'ScheduleFrequency_SCHEDULE_FREQUENCY'

Length of output: 3774


#!/bin/bash
# Search for ScheduleFrequency enum definition in .proto files
rg -n "enum ScheduleFrequency" --context 3 --glob '*.proto'

# Verify the numeric value assigned to SCHEDULE_FREQUENCY_DAILY
rg -n "SCHEDULE_FREQUENCY_DAILY" --context 1 --glob '*.proto'

#!/bin/bash
# Locate the protobuf definition to confirm numeric values
rg -n "enum ScheduleFrequency" -g '*.proto' -A5

# Extract the numeric assignment for DAILY
rg -n "SCHEDULE_FREQUENCY_DAILY" -g '*.proto' -A2

# Inspect how the test maps/scans the Frequency column
rg -n "Frequency:" -C3 storage/postgres/time_trigger_test.go

🏁 Script executed:

#!/bin/bash
# Locate protobuf enum definition for ScheduleFrequency
rg -n "enum ScheduleFrequency" -g '*.proto' -A5

# Extract the numeric value for DAILY
rg -n "SCHEDULE_FREQUENCY_DAILY" -g '*.proto' -A2

# Verify the frequency column type in the migration for time_triggers
rg -n "time_triggers" -g '*.sql' -C3

Length of output: 2338


No separate INT column for Frequency—cron_expression TEXT is used
The time_triggers table stores the schedule in the cron_expression TEXT column rather than a dedicated frequency INT field. There is no direct enum-to-INT mapping in the migration, so the original request to verify an integer representation of ScheduleFrequency_SCHEDULE_FREQUENCY_DAILY does not apply.

Likely an incorrect or invalid review comment.

api/server.go (1)

60-69: Good improvement in error handling for scheduler initialization.

The change properly handles errors from NewSchedulerService and prevents server startup if the scheduler fails to initialize. This is a significant improvement over the previous implementation.

plugin/payroll/policy.go (1)

155-172: Well-structured schedule validation method.

The validation logic is comprehensive and covers all essential checks including nil schedule, frequency validation, start time presence, and end time logic.

internal/scheduler/scheduler.go (3)

34-50: Excellent improvement in error handling.

The change from using logger.Fatal to returning an error is a significant improvement. This allows proper error propagation and gives callers the ability to handle initialization failures gracefully.


194-214: Good migration to protobuf-based schedule handling.

The refactoring to use rtypes.Policy and protobuf types improves type safety and consistency. The error handling and default value handling are well implemented.


421-424: Good handling of month overflow edge case.

The logic correctly handles months with fewer days than the target day (e.g., scheduling for the 31st on months with 30 days). This is a thoughtful implementation detail.

Comment thread internal/types/time_trigger.go
Comment thread storage/postgres/time_trigger_test.go
Comment thread api/server.go
Comment thread plugin/payroll/policy.go Outdated
Comment thread internal/scheduler/scheduler.go Outdated
Comment thread internal/scheduler/scheduler.go

@RaghavSood RaghavSood left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@johnnyluo
johnnyluo merged commit 2ee2971 into main Jun 4, 2025
@johnnyluo
johnnyluo deleted the validate-schedule branch June 4, 2025 02:38
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants