Skip to content
Open
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
36 changes: 35 additions & 1 deletion shortcuts/base/base_resolve.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ var BaseURLResolve = common.Shortcut{
"base:block:read",
"base:field:read",
"base:record:read",
"base:table:read",
"wiki:node:retrieve",
},
AuthTypes: authTypes(),
Expand Down Expand Up @@ -61,9 +62,15 @@ var BaseURLResolve = common.Shortcut{
case "base_url":
baseToken := firstPathSegmentAfter(parsed.Path, "/base/")
if selectedBlockID := strings.TrimSpace(parsed.Query().Get("table")); selectedBlockID != "" {
return common.NewDryRunAPI().
dry := common.NewDryRunAPI().
Desc("2-step: list Base blocks first, then confirm table selection if the block list does not include it")
dry.
POST("/open-apis/base/v3/bases/:base_token/blocks/list").
Desc("[1] List Base blocks and match selected_block_id").
Body(map[string]interface{}{}).
GET("/open-apis/base/v3/bases/:base_token/tables/:selected_block_id").
Desc("[2] Fallback: confirm selected_block_id as a table when block list is nonmatching")
return dry.
Set("base_token", baseToken).
Set("selected_block_id", selectedBlockID)
}
Expand All @@ -83,6 +90,8 @@ var BaseURLResolve = common.Shortcut{
dry.POST("/open-apis/base/v3/bases/:base_token/blocks/list").
Desc("[2] List Base blocks and match selected_block_id").
Body(map[string]interface{}{})
dry.GET("/open-apis/base/v3/bases/:base_token/tables/:selected_block_id").
Desc("[3] Fallback: confirm selected_block_id as a table when block list is nonmatching")
return dry.
Set("base_token", "<obj_token from step 1>").
Set("selected_block_id", selectedBlockID)
Expand Down Expand Up @@ -470,6 +479,16 @@ func enrichBaseResolveHint(runtime *common.RuntimeContext, out map[string]interf
return
}

if table, found := resolveSelectedBaseTable(runtime, baseToken, selectedBlockID); found {
out["block_type"] = "table"
if name := strings.TrimSpace(tableNameFromMap(table)); name != "" {
out["block_name"] = name
}
applyResolvedTableSelection(out, selection)
enrichResolvedTable(runtime, out, baseToken, selectedBlockID)
return
}

out["hint"] = resolveUnknownBlockHint()
}

Expand Down Expand Up @@ -503,6 +522,21 @@ func resolveSelectedBaseBlock(runtime *common.RuntimeContext, baseToken, selecte
return resolvedBaseBlock{}, false, nil
}

func resolveSelectedBaseTable(runtime *common.RuntimeContext, baseToken, selectedTableID string) (map[string]interface{}, bool) {
table, err := baseV3Call(runtime, "GET", baseV3Path("bases", baseToken, "tables", selectedTableID), nil, nil)
if err != nil {
return nil, false
}
confirmedID := strings.TrimSpace(tableID(table))
if confirmedID != "" && confirmedID != selectedTableID {
return nil, false
}
if confirmedID == "" {
table["id"] = selectedTableID
}
return table, true
}

func enrichResolvedTable(runtime *common.RuntimeContext, out map[string]interface{}, baseToken, tableID string) {
out["table_id"] = tableID
fields, total, err := listAllFields(runtime, baseToken, tableID, 0, 100)
Expand Down
39 changes: 39 additions & 0 deletions shortcuts/base/base_resolve_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,31 @@ func TestBaseURLResolveBaseURL(t *testing.T) {
}
})

t.Run("table selection falls back when block list omits table", func(t *testing.T) {
factory, stdout, reg := newExecuteFactory(t)
reg.Register(baseBlockListResolveStub("bas123",
map[string]interface{}{"id": "blk_dashboard", "type": "dashboard", "name": "Sales"},
))
reg.Register(tableGetResolveStub("bas123", "tbl123", "Orders"))
reg.Register(fieldListStub("bas123", "tbl123"))
err := runShortcutWithAuthTypes(t, BaseURLResolve, authTypes(), []string{
"+url-resolve",
"--url", "https://example.larkoffice.com/base/bas123?table=tbl123&view=vew123&record=rec123",
"--as", "user",
}, factory, stdout)
if err != nil {
t.Fatalf("err=%v", err)
}

data := decodeBaseEnvelope(t, stdout)
if data["block_id"] != "tbl123" || data["selection_source"] != "url_query" || data["block_type"] != "table" || data["table_id"] != "tbl123" || data["view_id"] != "vew123" || data["record_id"] != "rec123" {
t.Fatalf("missing fallback table coordinates: %#v", data)
}
if data["block_name"] != "Orders" {
t.Fatalf("missing table name from fallback: %#v", data)
}
})

t.Run("base only", func(t *testing.T) {
factory, stdout, _ := newExecuteFactory(t)
err := runShortcutWithAuthTypes(t, BaseURLResolve, authTypes(), []string{
Expand Down Expand Up @@ -269,6 +294,20 @@ func baseBlockListResolveStub(baseToken string, blocks ...map[string]interface{}
}
}

func tableGetResolveStub(baseToken, tableID, name string) *httpmock.Stub {
return &httpmock.Stub{
Method: "GET",
URL: "/open-apis/base/v3/bases/" + baseToken + "/tables/" + tableID,
Body: map[string]interface{}{
"code": 0,
"data": map[string]interface{}{
"id": tableID,
"name": name,
},
},
}
}

func TestBaseURLResolveWikiURL(t *testing.T) {
t.Run("bitable", func(t *testing.T) {
factory, stdout, reg := newExecuteFactory(t)
Expand Down
4 changes: 4 additions & 0 deletions tests/cli_e2e/base/base_url_resolve_dryrun_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ func TestBaseURLResolveSelectedBlockDryRun(t *testing.T) {

require.Equal(t, "POST", clie2e.DryRunGet(result.Stdout, "api.0.method").String(), result.Stdout)
require.Equal(t, "/open-apis/base/v3/bases/app_x/blocks/list", clie2e.DryRunGet(result.Stdout, "api.0.url").String(), result.Stdout)
require.Equal(t, "GET", clie2e.DryRunGet(result.Stdout, "api.1.method").String(), result.Stdout)
require.Equal(t, "/open-apis/base/v3/bases/app_x/tables/blk_selected", clie2e.DryRunGet(result.Stdout, "api.1.url").String(), result.Stdout)
require.Equal(t, "blk_selected", clie2e.DryRunGet(result.Stdout, "selected_block_id").String(), result.Stdout)
}

Expand All @@ -58,5 +60,7 @@ func TestBaseURLResolveWikiSelectedBlockDryRun(t *testing.T) {
require.Equal(t, "wik_x", clie2e.DryRunGet(result.Stdout, "api.0.params.token").String(), result.Stdout)
require.Equal(t, "POST", clie2e.DryRunGet(result.Stdout, "api.1.method").String(), result.Stdout)
require.Equal(t, "/open-apis/base/v3/bases/%3Cobj_token%20from%20step%201%3E/blocks/list", clie2e.DryRunGet(result.Stdout, "api.1.url").String(), result.Stdout)
require.Equal(t, "GET", clie2e.DryRunGet(result.Stdout, "api.2.method").String(), result.Stdout)
require.Equal(t, "/open-apis/base/v3/bases/%3Cobj_token%20from%20step%201%3E/tables/wkf_selected", clie2e.DryRunGet(result.Stdout, "api.2.url").String(), result.Stdout)
require.Equal(t, "wkf_selected", clie2e.DryRunGet(result.Stdout, "selected_block_id").String(), result.Stdout)
}
Loading