Skip to content

Fix seeding#958

Open
paulcretu wants to merge 4 commits intomainfrom
paul/fix-seeding
Open

Fix seeding#958
paulcretu wants to merge 4 commits intomainfrom
paul/fix-seeding

Conversation

@paulcretu
Copy link
Member

@paulcretu paulcretu commented Sep 21, 2025

  • Seeding was broken due to maintainer emails returning nil from API since they require auth
  • Set to empty list by default
  • Also make seeding fail loudly

Summary by CodeRabbit

  • New Features

    • Add support for specifying maintainer email addresses when creating a feed.
    • Seed page is enabled by default in development when the related environment variable is not set; production visibility remains controlled by the env flag.
  • Chores

    • Improved seeding diagnostics with clearer success and error logs.
    • Stricter error handling during feed retrieval in seeding to surface failures immediately.

@paulcretu paulcretu requested a review from skanderm as a code owner September 21, 2025 06:29
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 21, 2025

Walkthrough

Adds maintainer_emails argument handling to Radio.Feed create action, switches seed action handlers to use feeds!(), introduces logging in seed feed changes, and adjusts UI seed page gating to default-enable in development when the env var is unset.

Changes

Cohort / File(s) Summary
Server: Radio feed create arg and seed handlers
server/lib/orcasite/radio/feed.ex, server/lib/orcasite/radio/seed.ex
Feed: adds create arg maintainer_emails (array of string, default []), and sets attribute via change callback. Seed: replaces calls to feeds() with feeds!() in time_range/latest handlers (now raising on failure).
Server: Seed feeds logging
server/lib/orcasite/radio/seed/changes/seed_feeds.ex
Adds require Logger and logs info on successful bulk create (count) and error on failures (inspected errors).
UI: Seed page gating
ui/src/pages/seed.tsx
getStaticProps now enables the page by default in development when ENABLE_SEED_FROM_PROD is undefined; otherwise respects ENABLE_SEED_FROM_PROD === "true". Return shape unchanged.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client as Client
  participant API as Radio.Feed
  participant DB as DB

  Note over Client,API: Feed creation with maintainer_emails
  Client->>API: create(feed params, maintainer_emails?)
  rect rgba(230,245,255,0.5)
    note right of API: Change callback reads maintainer_emails (default [])
    API->>API: changeset.put_attr(:maintainer_emails, value)
  end
  API->>DB: insert feed with maintainer_emails
  DB-->>API: result
  API-->>Client: created feed or error
Loading
sequenceDiagram
  autonumber
  actor User as User
  participant Next as Next.js Build
  participant Env as Process Env

  Note over User,Next: Seed page static generation
  Next->>Env: read ENABLE_SEED_FROM_PROD, NODE_ENV
  alt ENV var defined
    Next->>Next: enabled = (ENV == "true")
  else ENV var undefined
    Next->>Next: enabled = (NODE_ENV == "development")
  end
  Next-->>User: { notFound: !enabled } or { props: {} }
Loading
sequenceDiagram
  autonumber
  participant Seed as Radio.Seed action
  participant Feed as Feed provider
  participant Logger as Logger

  Note over Seed,Feed: Seed handlers now use feeds!()
  Seed->>Feed: feeds!()  (raises on failure)
  alt Success
    Seed-->>Seed: continue seeding
    Logger-->>Seed: info("Seeded N feeds")
  else Failure
    Seed-->>Seed: exception propagates
    Logger-->>Seed: error("...errors...")
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • dthaler

Poem

A nibble of fields, a hop through the seed,
I toggle the gate by environment’s lead.
With logs that chirp when feeds succeed—
And bangs that throw if things mislead—
I thump my paw: all changes heed! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Fix seeding" concisely captures the primary intent of the pull request, which is to repair seeding behavior by defaulting maintainer_emails to an empty list and making seeding fail loudly as described in the PR. It is directly related to the changeset and not misleading or off-topic. The short phrasing is clear enough for a teammate scanning the history to understand the main change.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch paul/fix-seeding

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@paulcretu paulcretu temporarily deployed to orcasite-pr-958 September 21, 2025 06:33 Inactive
@paulcretu paulcretu requested a review from Copilot September 21, 2025 06:37
Copy link

Copilot AI left a comment

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 fixes broken seeding functionality that was failing due to maintainer emails returning nil from an API that requires authentication. The fix includes setting maintainer emails to an empty list by default and improving seeding error handling.

  • Sets default empty list for maintainer emails when creating feeds
  • Updates seeding to fail loudly with explicit error logging
  • Enables seed page by default in development environment

Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
ui/src/pages/seed.tsx Updates seed page visibility logic to default to enabled in development
server/lib/orcasite/radio/seed/changes/seed_feeds.ex Adds logging for seeding success and error cases
server/lib/orcasite/radio/seed.ex Changes feeds() to feeds!() for explicit error handling
server/lib/orcasite/radio/feed.ex Adds maintainer_emails argument with empty array default

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
server/lib/orcasite/radio/feed.ex (1)

50-50: Mark PII as sensitive.

Email addresses are PII; prevent accidental logging/inspection.

-attribute :maintainer_emails, {:array, :string}, allow_nil?: false, public?: true, default: []
+attribute :maintainer_emails, {:array, :string},
+  allow_nil?: false,
+  public?: true,
+  default: [],
+  sensitive?: true
🧹 Nitpick comments (3)
server/lib/orcasite/radio/seed/changes/seed_feeds.ex (1)

22-23: Prefer lazy logging with structured metadata.

Avoid interpolation cost and add useful fields.

-Logger.info("Successfully seeded #{count} feeds")
+Logger.info(fn -> "seed_feeds: success" end, count: count, resource: :feed)
server/lib/orcasite/radio/seed.ex (1)

93-93: Same here—intentional exception on failure.

If you want a custom error message upstream, consider wrapping in try/rescue and re-raising with context.

-__MODULE__.feeds!()
+try do
+  __MODULE__.feeds!()
+rescue
+  e ->
+    require Logger
+    Logger.error(fn -> "seed.latest: feeds! failed" end, error: e)
+    reraise(e, __STACKTRACE__)
+end
ui/src/pages/seed.tsx (1)

314-317: Build-time gating may require a redeploy to toggle. Consider SSR if you need runtime control.

getStaticProps reads env at build time; switching ENABLE_SEED_FROM_PROD post-deploy won’t take effect until a rebuild.

-export async function getStaticProps() {
+export async function getServerSideProps() {
   const enableSeedFromProd = process.env.ENABLE_SEED_FROM_PROD
     ? process.env.ENABLE_SEED_FROM_PROD === "true"
     : process.env.NODE_ENV === "development"; // default to enabled in dev

   return !enableSeedFromProd ? { notFound: true } : { props: {} };
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d8ed033 and c6abb09.

📒 Files selected for processing (4)
  • server/lib/orcasite/radio/feed.ex (1 hunks)
  • server/lib/orcasite/radio/seed.ex (2 hunks)
  • server/lib/orcasite/radio/seed/changes/seed_feeds.ex (2 hunks)
  • ui/src/pages/seed.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (3)
server/lib/orcasite/radio/seed/changes/seed_feeds.ex (2)

6-7: Logger import looks good.


27-32: Log with metadata and confirm add_error/2 input shape

Use structured logging and ensure the errors value passed to Ash.Changeset.add_error/2 is the form Ash expects (a single error or a list/keyword). If Ash.bulk_create(..., return_errors?: true) returns a wrapper like %{errors: inner}, pass inner to add_error/2.

File: server/lib/orcasite/radio/seed/changes/seed_feeds.ex (lines 27–32)

-Logger.error("Error while seeding feeds: #{inspect(errors)}")
+Logger.error(fn -> "seed_feeds: error" end, errors: errors, resource: :feed)

Run to verify error shape (or temporarily log inspect(errors)):
rg -n "Ash.bulk_create(" -C3 server/lib/orcasite/radio/seed/changes/seed_feeds.ex
rg -n "add_error(" server/lib/orcasite/radio/seed/changes/seed_feeds.ex -C2

server/lib/orcasite/radio/seed.ex (1)

64-64: Good: fail fast with feeds!().

Confirms seeding fails loudly as intended. Ensure callers (GraphQL/Oban) surface the raised error cleanly to operators.

Comment on lines +264 to +271
argument :maintainer_emails, {:array, :string}, default: []

change &change_lat_lng/2

change fn changeset, _context ->
maintainer_emails = Ash.Changeset.get_argument(changeset, :maintainer_emails) || []
Ash.Changeset.change_attribute(changeset, :maintainer_emails, maintainer_emails)
end
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

🧩 Analysis chain

Ensure upserts can update maintainer_emails; also normalize values.

Currently not included in upsert_fields, so existing rows won’t update on conflict; also normalize to lower-case/unique.

Apply both diffs:

  • Normalize on create:
-  change fn changeset, _context ->
-    maintainer_emails = Ash.Changeset.get_argument(changeset, :maintainer_emails) || []
-    Ash.Changeset.change_attribute(changeset, :maintainer_emails, maintainer_emails)
-  end
+  change fn changeset, _context ->
+    maintainer_emails =
+      (Ash.Changeset.get_argument(changeset, :maintainer_emails) || [])
+      |> Enum.map(&String.downcase/1)
+      |> Enum.uniq()
+
+    Ash.Changeset.change_attribute(changeset, :maintainer_emails, maintainer_emails)
+  end
  • Allow updates on conflict (outside this hunk; in upsert_fields):
       upsert_fields [
         :name,
         :node_name,
         :intro_html,
         :image_url,
         :visible,
         :bucket,
         :bucket_region,
         :cloudfront_url,
         :dataplicity_id,
         :orcahello_id,
-        :location_point
+        :location_point,
+        :maintainer_emails
       ]

🏁 Script executed:

#!/bin/bash
# Verify DB migration exists for the new column
rg -n "maintainer_emails" -g "priv/**/migrations/*.exs" -C2 || echo "MIGRATION NOT FOUND"
# Verify GraphQL schema exposure matches policy (nullable, field policy)
rg -n "nullable_fields.*maintainer_emails" server/lib/orcasite/radio/feed.ex -C2

Length of output: 273


Add missing DB migration and update upsert logic

  • Create a new Ecto migration under priv/repo/migrations to add :maintainer_emails (type {:array, :string}, default []) to the feeds table.
  • In server/lib/orcasite/radio/feed.ex – inside the upsert_fields list, append :maintainer_emails; in the change hook, normalize with Enum.map(&String.downcase/1) and Enum.uniq().

GraphQL exposure (nullable_fields [:maintainer_emails]) is already in place.

🤖 Prompt for AI Agents
In server/lib/orcasite/radio/feed.ex around lines 264 to 271, the PR missed
adding a DB migration and updating upsert logic for the new maintainer_emails
field: add an Ecto migration file under priv/repo/migrations that alters the
feeds table to add a maintainer_emails column of type {:array, :string} with
default [] and null: false (or appropriate nullability), and run mix
ecto.migrate; then update server/lib/orcasite/radio/feed.ex by appending
:maintainer_emails to the upsert_fields list and modify the change hook to
normalize the incoming maintainer_emails via Enum.map(&String.downcase/1) |>
Enum.uniq() before calling Ash.Changeset.change_attribute (keeping
change_lat_lng/2 intact). Ensure the migration and schema/upsert changes remain
consistent and tests/GraphQL nullable exposure still pass.

Copy link
Contributor

@skanderm skanderm left a comment

Choose a reason for hiding this comment

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

Small request to make this more idiomatic - you can remove the argument and change in favor of adding maintainer_emails to the accept list: https://hexdocs.pm/ash/dsl-ash-resource.html#actions-create-accept

description "A comma-separated string of longitude and latitude"
end

argument :maintainer_emails, {:array, :string}, default: []
Copy link
Contributor

Choose a reason for hiding this comment

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

If the argument is an attribute and you're setting the attribute to the argument, you can simply add it to the accept list above and skip the argument and change _attribute changeset. You could add it to the upsert_fields too, but it's less important since this is just for seeding.

Copy link
Member Author

@paulcretu paulcretu Sep 21, 2025

Choose a reason for hiding this comment

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

Hm I need it to default to empty list though, in case the API returns nil for maintainer_emails (which it will because it's an auth only field). So I guess I could remove the argument but I still need the change no? Do you have a better suggestion? Curious how you would tackle it, I don't really know what's idiomatic

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok yeah really good point. In prod we don't want that to be nil, but an empty list works.

Copy link
Contributor

@skanderm skanderm left a comment

Choose a reason for hiding this comment

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

Fair point about non-nullability of maintainer_emails, so you can simplify the change with a builtin set_attribute

description "A comma-separated string of longitude and latitude"
end

argument :maintainer_emails, {:array, :string}, default: []
Copy link
Contributor

Choose a reason for hiding this comment

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

Ok yeah really good point. In prod we don't want that to be nil, but an empty list works.

Comment on lines +268 to +271
change fn changeset, _context ->
maintainer_emails = Ash.Changeset.get_argument(changeset, :maintainer_emails) || []
Ash.Changeset.change_attribute(changeset, :maintainer_emails, maintainer_emails)
end
Copy link
Contributor

Choose a reason for hiding this comment

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

This can still be simplified to:

Suggested change
change fn changeset, _context ->
maintainer_emails = Ash.Changeset.get_argument(changeset, :maintainer_emails) || []
Ash.Changeset.change_attribute(changeset, :maintainer_emails, maintainer_emails)
end
change set_attribute(:maintainer_emails, arg(:maintainer_emails), set_when_nil?: false)

See: https://hexdocs.pm/ash/Ash.Resource.Change.Builtins.html#set_attribute/3

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