Skip to content

feat(connector): Snowflake create — password, key-pair, OAuth SSO, OAuth individual#17

Merged
bradfair merged 10 commits into
mainfrom
feature/connector-create-snowflake-password
Apr 21, 2026
Merged

feat(connector): Snowflake create — password, key-pair, OAuth SSO, OAuth individual#17
bradfair merged 10 commits into
mainfrom
feature/connector-create-snowflake-password

Conversation

@bradfair
Copy link
Copy Markdown
Contributor

@bradfair bradfair commented Apr 21, 2026

Summary

  • Ship all four Snowflake connector create auth-mode leaves under the Phase 2 inheritable-flags Group pattern (mirrors create_postgres.go). ana connector create snowflake {password|keypair|oauth-sso|oauth-individual}.
  • Rename resolvePasswordresolveSecret so non-password secrets (OAuth client secret, private-key passphrase) flow through one helper.
  • Trim internal/connector/CLAUDE.md to nav hints, slim README.md (relocate CI-scope + Windows-SmartScreen details to docs/), and flip the Snowflake row in docs/cli-readiness.md from 🟦 → 🟢 with the OAuth footnote corrected to match the pending-row-then-browser-activation model actually shipped.

Gaps flagged for follow-up

  • E2E harness (e2e/) is Postgres-only. Snowflake smoke coverage needs a DialectSpec refactor of e2e/harness/resources.go + new ANA_E2E_SF_* env vars. Shipping as its own PR.
  • Phase 3e (Databricks) and 3g (dialect-aware connector update) unblocked and queued.

Summary by CodeRabbit

  • New Features

    • Added Snowflake connector creation with auth modes: password, keypair, OAuth SSO, and per-member OAuth
    • Snowflake connectors support optional warehouse, schema, and role; CLI help now lists Snowflake as a dialect
  • Refactor

    • Generalized secret resolution for passwords and other secrets (e.g., OAuth client secret, key passphrases) and unified error/usage messages
    • Connector config now supports multiple dialects and an auth strategy field

Adds the `ana connector create snowflake password` leaf under the
existing two-level dialect/auth-mode Group tree.

- `configEnvelope` grows a top-level `authStrategy` field and a
  `snowflake` pointer sibling to `postgres`, matching the captured wire
  shape (`config.authStrategy` + `config.snowflake.*`, see
  `api-catalog/POST_connector.create.snowflake.password.json`).
- `snowflakeSpec` covers all four Snowflake auth modes in one struct;
  every credential field is `omitempty`. Server discriminates by
  populated field + envelope `authStrategy`.
- `newSnowflakeCreateGroup` declares dialect-common flags (`--name`,
  `--locator`, `--database`, `--warehouse`, `--schema`, `--role`) via
  the Phase 2 inheritable `Flags` closure so future auth-mode leaves
  (`keypair`, `oauth-sso`, `oauth-individual`) inherit them.
- Password leaf mirrors the Postgres-password canonical pattern 1:1:
  `Flagger` for `--help` rendering, explicit empty-string rejection,
  `resolvePassword` for stdin, `RenderOutput` for JSON/typed dispatch.
Adds the `ana connector create snowflake keypair` leaf. Shares
authStrategy=service_role with password mode; server discriminates by
populated credential field (`privateKey` vs `password`).

- Private key is read from `--private-key-file` (PEM-encoded PKCS#8).
  File path, not stdin — multi-line keys are typically already on disk
  and file-upload mirrors the web UI's control.
- `--user` is still required: Snowflake binds RSA public keys to a user.
- Optional `--private-key-passphrase` / `--private-key-passphrase-stdin`
  for encrypted PKCS#8 keys. `resolveOptionalPassphrase` returns "" when
  neither is set so `omitempty` drops the field from the wire body.
- Tests mirror the Snowflake password + Postgres password pattern 1:1
  plus file-lifecycle cases (missing file, empty file, passphrase via
  flag or stdin, passphrase absent).

Wire shape matches `api-catalog/POST_connector.create.snowflake.keypair.json`.
Prep for the OAuth leaves, which need the same stdin-or-flag secret
handling but for `--oauth-client-secret` / `--client-secret` / `--pat`.
The body is identical; only the error-message flag name varies.

- Add `flagName string` as the first parameter; error messages now read
  `--<name>-stdin set but stdin was empty` and `--<name> or
  --<name>-stdin is required`.
- All three existing call sites (postgres password, snowflake password,
  update) pass `"password"` — behavior unchanged.
- `cli.ReadPassword` stays the backend, so JWT-sized buffer and
  whitespace-preservation semantics carry over to every secret kind.
Adds `ana connector create snowflake oauth-sso` (authStrategy=oauth_sso,
shared-token OAuth).

- Required: --oauth-client-id + secret (via --oauth-client-secret or
  --oauth-client-secret-stdin). No username/password/privateKey.
- Server accepts the row in pending-OAuth state; CLI cannot receive the
  browser redirect at /auth/snowflake/callback, so the success output
  reminds the user to complete the handshake at app.textql.com.
- Uses the renamed resolveSecret helper for the OAuth client secret.

Wire shape matches api-catalog/POST_connector.create.snowflake.oauth-sso.json.
Adds `ana connector create snowflake oauth-individual`
(authStrategy=per_member_oauth).

Wire fields are identical to oauth-sso — only the authStrategy differs.
Unlike oauth-sso, no up-front browser handshake is needed: each member
authenticates lazily at their first query, so the CLI just creates the
row.

Wire shape matches api-catalog/POST_connector.create.snowflake.oauth-individual.json.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 21, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ed5dc9ec-3a58-429f-bd32-3f9fa29cbb21

📥 Commits

Reviewing files that changed from the base of the PR and between bafd782 and 3213363.

⛔ Files ignored due to path filters (1)
  • internal/connector/CLAUDE.md is excluded by !**/*.md
📒 Files selected for processing (8)
  • cmd/ana/main.go
  • cmd/ana/main_test.go
  • internal/connector/connector.go
  • internal/connector/create_snowflake_oauth_sso.go
  • internal/connector/create_snowflake_oauth_sso_test.go
  • internal/connector/types.go
  • internal/connector/types_postgres.go
  • internal/connector/types_snowflake.go

📝 Walkthrough

Walkthrough

Adds Snowflake connector creation subtree (password, keypair, OAuth SSO, per-member OAuth) to the CLI and generalizes secret handling by replacing resolvePassword with resolveSecret; updates types to support multi-dialect configs and threads an endpoint through connector dependencies.

Changes

Cohort / File(s) Summary
Secret resolution & update call sites
internal/connector/create.go, internal/connector/update.go
Replaced resolvePassword with resolveSecret(flagName, ...), updated error text/usage messages to reference generic secret flags, and adapted update/create call sites to use the new helper.
Postgres adjustments & tests
internal/connector/create_postgres.go, internal/connector/create_postgres_password_test.go
Swapped password resolver to resolveSecret("password", ...) and updated tests to expect snowflake listed in create group help.
New Snowflake CLI subtree
internal/connector/create_snowflake.go, internal/connector/create_snowflake_password.go, internal/connector/create_snowflake_keypair.go, internal/connector/create_snowflake_oauth_sso.go, internal/connector/create_snowflake_oauth_individual.go
Added newSnowflakeCreateGroup and four auth-mode leaf commands (password, keypair, oauth-sso, oauth-individual) with flag wiring, validation, secret resolution, request construction, RPC invocation, and output rendering.
Snowflake tests
internal/connector/create_snowflake_password_test.go, internal/connector/create_snowflake_keypair_test.go, internal/connector/create_snowflake_oauth_sso_test.go, internal/connector/create_snowflake_oauth_individual_test.go
Comprehensive tests for each Snowflake auth mode covering happy paths, stdin secret handling, validation errors, optional fields, JSON output bypass, and RPC/render error propagation.
Types: multi-dialect & new specs
internal/connector/types.go, internal/connector/types_postgres.go, internal/connector/types_snowflake.go
Moved per-dialect specs into separate files, added AuthStrategy and snowflake pointer to configEnvelope, and introduced postgresSpec and snowflakeSpec structs.
Deps endpoint wiring
internal/connector/connector.go, cmd/ana/main.go, cmd/ana/main_test.go
Added Endpoint field and resolveEndpoint() on Deps; propagated endpoint into verb construction and updated buildVerbs signature/call sites.

Sequence Diagram(s)

sequenceDiagram
    participant User as User (stdin/CLI)
    participant CLI as ana connector CLI
    participant Client as Deps.Unary (Client)
    participant Service as Connector Service

    User->>CLI: run "ana connector create snowflake <mode> --flags" (may use --*-stdin)
    CLI->>User: read stdin line (if --*-stdin set)
    CLI->>Client: Unary(CreateConnector request JSON)
    Client->>Service: POST /CreateConnector
    Service-->>Client: Response (connectorId, name, type)
    Client-->>CLI: return response
    CLI->>User: render output (JSON or formatted text)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Poem

🐰 I nibbled flags and read the stdin line,
Built Snowflake bridges, one secret at a time.
From password to keypair, OAuth in tow—
Hopping through connectors, watch the rabbit go!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is incomplete; it provides a detailed summary but lacks the required checklist items (lint, cover, docs updates confirmation). Add completed checklist items confirming that make lint and make cover pass, and verify whether docs/CLAUDE.md updates were made as mentioned.
Docstring Coverage ⚠️ Warning Docstring coverage is 15.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main feature: adding four Snowflake connector create auth modes (password, key-pair, OAuth SSO, OAuth individual).
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/connector-create-snowflake-password

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

Copy link
Copy Markdown

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/connector/create_snowflake_oauth_sso.go`:
- Around line 36-40: The Help text currently hardcodes app.textql.com; update
the code to use the active profile endpoint instead: add a dependency in
connector.Deps that exposes a loader function following the auth package
LoadCfg/SaveCfg pattern to retrieve the active endpoint, then have
snowflakeOAuthSSOCmd.Help build its URL using that endpoint rather than a
hardcoded host, and update the success message printed after connector creation
(the post-create success output in the same file) to use the same loaded
endpoint. Ensure the new dep is wired into the command initialization and used
where the OAuth redirect URL is shown.

In `@internal/connector/types.go`:
- Around line 44-62: The snowflakeSpec struct is currently defined in types.go
and should be moved to a dialect-specific file to preserve the package's
per-dialect type boundary; create a new file (e.g., types_snowflake.go) in
package connector and move the snowflakeSpec type declaration there (preserving
its json tags and exact field names: Locator, Database, Warehouse, Schema, Role,
Username, Password, PrivateKey, PrivateKeyPassphrase, OAuthClientID,
OAuthClientSecret), remove the definition from types.go, run go vet/go build to
ensure no references break, and keep the type unexported as snowflakeSpec.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: fb6a306c-8212-47ed-9215-b9dcecd515c1

📥 Commits

Reviewing files that changed from the base of the PR and between 578c6cf and bafd782.

⛔ Files ignored due to path filters (6)
  • README.md is excluded by !**/*.md
  • docs/CLAUDE.md is excluded by !**/docs/**, !**/*.md
  • docs/ci-scope.md is excluded by !**/docs/**, !**/*.md
  • docs/cli-readiness.md is excluded by !**/docs/**, !**/*.md
  • docs/windows-smartscreen.md is excluded by !**/docs/**, !**/*.md
  • internal/connector/CLAUDE.md is excluded by !**/*.md
📒 Files selected for processing (14)
  • internal/connector/create.go
  • internal/connector/create_postgres.go
  • internal/connector/create_postgres_password_test.go
  • internal/connector/create_snowflake.go
  • internal/connector/create_snowflake_keypair.go
  • internal/connector/create_snowflake_keypair_test.go
  • internal/connector/create_snowflake_oauth_individual.go
  • internal/connector/create_snowflake_oauth_individual_test.go
  • internal/connector/create_snowflake_oauth_sso.go
  • internal/connector/create_snowflake_oauth_sso_test.go
  • internal/connector/create_snowflake_password.go
  • internal/connector/create_snowflake_password_test.go
  • internal/connector/types.go
  • internal/connector/update.go

Comment thread internal/connector/create_snowflake_oauth_sso.go
Comment thread internal/connector/types.go Outdated
Hardcoded https://app.textql.com in snowflake oauth-sso Help and success
output directed users at the wrong environment on non-prod / self-hosted
profiles. Thread the resolved endpoint through connector.Deps and format
the success note from it.
Moves postgresSpec and snowflakeSpec out of types.go into dedicated
types_postgres.go and types_snowflake.go. types.go keeps only shared
wire shapes (createReq, updateReq, configEnvelope, createResp,
getConnectorResp). Scales cleanly as new dialects land.
@bradfair
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 21, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@bradfair bradfair added this pull request to the merge queue Apr 21, 2026
Merged via the queue into main with commit c0f6494 Apr 21, 2026
10 checks passed
@bradfair bradfair deleted the feature/connector-create-snowflake-password branch April 21, 2026 20:49
@hpt-bot hpt-bot Bot mentioned this pull request Apr 21, 2026
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