From 64b1b3f3ed5ee6ca55c0cd111497f2a1ce1b00b7 Mon Sep 17 00:00:00 2001 From: ZEden0 Date: Tue, 16 Jun 2026 16:25:36 +0800 Subject: [PATCH] feat(docs): support lang for fetch v2 (#1459) --- internal/core/config.go | 2 +- internal/core/config_test.go | 21 ++++++++++++++ shortcuts/doc/docs_fetch_v2.go | 14 +++++++++ shortcuts/doc/docs_fetch_v2_test.go | 44 +++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 1 deletion(-) diff --git a/internal/core/config.go b/internal/core/config.go index 040b59b3..846ee2ba 100644 --- a/internal/core/config.go +++ b/internal/core/config.go @@ -265,8 +265,8 @@ func ResolveConfigFromMulti(raw *MultiAppConfig, kc keychain.KeychainAccess, pro AppID: app.AppId, AppSecret: secret, Brand: app.Brand, - DefaultAs: app.DefaultAs, Lang: app.Lang, + DefaultAs: app.DefaultAs, } if len(app.Users) > 0 { cfg.UserOpenId = app.Users[0].UserOpenId diff --git a/internal/core/config_test.go b/internal/core/config_test.go index 3d727c50..4a501b68 100644 --- a/internal/core/config_test.go +++ b/internal/core/config_test.go @@ -132,6 +132,27 @@ func TestResolveConfigFromMulti_AcceptsPlainSecret(t *testing.T) { } } +func TestResolveConfigFromMulti_CarriesLang(t *testing.T) { + raw := &MultiAppConfig{ + Apps: []AppConfig{ + { + AppId: "cli_abc", + AppSecret: PlainSecret("my-secret"), + Brand: BrandFeishu, + Lang: "en", + }, + }, + } + + cfg, err := ResolveConfigFromMulti(raw, nil, "") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if cfg.Lang != "en" { + t.Errorf("Lang = %q, want %q", cfg.Lang, "en") + } +} + func TestResolveConfigFromMulti_MatchingKeychainRefPassesValidation(t *testing.T) { // Keychain ref matches appId, so validation passes. // The subsequent ResolveSecretInput will fail (no real keychain), diff --git a/shortcuts/doc/docs_fetch_v2.go b/shortcuts/doc/docs_fetch_v2.go index 5fa0ef67..b3e4f2fc 100644 --- a/shortcuts/doc/docs_fetch_v2.go +++ b/shortcuts/doc/docs_fetch_v2.go @@ -19,6 +19,7 @@ func v2FetchFlags() []common.Flag { return []common.Flag{ {Name: "doc-format", Desc: "output content format; xml keeps DocxXML structure and optional block ids, markdown is plain export", Default: "xml", Enum: []string{"xml", "markdown"}}, {Name: "detail", Desc: "detail level; simple for reading, with-ids for block references, full for styles and edit metadata", Default: "simple", Enum: []string{"simple", "with-ids", "full"}}, + {Name: "lang", Desc: "user cite display language, e.g. en-US, zh-CN, ja-JP"}, {Name: "revision-id", Desc: "document revision id; -1 means latest", Type: "int", Default: "-1"}, {Name: "scope", Desc: "read scope; full reads whole doc, outline lists headings, section expands from heading anchor, range uses block ids, keyword searches text", Default: "full", Enum: []string{"full", "outline", "range", "keyword", "section"}}, {Name: "start-block-id", Desc: "range/section anchor block id; required for section and optional start for range"}, @@ -89,6 +90,9 @@ func buildFetchBody(runtime *common.RuntimeContext) map[string]interface{} { if v := runtime.Int("revision-id"); v > 0 { body["revision_id"] = v } + if lang := resolveFetchLang(runtime); lang != "" { + body["lang"] = lang + } detail := effectiveFetchDetail(runtime) switch detail { @@ -118,6 +122,16 @@ func buildFetchBody(runtime *common.RuntimeContext) map[string]interface{} { return body } +func resolveFetchLang(runtime *common.RuntimeContext) string { + if runtime.Changed("lang") { + return strings.TrimSpace(runtime.Str("lang")) + } + if runtime.Config == nil { + return "" + } + return strings.TrimSpace(string(runtime.Config.Lang)) +} + // buildReadOption 拼装 read_option JSON;full/空模式返回 nil,让服务端走默认全文路径。 func buildReadOption(runtime *common.RuntimeContext) map[string]interface{} { mode := strings.TrimSpace(runtime.Str("scope")) diff --git a/shortcuts/doc/docs_fetch_v2_test.go b/shortcuts/doc/docs_fetch_v2_test.go index 89a1334d..1210f0ac 100644 --- a/shortcuts/doc/docs_fetch_v2_test.go +++ b/shortcuts/doc/docs_fetch_v2_test.go @@ -10,6 +10,7 @@ import ( "testing" "github.com/larksuite/cli/internal/cmdutil" + "github.com/larksuite/cli/internal/core" "github.com/larksuite/cli/internal/httpmock" "github.com/larksuite/cli/shortcuts/common" "github.com/spf13/cobra" @@ -62,6 +63,47 @@ func TestBuildFetchBodyOmitsEmptyScene(t *testing.T) { } } +func TestBuildFetchBodyIncludesExplicitLang(t *testing.T) { + t.Parallel() + + runtime := newFetchBodyTestRuntime(context.Background()) + if err := runtime.Cmd.Flags().Set("lang", "en-US"); err != nil { + t.Fatalf("set lang: %v", err) + } + + body := buildFetchBody(runtime) + if got := body["lang"]; got != "en-US" { + t.Fatalf("lang = %#v, want %q", got, "en-US") + } +} + +func TestBuildFetchBodyUsesRuntimeConfigLang(t *testing.T) { + t.Parallel() + + runtime := newFetchBodyTestRuntime(context.Background()) + runtime.Config = &core.CliConfig{Lang: "zh_cn"} + + body := buildFetchBody(runtime) + if got := body["lang"]; got != "zh_cn" { + t.Fatalf("lang = %#v, want %q", got, "zh_cn") + } +} + +func TestBuildFetchBodyExplicitBlankLangOmitsLang(t *testing.T) { + t.Parallel() + + runtime := newFetchBodyTestRuntime(context.Background()) + runtime.Config = &core.CliConfig{Lang: "zh_cn"} + if err := runtime.Cmd.Flags().Set("lang", ""); err != nil { + t.Fatalf("set lang: %v", err) + } + + body := buildFetchBody(runtime) + if _, ok := body["lang"]; ok { + t.Fatalf("did not expect blank explicit lang in fetch body: %#v", body) + } +} + func TestDocsFetchDryRunDefaultsToV2Endpoint(t *testing.T) { t.Parallel() @@ -262,6 +304,7 @@ func newFetchBodyTestRuntime(ctx context.Context) *common.RuntimeContext { cmd := &cobra.Command{Use: "+fetch"} cmd.Flags().String("doc-format", "xml", "") cmd.Flags().String("detail", "simple", "") + cmd.Flags().String("lang", "", "") cmd.Flags().Int("revision-id", -1, "") cmd.Flags().String("scope", "full", "") cmd.Flags().String("start-block-id", "", "") @@ -281,6 +324,7 @@ func newFetchShortcutTestRuntime(t *testing.T, apiVersion string, setFlags map[s cmd.Flags().String("doc", "doxcnFetchDryRun", "") cmd.Flags().String("doc-format", "xml", "") cmd.Flags().String("detail", "simple", "") + cmd.Flags().String("lang", "", "") cmd.Flags().Int("revision-id", -1, "") cmd.Flags().String("scope", "full", "") cmd.Flags().String("start-block-id", "", "")