Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions shortcuts/slides/slides_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,31 @@ var SlidesCreate = common.Shortcut{
}
}

// Fetch presentation URL via drive meta (best-effort)
if metaData, err := runtime.CallAPI(
"POST",
"/open-apis/drive/v1/metas/batch_query",
nil,
map[string]interface{}{
"request_docs": []map[string]interface{}{
{
"doc_token": presentationID,
"doc_type": "slides",
},
},
"with_url": true,
},
); err == nil {
metas := common.GetSlice(metaData, "metas")
if len(metas) > 0 {
if meta, ok := metas[0].(map[string]interface{}); ok {
if url := common.GetString(meta, "url"); url != "" {
result["url"] = url
}
}
}
}

if grant := common.AutoGrantCurrentUserDrivePermission(runtime, presentationID, "slides"); grant != nil {
result["permission_grant"] = grant
}
Expand Down
73 changes: 72 additions & 1 deletion shortcuts/slides/slides_create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
"github.com/larksuite/cli/shortcuts/common"
)

// TestSlidesCreateBasic verifies that slides +create returns the presentation ID and title in user mode.
// TestSlidesCreateBasic verifies that slides +create returns the presentation ID, title, and URL in user mode.
func TestSlidesCreateBasic(t *testing.T) {
t.Parallel()

Expand All @@ -34,6 +34,7 @@ func TestSlidesCreateBasic(t *testing.T) {
},
},
})
registerBatchQueryStub(reg, "pres_abc123", "https://example.feishu.cn/slides/pres_abc123")

err := runSlidesCreateShortcut(t, f, stdout, []string{
"+create",
Expand All @@ -51,6 +52,9 @@ func TestSlidesCreateBasic(t *testing.T) {
if data["title"] != "项目汇报" {
t.Fatalf("title = %v, want 项目汇报", data["title"])
}
if data["url"] != "https://example.feishu.cn/slides/pres_abc123" {
t.Fatalf("url = %v, want https://example.feishu.cn/slides/pres_abc123", data["url"])
}
if _, ok := data["permission_grant"]; ok {
t.Fatalf("did not expect permission_grant in user mode")
}
Expand All @@ -73,6 +77,7 @@ func TestSlidesCreateBotAutoGrant(t *testing.T) {
},
},
})
registerBatchQueryStub(reg, "pres_bot", "https://example.feishu.cn/slides/pres_bot")
reg.Register(&httpmock.Stub{
Method: "POST",
URL: "/open-apis/drive/v1/permissions/pres_bot/members",
Expand Down Expand Up @@ -125,6 +130,7 @@ func TestSlidesCreateBotSkippedWithoutCurrentUser(t *testing.T) {
},
},
})
registerBatchQueryStub(reg, "pres_no_user", "https://example.feishu.cn/slides/pres_no_user")

err := runSlidesCreateShortcut(t, f, stdout, []string{
"+create",
Expand Down Expand Up @@ -182,6 +188,7 @@ func TestSlidesCreateDefaultTitle(t *testing.T) {
},
},
})
registerBatchQueryStub(reg, "pres_default", "https://example.feishu.cn/slides/pres_default")

err := runSlidesCreateShortcut(t, f, stdout, []string{
"+create",
Expand Down Expand Up @@ -244,6 +251,7 @@ func TestSlidesCreateWithSlides(t *testing.T) {
},
},
})
registerBatchQueryStub(reg, "pres_with_slides", "https://example.feishu.cn/slides/pres_with_slides")
reg.Register(&httpmock.Stub{
Method: "POST",
URL: "/open-apis/slides_ai/v1/xml_presentations/pres_with_slides/slide",
Expand Down Expand Up @@ -420,6 +428,7 @@ func TestSlidesCreateWithSlidesEmptyArray(t *testing.T) {
},
},
})
registerBatchQueryStub(reg, "pres_empty_slides", "https://example.feishu.cn/slides/pres_empty_slides")

err := runSlidesCreateShortcut(t, f, stdout, []string{
"+create",
Expand Down Expand Up @@ -492,6 +501,7 @@ func TestSlidesCreateWithoutSlidesUnchanged(t *testing.T) {
},
},
})
registerBatchQueryStub(reg, "pres_no_slides", "https://example.feishu.cn/slides/pres_no_slides")

err := runSlidesCreateShortcut(t, f, stdout, []string{
"+create",
Expand Down Expand Up @@ -520,6 +530,51 @@ func TestSlidesCreateWithoutSlidesUnchanged(t *testing.T) {
}
}

// TestSlidesCreateURLFetchBestEffort verifies that the shortcut succeeds even when batch_query fails.
func TestSlidesCreateURLFetchBestEffort(t *testing.T) {
t.Parallel()

f, stdout, _, reg := cmdutil.TestFactory(t, slidesTestConfig(t, ""))
reg.Register(&httpmock.Stub{
Method: "POST",
URL: "/open-apis/slides_ai/v1/xml_presentations",
Body: map[string]interface{}{
"code": 0,
"msg": "ok",
"data": map[string]interface{}{
"xml_presentation_id": "pres_no_url",
"revision_id": 1,
},
},
})
// batch_query returns an error — URL fetch should be silently skipped
reg.Register(&httpmock.Stub{
Method: "POST",
URL: "/open-apis/drive/v1/metas/batch_query",
Body: map[string]interface{}{
"code": 99999,
"msg": "no permission",
},
})

err := runSlidesCreateShortcut(t, f, stdout, []string{
"+create",
"--title", "No URL",
"--as", "user",
})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

data := decodeSlidesCreateEnvelope(t, stdout)
if data["xml_presentation_id"] != "pres_no_url" {
t.Fatalf("xml_presentation_id = %v, want pres_no_url", data["xml_presentation_id"])
}
if _, ok := data["url"]; ok {
t.Fatalf("did not expect url when batch_query fails")
}
}

// TestXmlEscape verifies that XML special characters are properly escaped.
func TestXmlEscape(t *testing.T) {
t.Parallel()
Expand Down Expand Up @@ -567,6 +622,22 @@ func runSlidesCreateShortcut(t *testing.T, f *cmdutil.Factory, stdout *bytes.Buf
return parent.Execute()
}

// registerBatchQueryStub registers a drive meta batch_query mock that returns the given URL.
func registerBatchQueryStub(reg *httpmock.Registry, token, url string) {
reg.Register(&httpmock.Stub{
Method: "POST",
URL: "/open-apis/drive/v1/metas/batch_query",
Body: map[string]interface{}{
"code": 0,
"data": map[string]interface{}{
"metas": []map[string]interface{}{
{"doc_token": token, "doc_type": "slides", "title": "", "url": url},
},
},
},
})
}

// decodeSlidesCreateEnvelope parses the JSON output and returns the data map.
func decodeSlidesCreateEnvelope(t *testing.T, stdout *bytes.Buffer) map[string]interface{} {
t.Helper()
Expand Down
1 change: 1 addition & 0 deletions skills/lark-slides/references/lark-slides-create.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ lark-cli slides +create --title "项目汇报" --slides '[...]' --dry-run

- **`xml_presentation_id`**(string):演示文稿的唯一标识符,后续添加页面时需要此 ID
- **`title`**(string):演示文稿标题
- **`url`**(string,可选):演示文稿的在线链接,如有返回则务必展示给用户(需要 drive 相关权限;若获取失败则不返回此字段)
- **`revision_id`**(integer):演示文稿版本号
- **`slide_ids`**(string[],可选):仅传 `--slides` 时返回,成功添加的页面 ID 列表
- **`slides_added`**(integer,可选):仅传 `--slides` 时返回,成功添加的页面数量
Expand Down
Loading