Skip to content

Commit

Permalink
as: Abstract percentages and concentration parsing
Browse files Browse the repository at this point in the history
These kind of of validation checks are so common and might appear even
more as the schema grows. In the same way that definitions are used in
the JSON schema for defining these types, here I've abstracted them to
separate functions to facilitate the maintenance down the road.

I've also added some test to increase coverage and explicitly test the
latest additions.
  • Loading branch information
pablojimpas committed Apr 14, 2023
1 parent 66a208d commit 855b3ba
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 18 deletions.
42 changes: 24 additions & 18 deletions pkg/messageprocessors/normalizedpayload/uplink.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,24 @@ func parseNumber(selector func(dst *Measurement) **float64, vals ...fieldValidat
}
}

// parsePercentage parses and validates a percentage.
func parsePercentage(selector func(dst *Measurement) **float64) fieldParser {
return parseNumber(
selector,
minimum(0.0),
maximum(100.0),
)
}

// parseConcentration parses and validates a concentration. Concentration must be in ppm between 0 and 1000000.
func parseConcentration(selector func(dst *Measurement) **float64) fieldParser {
return parseNumber(
selector,
minimum(0.0),
maximum(1000000.0),
)
}

// minimum returns a field validator that checks the inclusive minimum.
func minimum[T constraints.Ordered](min T) fieldValidator[T] {
return func(v T, path string) error {
Expand Down Expand Up @@ -212,12 +230,10 @@ var fieldParsers = map[string]fieldParser{
},
minimum(0.0),
),
"soil.moisture": parseNumber(
"soil.moisture": parsePercentage(
func(dst *Measurement) **float64 {
return &dst.Soil.Moisture
},
minimum(0.0),
maximum(100.0),
),
"soil.temperature": parseNumber(
func(dst *Measurement) **float64 {
Expand All @@ -239,26 +255,20 @@ var fieldParsers = map[string]fieldParser{
minimum(0.0),
maximum(14.0),
),
"soil.n": parseNumber(
"soil.n": parseConcentration(
func(dst *Measurement) **float64 {
return &dst.Soil.Nitrogen
},
minimum(0.0),
maximum(1000000.0),
),
"soil.p": parseNumber(
"soil.p": parseConcentration(
func(dst *Measurement) **float64 {
return &dst.Soil.Phosphorus
},
minimum(0.0),
maximum(1000000.0),
),
"soil.k": parseNumber(
"soil.k": parseConcentration(
func(dst *Measurement) **float64 {
return &dst.Soil.Potassium
},
minimum(0.0),
maximum(1000000.0),
),
"air": object(
func(dst *Measurement) *Air {
Expand All @@ -271,12 +281,10 @@ var fieldParsers = map[string]fieldParser{
},
minimum(-273.15),
),
"air.relativeHumidity": parseNumber(
"air.relativeHumidity": parsePercentage(
func(dst *Measurement) **float64 {
return &dst.Air.RelativeHumidity
},
minimum(0.0),
maximum(100.0),
),
"air.pressure": parseNumber(
func(dst *Measurement) **float64 {
Expand All @@ -285,12 +293,10 @@ var fieldParsers = map[string]fieldParser{
minimum(900.0),
maximum(1100.0),
),
"air.co2": parseNumber(
"air.co2": parseConcentration(
func(dst *Measurement) **float64 {
return &dst.Air.CO2
},
minimum(0.0),
maximum(1000000.0),
),
"air.lightIntensity": parseNumber(
func(dst *Measurement) **float64 {
Expand Down
62 changes: 62 additions & 0 deletions pkg/messageprocessors/normalizedpayload/uplink_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,35 @@ func TestUplink(t *testing.T) {
},
},
},
{
name: "one soil nutrient concentration",
normalizedPayload: []*structpb.Struct{
{
Fields: map[string]*structpb.Value{
"soil": {
Kind: &structpb.Value_StructValue{
StructValue: &structpb.Struct{
Fields: map[string]*structpb.Value{
"n": {
Kind: &structpb.Value_NumberValue{
NumberValue: 999999.99,
},
},
},
},
},
},
},
},
},
expected: []normalizedpayload.Measurement{
{
Soil: normalizedpayload.Soil{
Nitrogen: float64Ptr(999999.99),
},
},
},
},
{
name: "two air temperatures",
normalizedPayload: []*structpb.Struct{
Expand Down Expand Up @@ -122,6 +151,39 @@ func TestUplink(t *testing.T) {
{},
},
},
{
name: "above 100 percent soil moisture",
normalizedPayload: []*structpb.Struct{
{
Fields: map[string]*structpb.Value{
"soil": {
Kind: &structpb.Value_StructValue{
StructValue: &structpb.Struct{
Fields: map[string]*structpb.Value{
"moisture": {
Kind: &structpb.Value_NumberValue{
NumberValue: 120,
},
},
},
},
},
},
},
},
},
expected: []normalizedpayload.Measurement{
{},
},
expectedValidationErrors: [][]error{
{
normalizedpayload.ErrFieldMaximum.WithAttributes(
"path", "soil.moisture",
"maximum", 100.0,
),
},
},
},
{
name: "below absolute zero",
normalizedPayload: []*structpb.Struct{
Expand Down

0 comments on commit 855b3ba

Please sign in to comment.