Files
dingtalk-workspace-cli/internal/cli/schema_dry_run_capabilities.go
T

150 lines
4.7 KiB
Go

// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
package cli
import (
"fmt"
"sort"
"strings"
"sync"
)
// dryRunCapabilityGroup is a reviewed positive capability declaration. An
// absent canonical deliberately publishes no dry_run field.
type dryRunCapabilityGroup struct {
PreviewKind string
CanonicalPaths []string
}
// reviewedDryRunCapabilityGroups contains only command-owned preview paths.
// Inheriting the root --dry-run flag or reaching the generic EchoRunner is not
// evidence of a stable capability and must never add a command to this list.
// CI executes each selected example and compares the observed preview kind to
// this reviewed declaration.
var reviewedDryRunCapabilityGroups = []dryRunCapabilityGroup{
{PreviewKind: DryRunPreviewRequest, CanonicalPaths: []string{
"event.stop",
}},
{PreviewKind: DryRunPreviewPlan, CanonicalPaths: []string{
"chat.download_media",
"doc.download_file",
"doc.import_get",
"doc.media_insert",
"doc.query_export_job",
"doc.upload",
"drive.download_file",
"drive.upload",
"sheet.filter_view_get_criteria",
"sheet.filter_view_info",
"sheet.filter_view_list_criteria",
"sheet.media_upload",
"sheet.submit_export_job",
"sheet.write_image",
"todo.add_todo_attachment",
}},
}
var reviewedDryRunCapabilitiesLazy struct {
once sync.Once
byCanonical map[string]DryRunSpec
err error
}
func loadReviewedDryRunCapabilities() (map[string]DryRunSpec, error) {
reviewedDryRunCapabilitiesLazy.once.Do(func() {
byCanonical := make(map[string]DryRunSpec)
for _, group := range reviewedDryRunCapabilityGroups {
spec := DryRunSpec{PreviewKind: group.PreviewKind}
if err := spec.Validate("<reviewed-dry-run-registry>"); err != nil {
reviewedDryRunCapabilitiesLazy.err = err
return
}
previous := ""
for _, raw := range group.CanonicalPaths {
canonical := strings.TrimSpace(raw)
if canonical == "" {
reviewedDryRunCapabilitiesLazy.err = fmt.Errorf("reviewed dry-run capability has empty canonical path")
return
}
if previous != "" && canonical <= previous {
reviewedDryRunCapabilitiesLazy.err = fmt.Errorf("reviewed dry-run capability paths for %s are not strictly sorted at %q", group.PreviewKind, canonical)
return
}
previous = canonical
if _, duplicate := byCanonical[canonical]; duplicate {
reviewedDryRunCapabilitiesLazy.err = fmt.Errorf("duplicate reviewed dry-run capability %s", canonical)
return
}
byCanonical[canonical] = spec
}
}
reviewedDryRunCapabilitiesLazy.byCanonical = byCanonical
})
if reviewedDryRunCapabilitiesLazy.err != nil {
return nil, reviewedDryRunCapabilitiesLazy.err
}
out := make(map[string]DryRunSpec, len(reviewedDryRunCapabilitiesLazy.byCanonical))
for canonical, spec := range reviewedDryRunCapabilitiesLazy.byCanonical {
out[canonical] = spec
}
return out, nil
}
// ReviewedDryRunCapabilities returns a defensive copy of the positive,
// reviewed capability registry for delivery gates.
func ReviewedDryRunCapabilities() (map[string]DryRunSpec, error) {
return loadReviewedDryRunCapabilities()
}
func reviewedDryRunCapability(canonical string) (*DryRunSpec, error) {
capabilities, err := loadReviewedDryRunCapabilities()
if err != nil {
return nil, err
}
spec, ok := capabilities[strings.TrimSpace(canonical)]
if !ok {
return nil, nil
}
return &spec, nil
}
// ValidateReviewedDryRunCapabilityDelivery proves that every positive source
// entry reaches the final typed registry and no serializer invents one. It
// deliberately imposes no minimum capability count or all-command coverage.
func ValidateReviewedDryRunCapabilityDelivery(registry SchemaRegistry) error {
expected, err := loadReviewedDryRunCapabilities()
if err != nil {
return err
}
actual := make(map[string]DryRunSpec)
for _, product := range registry.Products {
for _, tool := range product.Tools {
if tool.DryRun != nil {
actual[tool.Identity.CanonicalPath] = *tool.DryRun
}
}
}
var problems []string
for canonical, want := range expected {
got, ok := actual[canonical]
if !ok {
problems = append(problems, fmt.Sprintf("reviewed dry-run capability %s is missing from final Schema", canonical))
continue
}
if got != want {
problems = append(problems, fmt.Sprintf("Schema dry-run capability %s = %#v, want %#v", canonical, got, want))
}
}
for canonical := range actual {
if _, ok := expected[canonical]; !ok {
problems = append(problems, fmt.Sprintf("Schema tool %s publishes an unreviewed dry-run capability", canonical))
}
}
if len(problems) > 0 {
sort.Strings(problems)
return fmt.Errorf("%s", strings.Join(problems, "; "))
}
return nil
}