Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions gen/cli_yargs_variadic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,41 @@ func TestCLI_YargsGlobalVariadicBoolean(t *testing.T) {
t.Error(`expected "verbose?: boolean[] | undefined;" in GlobalFlags interface`)
}
}

// TestCLI_YargsRequiredVariadicFlag ensures a flag that is both required and
// variadic is registered as a demanded array option, so yargs rejects zero
// occurrences while still accepting many values.
func TestCLI_YargsRequiredVariadicFlag(t *testing.T) {
doc := &spec.Document{
OpenCLIVersion: "1.0.0-alpha.14",
Info: spec.Info{Title: "VarTest CLI", Binary: "vartest"},
Commands: &spec.CommandItem{
Segment: "greet",
Flags: []spec.FlagItem{
{Name: "items", Type: "string", Variadic: true, Required: true},
},
},
}

files, err := CLI(doc, GenCLIWithFramework(YargsFramework))
if err != nil {
t.Fatalf("unexpected error generating yargs output: %v", err)
}

var all strings.Builder
for _, content := range files {
all.Write(content)
all.WriteByte('\n')
}
got := all.String()

if !strings.Contains(got, `type: "array",`) {
t.Error("expected required variadic flag to be registered as an array option")
}
if !strings.Contains(got, "demandOption: true,") {
t.Error("expected required variadic flag to emit demandOption: true")
}
if !strings.Contains(string(files["gencli/params.ts"]), " items: string[];\n") {
t.Error("expected required variadic flag to be a non-optional string[] field in the action flags interface")
}
}
4 changes: 2 additions & 2 deletions spec.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@
},
"variadic": {
"type": "boolean",
"description": "Indicates if the the flag value is variadic. Variadic flags can be supplied multiple times. e.g.: `--flag value --flag value_two --flag value_three`",
"description": "Indicates if the the flag value is variadic. Variadic flags can be supplied multiple times. e.g.: `--flag value --flag value_two --flag value_three`. A variadic flag may also be `required`, meaning it must be supplied at least once. Use `minItems`/`maxItems` to constrain the number of values when the flag is supplied.",
"default": false
},
"minItems": {
Expand Down Expand Up @@ -434,7 +434,7 @@
},
"required": {
"type": "boolean",
"description": "Determines if the flag is mandatory. If required flags are not supplied, the command will exit in error.",
"description": "Determines if the flag is mandatory. If required flags are not supplied, the command will exit in error. May be combined with `variadic` to require a flag that accepts multiple values.",
"default": false
},
"default": {
Expand Down
4 changes: 2 additions & 2 deletions validate/spec.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@
},
"variadic": {
"type": "boolean",
"description": "Indicates if the the flag value is variadic. Variadic flags can be supplied multiple times. e.g.: `--flag value --flag value_two --flag value_three`",
"description": "Indicates if the the flag value is variadic. Variadic flags can be supplied multiple times. e.g.: `--flag value --flag value_two --flag value_three`. A variadic flag may also be `required`, meaning it must be supplied at least once. Use `minItems`/`maxItems` to constrain the number of values when the flag is supplied.",
"default": false
},
"minItems": {
Expand Down Expand Up @@ -434,7 +434,7 @@
},
"required": {
"type": "boolean",
"description": "Determines if the flag is mandatory. If required flags are not supplied, the command will exit in error.",
"description": "Determines if the flag is mandatory. If required flags are not supplied, the command will exit in error. May be combined with `variadic` to require a flag that accepts multiple values.",
"default": false
},
"default": {
Expand Down
10 changes: 1 addition & 9 deletions validate/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ func validateFileReferences(cmdLine, itemName, itemType string, index int, altSo
return nil
}

// validateFlagConstraints checks for duplicate flag names/aliases and variadic+required constraints
// validateFlagConstraints checks for duplicate flag names/aliases and minItems/maxItems constraints
func validateFlagConstraints(cmd *spec.CommandItem) error {
// Check for duplicate flag names and aliases
seen := make(map[string]int)
Expand Down Expand Up @@ -389,14 +389,6 @@ func validateFlagConstraints(cmd *spec.CommandItem) error {
seen[alias] = i
}

// Validate variadic flags aren't required
if flag.Variadic && flag.Required {
return &ValidationError{
Message: fmt.Sprintf("variadic flag '%s' cannot be marked as required (variadic flags can be provided 0 or more times)", flagName),
Path: fmt.Sprintf("command '%s', flags[%d]", cmd.CommandLine, i),
}
}

// Check minItems/maxItems are only used with variadic
if (flag.MinItems > 0 || flag.MaxItems > 0) && !flag.Variadic {
var field string
Expand Down
19 changes: 12 additions & 7 deletions validate/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,6 @@ func TestValidateYAML_LogicalValidationErrors(t *testing.T) {
" - name: \"name\"\n summary: \"A name to include in the greeting\"\n required: true\n type: \"string\"\n minItems: 1"),
wantErr: "argument 'name' has minItems but is not variadic",
},
{
name: "variadic flag cannot be required",
input: replaceOnce(t, petstoreYAML,
" variadic: true",
" variadic: true\n required: true"),
wantErr: "variadic flag 'photo-urls' cannot be marked as required",
},
{
name: "duplicate flag alias",
input: replaceOnce(t, pleasantriesYAML,
Expand Down Expand Up @@ -278,3 +271,15 @@ func TestValidateJSON_UnmarshalError(t *testing.T) {
t.Fatalf("expected unmarshal error message, got: %v", err)
}
}

// TestValidateYAML_RequiredVariadicFlag ensures a flag may be both required and
// variadic (must be supplied at least once, accepts multiple values). See
// https://github.com/bcdxn/opencli/issues/20.
func TestValidateYAML_RequiredVariadicFlag(t *testing.T) {
input := replaceOnce(t, petstoreYAML,
" variadic: true",
" variadic: true\n required: true")
if err := validate.ValidateYAML(input); err != nil {
t.Fatalf("required variadic flag should validate, got: %v", err)
}
}
4 changes: 2 additions & 2 deletions web/src/spec.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@
},
"variadic": {
"type": "boolean",
"description": "Indicates if the the flag value is variadic. Variadic flags can be supplied multiple times. e.g.: `--flag value --flag value_two --flag value_three`",
"description": "Indicates if the the flag value is variadic. Variadic flags can be supplied multiple times. e.g.: `--flag value --flag value_two --flag value_three`. A variadic flag may also be `required`, meaning it must be supplied at least once. Use `minItems`/`maxItems` to constrain the number of values when the flag is supplied.",
"default": false
},
"minItems": {
Expand Down Expand Up @@ -434,7 +434,7 @@
},
"required": {
"type": "boolean",
"description": "Determines if the flag is mandatory. If required flags are not supplied, the command will exit in error.",
"description": "Determines if the flag is mandatory. If required flags are not supplied, the command will exit in error. May be combined with `variadic` to require a flag that accepts multiple values.",
"default": false
},
"default": {
Expand Down
Loading