Skip to content

Comments

Make large exports go to ActiveStorage#990

Merged
skyfallwastaken merged 3 commits intomainfrom
large-exports
Feb 21, 2026
Merged

Make large exports go to ActiveStorage#990
skyfallwastaken merged 3 commits intomainfrom
large-exports

Conversation

@skyfallwastaken
Copy link
Member

No description provided.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 21, 2026

Greptile Summary

This PR moves heartbeat data exports from direct email attachments to ActiveStorage-hosted zip files with time-limited download links. Instead of attaching potentially large JSON files directly to emails, exports are now compressed into zip archives, uploaded to ActiveStorage, and recipients receive an email with a download link that expires after 7 days (enforced by a scheduled HeartbeatExportCleanupJob).

  • Critical: rubyzip gem is missing from the Gemfile. It's only available as a transitive dependency of selenium-webdriver (test group), so require "zip" in HeartbeatExportJob will fail in production with a LoadError.
  • Adds standard ActiveStorage migration and tables (active_storage_blobs, active_storage_attachments, active_storage_variant_records).
  • Introduces HeartbeatExportCleanupJob to purge blobs after 7 days, with a metadata guard to avoid accidentally deleting non-export blobs.
  • Tests are thorough: covers blob creation, cleanup scheduling, zip content verification, and email download link presence.

Confidence Score: 2/5

  • This PR will crash in production due to a missing gem dependency and should not be merged until rubyzip is added to the Gemfile.
  • The architecture and test coverage are solid, but the missing rubyzip gem declaration in the Gemfile is a blocking issue — the export job will raise a LoadError in production since rubyzip is only a transitive dependency of selenium-webdriver (test group). Once fixed, this would be a high-confidence merge.
  • app/jobs/heartbeat_export_job.rb — requires rubyzip gem which is not available in production. The Gemfile also needs updating.

Important Files Changed

Filename Overview
app/jobs/heartbeat_export_job.rb Core change: switches from email attachment to ActiveStorage blob + zip. Critical issue: require "zip" depends on rubyzip gem which is only a transitive test dependency — will crash in production.
app/jobs/heartbeat_export_cleanup_job.rb New cleanup job that purges ActiveStorage blobs after 7 days. Simple, well-guarded with nil and metadata checks.
app/mailers/heartbeat_export_mailer.rb Switched from file attachment to download URL via rails_blob_url. Clean change.
app/views/heartbeat_export_mailer/export_ready.html.erb Updated HTML email template to show download link instead of attachment notice. Properly auto-escaped by ERB.
db/migrate/20260221113553_create_active_storage_tables.active_storage.rb Standard Rails ActiveStorage migration generated by rails active_storage:install. No custom modifications.
test/jobs/heartbeat_export_cleanup_job_test.rb New test for cleanup job covering both purge and safety guard cases. Good coverage.
test/jobs/heartbeat_export_job_test.rb Updated tests to verify blob creation, cleanup job scheduling, zip contents, and download link in email. Thorough.
test/mailers/heartbeat_export_mailer_test.rb Updated mailer test to verify download link presence instead of attachment. Properly cleans up blob after test.

Sequence Diagram

sequenceDiagram
    participant User
    participant HeartbeatExportJob
    participant ActiveStorage
    participant HeartbeatExportMailer
    participant HeartbeatExportCleanupJob

    User->>HeartbeatExportJob: Trigger export (user_id, params)
    HeartbeatExportJob->>HeartbeatExportJob: Query heartbeats & build JSON
    HeartbeatExportJob->>HeartbeatExportJob: Compress JSON into ZIP (Tempfile)
    HeartbeatExportJob->>ActiveStorage: create_and_upload!(zip, metadata)
    ActiveStorage-->>HeartbeatExportJob: blob
    HeartbeatExportJob->>HeartbeatExportCleanupJob: schedule cleanup in 7 days (blob.id)
    HeartbeatExportJob->>HeartbeatExportMailer: export_ready(user, blob, filename)
    HeartbeatExportMailer->>HeartbeatExportMailer: Generate rails_blob_url(blob)
    HeartbeatExportMailer-->>User: Email with download link
    User->>ActiveStorage: GET download link
    ActiveStorage-->>User: ZIP file download
    Note over HeartbeatExportCleanupJob: After 7 days...
    HeartbeatExportCleanupJob->>ActiveStorage: blob.purge
Loading

Last reviewed commit: 7f79bff

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.

10 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

@skyfallwastaken skyfallwastaken enabled auto-merge (squash) February 21, 2026 11:51
@skyfallwastaken skyfallwastaken merged commit 1b7e046 into main Feb 21, 2026
13 checks passed
@skyfallwastaken skyfallwastaken deleted the large-exports branch February 21, 2026 11:53
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.

1 participant