Skip to content

feat(cli): support custom schemas in records commands via --schema#187

Open
Fermionic-Lyu wants to merge 1 commit into
mainfrom
feat/cli-records-schema-selection
Open

feat(cli): support custom schemas in records commands via --schema#187
Fermionic-Lyu wants to merge 1 commit into
mainfrom
feat/cli-records-schema-selection

Conversation

@Fermionic-Lyu

@Fermionic-Lyu Fermionic-Lyu commented Jun 27, 2026

Copy link
Copy Markdown
Member

What

Adds a --schema <name> option to records list/create/update/delete. It maps to PostgREST's profile header — Accept-Profile for reads, Content-Profile for writes — with the table path left bare. Omitted → backend default (public).

New src/commands/records/profile.ts (schemaProfileHeaders) centralizes the verb→header mapping; covered by profile.test.ts (3 cases, green on Node 20).

Why

Pairs with v2.2.3 custom-schema exposure (InsForge migration 056) and the SDK .schema() support (InsForge-sdk-js#98), giving the records commands parity for custom schemas.

Note

db query (raw SQL) and migrations remain the primary, fully schema-aware data path in the CLI — this just brings the records data-API commands in line.

🤖 Generated with Claude Code


Summary by cubic

Adds a --schema <name> option to records list/create/update/delete to target custom schemas via PostgREST profile headers. Defaults to public when omitted.

  • New Features
    • Sends Accept-Profile for reads and Content-Profile for writes; table path stays bare.
    • Adds schemaProfileHeaders helper with unit tests.
    • Aligns CLI records commands with backend custom-schema support and SDK .schema().

Written for commit 18be0ee. Summary will update on new commits.

Review in cubic

Note

Add --schema flag to records CLI commands for custom PostgREST schema support

  • Adds a --schema <name> option to the records create, update, delete, and list commands.
  • Introduces a new schemaProfileHeaders utility that returns Accept-Profile for read operations and Content-Profile for write operations, or an empty object when no schema is provided.
  • Each command passes the generated headers to its respective HTTP request, following the PostgREST content profile spec.

Macroscope summarized 18be0ee.

Summary by CodeRabbit

  • New Features
    • Added a --schema option to records create, list, update, and delete commands, with public as the default.
    • Record operations now honor the selected schema when reading or writing data.
  • Tests
    • Added coverage for schema-aware request header behavior, including read and write cases.

records list/create/update/delete gain a --schema option that maps to
PostgREST's Accept-Profile (reads) / Content-Profile (writes) header, leaving
the table path bare. Omitted → backend default (public). Pairs with v2.2.3
custom-schema exposure (InsForge migration 056) and the SDK .schema() support.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Adds a new schemaProfileHeaders helper in profile.ts that returns Accept-Profile or Content-Profile HTTP headers based on operation kind. The list, create, update, and delete records subcommands each gain a --schema <name> CLI option and pass the resulting headers to their ossFetch calls.

Changes

Schema Profile Headers for Records Commands

Layer / File(s) Summary
schemaProfileHeaders utility
src/commands/records/profile.ts, src/commands/records/profile.test.ts
New exported function returns Accept-Profile for reads, Content-Profile for writes, or {} when no schema is given; tests cover all three cases.
--schema option wired into all subcommands
src/commands/records/list.ts, src/commands/records/create.ts, src/commands/records/update.ts, src/commands/records/delete.ts
Each subcommand imports schemaProfileHeaders, adds a --schema <name> option, and forwards the generated headers to the corresponding ossFetch request.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

🐇 Hop along, little schema friend,
A header now sent 'round every bend!
Accept-Profile reads with care,
Content-Profile writes with flair,
The bunny profiles headers all day,
And schemas never go astray! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding --schema support to records commands.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cli-records-schema-selection

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

@jwfing jwfing left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review: feat(cli): support custom schemas in records commands via --schema

Summary: Small, well-scoped change that adds a --schema option to the four records data-API commands by mapping it to PostgREST profile headers via a single centralized helper; the implementation is correct and I found no blocking issues.

Requirements context

No matching spec/plan found under a /docs/superpowers/ directory — this repo keeps design docs in docs/specs/, and the three present there (*-diagnose-*, *-db-migrations-command-design.md) don't cover the records data-API. Assessed against the PR description, the referenced backend migration 056, and SDK PR InsForge-sdk-js#98.

Findings

Critical

(none)

Suggestion

  • Software engineering — no command-level test for the header wiring. src/commands/records/profile.test.ts:1-22 thoroughly covers the pure helper, but nothing asserts that each command passes the right kind. The read/write distinction is easy to mis-wire (a future copy-paste could send 'read' from update.ts), and that class of bug would slip past the current suite. I acknowledge there are currently no command-level tests for records at all, so this is consistent with the existing pattern — but a thin test that stubs ossFetch and asserts listAccept-Profile and create/update/deleteContent-Profile would lock in the contract cheaply. Non-blocking.

Information

  • Functionality — "default = public" depends on backend db-schemas ordering. The option help text and the doc comment in src/commands/records/profile.ts:5-6 state that omitting --schema falls back to public. Per PostgREST semantics (verified against the v14 docs), an absent profile header selects the first schema in db-schemas, not literally public. This holds only as long as the InsForge backend lists public first — true today, just worth being aware the guarantee lives in backend config, not the CLI.
  • Security — --schema reaches an HTTP header unvalidated, but this is low risk. The value flows into Accept-Profile/Content-Profile (src/commands/records/profile.ts:14). Node's fetch/undici rejects header values containing CR/LF, so request-splitting isn't feasible, and PostgREST rejects unknown schemas server-side. No change needed; noting it for completeness.
  • Verified API correctness. Confirmed against PostgREST v14 docs that reads use Accept-Profile and all writes — including DELETE (which carries no body) — use Content-Profile. The mapping in profile.ts:13 is correct. Header merge in ossFetch (src/lib/api/oss.ts:166-170) preserves Content-Type/Authorization while adding the profile header, so there's no clobbering.

Dimension coverage

  • Software engineering: Helper is unit-tested; import style (./profile.js ESM extension), error handling, and naming all match surrounding code. Only gap is the wiring test above.
  • Functionality: All four record commands updated; scope matches the PR claim. No gaps in the CLI layer (cross-schema enforcement is correctly delegated to the backend).
  • Security: No auth weakening, no new secrets/PII logged, no new dependencies. See header note above.
  • Performance: No new queries, loops, allocations, or blocking I/O — one tiny synchronous object construction per request.

Verdict

approved (informational — no Critical findings; the two Suggestion/Information items are non-blocking). Posted as a comment; explicit GitHub approval remains a human action.

@greptile-apps

greptile-apps Bot commented Jun 27, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds a --schema <name> option to the four records commands (list, create, update, delete) by introducing a new profile.ts utility that maps the verb to the correct PostgREST header (Accept-Profile for reads, Content-Profile for writes/mutations). When the flag is omitted the backend's default schema (public) is used unchanged.

  • src/commands/records/profile.ts: New thin utility that returns the correct PostgREST profile header or an empty object when no schema is given; covered by three focused unit tests.
  • list/create/update/delete.ts: Each command gains a --schema option and passes the result of schemaProfileHeaders into the headers field of their respective ossFetch calls; ossFetch in oss.ts already spreads caller-supplied headers on top of the base Content-Type/Authorization headers, so no merging issue exists.

Confidence Score: 5/5

Safe to merge — the change is additive, all four commands continue to behave identically when --schema is omitted, and the header mapping aligns with PostgREST's Accept-Profile/Content-Profile spec.

The implementation is small and self-contained: a single utility function, four one-line call-site additions, and header merging that is already handled correctly by the existing ossFetch layer. No existing behavior is altered when the new flag is absent.

No files require special attention.

Important Files Changed

Filename Overview
src/commands/records/profile.ts New utility correctly maps read→Accept-Profile and write→Content-Profile; falsy guard handles both undefined and empty string.
src/commands/records/profile.test.ts Three test cases cover the no-schema (undefined, omitted, empty-string), read, and write paths; all assertions look correct.
src/commands/records/list.ts Adds --schema option; passes schemaProfileHeaders result as headers to ossFetch; no regression when schema is omitted.
src/commands/records/create.ts Adds --schema option; passes Content-Profile header via schemaProfileHeaders('write'); correct for POST mutations.
src/commands/records/update.ts Adds --schema option; passes Content-Profile header via schemaProfileHeaders('write'); correct for PATCH mutations.
src/commands/records/delete.ts Adds --schema option; passes Content-Profile header via schemaProfileHeaders('write') for DELETE — correct per PostgREST spec.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant User as CLI User
    participant Cmd as records list/create/update/delete
    participant Profile as profile.ts (schemaProfileHeaders)
    participant OssFetch as ossFetch (oss.ts)
    participant Backend as InsForge Backend (PostgREST)

    User->>Cmd: records list mytable --schema analytics
    Cmd->>Profile: schemaProfileHeaders('read', 'analytics')
    Profile-->>Cmd: "{ 'Accept-Profile': 'analytics' }"
    Cmd->>OssFetch: "ossFetch(path, { headers: {...} })"
    Note over OssFetch: Merges Content-Type, Authorization,<br/>and Accept-Profile headers
    OssFetch->>Backend: "GET /api/database/records/mytable<br/>Accept-Profile: analytics"
    Backend-->>OssFetch: rows from analytics schema
    OssFetch-->>Cmd: Response
    Cmd-->>User: Tabular output

    User->>Cmd: "records create mytable --schema analytics --data '{...}'"
    Cmd->>Profile: schemaProfileHeaders('write', 'analytics')
    Profile-->>Cmd: "{ 'Content-Profile': 'analytics' }"
    Cmd->>OssFetch: "ossFetch(path, { method: 'POST', headers: {...} })"
    OssFetch->>Backend: "POST /api/database/records/mytable<br/>Content-Profile: analytics"
    Backend-->>OssFetch: created row(s)
    OssFetch-->>Cmd: Response
    Cmd-->>User: Success message
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant User as CLI User
    participant Cmd as records list/create/update/delete
    participant Profile as profile.ts (schemaProfileHeaders)
    participant OssFetch as ossFetch (oss.ts)
    participant Backend as InsForge Backend (PostgREST)

    User->>Cmd: records list mytable --schema analytics
    Cmd->>Profile: schemaProfileHeaders('read', 'analytics')
    Profile-->>Cmd: "{ 'Accept-Profile': 'analytics' }"
    Cmd->>OssFetch: "ossFetch(path, { headers: {...} })"
    Note over OssFetch: Merges Content-Type, Authorization,<br/>and Accept-Profile headers
    OssFetch->>Backend: "GET /api/database/records/mytable<br/>Accept-Profile: analytics"
    Backend-->>OssFetch: rows from analytics schema
    OssFetch-->>Cmd: Response
    Cmd-->>User: Tabular output

    User->>Cmd: "records create mytable --schema analytics --data '{...}'"
    Cmd->>Profile: schemaProfileHeaders('write', 'analytics')
    Profile-->>Cmd: "{ 'Content-Profile': 'analytics' }"
    Cmd->>OssFetch: "ossFetch(path, { method: 'POST', headers: {...} })"
    OssFetch->>Backend: "POST /api/database/records/mytable<br/>Content-Profile: analytics"
    Backend-->>OssFetch: created row(s)
    OssFetch-->>Cmd: Response
    Cmd-->>User: Success message
Loading

Reviews (1): Last reviewed commit: "feat(cli): support custom schemas in rec..." | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/commands/records/profile.test.ts (1)

4-22: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add one command-level test for the ossFetch header wiring.

This suite locks the helper, but not the actual list/create/update/delete integrations. A small mock-based test for one read command and one write command would catch regressions where a subcommand forgets to pass the helper output to ossFetch.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/records/profile.test.ts` around lines 4 - 22, Add a
command-level test that verifies the `ossFetch` header wiring through at least
one read and one write command, since the current suite only covers
`schemaProfileHeaders`. Use a mock-based assertion around one of the subcommand
handlers (for example `list` and `create`, or equivalent command entry points)
to confirm they pass the helper output into `ossFetch` with the expected
`Accept-Profile`/`Content-Profile` headers when a schema is provided.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/commands/records/profile.test.ts`:
- Around line 4-22: Add a command-level test that verifies the `ossFetch` header
wiring through at least one read and one write command, since the current suite
only covers `schemaProfileHeaders`. Use a mock-based assertion around one of the
subcommand handlers (for example `list` and `create`, or equivalent command
entry points) to confirm they pass the helper output into `ossFetch` with the
expected `Accept-Profile`/`Content-Profile` headers when a schema is provided.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f0ef0f38-4851-44e5-862b-4fd083f16377

📥 Commits

Reviewing files that changed from the base of the PR and between 45ed6f8 and 18be0ee.

📒 Files selected for processing (6)
  • src/commands/records/create.ts
  • src/commands/records/delete.ts
  • src/commands/records/list.ts
  • src/commands/records/profile.test.ts
  • src/commands/records/profile.ts
  • src/commands/records/update.ts

@cubic-dev-ai cubic-dev-ai 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.

No issues found across 6 files

Re-trigger cubic

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.

2 participants