apiclient

package
v1.0.63 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 3, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

Documentation

Overview

Package apiclient provides a lightweight HTTP client for calling DingTalk OpenAPI (https://api.dingtalk.com) directly, bypassing the MCP JSON-RPC transport. It is used exclusively by the `dws api` command.

Index

Constants

View Source
const (
	// DefaultBaseURL is the DingTalk new-style OpenAPI base URL.
	DefaultBaseURL = "https://api.dingtalk.com"

	// LegacyBaseURL is the DingTalk legacy (oapi) API base URL.
	LegacyBaseURL = "https://oapi.dingtalk.com"

	// AuthHeader is the new-style OpenAPI authentication header.
	AuthHeader = "x-acs-dingtalk-access-token"

	// LegacyAuthParam is the query parameter used for legacy API authentication.
	LegacyAuthParam = "access_token"
)
View Source
const (
	// DefaultPageLimit is the maximum number of pages fetched with --page-all
	// when --page-limit is not explicitly set.
	DefaultPageLimit = 10

	// MaxPageLimit is the hard safety cap to prevent infinite loops when an
	// API endpoint has a bug that causes has_more to never become false.
	// Use --page-limit 0 to hit this cap; any explicit positive value is
	// honoured up to this ceiling.
	MaxPageLimit = 500

	// DefaultPageDelay is the delay between paginated requests in milliseconds.
	DefaultPageDelay = 200
)

Variables

View Source
var AllowedHosts = map[string]bool{
	"api.dingtalk.com":          true,
	"oapi.dingtalk.com":         true,
	"api-deap.dingtalk.com":     true,
	"pre-api-deap.dingtalk.com": true,
}

AllowedHosts is the set of trusted DingTalk API hosts. Only these hosts may receive access tokens to prevent token leakage.

View Source
var AllowedMethods = map[string]bool{
	"GET": true, "POST": true, "PUT": true, "PATCH": true, "DELETE": true,
}

AllowedMethods is the set of HTTP methods permitted for raw API calls.

Functions

func HandleResponse

func HandleResponse(resp *RawAPIResponse, opts ResponseOptions) error

HandleResponse routes response processing based on Content-Type and status code.

func IsDeferredInput added in v1.0.60

func IsDeferredInput(raw string) bool

IsDeferredInput reports whether a value would read bytes from stdin or a file. Dry-run uses it to avoid touching either input source.

func IsLegacyAPI

func IsLegacyAPI(urlStr string) bool

IsLegacyAPI returns true if the URL targets the legacy oapi.dingtalk.com endpoint. Legacy APIs use query-parameter authentication instead of header-based auth.

func MaskToken

func MaskToken(token string) string

MaskToken returns a masked version of a token for display in dry-run and log output. Shows the first 4 characters followed by "****".

func NormalisePath

func NormalisePath(path, baseURL string) string

NormalisePath normalises an API path:

  • Full URLs are accepted as-is (after stripping query/fragment)
  • Relative paths are prefixed with the base URL
  • Query strings and fragments are stripped (must use --params)

func ParseJSONMap

func ParseJSONMap(raw, flagName string, stdin io.Reader) (map[string]any, error)

ParseJSONMap parses a --params flag value into a map[string]any. Supports:

  • JSON string: '{"key":"value"}'
  • "-" to read from stdin
  • "@file" to read from a JSON file
  • Empty string returns nil (no params)

func ParseOptionalBody

func ParseOptionalBody(method, raw string, stdin io.Reader) (any, error)

ParseOptionalBody parses a --data flag value into a request body. Returns nil for empty input. GET requests are not allowed to have a body.

func PrintDryRun

func PrintDryRun(w io.Writer, req RawAPIRequest, baseURL, token string) error

PrintDryRun outputs a dry-run preview of the API request that would be sent.

func ValidateFlagExclusion

func ValidateFlagExclusion(outputPath string, pageAll bool) error

ValidateFlagExclusion checks mutual exclusion between flags.

func ValidateInputStdinExclusion added in v1.0.60

func ValidateInputStdinExclusion(params, data string, file *FileUpload) error

ValidateInputStdinExclusion ensures at most one API input consumes stdin.

func ValidateMethod

func ValidateMethod(method string) (string, error)

ValidateMethod checks that the HTTP method is one of the five allowed methods.

func ValidatePath

func ValidatePath(path string) error

ValidatePath checks the API path for injection attacks and dangerous characters.

func ValidateRedirect added in v1.0.60

func ValidateRedirect(req *http.Request, via []*http.Request) error

ValidateRedirect only permits same-origin HTTPS redirects. The raw API token is carried in a non-standard header (or a legacy query parameter), so Go's default cross-origin credential stripping is not sufficient here.

func ValidateStdinExclusion

func ValidateStdinExclusion(params, data string) error

ValidateStdinExclusion checks that --params and --data don't both read from stdin.

func ValidateTargetHost

func ValidateTargetHost(fullURL string) error

ValidateTargetHost checks that the resolved request URL targets a trusted DingTalk host. This prevents access-token leakage to arbitrary domains.

func ValidateUserInput

func ValidateUserInput(value, fieldName string) error

ValidateUserInput checks a user-provided string for control characters and dangerous Unicode codepoints that could enable injection attacks.

Types

type APIClient

type APIClient struct {
	BaseURL     string
	HTTPClient  *http.Client
	Token       string
	DingTalkExt string
	// TargetValidator defaults to ValidateTargetHost. Tests may replace it to
	// exercise transport behavior against an in-process HTTP server.
	TargetValidator func(string) error
}

APIClient wraps an HTTP client for DingTalk OpenAPI calls.

func NewClient

func NewClient(token, baseURL string) *APIClient

NewClient creates an APIClient with sensible defaults.

func (*APIClient) Do

Do sends a raw API request and returns the response.

func (*APIClient) PaginateAll

func (c *APIClient) PaginateAll(ctx context.Context, req RawAPIRequest, opts PaginationOptions) ([]any, error)

PaginateAll fetches all pages of a paginated API and merges the results. DingTalk APIs use two pagination patterns:

  • cursor/next_cursor/has_more (in response body)
  • next_token (in response body)

The function auto-detects which pattern the API uses.

func (*APIClient) UploadMultipart added in v1.0.63

func (c *APIClient) UploadMultipart(ctx context.Context, req MultipartUploadRequest) (*RawAPIResponse, error)

UploadMultipart streams a file and string form fields to an OpenAPI endpoint. This compatibility surface is used by the dingtalk-tag scoped Skill upload flow, whose credential must be sent as a Bearer token.

type FileUpload added in v1.0.60

type FileUpload struct {
	FieldName string
	Path      string
	FileName  string
	Reader    io.Reader
}

FileUpload describes one multipart file. Reader is set for stdin and tests; when it is nil, Path is opened lazily immediately before the request.

func ParseFileSpec added in v1.0.60

func ParseFileSpec(raw string) (*FileUpload, error)

ParseFileSpec parses --file [field=]path without opening the file.

type MultipartUploadRequest added in v1.0.63

type MultipartUploadRequest struct {
	Path      string
	FieldName string
	FileName  string
	File      io.Reader
	Fields    map[string]string
	// BearerAuth sends Token through Authorization: Bearer instead of the
	// DingTalk OAuth header. It is reserved for scoped upload credentials.
	BearerAuth bool
}

MultipartUploadRequest describes a streaming multipart upload used by narrow helper commands that already own an open file reader.

type PaginationOptions

type PaginationOptions struct {
	PageLimit int       // Maximum pages (0 = unlimited, capped at MaxPageLimit)
	PageDelay int       // Delay between pages in milliseconds
	LogWriter io.Writer // Optional: progress log output (typically stderr)
}

PaginationOptions controls automatic pagination behaviour.

type RawAPIRequest

type RawAPIRequest struct {
	Method string         // GET, POST, PUT, PATCH, DELETE
	Path   string         // /v1.0/calendar/events or full URL
	Params map[string]any // query parameters
	Data   any            // request body (JSON), nil for GET
	File   *FileUpload    // optional single streaming multipart file
	// Sources are preview-only metadata for deferred stdin/@file inputs.
	ParamsSource string
	DataSource   string
}

RawAPIRequest describes a raw API request to DingTalk OpenAPI.

type RawAPIResponse

type RawAPIResponse struct {
	StatusCode int
	Header     http.Header
	// Body remains available for tests and package callers that construct a
	// response in memory. Live requests use BodyReader and are consumed once.
	Body       []byte
	BodyReader io.ReadCloser
}

RawAPIResponse encapsulates the raw HTTP response.

type ResponseError added in v1.0.60

type ResponseError struct {
	Err error
}

ResponseError marks failures caused by the remote HTTP/business response. Local rendering, jq validation, and download filesystem failures deliberately remain unwrapped so the CLI can preserve their validation/internal category.

func (*ResponseError) Error added in v1.0.60

func (e *ResponseError) Error() string

func (*ResponseError) Unwrap added in v1.0.60

func (e *ResponseError) Unwrap() error

type ResponseOptions

type ResponseOptions struct {
	OutputPath string        // --output file path for binary responses
	Format     output.Format // output format (json|table|raw)
	JqExpr     string        // --jq expression
	Fields     string        // --fields comma-separated field names
	Out        io.Writer     // stdout
	ErrOut     io.Writer     // stderr
}

ResponseOptions controls how an API response is processed.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL