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
- Variables
- func HandleResponse(resp *RawAPIResponse, opts ResponseOptions) error
- func IsDeferredInput(raw string) bool
- func IsLegacyAPI(urlStr string) bool
- func MaskToken(token string) string
- func NormalisePath(path, baseURL string) string
- func ParseJSONMap(raw, flagName string, stdin io.Reader) (map[string]any, error)
- func ParseOptionalBody(method, raw string, stdin io.Reader) (any, error)
- func PrintDryRun(w io.Writer, req RawAPIRequest, baseURL, token string) error
- func ValidateFlagExclusion(outputPath string, pageAll bool) error
- func ValidateInputStdinExclusion(params, data string, file *FileUpload) error
- func ValidateMethod(method string) (string, error)
- func ValidatePath(path string) error
- func ValidateRedirect(req *http.Request, via []*http.Request) error
- func ValidateStdinExclusion(params, data string) error
- func ValidateTargetHost(fullURL string) error
- func ValidateUserInput(value, fieldName string) error
- type APIClient
- func (c *APIClient) Do(ctx context.Context, req RawAPIRequest) (*RawAPIResponse, error)
- func (c *APIClient) PaginateAll(ctx context.Context, req RawAPIRequest, opts PaginationOptions) ([]any, error)
- func (c *APIClient) UploadMultipart(ctx context.Context, req MultipartUploadRequest) (*RawAPIResponse, error)
- type FileUpload
- type MultipartUploadRequest
- type PaginationOptions
- type RawAPIRequest
- type RawAPIResponse
- type ResponseError
- type ResponseOptions
Constants ¶
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" )
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 ¶
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.
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
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
ValidateMethod checks that the HTTP method is one of the five allowed methods.
func ValidatePath ¶
ValidatePath checks the API path for injection attacks and dangerous characters.
func ValidateRedirect ¶ added in v1.0.60
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 ¶
ValidateStdinExclusion checks that --params and --data don't both read from stdin.
func ValidateTargetHost ¶
ValidateTargetHost checks that the resolved request URL targets a trusted DingTalk host. This prevents access-token leakage to arbitrary domains.
func ValidateUserInput ¶
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 (*APIClient) Do ¶
func (c *APIClient) Do(ctx context.Context, req RawAPIRequest) (*RawAPIResponse, error)
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
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.