Skip to content

Commit

Permalink
Merge pull request #6154 from pablojimpas/fix/sync-normalizedpayload-…
Browse files Browse the repository at this point in the history
…validation-to-schema

as: Sync normalized payload data model and validation to the latest schema
  • Loading branch information
johanstokking authored Apr 14, 2023
2 parents 895c886 + 76a4ecd commit 6215743
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 3 deletions.
99 changes: 96 additions & 3 deletions pkg/messageprocessors/normalizedpayload/uplink.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,25 @@ import (
"google.golang.org/protobuf/types/known/structpb"
)

// Soil is an soil measurement.
type Soil struct {
Depth *float64
Moisture *float64
Temperature *float64
ElectricalConductivity *float64
PHLevel *float64
Nitrogen *float64
Phosphorus *float64
Potassium *float64
}

// Air is an air measurement.
type Air struct {
Temperature *float64
RelativeHumidity *float64
Pressure *float64
CO2 *float64
LightIntensity *float64
}

// Wind is a wind measurement.
Expand All @@ -40,6 +54,7 @@ type Wind struct {
// Measurement is a measurement.
type Measurement struct {
Time *time.Time
Soil Soil
Air Air
Wind Wind
}
Expand Down Expand Up @@ -126,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 @@ -186,6 +219,57 @@ var fieldParsers = map[string]fieldParser{
return &dst.Time
},
),
"soil": object(
func(dst *Measurement) *Soil {
return &dst.Soil
},
),
"soil.depth": parseNumber(
func(dst *Measurement) **float64 {
return &dst.Soil.Depth
},
minimum(0.0),
),
"soil.moisture": parsePercentage(
func(dst *Measurement) **float64 {
return &dst.Soil.Moisture
},
),
"soil.temperature": parseNumber(
func(dst *Measurement) **float64 {
return &dst.Soil.Temperature
},
minimum(-273.15),
),
"soil.ec": parseNumber(
func(dst *Measurement) **float64 {
return &dst.Soil.ElectricalConductivity
},
minimum(0.0),
maximum(621.0),
),
"soil.pH": parseNumber(
func(dst *Measurement) **float64 {
return &dst.Soil.PHLevel
},
minimum(0.0),
maximum(14.0),
),
"soil.n": parseConcentration(
func(dst *Measurement) **float64 {
return &dst.Soil.Nitrogen
},
),
"soil.p": parseConcentration(
func(dst *Measurement) **float64 {
return &dst.Soil.Phosphorus
},
),
"soil.k": parseConcentration(
func(dst *Measurement) **float64 {
return &dst.Soil.Potassium
},
),
"air": object(
func(dst *Measurement) *Air {
return &dst.Air
Expand All @@ -197,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 @@ -211,6 +293,17 @@ var fieldParsers = map[string]fieldParser{
minimum(900.0),
maximum(1100.0),
),
"air.co2": parseConcentration(
func(dst *Measurement) **float64 {
return &dst.Air.CO2
},
),
"air.lightIntensity": parseNumber(
func(dst *Measurement) **float64 {
return &dst.Air.LightIntensity
},
minimum(0.0),
),
"wind": object(
func(dst *Measurement) *Wind {
return &dst.Wind
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 6215743

Please sign in to comment.