mirror of
https://github.com/larksuite/cli.git
synced 2026-07-04 06:29:52 +08:00
- +node-get: wrap wiki.spaces.get_node; accepts node_token, obj_token, or a Lark URL (URL path auto-infers obj_type); formatted output with creator / updated_at. No synthesized url — get_node returns none and a BuildResourceURL fallback is a non-canonical link that misleads in a read/confirm command (sibling read shortcuts omit it too) - +node-delete: wrap space.node delete; high-risk-write (--yes gated), async delete-node task polling, auto-resolves space_id via get_node when --space-id omitted, actionable hints for codes 131011 / 131003. The delete-node task result lives under the gateway's generic `simple_task_result` key (NOT `delete_node_result`) - +space-create: wrap spaces.create; user-only identity, --name required (no empty-name spaces), flattened space output, no url - factor the shared wiki async-task poll loop into wiki_async_task.go; preserve upstream Lark Detail.Code on poll exhaustion (no longer rebuilt via lossy ErrWithHint) - drive +task_result: add wiki_delete_node scenario so +node-delete's async-timeout next_command actually resolves - skill docs: reference pages for the 3 new shortcuts + SKILL.md shortcuts table (no raw nodes.delete API exists — it's shortcut-only, so it is intentionally absent from API Resources / permission table); drop the circular TestWikiShortcutsIncludeAllCommands change-detector Change-Id: I316f78290cec5bc50f80d629173e3bf2a35dd005
121 lines
4.2 KiB
Go
121 lines
4.2 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package wiki
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/larksuite/cli/internal/output"
|
|
"github.com/larksuite/cli/shortcuts/common"
|
|
)
|
|
|
|
// WikiSpaceCreate wraps wiki.spaces.create. The raw API only takes two
|
|
// optional string fields, so the shortcut's value is flag ergonomics
|
|
// (no hand-written --params JSON), output flattening (data.space.* lifted
|
|
// to the top level), and a dry-run preview.
|
|
//
|
|
// The API only accepts a user access token (no tenant/bot), so AuthTypes is
|
|
// user-only — the framework's CheckIdentity rejects --as bot for us.
|
|
var WikiSpaceCreate = common.Shortcut{
|
|
Service: "wiki",
|
|
Command: "+space-create",
|
|
Description: "Create a wiki space",
|
|
Risk: "write",
|
|
// The API accepts wiki:wiki or wiki:space:write_only. The framework's
|
|
// scope preflight does exact-string matching (see +space-list), so
|
|
// declare the narrowest form the API takes to avoid false-rejecting
|
|
// tokens that only carry wiki:space:write_only.
|
|
Scopes: []string{"wiki:space:write_only"},
|
|
AuthTypes: []string{"user"},
|
|
Flags: []common.Flag{
|
|
{Name: "name", Desc: "wiki space name", Required: true},
|
|
{Name: "description", Desc: "wiki space description"},
|
|
},
|
|
Tips: []string{
|
|
"Only --as user is supported; the create API does not accept a tenant/bot token.",
|
|
"The underlying spaces.create API is flagged danger in the schema browser; a space is recoverable via `wiki +delete-space` if created by mistake.",
|
|
"--name is required: an unnamed space is almost always an accident and is hard to find later.",
|
|
},
|
|
Validate: func(ctx context.Context, runtime *common.RuntimeContext) error {
|
|
_, err := readWikiSpaceCreateSpec(runtime)
|
|
return err
|
|
},
|
|
DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI {
|
|
spec, err := readWikiSpaceCreateSpec(runtime)
|
|
if err != nil {
|
|
return common.NewDryRunAPI().Set("error", err.Error())
|
|
}
|
|
return common.NewDryRunAPI().
|
|
POST(wikiSpacesAPIPath).
|
|
Body(spec.RequestBody())
|
|
},
|
|
Execute: func(ctx context.Context, runtime *common.RuntimeContext) error {
|
|
spec, err := readWikiSpaceCreateSpec(runtime)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Fprintf(runtime.IO().ErrOut, "Creating wiki space %q...\n", spec.Name)
|
|
|
|
data, err := runtime.CallAPI("POST", wikiSpacesAPIPath, nil, spec.RequestBody())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
raw := common.GetMap(data, "space")
|
|
if raw == nil {
|
|
return output.Errorf(output.ExitAPI, "api_error", "wiki space create returned no space")
|
|
}
|
|
|
|
out := wikiSpaceCreateOutput(raw)
|
|
fmt.Fprintf(runtime.IO().ErrOut, "Created wiki space %s\n", common.MaskToken(common.GetString(out, "space_id")))
|
|
runtime.Out(out, nil)
|
|
return nil
|
|
},
|
|
}
|
|
|
|
// wikiSpaceCreateSpec is the normalized CLI input.
|
|
type wikiSpaceCreateSpec struct {
|
|
Name string
|
|
Description string
|
|
}
|
|
|
|
// RequestBody converts the normalized input into the OpenAPI payload. Both
|
|
// fields are optional per the API, but Validate enforces a non-empty name,
|
|
// so name is always present here.
|
|
func (spec wikiSpaceCreateSpec) RequestBody() map[string]interface{} {
|
|
body := map[string]interface{}{"name": spec.Name}
|
|
if spec.Description != "" {
|
|
body["description"] = spec.Description
|
|
}
|
|
return body
|
|
}
|
|
|
|
func readWikiSpaceCreateSpec(runtime *common.RuntimeContext) (wikiSpaceCreateSpec, error) {
|
|
spec := wikiSpaceCreateSpec{
|
|
Name: strings.TrimSpace(runtime.Str("name")),
|
|
Description: strings.TrimSpace(runtime.Str("description")),
|
|
}
|
|
if spec.Name == "" {
|
|
return wikiSpaceCreateSpec{}, output.ErrValidation("--name is required and cannot be blank")
|
|
}
|
|
return spec, nil
|
|
}
|
|
|
|
// wikiSpaceCreateOutput flattens data.space into the top-level envelope. It
|
|
// reads the raw map (rather than parseWikiSpaceRecord) so the description
|
|
// the caller just set round-trips back in the output.
|
|
func wikiSpaceCreateOutput(raw map[string]interface{}) map[string]interface{} {
|
|
return map[string]interface{}{
|
|
"space_id": common.GetString(raw, "space_id"),
|
|
"name": common.GetString(raw, "name"),
|
|
"description": common.GetString(raw, "description"),
|
|
"space_type": common.GetString(raw, "space_type"),
|
|
"visibility": common.GetString(raw, "visibility"),
|
|
"open_sharing": common.GetString(raw, "open_sharing"),
|
|
}
|
|
}
|