Documentation
This commit is contained in:
parent
163957ad27
commit
1f337016fa
7 changed files with 452 additions and 1 deletions
29
build.sbt
29
build.sbt
|
|
@ -30,6 +30,11 @@ val Deps = new {
|
|||
val Effect: ModuleID = "org.typelevel" %% "cats-effect" % "3.6.3"
|
||||
}
|
||||
|
||||
val Circe = new {
|
||||
val Core: ModuleID = "io.circe" %% "circe-core" % "0.14.15"
|
||||
val Parser: ModuleID = "io.circe" %% "circe-parser" % "0.14.15"
|
||||
}
|
||||
|
||||
val Gs = new {
|
||||
val Datagen: ModuleID = "gs" %% "gs-datagen-core-v0" % "0.3.3"
|
||||
val Uuid: ModuleID = "gs" %% "gs-uuid-v0" % "0.4.1"
|
||||
|
|
@ -50,7 +55,8 @@ lazy val `gs-predicate` = project
|
|||
.aggregate(
|
||||
`test-support`,
|
||||
api,
|
||||
keyValue
|
||||
keyValue,
|
||||
json
|
||||
)
|
||||
.settings(noPublishSettings)
|
||||
.settings(name := s"${gsProjectName.value}-v${semVerMajor.value}")
|
||||
|
|
@ -107,3 +113,24 @@ lazy val keyValue = project
|
|||
Deps.Gs.Uuid
|
||||
)
|
||||
)
|
||||
|
||||
/** JSON - Defines predicates that can match on JSON values.
|
||||
*/
|
||||
lazy val json = project
|
||||
.in(file("modules/json"))
|
||||
.dependsOn(`test-support` % "test->test")
|
||||
.dependsOn(api)
|
||||
.settings(sharedSettings)
|
||||
.settings(testSettings)
|
||||
.settings(
|
||||
name := s"${gsProjectName.value}-json-v${semVerMajor.value}"
|
||||
)
|
||||
.settings(
|
||||
libraryDependencies ++= Seq(
|
||||
Deps.Cats.Core,
|
||||
Deps.Cats.Effect,
|
||||
Deps.Circe.Core,
|
||||
Deps.Circe.Parser,
|
||||
Deps.Gs.Uuid
|
||||
)
|
||||
)
|
||||
|
|
|
|||
6
docs/README.md
Normal file
6
docs/README.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# Library Documentation
|
||||
|
||||
- [Terminology](./terminology.md)
|
||||
- [Predicates: Standard](./predicates-standard.md)
|
||||
- [Predicates: Key-Value](./predicates-key-value.md)
|
||||
- [Predicates: JSON](#)
|
||||
187
docs/predicates-json.md
Normal file
187
docs/predicates-json.md
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
# Predicates: JSON
|
||||
|
||||
Refer to [Terminology](./terminology.md) for information about specific terms.
|
||||
|
||||
[[_TOC_]]
|
||||
|
||||
## Description
|
||||
|
||||
These predicates are focused on reasoning about JSON values.
|
||||
|
||||
## Input
|
||||
|
||||
All key-value predicates accept a single input: some JSON value.
|
||||
|
||||
## JSON Query Strings
|
||||
|
||||
This library may query JSON values using query strings. These are deliberately
|
||||
_not_ powerful and are not intended for `jq`-like functionality.
|
||||
|
||||
These query strings directly interact with the query system.
|
||||
|
||||
### Empty Query
|
||||
|
||||
An empty query string indicates that the predicate should address the value
|
||||
verbatim.
|
||||
|
||||
#### Example: Plain String
|
||||
|
||||
**Input**:
|
||||
|
||||
This example will match.
|
||||
|
||||
```json
|
||||
"some string"
|
||||
```
|
||||
|
||||
**Predicate**:
|
||||
|
||||
```json
|
||||
{
|
||||
"predicate": "json-value-equals",
|
||||
"parameters": {
|
||||
"queryString": "",
|
||||
"value": "some string"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Value (`.`)
|
||||
|
||||
The `.` operator accesses a named value by key within the current object. This
|
||||
will not match if the current value is not an object.
|
||||
|
||||
#### Example: Mismatched Types
|
||||
|
||||
**Input**:
|
||||
|
||||
This example will not match, because the value at `x` is a string, so no key can
|
||||
possibly be accessed through it.
|
||||
|
||||
```json
|
||||
{ "x": "some string" }
|
||||
```
|
||||
|
||||
**Predicate**:
|
||||
|
||||
```json
|
||||
{
|
||||
"predicate": "json-value-equals",
|
||||
"parameters": {
|
||||
"queryString": "x.y",
|
||||
"value": "some string"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### JSON Key
|
||||
|
||||
Any text aside from `.` and the contents of `[]` is considered a key. These keys
|
||||
refer to named elements of the JSON structure.
|
||||
|
||||
```json
|
||||
{
|
||||
"x": { "y": { "z": 100 } }
|
||||
}
|
||||
```
|
||||
|
||||
`x`, `y`, and `z` are all keys.
|
||||
|
||||
### Array Operator
|
||||
|
||||
The `[any|all|<index>]` syntax is used to reason about arrays:
|
||||
|
||||
- `[any]` indicates that the predicate will match if _any_ of the selected
|
||||
values based on traversing the array match.
|
||||
- `[all]` indicates that the predicate will match only if _all_ of the selected
|
||||
values based on traversing the array match.
|
||||
- `[<index>]` indicates that the predicate will match only if the specified
|
||||
index exists within the array and the selected value at that index matches.
|
||||
|
||||
#### Example: Value Equals (Any)
|
||||
|
||||
The following scenario will _match_:
|
||||
|
||||
**Input**:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"things": [
|
||||
{ "x": 100, "y": 200 }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Predicate**:
|
||||
|
||||
```json
|
||||
{
|
||||
"predicate": "json-value-equals",
|
||||
"parameters": {
|
||||
"queryString": "data.things[any].x",
|
||||
"value": 100
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Example: Value Equals (All)
|
||||
|
||||
The following scenario will _match_:
|
||||
|
||||
**Input**:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"things": [
|
||||
{ "x": 100, "y": 200 },
|
||||
{ "x": 100, "y": 300 },
|
||||
{ "x": 100, "y": 400 }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Predicate**:
|
||||
|
||||
```json
|
||||
{
|
||||
"predicate": "json-value-equals",
|
||||
"parameters": {
|
||||
"queryString": "data.things[all].x",
|
||||
"value": 100
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Example: Value Equals (Index)
|
||||
|
||||
The following scenario will _match_:
|
||||
|
||||
**Input**:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"things": [
|
||||
{ "x": 100, "y": 200 },
|
||||
{ "x": 999, "y": 300 },
|
||||
{ "x": 100, "y": 400 }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Predicate**:
|
||||
|
||||
```json
|
||||
{
|
||||
"predicate": "json-value-equals",
|
||||
"parameters": {
|
||||
"queryString": "data.things[1].x",
|
||||
"value": 999
|
||||
}
|
||||
}
|
||||
```
|
||||
109
docs/predicates-key-value.md
Normal file
109
docs/predicates-key-value.md
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
# Predicates: Key-Value
|
||||
|
||||
Refer to [Terminology](./terminology.md) for information about specific terms.
|
||||
|
||||
[[_TOC_]]
|
||||
|
||||
## Description
|
||||
|
||||
These predicates are focused on reasoning about keys and their associated
|
||||
(or not associated) values.
|
||||
|
||||
## Input
|
||||
|
||||
All key-value predicates accept a single input: some key-value provider. This
|
||||
is the state referenced by the predicates.
|
||||
|
||||
This provider can answer two questions:
|
||||
|
||||
- Does the given key exist?
|
||||
- What is the value associated with a given key?
|
||||
|
||||
## Predicate: Key Exists
|
||||
|
||||
Matches if the key-value provider contains the given key.
|
||||
|
||||
## Predicate: Value Equals
|
||||
|
||||
Given some key `K` and expected value `EV`, this predicate matches if:
|
||||
|
||||
- There is some value `V` associated with `K`.
|
||||
- `V` is equal to `EV`.
|
||||
|
||||
## Predicate: Value Not Equals
|
||||
|
||||
Given some key `K` and unexpected value `UV`, this predicate matches if:
|
||||
|
||||
- There is some value `V` associated with `K`.
|
||||
- `V` is not equal to `UV`.
|
||||
|
||||
## Predicate: String Contains
|
||||
|
||||
> [!important]
|
||||
> This predicate is only supported for string values.
|
||||
|
||||
Given some key `K` and string `S`, this predicate matches if:
|
||||
|
||||
- There is some value `V` associated with `K`.
|
||||
- `V` contains the substring `S`.
|
||||
|
||||
### Examples
|
||||
|
||||
| `S` | `V` | Result |
|
||||
| ------- | ------- | ------- |
|
||||
| `""` | `""` | `match` |
|
||||
| `""` | `"a"` | `match` |
|
||||
| `"a"` | `""` | `miss` |
|
||||
| `"a"` | `"abc"` | `match` |
|
||||
| `"ab"` | `"abc"` | `match` |
|
||||
| `"b"` | `"abc"` | `match` |
|
||||
| `"bc"` | `"abc"` | `match` |
|
||||
| `"c"` | `"abc"` | `match` |
|
||||
| `"d"` | `"abc"` | `miss` |
|
||||
| `"abc"` | `"abc"` | `match` |
|
||||
|
||||
## Predicate: String Starts With
|
||||
|
||||
> [!important]
|
||||
> This predicate is only supported for string values.
|
||||
|
||||
Given some key `K` and prefix `P`, this predicate matches if:
|
||||
|
||||
- There is some value `V` associated with `K`.
|
||||
- `V` starts with the prefix `P`.
|
||||
|
||||
### Examples
|
||||
|
||||
| `P` | `V` | Result |
|
||||
| ------- | ------- | ------- |
|
||||
| `""` | `""` | `match` |
|
||||
| `""` | `"a"` | `match` |
|
||||
| `"a"` | `""` | `miss` |
|
||||
| `"a"` | `"abc"` | `match` |
|
||||
| `"ab"` | `"abc"` | `match` |
|
||||
| `"b"` | `"abc"` | `miss` |
|
||||
| `"d"` | `"abc"` | `miss` |
|
||||
| `"abc"` | `"abc"` | `match` |
|
||||
|
||||
## Predicate: String Ends With
|
||||
|
||||
> [!important]
|
||||
> This predicate is only supported for string values.
|
||||
|
||||
Given some key `K` and suffix `S`, this predicate matches if:
|
||||
|
||||
- There is some value `V` associated with `K`.
|
||||
- `V` ends with the suffix `S`.
|
||||
|
||||
### Examples
|
||||
|
||||
| `S` | `V` | Result |
|
||||
| ------- | ------- | ------- |
|
||||
| `""` | `""` | `match` |
|
||||
| `""` | `"a"` | `match` |
|
||||
| `"a"` | `""` | `miss` |
|
||||
| `"a"` | `"abc"` | `miss` |
|
||||
| `"bc"` | `"abc"` | `match` |
|
||||
| `"c"` | `"abc"` | `match` |
|
||||
| `"d"` | `"abc"` | `miss` |
|
||||
| `"abc"` | `"abc"` | `match` |
|
||||
57
docs/predicates-standard.md
Normal file
57
docs/predicates-standard.md
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
# Predicates: Standard
|
||||
|
||||
Refer to [Terminology](./terminology.md) for information about specific terms.
|
||||
|
||||
[[_TOC_]]
|
||||
|
||||
## And
|
||||
|
||||
The `and` predicate calculates the logical AND of some list of predicates. For
|
||||
this predicate, a `match` corresponds to `true` and a `miss` corresponds to
|
||||
`false`.
|
||||
|
||||
> Only match if all contained predicates match.
|
||||
|
||||
### Case: Empty List
|
||||
|
||||
The predicate always misses.
|
||||
|
||||
Since there are no predicates, nothing can possibly match.
|
||||
|
||||
### Case: Single Predicate
|
||||
|
||||
Identiical to that single predicate.
|
||||
|
||||
### Case: Multiple Predicates
|
||||
|
||||
The logical AND of the predicate results.
|
||||
|
||||
## Or
|
||||
|
||||
The `or` predicate calculates the logical OR of some list of predicates. For
|
||||
this predicate, a `match` corresponds to `true` and a `miss` corresponds to
|
||||
`false`.
|
||||
|
||||
> Only match if any contained predicate matches.
|
||||
|
||||
### Case: Empty List
|
||||
|
||||
The predicate always misses.
|
||||
|
||||
Since there are no predicates, nothing can possibly match.
|
||||
|
||||
### Case: Single Predicate
|
||||
|
||||
Identiical to that single predicate.
|
||||
|
||||
### Case: Multiple Predicates
|
||||
|
||||
The logical OR of the predicate results.
|
||||
|
||||
## True
|
||||
|
||||
This predicate always matches.
|
||||
|
||||
## False
|
||||
|
||||
This predicate always misses.
|
||||
33
docs/terminology.md
Normal file
33
docs/terminology.md
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# Predicate Terminology
|
||||
|
||||
[[_TOC_]]
|
||||
|
||||
## Definition: Predicate
|
||||
|
||||
Any function that accepts exactly one input and produces a
|
||||
[Predicate Result](#definition-predicate-result) (aka a Boolean output).
|
||||
|
||||
Predicates are ways to express logical conditions.
|
||||
|
||||
## Definition: Predicate Result
|
||||
|
||||
The result of evaluating a [Predicate](#definition-predicate). Predicates may:
|
||||
|
||||
- [Match](#definition-match)
|
||||
- [Miss](#definition-miss)
|
||||
|
||||
## Definition: Match
|
||||
|
||||
The term **match** is a specific [Result](#definition-predicate-result).
|
||||
Semantically, it means that the predicate ran successfully against the given
|
||||
input. The conditions within were met.
|
||||
|
||||
If the predicate were expressed as a Boolean, this would correspond to `true`.
|
||||
|
||||
## Definition: Miss
|
||||
|
||||
The term **miss** is a specific [Result](#definition-predicate-result).
|
||||
Semantically, it means that the predicate did not run successfully against the
|
||||
given input. The conditions within were not met.
|
||||
|
||||
If the predicate were expressed as a Boolean, this would correspond to `false`.
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package gs.predicate.v0.json
|
||||
|
||||
import cats.Applicative
|
||||
import gs.predicate.v0.api.Predicate
|
||||
import gs.uuid.v0.UUID
|
||||
import io.circe.Json
|
||||
|
||||
/** Predicate that matches if JSON blob is an object that contains the given
|
||||
* key.
|
||||
*
|
||||
* @param id
|
||||
* The unique identifier of this [[Predicate]].
|
||||
* @param key
|
||||
* The key that should exist.
|
||||
*/
|
||||
final class JsonKeyExists[F[_]: Applicative](
|
||||
val id: UUID,
|
||||
val key: String
|
||||
) extends Predicate[F, Json]:
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def eval(input: Json): F[Predicate.Result] =
|
||||
// \\ is recursive, we need a path-based lookup or something
|
||||
Applicative[F].pure(Predicate.Result(input.\\(key).nonEmpty))
|
||||
|
||||
object JsonKeyExists:
|
||||
|
||||
def apply[F[_]: Applicative](key: String): JsonKeyExists[F] =
|
||||
new JsonKeyExists[F](UUID.v7(), key)
|
||||
|
||||
end JsonKeyExists
|
||||
Loading…
Add table
Reference in a new issue