mirror of
https://github.com/mikefarah/yq.git
synced 2024-11-12 05:38:04 +00:00
Added error operator for custom validation #1259
This commit is contained in:
parent
98b411f82e
commit
86c2b03630
56
pkg/yqlib/doc/operators/error.md
Normal file
56
pkg/yqlib/doc/operators/error.md
Normal file
@ -0,0 +1,56 @@
|
||||
# Error
|
||||
|
||||
Use this operation to short-circuit expressions. Useful for validation.
|
||||
|
||||
{% hint style="warning" %}
|
||||
Note that versions prior to 4.18 require the 'eval/e' command to be specified. 
|
||||
|
||||
`yq e <exp> <file>`
|
||||
{% endhint %}
|
||||
|
||||
## Validate a particular value
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: hello
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq 'select(.a == "howdy") or error(".a [" + .a + "] is not howdy!")' sample.yml
|
||||
```
|
||||
will output
|
||||
```bash
|
||||
Error: .a [hello] is not howdy!
|
||||
```
|
||||
|
||||
## Validate the environment variable is a number - invalid
|
||||
Running
|
||||
```bash
|
||||
numberOfCats="please" yq --null-input 'env(numberOfCats) | select(tag == "!!int") or error("numberOfCats is not a number :(")'
|
||||
```
|
||||
will output
|
||||
```bash
|
||||
Error: numberOfCats is not a number :(
|
||||
```
|
||||
|
||||
## Validate the environment variable is a number - valid
|
||||
`with` can be a convenient way of encapsulating validation.
|
||||
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
name: Bob
|
||||
favouriteAnimal: cat
|
||||
```
|
||||
then
|
||||
```bash
|
||||
numberOfCats="3" yq '
|
||||
with(env(numberOfCats); select(tag == "!!int") or error("numberOfCats is not a number :(")) |
|
||||
.numPets = env(numberOfCats)
|
||||
' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
name: Bob
|
||||
favouriteAnimal: cat
|
||||
numPets: 3
|
||||
```
|
||||
|
3
pkg/yqlib/doc/operators/headers/error.md
Normal file
3
pkg/yqlib/doc/operators/headers/error.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Error
|
||||
|
||||
Use this operation to short-circuit expressions. Useful for validation.
|
@ -363,6 +363,8 @@ func initLexer() (*lex.Lexer, error) {
|
||||
lexer.Add([]byte(`tz`), opToken(tzOpType))
|
||||
lexer.Add([]byte(`with_dtf`), opToken(withDtFormatOpType))
|
||||
|
||||
lexer.Add([]byte(`error`), opToken(errorOpType))
|
||||
|
||||
lexer.Add([]byte(`toyaml\([0-9]+\)`), encodeWithIndent(YamlOutputFormat))
|
||||
lexer.Add([]byte(`to_yaml\([0-9]+\)`), encodeWithIndent(YamlOutputFormat))
|
||||
|
||||
|
@ -91,6 +91,7 @@ var columnOpType = &operationType{Type: "LINE", NumArgs: 0, Precedence: 50, Hand
|
||||
|
||||
var collectOpType = &operationType{Type: "COLLECT", NumArgs: 1, Precedence: 50, Handler: collectOperator}
|
||||
var mapOpType = &operationType{Type: "MAP", NumArgs: 1, Precedence: 50, Handler: mapOperator}
|
||||
var errorOpType = &operationType{Type: "ERROR", NumArgs: 1, Precedence: 50, Handler: errorOperator}
|
||||
var pickOpType = &operationType{Type: "PICK", NumArgs: 1, Precedence: 50, Handler: pickOperator}
|
||||
var evalOpType = &operationType{Type: "EVAL", NumArgs: 1, Precedence: 50, Handler: evalOperator}
|
||||
var mapValuesOpType = &operationType{Type: "MAP_VALUES", NumArgs: 1, Precedence: 50, Handler: mapValuesOperator}
|
||||
|
20
pkg/yqlib/operator_error.go
Normal file
20
pkg/yqlib/operator_error.go
Normal file
@ -0,0 +1,20 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func errorOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||
|
||||
log.Debugf("-- errorOperation")
|
||||
|
||||
rhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.RHS)
|
||||
if err != nil {
|
||||
return Context{}, err
|
||||
}
|
||||
errorMessage := "aborted"
|
||||
if rhs.MatchingNodes.Len() > 0 {
|
||||
errorMessage = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value
|
||||
}
|
||||
return Context{}, fmt.Errorf(errorMessage)
|
||||
}
|
40
pkg/yqlib/operator_error_test.go
Normal file
40
pkg/yqlib/operator_error_test.go
Normal file
@ -0,0 +1,40 @@
|
||||
package yqlib
|
||||
|
||||
import "testing"
|
||||
|
||||
const validationExpression = `
|
||||
with(env(numberOfCats); select(tag == "!!int") or error("numberOfCats is not a number :(")) |
|
||||
.numPets = env(numberOfCats)
|
||||
`
|
||||
|
||||
var errorOperatorScenarios = []expressionScenario{
|
||||
{
|
||||
description: "Validate a particular value",
|
||||
document: `a: hello`,
|
||||
expression: `select(.a == "howdy") or error(".a [" + .a + "] is not howdy!")`,
|
||||
expectedError: ".a [hello] is not howdy!",
|
||||
},
|
||||
{
|
||||
description: "Validate the environment variable is a number - invalid",
|
||||
environmentVariables: map[string]string{"numberOfCats": "please"},
|
||||
expression: `env(numberOfCats) | select(tag == "!!int") or error("numberOfCats is not a number :(")`,
|
||||
expectedError: "numberOfCats is not a number :(",
|
||||
},
|
||||
{
|
||||
description: "Validate the environment variable is a number - valid",
|
||||
subdescription: "`with` can be a convenient way of encapsulating validation.",
|
||||
environmentVariables: map[string]string{"numberOfCats": "3"},
|
||||
document: "name: Bob\nfavouriteAnimal: cat\n",
|
||||
expression: validationExpression,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::name: Bob\nfavouriteAnimal: cat\nnumPets: 3\n",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestErrorOperatorScenarios(t *testing.T) {
|
||||
for _, tt := range errorOperatorScenarios {
|
||||
testScenario(t, &tt)
|
||||
}
|
||||
documentOperatorScenarios(t, "error", errorOperatorScenarios)
|
||||
}
|
Loading…
Reference in New Issue
Block a user