mirror of
https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli.git
synced 2026-09-29 16:57:48 +08:00
Merge branch 'feat/schema-calendar-format-migration' into feat/calendar-event-all-day-online-meeting
This commit is contained in:
@@ -1051,11 +1051,13 @@ func (p ParameterSpec) ToPayload() (map[string]any, error) {
|
||||
if p.Format != "" || p.Type != "string" {
|
||||
return nil, fmt.Errorf("parameter %q anyOf requires string type and no top-level format", p.Name)
|
||||
}
|
||||
value, err := typedJSONValue(p.AnyOf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// FormatAlternative contains only a string: project the JSON shape
|
||||
// directly instead of introducing an impossible marshal-error path.
|
||||
branches := make([]any, len(p.AnyOf))
|
||||
for i, branch := range p.AnyOf {
|
||||
branches[i] = map[string]any{"format": branch.Format}
|
||||
}
|
||||
payload["anyOf"] = value
|
||||
payload["anyOf"] = branches
|
||||
}
|
||||
if len(p.Enum) > 0 {
|
||||
payload["enum"] = append([]string(nil), p.Enum...)
|
||||
|
||||
@@ -3,10 +3,12 @@ package cli
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contract"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contractfinal"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/runtimeannotate"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -90,3 +92,56 @@ func TestCrossPlatformCoverageCatalogFormatAlternatives(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrossPlatformCoverageSchemaFormatAlternativesRejectInvalidAnnotations(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
values []string
|
||||
format string
|
||||
want string
|
||||
}{
|
||||
{"missing value", nil, "", "invalid or conflicting"},
|
||||
{"multiple values", []string{"[]", "[]"}, "", "invalid or conflicting"},
|
||||
{"explicit format", []string{`[{"format":"date"},{"format":"date-time"}]`}, "date-time", "invalid or conflicting"},
|
||||
{"invalid json", []string{"["}, "", "anyOf:"},
|
||||
{"unknown branch field", []string{`[{"pattern":".*"}]`}, "", "unknown field"},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
cmd := &cobra.Command{Use: "sample"}
|
||||
cmd.Flags().String("time", "", "time")
|
||||
cmd.Flags().Lookup("time").Annotations = map[string][]string{runtimeannotate.AnnotationFlagAnyOf: tc.values}
|
||||
if tc.format != "" {
|
||||
runtimeannotate.AnnotateRuntimeFlagFormat(cmd, "time", tc.format)
|
||||
}
|
||||
_, err := runtimeCommandParameterSpecs(cmd, "sample.run", RuntimeSchemaConstraints{})
|
||||
if err == nil || !strings.Contains(err.Error(), tc.want) {
|
||||
t.Fatalf("error=%v, want %q", err, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrossPlatformCoverageSchemaFormatAlternativesPayloadAndProvenance(t *testing.T) {
|
||||
union := []contract.FormatAlternative{{Format: "date"}, {Format: "date-time"}}
|
||||
for _, p := range []ParameterSpec{{Name: "time", Type: "integer", AnyOf: union}, {Name: "time", Type: "string", Format: "date-time", AnyOf: union}} {
|
||||
if _, err := p.ToPayload(); err == nil || !strings.Contains(err.Error(), "no top-level format") {
|
||||
t.Fatalf("invalid combination accepted: %v", err)
|
||||
}
|
||||
}
|
||||
p := ParameterSpec{Name: "time", Type: "string", Property: "startDateTime", AnyOf: union}
|
||||
value, ok := p.provenanceValue("anyOf")
|
||||
if !ok || !reflect.DeepEqual(value, union) {
|
||||
t.Fatalf("provenance value: %v, %v", value, ok)
|
||||
}
|
||||
p.FieldProvenance = schemaDeliveryTestProvenance(map[string]any{
|
||||
"type": "string", "description": "", "property": "startDateTime", "required": false, "required_when": "", "anyOf": union,
|
||||
})
|
||||
registry := schemaDeliveryTestRegistry(schemaDeliveryTestTool{Canonical: "sample.run", CLIPath: "sample run", Parameters: []ParameterSpec{p}})
|
||||
if err := validateFinalSchemaProvenanceCoverage(registry); err != nil {
|
||||
t.Fatalf("valid provenance rejected: %v", err)
|
||||
}
|
||||
delete(registry.Products[0].Tools[0].Parameters[0].FieldProvenance, "anyOf")
|
||||
if err := validateFinalSchemaProvenanceCoverage(registry); err == nil || !strings.Contains(err.Error(), "has no provenance for anyOf") {
|
||||
t.Fatalf("missing provenance accepted: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,3 +57,19 @@ func TestCrossPlatformCoverageParamFormatAlternativesClone(t *testing.T) {
|
||||
t.Fatal("read exposed stored slice")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrossPlatformCoverageParamFormatAlternativesAnnotation(t *testing.T) {
|
||||
cmd := &cobra.Command{Use: "sample"}
|
||||
cmd.Flags().String("time", "", "")
|
||||
branches := []contract.FormatAlternative{{Format: "date-time"}, {Format: "date"}}
|
||||
if err := ApplyParamDecls(cmd, []contract.ParamDecl{{Name: "time", AnyOf: branches}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got := cmd.Flags().Lookup("time").Annotations[runtimeannotate.AnnotationFlagAnyOf]
|
||||
if len(got) != 1 || got[0] != `[{"format":"date"},{"format":"date-time"}]` {
|
||||
t.Fatalf("annotation=%v", got)
|
||||
}
|
||||
if branches[0].Format != "date-time" {
|
||||
t.Fatal("annotation sorting mutated declaration")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -671,10 +671,9 @@ func normalizeParameter(raw json.RawMessage) (parameterSchema, error) {
|
||||
if err := json.Unmarshal(raw, &schema); err != nil {
|
||||
return parameterSchema{}, err
|
||||
}
|
||||
anyOf, err := canonicalRawJSON(parameter.AnyOf)
|
||||
if err != nil {
|
||||
return parameterSchema{}, err
|
||||
}
|
||||
// Both decodes above already validated this RawMessage as JSON.
|
||||
// Canonicalizing that validated fragment cannot fail.
|
||||
anyOf, _ := canonicalRawJSON(parameter.AnyOf)
|
||||
parameterType := schemaType(schema)
|
||||
if parameterType == "unspecified" {
|
||||
return parameterSchema{}, fmt.Errorf("type is missing")
|
||||
|
||||
Reference in New Issue
Block a user