bf9395e022
CI / results (push) Blocked by required conditions
CI / license-header (push) Has been skipped
CI / e2e-dry-run (push) Has been skipped
CI / e2e-live (push) Waiting to run
CI / fast-gate (push) Failing after 0s
Test PR Label Logic / test-pr-labels (push) Failing after 1s
Skill Format Check / check-format (push) Failing after 2s
CI / security (push) Failing after 5s
CI / unit-test (push) Has been skipped
CI / lint (push) Has been skipped
CI / script-test (push) Has been skipped
CI / deterministic-gate (push) Has been skipped
CI / coverage (push) Has been skipped
CI / deadcode (push) Waiting to run
121 lines
4.3 KiB
Go
121 lines
4.3 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package wiki
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/larksuite/cli/errs"
|
|
"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.CallAPITyped("POST", wikiSpacesAPIPath, nil, spec.RequestBody())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
raw := common.GetMap(data, "space")
|
|
if raw == nil {
|
|
return errs.NewInternalError(errs.SubtypeInvalidResponse, "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{}, errs.NewValidationError(errs.SubtypeInvalidArgument, "--name is required and cannot be blank").WithParam("--name")
|
|
}
|
|
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"),
|
|
}
|
|
}
|