Skip to content

Commit

Permalink
PC-10133: Move api models to separate package (#143)
Browse files Browse the repository at this point in the history
* add validation poc

* further refine the validation example

* current progress

* simplify rule logic

* remove v1alpha aliases

* add errors tests

* add rules tests

* add strings tests

* make sure labels testing is deterministic

* invert dependency between project and v1alpha

* facilitate recent suggestions and feedback

* rename object validation to struct

* fix error handling

* change labels validation rule name

* let compiler infer the type

* declare the function once

* move service to separate pkg

* extract common validation

* use common validators

* revert changes

* move replya away

* come up with extended API for validaiton

* remove unneccessary level

* refactor naming to match fluent

* move replay

* extend tests coverage

* move sli analysis away for time being

* fix replya status through redefinition

* fix prepending name solution
  • Loading branch information
nieomylnieja authored Oct 11, 2023
1 parent 3d2594b commit f6ac5c9
Show file tree
Hide file tree
Showing 32 changed files with 969 additions and 483 deletions.
1 change: 0 additions & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ linters:
- exportloopref
- gochecknoinits
- gocognit
- goconst
- gocritic
- gocyclo
- gofmt
Expand Down
4 changes: 2 additions & 2 deletions manifest/v1alpha/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func (o *ObjectError) MarshalJSON() ([]byte, error) {
var errs []json.RawMessage
for _, oErr := range o.Errors {
switch v := oErr.(type) {
case validation.FieldError, *validation.FieldError:
case *validation.PropertyError:
data, err := json.Marshal(v)
if err != nil {
return nil, err
Expand Down Expand Up @@ -95,7 +95,7 @@ func (o *ObjectError) UnmarshalJSON(bytes []byte) error {
o.Object = intermediate.Object
for _, rawErr := range intermediate.Errors {
if len(rawErr) > 0 && rawErr[0] == '{' {
var fErr validation.FieldError
var fErr *validation.PropertyError
if err := json.Unmarshal(rawErr, &fErr); err != nil {
return err
}
Expand Down
32 changes: 16 additions & 16 deletions manifest/v1alpha/errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ var errorsTestData embed.FS

func TestObjectError(t *testing.T) {
errs := []error{
validation.FieldError{
FieldPath: "metadata.name",
FieldValue: "default",
Errors: []string{"here's an error"},
&validation.PropertyError{
PropertyName: "metadata.name",
PropertyValue: "default",
Errors: []validation.RuleError{{Message: "here's an error"}},
},
validation.FieldError{
FieldPath: "spec.description",
FieldValue: "some long description",
Errors: []string{"here's another error"},
&validation.PropertyError{
PropertyName: "spec.description",
PropertyValue: "some long description",
Errors: []validation.RuleError{{Message: "here's another error"}},
},
}

Expand Down Expand Up @@ -68,15 +68,15 @@ func TestObjectError_UnmarshalJSON(t *testing.T) {
Project: "default",
},
Errors: []error{
validation.FieldError{
FieldPath: "metadata.project",
FieldValue: "default",
Errors: []string{"nested"},
&validation.PropertyError{
PropertyName: "metadata.project",
PropertyValue: "default",
Errors: []validation.RuleError{{Message: "nested"}},
},
errors.New("some error"),
&validation.FieldError{
FieldPath: "metadata.name",
FieldValue: "my-project",
&validation.PropertyError{
PropertyName: "metadata.name",
PropertyValue: "my-project",
},
},
}
Expand All @@ -90,7 +90,7 @@ func TestObjectError_UnmarshalJSON(t *testing.T) {
assert.Equal(t, expected.Object, actual.Object)
assert.Equal(t, expected.Errors[0], actual.Errors[0])
assert.Equal(t, expected.Errors[1].Error(), actual.Errors[1].Error())
assert.Equal(t, *expected.Errors[2].(*validation.FieldError), actual.Errors[2])
assert.Equal(t, expected.Errors[2].(*validation.PropertyError), actual.Errors[2])
}

func expectedErrorOutput(t *testing.T, name string) string {
Expand Down
2 changes: 1 addition & 1 deletion manifest/v1alpha/labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type (
)

func ValidationRuleLabels() validation.SingleRule[Labels] {
return func(v Labels) error { return v.Validate() }
return validation.NewSingleRule(func(v Labels) error { return v.Validate() })
}

// Validate checks if the Labels keys and values are valid.
Expand Down
6 changes: 3 additions & 3 deletions manifest/v1alpha/project/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import (
"github.com/nobl9/nobl9-go/validation"
)

var validateProject = validation.RulesForStruct[Project](
var projectValidation = validation.New[Project](
v1alpha.FieldRuleMetadataName(func(p Project) string { return p.Metadata.Name }),
v1alpha.FieldRuleMetadataDisplayName(func(p Project) string { return p.Metadata.DisplayName }),
v1alpha.FieldRuleMetadataLabels(func(p Project) v1alpha.Labels { return p.Metadata.Labels }),
v1alpha.FieldRuleSpecDescription(func(p Project) string { return p.Spec.Description }),
).Validate
)

func validate(p Project) error {
if errs := validateProject(p); len(errs) > 0 {
if errs := projectValidation.Validate(p); len(errs) > 0 {
return v1alpha.NewObjectError(p, errs)
}
return nil
Expand Down
6 changes: 3 additions & 3 deletions manifest/v1alpha/service/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ import (
"github.com/nobl9/nobl9-go/validation"
)

var validateService = validation.RulesForStruct[Service](
var serviceValidation = validation.New[Service](
v1alpha.FieldRuleMetadataName(func(s Service) string { return s.Metadata.Name }),
v1alpha.FieldRuleMetadataDisplayName(func(s Service) string { return s.Metadata.DisplayName }),
v1alpha.FieldRuleMetadataProject(func(s Service) string { return s.Metadata.Project }),
v1alpha.FieldRuleMetadataLabels(func(s Service) v1alpha.Labels { return s.Metadata.Labels }),
v1alpha.FieldRuleSpecDescription(func(s Service) string { return s.Spec.Description }),
).Validate
)

func validate(s Service) error {
if errs := validateService(s); len(errs) > 0 {
if errs := serviceValidation.Validate(s); len(errs) > 0 {
return v1alpha.NewObjectError(s, errs)
}
return nil
Expand Down
125 changes: 0 additions & 125 deletions manifest/v1alpha/sli_analysis.go

This file was deleted.

7 changes: 7 additions & 0 deletions manifest/v1alpha/slo.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ type SLOStatus struct {
ReplayStatus *ReplayStatus `json:"timeTravel,omitempty"`
}

type ReplayStatus struct {
Status string `json:"status"`
Unit string `json:"unit"`
Value int `json:"value"`
StartTime string `json:"startTime,omitempty"`
}

// Calendar struct represents calendar time window
type Calendar struct {
StartTime string `json:"startTime" validate:"required,dateWithTime,minDateTime" example:"2020-01-21 12:30:00"`
Expand Down
35 changes: 20 additions & 15 deletions manifest/v1alpha/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,32 @@ package v1alpha

import "github.com/nobl9/nobl9-go/validation"

func FieldRuleMetadataName[S any](getter func(S) string) validation.FieldRules[string, S] {
return validation.RulesForField[string]("metadata.name", getter).
With(validation.StringRequired(), validation.StringIsDNSSubdomain())
func FieldRuleMetadataName[S any](getter func(S) string) validation.PropertyRules[string, S] {
return validation.RulesFor(getter).
WithName("metadata.name").
Rules(validation.Required[string](), validation.StringIsDNSSubdomain())
}

func FieldRuleMetadataDisplayName[S any](getter func(S) string) validation.FieldRules[string, S] {
return validation.RulesForField[string]("metadata.displayName", getter).
With(validation.StringLength(0, 63))
func FieldRuleMetadataDisplayName[S any](getter func(S) string) validation.PropertyRules[string, S] {
return validation.RulesFor(getter).
WithName("metadata.displayName").
Rules(validation.StringLength(0, 63))
}

func FieldRuleMetadataProject[S any](getter func(S) string) validation.FieldRules[string, S] {
return validation.RulesForField[string]("metadata.project", getter).
With(validation.StringRequired(), validation.StringIsDNSSubdomain())
func FieldRuleMetadataProject[S any](getter func(S) string) validation.PropertyRules[string, S] {
return validation.RulesFor(getter).
WithName("metadata.project").
Rules(validation.Required[string](), validation.StringIsDNSSubdomain())
}

func FieldRuleMetadataLabels[S any](getter func(S) Labels) validation.FieldRules[Labels, S] {
return validation.RulesForField[Labels]("metadata.labels", getter).
With(ValidationRuleLabels())
func FieldRuleMetadataLabels[S any](getter func(S) Labels) validation.PropertyRules[Labels, S] {
return validation.RulesFor(getter).
WithName("metadata.labels").
Rules(ValidationRuleLabels())
}

func FieldRuleSpecDescription[S any](getter func(S) string) validation.FieldRules[string, S] {
return validation.RulesForField[string]("spec.description", getter).
With(validation.StringDescription())
func FieldRuleSpecDescription[S any](getter func(S) string) validation.PropertyRules[string, S] {
return validation.RulesFor(getter).
WithName("spec.description").
Rules(validation.StringDescription())
}
11 changes: 0 additions & 11 deletions manifest/v1alpha/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@ func NewValidator() *Validate {
val.RegisterStructValidation(directSpecStructLevelValidation, DirectSpec{})
val.RegisterStructValidation(webhookAlertMethodValidation, WebhookAlertMethod{})
val.RegisterStructValidation(emailAlertMethodValidation, EmailAlertMethod{})
val.RegisterStructValidation(sliAnalysisSpecStructValidation, SLIAnalysis{})
val.RegisterStructValidation(countMetricsSpecValidation, CountMetricsSpec{})
val.RegisterStructValidation(cloudWatchMetricStructValidation, CloudWatchMetric{})
val.RegisterStructValidation(annotationSpecStructDatesValidation, AnnotationSpec{})
Expand All @@ -151,7 +150,6 @@ func NewValidator() *Validate {
val.RegisterStructValidation(directSpecHistoricalRetrievalValidation, Direct{})
val.RegisterStructValidation(historicalDataRetrievalValidation, HistoricalDataRetrieval{})
val.RegisterStructValidation(historicalDataRetrievalDurationValidation, HistoricalRetrievalDuration{})
val.RegisterStructValidation(replayStructDatesValidation, Replay{})
val.RegisterStructValidation(validateAzureMonitorMetricsConfiguration, AzureMonitorMetric{})

_ = val.RegisterValidation("timeUnit", isTimeUnitValid)
Expand Down Expand Up @@ -2664,15 +2662,6 @@ func pingdomStatusValid(fl v.FieldLevel) bool {
return true
}

func sliAnalysisSpecStructValidation(sl v.StructLevel) {
sliAnalysis := sl.Current().Interface().(SLIAnalysis)
if (sliAnalysis.MetricSpec.RawMetric == nil && sliAnalysis.MetricSpec.CountMetrics == nil) ||
(sliAnalysis.MetricSpec.RawMetric != nil && sliAnalysis.MetricSpec.CountMetrics != nil) {
sl.ReportError(sliAnalysis.MetricSpec.RawMetric, "rawMetric", "RawMetric", "exactlyOneMetricType", "")
sl.ReportError(sliAnalysis.MetricSpec.CountMetrics, "countMetrics", "CountMetrics", "exactlyOneMetricType", "")
}
}

func countMetricsSpecValidation(sl v.StructLevel) {
countMetrics := sl.Current().Interface().(CountMetricsSpec)
if countMetrics.TotalMetric == nil {
Expand Down
2 changes: 2 additions & 0 deletions sdk/models/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package models defines sdk.Client API request and response models.
package models
28 changes: 28 additions & 0 deletions sdk/models/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package models

import (
"fmt"
"reflect"
"strings"

"github.com/nobl9/nobl9-go/validation"
)

func newValidationError(model interface{}, errs []error) error {
return ValidationError{
ModelName: reflect.TypeOf(model).Name(),
Errors: errs,
}
}

type ValidationError struct {
ModelName string
Errors []error
}

func (v ValidationError) Error() string {
b := new(strings.Builder)
b.WriteString(fmt.Sprintf("Validation for %s has failed for the following properties:\n", v.ModelName))
validation.JoinErrors(b, v.Errors, strings.Repeat(" ", 2))
return b.String()
}
Loading

0 comments on commit f6ac5c9

Please sign in to comment.