Skip to content

fix(base): reject wrapped JSON payloads in +record-upsert#305

Open
zfaustk wants to merge 3 commits intolarksuite:mainfrom
zfaustk:patch-1
Open

fix(base): reject wrapped JSON payloads in +record-upsert#305
zfaustk wants to merge 3 commits intolarksuite:mainfrom
zfaustk:patch-1

Conversation

@zfaustk
Copy link
Copy Markdown

@zfaustk zfaustk commented Apr 7, 2026

Summary

Fixes #272.

The current base +record-upsert --json path accepts a common wrong payload shape, {"fields": {...}}, and only fails later in the Base API layer. This change surfaces the contract earlier in the CLI by requiring a direct record object.

Changes

  • reject a top-level fields wrapper in validateRecordJSON() and show a concrete example of the expected shape
  • update base_shortcuts_test.go to accept direct record JSON and cover the wrapped-payload validation error
  • update base_execute_test.go to use the direct record shape for create/update and add a regression case for the wrapped payload

Validation

  • local focused diff reviewed against /tmp/larkcli-triage-VvPLOV/repo
  • git diff --check
  • Go tests not run in this environment because go is not installed in the container

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced validation for record upsert operations to reject improperly formatted JSON payloads and provide clear error messages guiding users toward the correct direct record object format.
  • Breaking Changes

    • Record operations now strictly require direct record object format; the previously supported wrapped format is no longer accepted.

zfaustk added 3 commits April 8, 2026 01:30
Reject the common --json payload mistake where users pass a top-level fields wrapper to base +record-upsert. The CLI now surfaces a direct hint before the request reaches the Base API.
Update base record-upsert validation tests to accept direct record JSON, reject the top-level fields wrapper, and preserve invalid-JSON pass-through behavior.
Adjust base +record-upsert execution tests to use direct record JSON for create/update and add a regression case for the top-level fields wrapper misuse.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 7, 2026

📝 Walkthrough

Walkthrough

The changes fix JSON payload validation for the base +record-upsert command to enforce direct record object format instead of accepting a wrapped {"fields":{...}} structure, addressing API incompatibility issues. Updated tests and core validation logic now reject the old wrapped format with a clear error message.

Changes

Cohort / File(s) Summary
Validation Logic
shortcuts/base/record_ops.go
Added JSON validation in validateRecordJSON to detect and reject payloads with a top-level "fields" wrapper, returning a helpful error message directing users to use direct record object format instead.
Test Assertions
shortcuts/base/base_shortcuts_test.go, shortcuts/base/base_execute_test.go
Updated test cases to verify rejection of wrapped {"fields":{...}} format with error containing "direct record object". Updated create/upsert request payloads from wrapped format to direct record object format. Modified test data field names in attachment-related stubs.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~15 minutes

Possibly related issues

Poem

🐰 A wrapper once wrapped in a field so tight,
But the API demanded the format done right,
We hop to the rescue with validation so keen,
Direct records only—clean, pure, and pristine! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ 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%. 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 'fix(base): reject wrapped JSON payloads in +record-upsert' clearly and specifically summarizes the main change: rejecting incorrect wrapped JSON payload format.
Description check ✅ Passed The description covers the summary, changes, and validation steps but lacks explicit test plan checkboxes as specified in the template.
Linked Issues check ✅ Passed The PR directly addresses issue #272 by rejecting wrapped JSON payloads (like {"fields":{...}}) in +record-upsert and surfacing validation errors earlier, enabling users to provide correct record payload shapes for successful API calls.
Out of Scope Changes check ✅ Passed Changes are narrowly scoped: validation logic updates in record_ops.go and corresponding test updates in two test files directly address the wrapped payload rejection requirement, with no unrelated modifications.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@github-actions github-actions bot added domain/base PR touches the base domain size/M Single-domain feat or fix with limited business impact labels Apr 7, 2026
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 `@shortcuts/base/base_execute_test.go`:
- Line 718: The test data contains a mojibake string for the field name where
the map entry with id "fld_status" uses corrupted bytes; replace the corrupted
value ("ç�¶æ��") with the correct UTF-8 Chinese string "状态" in the map literal,
and ensure the source file is saved/committed as UTF-8 to prevent recurrence;
scan for other occurrences of "ç�¶æ" in the test file to correct any additional
corrupted field names.
- Line 913: The test contains UTF-8 corrupted Chinese characters in the
options/keyword map literal (the map literal with "options":
[]interface{}{map[string]interface{}{"id": "opt_1", "name": "..."} } ) — replace
the corrupted sequences like "已" and "已��" with the correct Chinese
characters "已" and "已完成" respectively in that map (and the other identical
occurrences of the same option/keyword entries in the same test file) so the
option name and keyword strings use the original Chinese text.
🪄 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: 583d3c87-9f45-4dc5-b9c3-4062d1670d42

📥 Commits

Reviewing files that changed from the base of the PR and between d6fada0 and ef6baf2.

📒 Files selected for processing (3)
  • shortcuts/base/base_execute_test.go
  • shortcuts/base/base_shortcuts_test.go
  • shortcuts/base/record_ops.go

Body: map[string]interface{}{
"code": 0,
"data": map[string]interface{}{"id": "fld_status", "name": "状态", "type": "text"},
"data": map[string]interface{}{"id": "fld_status", "name": "状态", "type": "text"},
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Same UTF-8 encoding corruption — 状态 became ç¶æ.

The field name 状态 (meaning "status") shows the same mojibake corruption pattern as the attachment field name.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@shortcuts/base/base_execute_test.go` at line 718, The test data contains a
mojibake string for the field name where the map entry with id "fld_status" uses
corrupted bytes; replace the corrupted value ("��") with the correct UTF-8
Chinese string "状态" in the map literal, and ensure the source file is
saved/committed as UTF-8 to prevent recurrence; scan for other occurrences of
"ç�¶æ" in the test file to correct any additional corrupted field names.

Body: map[string]interface{}{
"code": 0,
"data": map[string]interface{}{"options": []interface{}{map[string]interface{}{"id": "opt_1", "name": "已完成"}}, "total": 1},
"data": map[string]interface{}{"options": []interface{}{map[string]interface{}{"id": "opt_1", "name": "已完成"}}, "total": 1},
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Same UTF-8 encoding corruption in search options test.

The keyword and option names have similar corruption:

  • å·²
  • 已完成已宿

These should be restored to the original Chinese characters unless this is intentional test data for encoding handling.

Also applies to: 916-916, 919-919

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@shortcuts/base/base_execute_test.go` at line 913, The test contains UTF-8
corrupted Chinese characters in the options/keyword map literal (the map literal
with "options": []interface{}{map[string]interface{}{"id": "opt_1", "name":
"..."} } ) — replace the corrupted sequences like "å·²" and "å·²å®�æ��" with the
correct Chinese characters "已" and "已完成" respectively in that map (and the other
identical occurrences of the same option/keyword entries in the same test file)
so the option name and keyword strings use the original Chinese text.

@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Apr 7, 2026

Greptile Summary

This PR adds early validation in validateRecordJSON() to reject {\"fields\":{...}} wrapper payloads for +record-upsert, surfacing the contract error in the CLI rather than the Base API layer. The core logic and the new test coverage are correct, but the PR also unintentionally corrupts unrelated test strings by replacing Chinese characters with Latin-1 mojibake.

  • P1: base_execute_test.go lines 601–685 and 913–919 replace valid UTF-8 Chinese field names (附件, 状态, 已完成) with garbled strings (éä»¶, ç¶æ, 已宿) — an editor encoding issue that should be reverted before merging."

Confidence Score: 4/5

Not safe to merge until encoding corruption in unrelated test strings is reverted.

The validation logic in record_ops.go is correct and the new test coverage is solid, but the PR unintentionally corrupts Chinese-character test strings with Latin-1 mojibake (P1) that must be fixed before merging.

shortcuts/base/base_execute_test.go — encoding corruption in unrelated test strings at lines 601–685 and 913–919.

Important Files Changed

Filename Overview
shortcuts/base/record_ops.go Adds validateRecordJSON check that correctly rejects {"fields":{...}} wrapper payloads; minor edge case on empty {"fields":{}}
shortcuts/base/base_execute_test.go Correctly updates upsert tests to direct record shape and adds regression case; introduces encoding corruption replacing Chinese characters with mojibake in unrelated test strings
shortcuts/base/base_shortcuts_test.go Adds correct validation test for wrapped-payload rejection alongside existing cases

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["CLI: +record-upsert --json value"] --> B["validateRecordJSON()"]
    B --> C{"parseJSONObject()"}
    C -->|"parse error"| D["return nil: defer to execution path"]
    C -->|"ok"| E{"len(body) == 1?"}
    E -->|"No"| F["return nil: pass through"]
    E -->|"Yes"| G{"body[\"fields\"] is non-empty map?"}
    G -->|"No"| F
    G -->|"Yes"| H["FlagErrorf: must be direct record object"]
    F --> I["executeRecordUpsert()"]
    D --> I
    I --> J{"record-id set?"}
    J -->|"Yes"| K["PATCH /records/:id"]
    J -->|"No"| L["POST /records"]
Loading

Reviews (1): Last reviewed commit: "test(base): reject wrapped upsert payloa..." | Re-trigger Greptile

Comment on lines 601 to +685
@@ -598,7 +609,7 @@ func TestBaseRecordExecuteReadCreateDelete(t *testing.T) {
"data": map[string]interface{}{
"record_id": "rec_x",
"fields": map[string]interface{}{
"附件": []interface{}{
"附件": []interface{}{
map[string]interface{}{
"file_token": "existing_tok",
"name": "existing.pdf",
@@ -629,7 +640,7 @@ func TestBaseRecordExecuteReadCreateDelete(t *testing.T) {
"data": map[string]interface{}{
"record_id": "rec_x",
"fields": map[string]interface{}{
"附件": []interface{}{
"附件": []interface{}{
map[string]interface{}{
"file_token": "existing_tok",
"name": "existing.pdf",
@@ -671,7 +682,7 @@ func TestBaseRecordExecuteReadCreateDelete(t *testing.T) {
}

updateBody := string(updateStub.CapturedBody)
if !strings.Contains(updateBody, `"附件"`) ||
if !strings.Contains(updateBody, `"附件"`) ||
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Encoding corruption in unrelated test strings

The PR replaces valid UTF-8 Chinese characters with Latin-1 mojibake across multiple unrelated stubs and assertions: "附件" (attachment) → "éä»¶" at lines 601, 612, 643, and 685; "状态" (status) → "ç¶æ" at line 718; "已完成""已宿" and keyword "已""å·²" at lines 913–919. This appears to be an editor encoding bug where the UTF-8 source was re-saved as Latin-1. The tests may still pass because the corruption is symmetric between stubs and assertions, but the data no longer represents realistic API responses and will mislead future maintainers. These lines were not part of the intended change — please revert to the original Chinese characters.

return nil
}
if len(body) == 1 {
if fields, ok := body["fields"].(map[string]interface{}); ok && len(fields) > 0 {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Empty {"fields":{}} wrapper silently bypasses the check

The len(fields) > 0 guard means a payload of {"fields": {}} is not rejected, even though it is still the wrong shape. Consider dropping the length check so the empty-wrapper case is also caught early:

Suggested change
if fields, ok := body["fields"].(map[string]interface{}); ok && len(fields) > 0 {
if _, ok := body["fields"].(map[string]interface{}); ok {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain/base PR touches the base domain size/M Single-domain feat or fix with limited business impact

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug Report: lark-cli base record-upsert POST 请求全部失败

1 participant