Skip to content

fix(api): support stdin and quoted JSON inputs on Windows#367

Merged
liangshuo-1 merged 2 commits intomainfrom
fix/params-stdin-resolve
Apr 9, 2026
Merged

fix(api): support stdin and quoted JSON inputs on Windows#367
liangshuo-1 merged 2 commits intomainfrom
fix/params-stdin-resolve

Conversation

@liangshuo-1
Copy link
Copy Markdown
Collaborator

@liangshuo-1 liangshuo-1 commented Apr 9, 2026

Summary

This PR adds Windows-safe stdin and single-quoted JSON handling for --params and --data, then locks the behavior down with real end-to-end regression coverage.

Changes

  • Add stdin (-) and surrounding single-quote support for api and generated service commands when parsing --params and --data
  • Add stdin-aware CLI E2E harness support and regression tests covering API/service success and validation-error paths
  • Format the Windows shell scenario test in internal/cmdutil/resolve_test.go so the branch stays lint-clean

Test Plan

  • make unit-test
  • go test -count=1 ./tests/...
  • go mod tidy (no changes)
  • go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6 run --new-from-rev=origin/main
  • Manual local verification of real binary dry-run flows for api/service with stdin success, empty stdin, double-stdin conflict, and single-quoted JSON inputs

Related Issues

Summary by CodeRabbit

  • New Features

    • Added stdin support for --params and --data flags using - as a sentinel value, enabling piped JSON data input to CLI commands.
    • Enhanced support for single-quoted JSON strings in command arguments.
  • Bug Fixes

    • Added validation to prevent simultaneous stdin consumption when both --params - and --data - are specified.
  • Documentation

    • Updated help text for --params and --data flags to document stdin support.

…indows (#64)

Windows PowerShell 5.x mangles JSON double-quotes when passing arguments
to native executables, causing --params and --data to fail with
"invalid JSON format". This commit adds two mitigations at the framework
level:

- stdin piping: `echo '{"k":"v"}' | lark-cli --params -` bypasses
  shell argument parsing entirely and works on all platforms/shells.
- single-quote stripping: cmd.exe passes literal single quotes which
  are now transparently removed before JSON parsing.

Implementation:
- New `cmdutil.ResolveInput(raw, stdin)` handles `-` (stdin), strip
  surrounding `'...'`, and plain passthrough.
- `ParseJSONMap` and `ParseOptionalBody` now accept an `io.Reader` and
  delegate to `ResolveInput` before JSON unmarshalling.
- `cmd/api` and `cmd/service` pass `IOStreams.In` and guard against
  simultaneous stdin usage by --params and --data.
- Empty stdin is rejected with a clear error message.

Closes #64

Change-Id: If21e735d0aed5c6a2d6674c1e6c898186fca3aba
Change-Id: I4e00bf1c6b6f3259f503e3414cae10fa4b34ba75
@github-actions github-actions bot added the size/L Large or sensitive change across domains or core paths label Apr 9, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 9, 2026

📝 Walkthrough

Walkthrough

Added stdin support ("-" sentinel) for --params and --data CLI flags across api and service commands via new ResolveInput helper that handles stdin reading, single-quote stripping, and input validation, preventing simultaneous stdin consumption from both flags.

Changes

Cohort / File(s) Summary
Command Refactoring
cmd/api/api.go, cmd/service/service.go
Replaced direct JSON unmarshalling with cmdutil.ParseJSONMap and cmdutil.ParseOptionalBody helpers, added stdin (-) support with validation preventing both --params and --data from reading stdin simultaneously.
Command Tests
cmd/api/api_test.go, cmd/service/service_test.go
Added conflict tests validating error when both flags use stdin; updated error message assertion in service test.
Utility Functions
internal/cmdutil/json.go, internal/cmdutil/json_test.go
Updated ParseJSONMap and ParseOptionalBody signatures to accept io.Reader for stdin, delegating resolution to ResolveInput; updated test invocations with nil stdin parameter.
New Resolve Utility
internal/cmdutil/resolve.go
Added ResolveInput(raw string, stdin io.Reader) function handling - stdin input, single-quote stripping, whitespace trimming, and validation.
Resolve Tests
internal/cmdutil/resolve_test.go
New comprehensive test file covering stdin reading, quote stripping, error cases, and Windows shell scenarios for ResolveInput, ParseJSONMap, and ParseOptionalBody.
E2E Test Infrastructure
tests/cli_e2e/core.go, tests/cli_e2e/core_test.go
Added Stdin []byte field to Request struct; implemented stdin piping to spawned CLI process; added test validating stdin passage.
E2E Stdin Regression Tests
tests/cli_e2e/stdin_regression_test.go
New E2E test file validating stdin success cases for api/service commands with --params - and --data -, plus error validation for empty stdin and dual-stdin conflicts.

Sequence Diagram(s)

sequenceDiagram
    participant User as User/Shell
    participant CLI as CLI Handler
    participant ResolveInput as ResolveInput()
    participant Stdin as Stdin Reader
    participant ParseFunc as ParseJSONMap/<br/>ParseOptionalBody
    
    User->>CLI: --params - --data input.json
    CLI->>ResolveInput: ResolveInput("--params", "-", stdin)
    ResolveInput->>Stdin: Read all bytes
    Stdin-->>ResolveInput: Content
    ResolveInput->>ResolveInput: Trim whitespace
    ResolveInput-->>CLI: Resolved string
    
    CLI->>ResolveInput: ResolveInput("--data", "input.json", stdin)
    ResolveInput->>ResolveInput: Strip single quotes (if present)
    ResolveInput-->>CLI: Resolved string
    
    CLI->>ParseFunc: Parse resolved inputs
    ParseFunc-->>CLI: Validated objects
    CLI-->>User: Request ready
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

size/L

Suggested reviewers

  • sang-neo03

Poem

🐰 Stdin whispers through the shell,
No more quoting woes to quell,
Pipes flow in from - so bright,
Windows parsing—now just right!
One flag at a time, the rules are clear,

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.32% 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 describes the primary change: adding stdin and quoted JSON input support for the api command on Windows.
Description check ✅ Passed The PR description follows the template structure with Summary, Changes, Test Plan, and Related Issues sections, providing comprehensive details about the implementation and verification.
Linked Issues check ✅ Passed All code changes directly address issue #64: support stdin input for --params and --data, strip single quotes to handle Windows shell quoting, validate against simultaneous stdin consumption, and provide E2E regression tests confirming the fix works across commands.
Out of Scope Changes check ✅ Passed All changes are scoped to the Windows JSON parsing issue: new stdin/quote handling in cmdutil, updated api/service commands to use it, E2E test infrastructure for stdin, and comprehensive regression tests; no unrelated modifications detected.

✏️ 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 fix/params-stdin-resolve

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

@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Apr 9, 2026

Greptile Summary

This PR adds Windows-safe stdin (-) and single-quoted JSON support for --params and --data in both the raw api and generated service commands, extracting the logic into shared cmdutil.ResolveInput, ParseJSONMap, and ParseOptionalBody helpers. The refactor is well-structured and the double-stdin conflict guard, empty-stdin error, and E2E regression suite are solid additions.

Confidence Score: 5/5

Safe to merge; only P2 findings remain — both are edge-case behavior nuances, not blocking defects.

The core stdin/single-quote logic is correct and well-tested. The two flagged issues are: (1) silent body-drop for GET+--data in the raw api command (unusual use-case, low real-world impact), and (2) a confusing error for the unlikely '-' input. Neither breaks the primary user paths described in the PR.

internal/cmdutil/json.go (ParseOptionalBody method gate) and internal/cmdutil/resolve.go (single-quote then stdin ordering)

Vulnerabilities

No security concerns identified. Stdin is read via io.ReadAll with a clear consumed-once constraint enforced by the conflict check. Single-quote stripping is purely cosmetic and does not bypass any downstream validation. No secrets handling or auth boundary changes.

Important Files Changed

Filename Overview
internal/cmdutil/resolve.go New ResolveInput helper correctly handles stdin (-), single-quote stripping, and empty/nil inputs; well-tested edge cases.
internal/cmdutil/json.go ParseJSONMap and ParseOptionalBody correctly delegate to ResolveInput; ParseOptionalBody restricts body parsing to POST/PUT/PATCH/DELETE — behavior change vs. old api.go which allowed any method to carry a body.
cmd/api/api.go Removes local parseJsonOpt and delegates to cmdutil helpers; double-stdin guard is correct, but ParseOptionalBody now silently ignores --data for GET/HEAD/etc., which the original code did not.
cmd/service/service.go Service command correctly adopts shared helpers; --data flag is only registered for POST/PUT/PATCH/DELETE so the method-gate in ParseOptionalBody is redundant but harmless here.
tests/cli_e2e/core.go Adds Stdin field to Request struct and wires it to cmd.Stdin; defaultAsInitOnce shared state is best-effort and intentional per comment.
tests/cli_e2e/stdin_regression_test.go Comprehensive E2E regression tests covering stdin success, empty stdin, double-stdin conflict, and single-quoted JSON for both api and service commands.
internal/cmdutil/resolve_test.go Good unit coverage for ResolveInput including nil stdin, read errors, whitespace-only input, and Windows shell scenarios.
internal/cmdutil/json_test.go Covers ParseOptionalBody and ParseJSONMap including stdin and single-quote flows.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["--params / --data flag value"] --> B{value == '-'?}
    B -- yes --> C{both params AND data == '-'?}
    C -- yes --> D["ErrValidation: cannot both read from stdin"]
    C -- no --> E["io.ReadAll(stdin)"]
    E --> F{empty after TrimSpace?}
    F -- yes --> G["ErrValidation: stdin is empty"]
    F -- no --> H["resolved string"]
    B -- no --> I{surrounded by single quotes?}
    I -- yes --> J["strip outer quotes → raw inner string"]
    I -- no --> K["use as-is"]
    J --> H
    K --> H
    H --> L{caller context}
    L -- ParseJSONMap --> M["json.Unmarshal → map[string]any"]
    L -- ParseOptionalBody --> N{httpMethod in POST/PUT/PATCH/DELETE?}
    N -- no --> O["return nil (body ignored)"]
    N -- yes --> P["json.Unmarshal → interface{}"]
Loading

Reviews (1): Last reviewed commit: "test: add stdin e2e regression coverage" | Re-trigger Greptile

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/cmdutil/json.go`:
- Around line 46-50: json.Unmarshal into map[string]any allows JSON "null" and
leaves result == nil, which currently returns success; update the code that
parses params (the block with variable result map[string]any and the
json.Unmarshal call) to explicitly reject non-object JSON by checking if result
== nil after Unmarshal and returning output.ErrValidation("%s invalid format,
expected JSON object", label) when it is nil (and still return result, nil only
when result is non-nil).

In `@tests/cli_e2e/stdin_regression_test.go`:
- Around line 198-203: The setDryRunConfigEnv helper currently sets app
ID/secret/brand but does not isolate CLI config state; update setDryRunConfigEnv
to also set the config directory to a unique temp location by calling
t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) so tests use an isolated
config/cache directory; modify the function named setDryRunConfigEnv to add that
single t.Setenv call alongside the existing LARKSUITE_CLI_* env setups.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2cd5bc38-9703-46f2-b05c-2849de0ede03

📥 Commits

Reviewing files that changed from the base of the PR and between e64d245 and 7de0af6.

📒 Files selected for processing (11)
  • cmd/api/api.go
  • cmd/api/api_test.go
  • cmd/service/service.go
  • cmd/service/service_test.go
  • internal/cmdutil/json.go
  • internal/cmdutil/json_test.go
  • internal/cmdutil/resolve.go
  • internal/cmdutil/resolve_test.go
  • tests/cli_e2e/core.go
  • tests/cli_e2e/core_test.go
  • tests/cli_e2e/stdin_regression_test.go

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 9, 2026

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@7de0af641c341d3abad567e99c1ff897d3f81690

🧩 Skill update

npx skills add larksuite/cli#fix/params-stdin-resolve -y -g

@liangshuo-1 liangshuo-1 merged commit 619ec8c into main Apr 9, 2026
16 of 17 checks passed
@liangshuo-1 liangshuo-1 deleted the fix/params-stdin-resolve branch April 9, 2026 11:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/L Large or sensitive change across domains or core paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows: --params JSON parsing fails for im messages read_users (and other non-empty params calls)

2 participants