From e7f5e8da92321cea984704908a591f13dc656b76 Mon Sep 17 00:00:00 2001 From: Bruno Santos Date: Fri, 9 Jul 2021 21:51:05 +0100 Subject: [PATCH 1/3] feat(validation): Add a shared storage between all substates of a Validation State --- validation_state.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/validation_state.go b/validation_state.go index 02fc610..fae4713 100644 --- a/validation_state.go +++ b/validation_state.go @@ -27,6 +27,9 @@ type ValidationState struct { Misc map[string]interface{} Errs *[]KeyError + + // ExtraData is a shared storage between all substates of a ValidationState + ExtraData *map[string]interface{} } // NewValidationState creates a new ValidationState with the provided location pointers and data instance @@ -46,6 +49,7 @@ func NewValidationState(s *Schema) *ValidationState { LocalEvaluatedPropertyNames: &map[string]bool{}, Misc: map[string]interface{}{}, Errs: &[]KeyError{}, + ExtraData: &map[string]interface{}{}, } } @@ -66,9 +70,21 @@ func (vs *ValidationState) NewSubState() *ValidationState { LocalEvaluatedPropertyNames: vs.LocalEvaluatedPropertyNames, Misc: map[string]interface{}{}, Errs: vs.Errs, + ExtraData: vs.ExtraData, } } +// GetExtraData retrieves a key from the shared validation store(ExtraData). +func (vs *ValidationState) GetExtraData(key string) (interface{}, bool) { + data, exists := (*vs.ExtraData)[key] + return data, exists +} + +// SetExtraData stores data into the shared validation store(ExtraData) +func (vs *ValidationState) SetExtraData(key string, value interface{}) { + (*vs.ExtraData)[key] = value +} + // ClearState resets a schema to it's core elements func (vs *ValidationState) ClearState() { vs.EvaluatedPropertyNames = &map[string]bool{} From 69907481c85d2b0a7b0213423bc49c097b2eb699 Mon Sep 17 00:00:00 2001 From: Bruno Santos Date: Thu, 29 Jul 2021 10:42:48 +0100 Subject: [PATCH 2/3] refactor(validation_state): rename ExtraData to AdditionalValidationData --- validation_state.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/validation_state.go b/validation_state.go index fae4713..3f29f7f 100644 --- a/validation_state.go +++ b/validation_state.go @@ -28,8 +28,8 @@ type ValidationState struct { Errs *[]KeyError - // ExtraData is a shared storage between all substates of a ValidationState - ExtraData *map[string]interface{} + // AdditionalValidationData is a shared storage between all substates of a ValidationState + AdditionalValidationData *map[string]interface{} } // NewValidationState creates a new ValidationState with the provided location pointers and data instance @@ -49,7 +49,7 @@ func NewValidationState(s *Schema) *ValidationState { LocalEvaluatedPropertyNames: &map[string]bool{}, Misc: map[string]interface{}{}, Errs: &[]KeyError{}, - ExtraData: &map[string]interface{}{}, + AdditionalValidationData: &map[string]interface{}{}, } } @@ -70,19 +70,19 @@ func (vs *ValidationState) NewSubState() *ValidationState { LocalEvaluatedPropertyNames: vs.LocalEvaluatedPropertyNames, Misc: map[string]interface{}{}, Errs: vs.Errs, - ExtraData: vs.ExtraData, + AdditionalValidationData: vs.AdditionalValidationData, } } -// GetExtraData retrieves a key from the shared validation store(ExtraData). -func (vs *ValidationState) GetExtraData(key string) (interface{}, bool) { - data, exists := (*vs.ExtraData)[key] +// GetAdditionalValidationData retrieves a key from the shared validation store(AdditionalValidationData). +func (vs *ValidationState) GetAdditionalValidationData(key string) (interface{}, bool) { + data, exists := (*vs.AdditionalValidationData)[key] return data, exists } -// SetExtraData stores data into the shared validation store(ExtraData) -func (vs *ValidationState) SetExtraData(key string, value interface{}) { - (*vs.ExtraData)[key] = value +// SetAdditionalValidationData stores data into the shared validation store(AdditionalValidationData) +func (vs *ValidationState) SetAdditionalValidationData(key string, value interface{}) { + (*vs.AdditionalValidationData)[key] = value } // ClearState resets a schema to it's core elements From 9ebec95497d2e0748619975f962c04a26bb44cf0 Mon Sep 17 00:00:00 2001 From: Bruno Santos Date: Wed, 27 Oct 2021 16:52:48 +0100 Subject: [PATCH 3/3] feat(validation): add test for validation state adicional data field --- validation_state.go | 1 + validation_state_test.go | 133 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 validation_state_test.go diff --git a/validation_state.go b/validation_state.go index 3f29f7f..63009e7 100644 --- a/validation_state.go +++ b/validation_state.go @@ -29,6 +29,7 @@ type ValidationState struct { Errs *[]KeyError // AdditionalValidationData is a shared storage between all substates of a ValidationState + // Its key space won't be cleared and is shared between all substates of a validation operation. AdditionalValidationData *map[string]interface{} } diff --git a/validation_state_test.go b/validation_state_test.go new file mode 100644 index 0000000..54e7755 --- /dev/null +++ b/validation_state_test.go @@ -0,0 +1,133 @@ +package jsonschema_test + +import ( + "context" + "encoding/json" + "testing" + + jptr "github.com/qri-io/jsonpointer" + "github.com/qri-io/jsonschema" +) + +const GeoLockKeyword = "geolock" +const GeoLockADKey = "geolockrefs" + +type GeoLock struct { + Scope string + Zone string +} + +type GeoLockRef struct { + DataPath string + Scope string + Zone string + Value string +} + +func NewGeoLock() jsonschema.Keyword { + return &GeoLock{} +} + +func (f *GeoLock) Validate(propPath string, data interface{}, errs *[]jsonschema.KeyError) {} + +func (f *GeoLock) Register(uri string, registry *jsonschema.SchemaRegistry) {} + +func (f *GeoLock) Resolve(pointer jptr.Pointer, uri string) *jsonschema.Schema { + return nil +} + +func (f *GeoLock) ValidateKeyword(ctx context.Context, currentState *jsonschema.ValidationState, data interface{}) { + glKeyword := currentState.Local.JSONProp("geolock").(*GeoLock) + + var refs []GeoLockRef + if tmp, found := currentState.GetAdditionalValidationData(GeoLockADKey); found { + refs = tmp.([]GeoLockRef) + } else { + refs = []GeoLockRef{} + } + + ref := GeoLockRef{ + DataPath: currentState.InstanceLocation.String(), + Scope: glKeyword.Scope, + Zone: glKeyword.Zone, + Value: data.(string), + } + + refs = append(refs, ref) + + currentState.SetAdditionalValidationData(GeoLockADKey, refs) +} + +func TestAdditionalValidationData(t *testing.T) { + + ctx := context.Background() + var schemaData = []byte(`{ + "$defs": { + "person": { + "type": "object", + "properties": { + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "address_city": { + "type": "string", + "geolock": { + "zone": "Europe", + "scope": "city" + } + } + } + } + }, + "type": "array", + "items": { + "$ref": "#/$defs/person" + } + }`) + + jsonschema.RegisterKeyword(GeoLockKeyword, NewGeoLock) + jsonschema.LoadDraft2019_09() + + rs := &jsonschema.Schema{} + if err := rs.UnmarshalJSON(schemaData); err != nil { + t.Errorf("failure to unmarshal schema: %s", err.Error()) + t.FailNow() + return + } + + expectedCount := 3 + var valid = []byte(`[ + { "firstName" : "Antonio", "lastName" : "Alves", "address_city": "pt#lisbon" }, + { "firstName" : "Peter", "lastName" : "Parque", "address_city": "de#berlin" }, + { "firstName" : "Juan", "lastName" : "Juniper", "address_city": "es#madrid" } + ]`) + var doc interface{} + if err := json.Unmarshal(valid, &doc); err != nil { + t.Errorf("failure to unmarshal test data: %s", err.Error()) + t.FailNow() + return + } + vs := rs.Validate(ctx, doc) + if vs.Errs != nil && len(*vs.Errs) > 0 { + t.Errorf("failure to validate test data: %v", vs.Errs) + t.FailNow() + return + } + + tmp, refExists := vs.GetAdditionalValidationData(GeoLockADKey) + if !refExists { + t.Errorf("Expected to find key %q validation state additional data.", GeoLockADKey) + t.FailNow() + return + } + foundLocks := tmp.([]GeoLockRef) + + if len(foundLocks) != expectedCount { + t.Errorf("Unexpected number of refs found. expected=%d, found=%d", expectedCount, len(foundLocks)) + t.FailNow() + return + } +}