Skip to content

Commit

Permalink
add once operator
Browse files Browse the repository at this point in the history
  • Loading branch information
lhitchon committed Nov 19, 2018
1 parent e020189 commit 1d4a65a
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 9 deletions.
24 changes: 24 additions & 0 deletions assertion/expression.go
Expand Up @@ -148,6 +148,27 @@ func noneExpression(collectionExpression CollectionExpression, resource Resource
return matches()
}

func onceExpression(collectionExpression CollectionExpression, resource Resource) (MatchResult, error) {
resources, err := collectResources(collectionExpression.Key, resource)
if err != nil {
return matchError(err)
}
matchCount := 0
for _, collectionResource := range resources {
match, err := andExpression(collectionExpression.Expressions, collectionResource)
if err != nil {
return matchError(err)
}
if match.Match {
matchCount++
}
}
if matchCount == 1 {
return matches()
}
return doesNotMatch("Once expression fails")
}

func booleanExpression(expression Expression, resource Resource) (MatchResult, error) {
if expression.Or != nil && len(expression.Or) > 0 {
return orExpression(expression.Or, resource)
Expand All @@ -170,6 +191,9 @@ func booleanExpression(expression Expression, resource Resource) (MatchResult, e
if expression.None.Key != "" {
return noneExpression(expression.None, resource)
}
if expression.Once.Key != "" {
return onceExpression(expression.Once, resource)
}
return searchAndMatch(expression, resource)
}

Expand Down
63 changes: 54 additions & 9 deletions assertion/testdata/collection-assertions.yaml
Expand Up @@ -4,7 +4,7 @@ test_cases:

- name: every_OK
rule:
id: COLLECTION_1
id: COLLECTION
message: Invalid key
severity: FAILURE
resource: sample
Expand All @@ -30,7 +30,7 @@ test_cases:

- name: every_FAILURE
rule:
id: COLLECTION_1
id: COLLECTION
message: Invalid key
severity: FAILURE
resource: sample
Expand Down Expand Up @@ -58,7 +58,7 @@ test_cases:

- name: every_multiple_assertions_FAILURE
rule:
id: COLLECTION_1
id: COLLECTION
message: Invalid key
severity: FAILURE
resource: sample
Expand All @@ -82,7 +82,7 @@ test_cases:

- name: some_OK
rule:
id: COLLECTION_3
id: COLLECTION
message: Invalid key
severity: FAILURE
resource: sample
Expand All @@ -108,7 +108,7 @@ test_cases:

- name: some_FAILURE
rule:
id: COLLECTION_4
id: COLLECTION
message: Invalid key
severity: FAILURE
resource: sample
Expand All @@ -129,7 +129,7 @@ test_cases:

- name: none_OK
rule:
id: COLLECTION_3
id: COLLECTION
message: Invalid key
severity: FAILURE
resource: sample
Expand All @@ -151,7 +151,7 @@ test_cases:

- name: none_with_multiple_assertions_OK
rule:
id: COLLECTION_3
id: COLLECTION
message: Invalid key
severity: FAILURE
resource: sample
Expand All @@ -178,7 +178,7 @@ test_cases:

- name: none_FAILURE
rule:
id: COLLECTION_4
id: COLLECTION
message: Invalid key
severity: FAILURE
resource: sample
Expand All @@ -201,7 +201,7 @@ test_cases:

- name: none_with_multiple_assertions_FAILURE
rule:
id: COLLECTION_3
id: COLLECTION
message: Invalid key
severity: FAILURE
resource: sample
Expand All @@ -226,3 +226,48 @@ test_cases:
- 0.0.0.0/0
result: FAILURE

- name: once_OK
rule:
id: COLLECTION
message: Duplicate names
severity: FAILURE
resource: example
assertions:
- once:
key: "tags[]"
expressions:
- key: name
op: eq
value: A
resource:
id: collection_id
type: example
properties:
tags:
- name: A
- name: B
result: OK

- name: once_FAILURE
rule:
id: COLLECTION
message: Duplicate names
severity: FAILURE
resource: example
assertions:
- once:
key: "tags[]"
expressions:
- key: name
op: eq
value: B
resource:
id: collection_id
type: example
properties:
tags:
- name: A
- name: B
- name: B
result: FAILURE

1 change: 1 addition & 0 deletions assertion/types.go
Expand Up @@ -52,6 +52,7 @@ type (
Every CollectionExpression
Some CollectionExpression
None CollectionExpression
Once CollectionExpression
}

// CollectionExpression assertion for every element of a collection
Expand Down
23 changes: 23 additions & 0 deletions docs/operations.md
Expand Up @@ -22,6 +22,7 @@
| [not-contains](#does-not-contain) | Does Not Contain |
| [not-empty](#not-empty) | Not Empty |
| [not-in](#not-in) | Not In |
| [once](#once) | Once |
| [or](#or) | Or |
| [present](#present) | Present |
| [regex](#regex) | Regex |
Expand Down Expand Up @@ -323,6 +324,28 @@ Example:
...
```

## once

Select an array from a resource, and run assertions against each element. Only one of the sub assertions should return true for the test to pass.
The key is a JMESPath expression that should return an array of objects. The key used in each sub assertion is relative to the selected objects.

This provides a simple looping mechanism that is easier to write and understand than a complex JMESPath expression.

Example:

```
- id: ONLY_ONE_DEFAULT
message: Default should be true for only one element
severity: FAILURE
resource: sample
assertions:
- once:
key: "items[]"
expressions:
- key: "default"
op: is-true
```

## is-true

Check that the data has a true value. Shorthand for using { op: "eq" , "value": true }
Expand Down

0 comments on commit 1d4a65a

Please sign in to comment.