From d68ef923f54b7962af1e028dfe939d73fdb324ea Mon Sep 17 00:00:00 2001 From: slinkydeveloper Date: Mon, 16 Nov 2020 15:48:58 +0100 Subject: [PATCH] Draft JSON Schema implementation Signed-off-by: Francesco Guardiani --- go.mod | 1 + go.sum | 5 + .../benchmarks/filter_benchmark_test.go | 141 ++++ pkg/eventfilter/json_schema/filter.go | 129 ++++ pkg/eventfilter/json_schema/filter_test.go | 267 +++++++ .../github.com/qri-io/jsonpointer/LICENSE | 21 + .../github.com/qri-io/jsonschema/LICENSE | 21 + .../github.com/qri-io/jsonpointer/.gitignore | 2 + .../qri-io/jsonpointer/CHANGELOG.md | 29 + vendor/github.com/qri-io/jsonpointer/LICENSE | 21 + vendor/github.com/qri-io/jsonpointer/Makefile | 3 + .../github.com/qri-io/jsonpointer/README.md | 62 ++ .../github.com/qri-io/jsonpointer/codecov.yml | 9 + vendor/github.com/qri-io/jsonpointer/go.mod | 1 + .../github.com/qri-io/jsonpointer/pointer.go | 185 +++++ .../qri-io/jsonpointer/traversal.go | 99 +++ .../github.com/qri-io/jsonschema/.gitignore | 3 + .../github.com/qri-io/jsonschema/CHANGELOG.md | 60 ++ .../qri-io/jsonschema/DEVELOPERS.md | 125 ++++ vendor/github.com/qri-io/jsonschema/LICENSE | 21 + vendor/github.com/qri-io/jsonschema/Makefile | 3 + vendor/github.com/qri-io/jsonschema/README.md | 185 +++++ .../qri-io/jsonschema/code_of_conduct.md | 13 + .../github.com/qri-io/jsonschema/codecov.yml | 9 + .../jsonschema/draft2019_09_keywords.go | 92 +++ vendor/github.com/qri-io/jsonschema/go.mod | 9 + vendor/github.com/qri-io/jsonschema/go.sum | 11 + .../github.com/qri-io/jsonschema/keyword.go | 157 ++++ .../qri-io/jsonschema/keywords_array.go | 444 +++++++++++ .../qri-io/jsonschema/keywords_boolean.go | 319 ++++++++ .../qri-io/jsonschema/keywords_conditional.go | 206 ++++++ .../qri-io/jsonschema/keywords_core.go | 693 ++++++++++++++++++ .../qri-io/jsonschema/keywords_numeric.go | 139 ++++ .../qri-io/jsonschema/keywords_object.go | 690 +++++++++++++++++ .../qri-io/jsonschema/keywords_optional.go | 325 ++++++++ .../qri-io/jsonschema/keywords_standard.go | 283 +++++++ .../qri-io/jsonschema/keywords_string.go | 113 +++ vendor/github.com/qri-io/jsonschema/schema.go | 360 +++++++++ .../qri-io/jsonschema/schema_registry.go | 90 +++ .../github.com/qri-io/jsonschema/traversal.go | 35 + vendor/github.com/qri-io/jsonschema/util.go | 85 +++ .../qri-io/jsonschema/validation_state.go | 194 +++++ vendor/modules.txt | 5 + 43 files changed, 5665 insertions(+) create mode 100644 pkg/eventfilter/benchmarks/filter_benchmark_test.go create mode 100644 pkg/eventfilter/json_schema/filter.go create mode 100644 pkg/eventfilter/json_schema/filter_test.go create mode 100644 third_party/VENDOR-LICENSE/github.com/qri-io/jsonpointer/LICENSE create mode 100644 third_party/VENDOR-LICENSE/github.com/qri-io/jsonschema/LICENSE create mode 100644 vendor/github.com/qri-io/jsonpointer/.gitignore create mode 100644 vendor/github.com/qri-io/jsonpointer/CHANGELOG.md create mode 100644 vendor/github.com/qri-io/jsonpointer/LICENSE create mode 100644 vendor/github.com/qri-io/jsonpointer/Makefile create mode 100644 vendor/github.com/qri-io/jsonpointer/README.md create mode 100644 vendor/github.com/qri-io/jsonpointer/codecov.yml create mode 100644 vendor/github.com/qri-io/jsonpointer/go.mod create mode 100644 vendor/github.com/qri-io/jsonpointer/pointer.go create mode 100644 vendor/github.com/qri-io/jsonpointer/traversal.go create mode 100644 vendor/github.com/qri-io/jsonschema/.gitignore create mode 100644 vendor/github.com/qri-io/jsonschema/CHANGELOG.md create mode 100644 vendor/github.com/qri-io/jsonschema/DEVELOPERS.md create mode 100644 vendor/github.com/qri-io/jsonschema/LICENSE create mode 100644 vendor/github.com/qri-io/jsonschema/Makefile create mode 100644 vendor/github.com/qri-io/jsonschema/README.md create mode 100644 vendor/github.com/qri-io/jsonschema/code_of_conduct.md create mode 100644 vendor/github.com/qri-io/jsonschema/codecov.yml create mode 100644 vendor/github.com/qri-io/jsonschema/draft2019_09_keywords.go create mode 100644 vendor/github.com/qri-io/jsonschema/go.mod create mode 100644 vendor/github.com/qri-io/jsonschema/go.sum create mode 100644 vendor/github.com/qri-io/jsonschema/keyword.go create mode 100644 vendor/github.com/qri-io/jsonschema/keywords_array.go create mode 100644 vendor/github.com/qri-io/jsonschema/keywords_boolean.go create mode 100644 vendor/github.com/qri-io/jsonschema/keywords_conditional.go create mode 100644 vendor/github.com/qri-io/jsonschema/keywords_core.go create mode 100644 vendor/github.com/qri-io/jsonschema/keywords_numeric.go create mode 100644 vendor/github.com/qri-io/jsonschema/keywords_object.go create mode 100644 vendor/github.com/qri-io/jsonschema/keywords_optional.go create mode 100644 vendor/github.com/qri-io/jsonschema/keywords_standard.go create mode 100644 vendor/github.com/qri-io/jsonschema/keywords_string.go create mode 100644 vendor/github.com/qri-io/jsonschema/schema.go create mode 100644 vendor/github.com/qri-io/jsonschema/schema_registry.go create mode 100644 vendor/github.com/qri-io/jsonschema/traversal.go create mode 100644 vendor/github.com/qri-io/jsonschema/util.go create mode 100644 vendor/github.com/qri-io/jsonschema/validation_state.go diff --git a/go.mod b/go.mod index ca3012765ab..7ecfd8b95bf 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 github.com/pkg/errors v0.9.1 github.com/prometheus/procfs v0.0.11 // indirect + github.com/qri-io/jsonschema v0.2.0 github.com/rickb777/date v1.13.0 github.com/robfig/cron/v3 v3.0.1 github.com/rogpeppe/fastuuid v1.2.0 diff --git a/go.sum b/go.sum index 1b23c9eee1c..46588ad0df9 100644 --- a/go.sum +++ b/go.sum @@ -560,6 +560,10 @@ github.com/prometheus/procfs v0.0.11 h1:DhHlBtkHWPYi8O2y31JkK0TF+DGM+51OopZjH/Ia github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/statsd_exporter v0.15.0 h1:UiwC1L5HkxEPeapXdm2Ye0u1vUJfTj7uwT5yydYpa1E= github.com/prometheus/statsd_exporter v0.15.0/go.mod h1:Dv8HnkoLQkeEjkIE4/2ndAA7WL1zHKK7WMqFQqu72rw= +github.com/qri-io/jsonpointer v0.1.1 h1:prVZBZLL6TW5vsSB9fFHFAMBLI4b0ri5vribQlTJiBA= +github.com/qri-io/jsonpointer v0.1.1/go.mod h1:DnJPaYgiKu56EuDp8TU5wFLdZIcAnb/uH9v37ZaMV64= +github.com/qri-io/jsonschema v0.2.0 h1:is8lirh3HYwTkC0e+4jL/vWEHwzPLojnl4FWkUoeEPU= +github.com/qri-io/jsonschema v0.2.0/go.mod h1:g7DPkiOsK1xv6T/Ao5scXRkd+yTFygcANPBaaqW+VrI= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rickb777/date v1.13.0 h1:+8AmwLuY1d/rldzdqvqTEg7107bZ8clW37x4nsdG3Hs= github.com/rickb777/date v1.13.0/go.mod h1:GZf3LoGnxPWjX+/1TXOuzHefZFDovTyNLHDMd3qH70k= @@ -579,6 +583,7 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= diff --git a/pkg/eventfilter/benchmarks/filter_benchmark_test.go b/pkg/eventfilter/benchmarks/filter_benchmark_test.go new file mode 100644 index 00000000000..20a0237efd4 --- /dev/null +++ b/pkg/eventfilter/benchmarks/filter_benchmark_test.go @@ -0,0 +1,141 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package benchmarks + +import ( + "testing" + + cetest "github.com/cloudevents/sdk-go/v2/test" + + "knative.dev/eventing/pkg/eventfilter" + "knative.dev/eventing/pkg/eventfilter/json_schema" +) + +func BenchmarkJsonSchemaFilter(b *testing.B) { + event := cetest.FullEvent() + + RunFilterBenchmarks(b, + func(i interface{}) eventfilter.Filter { + f, _ := json_schema.NewJsonSchemaFilter(i.(map[string]interface{})) + return f + }, + FilterBenchmark{ + name: "Pass with exact match of id", + arg: map[string]interface{}{ + "properties": map[string]interface{}{ + "id": map[string]interface{}{ + "const": event.ID(), + }, + }, + }, + event: event, + }, + FilterBenchmark{ + name: "Pass with exact match of all context attributes (except time)", + arg: map[string]interface{}{ + "properties": map[string]interface{}{ + "id": map[string]interface{}{ + "const": event.ID(), + }, + "source": map[string]interface{}{ + "const": event.Source(), + }, + "type": map[string]interface{}{ + "const": event.Type(), + }, + "dataschema": map[string]interface{}{ + "const": event.DataSchema(), + }, + "datacontenttype": map[string]interface{}{ + "const": event.DataContentType(), + }, + "subject": map[string]interface{}{ + "const": event.Subject(), + }, + }, + }, + event: event, + }, + FilterBenchmark{ + name: "No pass with exact match of id and source", + arg: map[string]interface{}{ + "properties": map[string]interface{}{ + "id": map[string]interface{}{ + "const": "qwertyuiopasdfghjklzxcvbnm", + }, + "source": map[string]interface{}{ + "const": "qwertyuiopasdfghjklzxcvbnm", + }, + }, + }, + event: event, + }, + FilterBenchmark{ + name: "No pass with if then", + arg: map[string]interface{}{ + "if": map[string]interface{}{ + "properties": map[string]interface{}{ + "id": map[string]interface{}{ + "const": event.ID(), + }, + }, + }, + "then": map[string]interface{}{ + "properties": map[string]interface{}{ + "type": map[string]interface{}{ + "const": "---" + event.Type(), + }, + }, + }, + }, + event: event, + }, + FilterBenchmark{ + name: "No pass with nested logic", + arg: map[string]interface{}{ + "anyOf": []interface{}{ + map[string]interface{}{ + "properties": map[string]interface{}{ + "type": map[string]interface{}{ + "const": "---" + event.Type(), + }, + }, + }, + map[string]interface{}{ + "oneOf": []interface{}{ + map[string]interface{}{ + "properties": map[string]interface{}{ + "type": map[string]interface{}{ + "const": event.Type(), + }, + }, + }, + map[string]interface{}{ + "properties": map[string]interface{}{ + "id": map[string]interface{}{ + "const": event.ID(), + }, + }, + }, + }, + }, + }, + }, + event: event, + }, + ) +} diff --git a/pkg/eventfilter/json_schema/filter.go b/pkg/eventfilter/json_schema/filter.go new file mode 100644 index 00000000000..27774713feb --- /dev/null +++ b/pkg/eventfilter/json_schema/filter.go @@ -0,0 +1,129 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package json_schema + +import ( + "context" + "encoding/json" + "fmt" + "io" + + cloudevents "github.com/cloudevents/sdk-go/v2" + "github.com/cloudevents/sdk-go/v2/binding" + "github.com/cloudevents/sdk-go/v2/binding/spec" + "github.com/cloudevents/sdk-go/v2/types" + "github.com/qri-io/jsonschema" + "knative.dev/pkg/logging" + + "knative.dev/eventing/pkg/eventfilter" +) + +type jsonSchemaFilter jsonschema.Schema + +func NewJsonSchemaFilter(jsonSchema map[string]interface{}) (eventfilter.Filter, error) { + schemaBytes, err := json.Marshal(jsonSchema) + if err != nil { + return nil, fmt.Errorf("error while marshalling the schema: %w", err) + } + + rs := &jsonschema.Schema{} + if err := json.Unmarshal(schemaBytes, rs); err != nil { + return nil, fmt.Errorf("error while parsing the schema: %w", err) + } + + return (*jsonSchemaFilter)(rs), nil +} + +func (j *jsonSchemaFilter) Filter(ctx context.Context, event cloudevents.Event) eventfilter.FilterResult { + // The nice thing here would be to have an abstraction that makes event looks like a json + // without triggering this copy. + // Unfortunately this library doesn't allow it, hence we need to copy stuff inside a map to + // give to the json schema validator + json := make(map[string]interface{}, 4) // A CloudEvent has at least 4 attributes + + // We're leveraging the binding to efficiently copy-paste attributes and extensions into the json + // to give to the json schema library. + // It also guarantees no nil attributes (which might break the semantics of the schema, cloudevents spec never allows null values) + err := binding.ToMessage(&event).ReadBinary(ctx, attributesWriter(json)) + if err != nil { + // This should never happen in theory + logging.FromContext(ctx).Warn("Error while trying to convert the input event attributes and extensions to json: ", err) + return eventfilter.FailFilter + } + + result := (*jsonschema.Schema)(j).Validate(ctx, json) + if result.IsValid() { + return eventfilter.PassFilter + } + return eventfilter.FailFilter +} + +var _ eventfilter.Filter = (*jsonSchemaFilter)(nil) + +type attributesWriter map[string]interface{} + +func (a attributesWriter) SetAttribute(attribute spec.Attribute, value interface{}) error { + v, err := coherceTypes(value) + if err != nil { + return err + } + a[attribute.Name()] = v + return nil +} + +func (a attributesWriter) SetExtension(name string, value interface{}) error { + v, err := coherceTypes(value) + if err != nil { + return err + } + a[name] = v + return nil +} + +func (a attributesWriter) Start(ctx context.Context) error { + return nil +} + +func (a attributesWriter) SetData(data io.Reader) error { + return nil +} + +func (a attributesWriter) End(ctx context.Context) error { + return nil +} + +// Only some types has to be converted to strings +// The coherced types maps more or less to the official Cloudevents json schema +// https://github.com/cloudevents/spec/blob/master/spec.json +func coherceTypes(value interface{}) (interface{}, error) { + validatedValue, err := types.Validate(value) + if err != nil { + // This should never happen because an event cannot contain an invalid type! + return nil, err + } + + switch t := validatedValue.(type) { + case types.URI: // Use string form of URLs. + return t.String(), nil + case types.URIRef: // Use string form of URLs. + return t.String(), nil + case types.Timestamp: // Use string form of URLs. + return t.String(), nil + default: + return validatedValue, nil + } +} diff --git a/pkg/eventfilter/json_schema/filter_test.go b/pkg/eventfilter/json_schema/filter_test.go new file mode 100644 index 00000000000..d38412ea4f6 --- /dev/null +++ b/pkg/eventfilter/json_schema/filter_test.go @@ -0,0 +1,267 @@ +/* +Copyright 2020 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package json_schema + +import ( + "context" + "testing" + + cetest "github.com/cloudevents/sdk-go/v2/test" + "github.com/cloudevents/sdk-go/v2/types" + "github.com/stretchr/testify/require" + + "knative.dev/eventing/pkg/eventfilter" +) + +func TestAttributesFilter_Filter(t *testing.T) { + e := cetest.FullEvent() + + tests := map[string]struct { + filter map[string]interface{} + want eventfilter.FilterResult + }{ + "Fail - If then schema": { + filter: map[string]interface{}{ + "if": map[string]interface{}{ + "properties": map[string]interface{}{ + "id": map[string]interface{}{ + "const": e.ID(), + }, + }, + }, + "then": map[string]interface{}{ + "properties": map[string]interface{}{ + "type": map[string]interface{}{ + "const": "---" + e.Type(), + }, + }, + }, + }, + want: eventfilter.FailFilter, + }, + "Pass - If then schema": { + filter: map[string]interface{}{ + "if": map[string]interface{}{ + "properties": map[string]interface{}{ + "id": map[string]interface{}{ + "const": e.ID(), + }, + }, + }, + "then": map[string]interface{}{ + "properties": map[string]interface{}{ + "type": map[string]interface{}{ + "const": e.Type(), + }, + }, + }, + }, + want: eventfilter.PassFilter, + }, + "Fail - Simple logic": { + filter: map[string]interface{}{ + "anyOf": []interface{}{ + map[string]interface{}{ + "properties": map[string]interface{}{ + "id": map[string]interface{}{ + "const": e.ID() + "---", + }, + }, + }, + map[string]interface{}{ + "properties": map[string]interface{}{ + "type": map[string]interface{}{ + "const": "---" + e.Type(), + }, + }, + }, + }, + }, + want: eventfilter.FailFilter, + }, + "Pass - Simple logic": { + filter: map[string]interface{}{ + "anyOf": []interface{}{ + map[string]interface{}{ + "properties": map[string]interface{}{ + "id": map[string]interface{}{ + "const": e.ID() + "---", + }, + }, + }, + map[string]interface{}{ + "properties": map[string]interface{}{ + "type": map[string]interface{}{ + "const": e.Type(), + }, + }, + }, + }, + }, + want: eventfilter.PassFilter, + }, + "Fail - Nested logic": { + filter: map[string]interface{}{ + "anyOf": []interface{}{ + map[string]interface{}{ + "properties": map[string]interface{}{ + "type": map[string]interface{}{ + "const": "---" + e.Type(), + }, + }, + }, + map[string]interface{}{ + "oneOf": []interface{}{ + map[string]interface{}{ + "properties": map[string]interface{}{ + "type": map[string]interface{}{ + "const": e.Type(), + }, + }, + }, + map[string]interface{}{ + "properties": map[string]interface{}{ + "id": map[string]interface{}{ + "const": e.ID(), + }, + }, + }, + }, + }, + }, + }, + want: eventfilter.FailFilter, + }, + "Pass - Nested logic": { + filter: map[string]interface{}{ + "anyOf": []interface{}{ + map[string]interface{}{ + "properties": map[string]interface{}{ + "type": map[string]interface{}{ + "const": "---" + e.Type(), + }, + }, + }, + map[string]interface{}{ + "oneOf": []interface{}{ + map[string]interface{}{ + "properties": map[string]interface{}{ + "type": map[string]interface{}{ + "const": "---" + e.Type(), + }, + }, + }, + map[string]interface{}{ + "properties": map[string]interface{}{ + "id": map[string]interface{}{ + "const": e.ID(), + }, + }, + }, + }, + }, + }, + }, + want: eventfilter.PassFilter, + }, + "Filter should be able to access to all fields": { + filter: map[string]interface{}{ + "properties": map[string]interface{}{ + "id": map[string]interface{}{ + "const": e.ID(), + }, + "source": map[string]interface{}{ + "const": e.Source(), + }, + "type": map[string]interface{}{ + "const": e.Type(), + }, + "dataschema": map[string]interface{}{ + "const": e.DataSchema(), + }, + "datacontenttype": map[string]interface{}{ + "const": e.DataContentType(), + }, + "subject": map[string]interface{}{ + "const": e.Subject(), + }, + "time": map[string]interface{}{ + "const": types.FormatTime(e.Time()), + }, + }, + }, + want: eventfilter.PassFilter, + }, + "Type cohercion": { + filter: map[string]interface{}{ + "properties": map[string]interface{}{ + "id": map[string]interface{}{ + "type": "string", + }, + "source": map[string]interface{}{ + "type": "string", + }, + "type": map[string]interface{}{ + "type": "string", + }, + "dataschema": map[string]interface{}{ + "type": "string", + }, + "datacontenttype": map[string]interface{}{ + "type": "string", + }, + "subject": map[string]interface{}{ + "type": "string", + }, + "time": map[string]interface{}{ + "type": "string", + }, + "exbool": map[string]interface{}{ + "type": "boolean", + }, + // This doesn't work, debugging it seems like the type is coherced correctly to int32, so maybe it's a validator bug + //"exint": map[string]interface{}{ + // "type": "integer", + //}, + }, + }, + want: eventfilter.PassFilter, + }, + "Filter should not be able to access to data": { + filter: map[string]interface{}{ + "required": []string{"data"}, + }, + want: eventfilter.FailFilter, + }, + "Simple failing schema": { + filter: map[string]interface{}{ + "type": "string", + }, + want: eventfilter.FailFilter, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + f, err := NewJsonSchemaFilter(tt.filter) + require.NoError(t, err) + + if got := f.Filter(context.TODO(), e); got != tt.want { + t.Errorf("Filter() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/third_party/VENDOR-LICENSE/github.com/qri-io/jsonpointer/LICENSE b/third_party/VENDOR-LICENSE/github.com/qri-io/jsonpointer/LICENSE new file mode 100644 index 00000000000..75c1a28d709 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/qri-io/jsonpointer/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Brendan O'Brien + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/third_party/VENDOR-LICENSE/github.com/qri-io/jsonschema/LICENSE b/third_party/VENDOR-LICENSE/github.com/qri-io/jsonschema/LICENSE new file mode 100644 index 00000000000..75c1a28d709 --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/qri-io/jsonschema/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Brendan O'Brien + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/qri-io/jsonpointer/.gitignore b/vendor/github.com/qri-io/jsonpointer/.gitignore new file mode 100644 index 00000000000..f45e81b44f0 --- /dev/null +++ b/vendor/github.com/qri-io/jsonpointer/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +coverage.txt \ No newline at end of file diff --git a/vendor/github.com/qri-io/jsonpointer/CHANGELOG.md b/vendor/github.com/qri-io/jsonpointer/CHANGELOG.md new file mode 100644 index 00000000000..8be0635a57e --- /dev/null +++ b/vendor/github.com/qri-io/jsonpointer/CHANGELOG.md @@ -0,0 +1,29 @@ +# (2020-05-06) + +This is an update to jsonpointer. It adds usability functions and options for perfomance optimized use. + +### Bug Fixes + +* **Test:** fix failing tests when using go 1.14. [c51da06](https://github.com/qri-io/jsonpointer/commit/c51da06b3a9796e12c0a8309b728b015c01387c0) + +### Features + +* **Head,Tail,IsEmpty:** added methods to get the first token, all tokens after the head and to check if a given pointer is empty [c51da06](https://github.com/qri-io/jsonpointer/commit/c51da06b3a9796e12c0a8309b728b015c01387c0) +* **RawDescendant,NewPointer:** methods that allow to directly append to the current pointer without safety checks and a way to create a pointer with pre-allocated memory for performance intensive use cases [c51da06](https://github.com/qri-io/jsonpointer/commit/c51da06b3a9796e12c0a8309b728b015c01387c0) + +# (2019-05-23) + +This is the first proper release of jsonpointer. In preparation for go 1.13, in which `go.mod` files and go modules are the primary way to handle go dependencies, we are going to do an official release of all our modules. This will be version v0.1.0 of jsonpointer. + +### Bug Fixes + +* **Parse:** fix incorrect handling of empty url fragment strings ([5919095](https://github.com/qri-io/jsonpointer/commit/5919095)) + + +### Features + +* **Descendant,WalkJSON:** added pointer descendant method, experimental WalkJSON func ([707e879](https://github.com/qri-io/jsonpointer/commit/707e879)) +* initial commit ([448ab45](https://github.com/qri-io/jsonpointer/commit/448ab45)) + + + diff --git a/vendor/github.com/qri-io/jsonpointer/LICENSE b/vendor/github.com/qri-io/jsonpointer/LICENSE new file mode 100644 index 00000000000..75c1a28d709 --- /dev/null +++ b/vendor/github.com/qri-io/jsonpointer/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Brendan O'Brien + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/qri-io/jsonpointer/Makefile b/vendor/github.com/qri-io/jsonpointer/Makefile new file mode 100644 index 00000000000..d025612f4e4 --- /dev/null +++ b/vendor/github.com/qri-io/jsonpointer/Makefile @@ -0,0 +1,3 @@ +# Let's keep all our changelog commands the same across all our packages: +update-changelog: + conventional-changelog -p angular -i CHANGELOG.md -s \ No newline at end of file diff --git a/vendor/github.com/qri-io/jsonpointer/README.md b/vendor/github.com/qri-io/jsonpointer/README.md new file mode 100644 index 00000000000..3f973accbe5 --- /dev/null +++ b/vendor/github.com/qri-io/jsonpointer/README.md @@ -0,0 +1,62 @@ +[![Qri](https://img.shields.io/badge/made%20by-qri-magenta.svg?style=flat-square)](https://qri.io) +[![GoDoc](https://godoc.org/github.com/qri-io/jsonpointer?status.svg)](http://godoc.org/github.com/qri-io/jsonpointer) +[![License](https://img.shields.io/github/license/qri-io/jsonpointer.svg?style=flat-square)](./LICENSE) +[![Codecov](https://img.shields.io/codecov/c/github/qri-io/jsonpointer.svg?style=flat-square)](https://codecov.io/gh/qri-io/jsonpointer) +[![CI](https://img.shields.io/circleci/project/github/qri-io/jsonpointer.svg?style=flat-square)](https://circleci.com/gh/qri-io/jsonpointer) +[![Go Report Card](https://goreportcard.com/badge/github.com/qri-io/jsonpointer)](https://goreportcard.com/report/github.com/qri-io/jsonpointer) + + +# jsonpointer +golang implementation of [IETF RFC6901](https://tools.ietf.org/html/rfc6901): +_JSON Pointer defines a string syntax for identifying a specific value within a JavaScript Object Notation (JSON) document._ + +### Installation +install with: +`go get -u github.com/qri-io/jsonpointer` + + +### Usage +Here's a quick example pulled from the [godoc](https://godoc.org/github.com/qri-io/jsonpointer): + +```go +import ( + "encoding/json" + "fmt" + "github.com/qri-io/jsonpointer" +) + +var document = []byte(`{ + "foo": { + "bar": { + "baz": [0,"hello!"] + } + } +}`) + +func main() { + parsed := map[string]interface{}{} + // be sure to handle errors in real-world code! + json.Unmarshal(document, &parsed) + + // parse a json pointer. Pointers can also be url fragments + // the following are equivelent pointers: + // "/foo/bar/baz/1" + // "#/foo/bar/baz/1" + // "http://example.com/document.json#/foo/bar/baz/1" + ptr, _ := jsonpointer.Parse("/foo/bar/baz/1") + + // evaluate the pointer against the document + // evaluation always starts at the root of the document + got, _ := ptr.Eval(parsed) + + fmt.Println(got) + // Output: hello! +} + +``` + +### License +MIT + +### Issues & Contributions +Contributions & Issues are more than welcome! Everything happens over on this repo's [github page](https://github.com/qri-io/jsonpointer) \ No newline at end of file diff --git a/vendor/github.com/qri-io/jsonpointer/codecov.yml b/vendor/github.com/qri-io/jsonpointer/codecov.yml new file mode 100644 index 00000000000..1a90176d045 --- /dev/null +++ b/vendor/github.com/qri-io/jsonpointer/codecov.yml @@ -0,0 +1,9 @@ +codecov: + ci: + - "ci/circle-ci" + notify: + require_ci_to_pass: no + after_n_builds: 2 +coverage: + range: "80...100" +comment: off \ No newline at end of file diff --git a/vendor/github.com/qri-io/jsonpointer/go.mod b/vendor/github.com/qri-io/jsonpointer/go.mod new file mode 100644 index 00000000000..a00555d67cd --- /dev/null +++ b/vendor/github.com/qri-io/jsonpointer/go.mod @@ -0,0 +1 @@ +module github.com/qri-io/jsonpointer diff --git a/vendor/github.com/qri-io/jsonpointer/pointer.go b/vendor/github.com/qri-io/jsonpointer/pointer.go new file mode 100644 index 00000000000..13f3ccdccdc --- /dev/null +++ b/vendor/github.com/qri-io/jsonpointer/pointer.go @@ -0,0 +1,185 @@ +// Package jsonpointer implements IETF rfc6901 +// JSON Pointers are a string syntax for +// identifying a specific value within a JavaScript Object Notation +// (JSON) document [RFC4627]. JSON Pointer is intended to be easily +// expressed in JSON string values as well as Uniform Resource +// Identifier (URI) [RFC3986] fragment identifiers. +// +// this package is intended to work like net/url from the go +// standard library +package jsonpointer + +import ( + "fmt" + "net/url" + "strconv" + "strings" +) + +const defaultPointerAllocationSize = 32 + +// Parse parses str into a Pointer structure. +// str may be a pointer or a url string. +// If a url string, Parse will use the URL's fragment component +// (the bit after the '#' symbol) +func Parse(str string) (Pointer, error) { + // fast paths that skip url parse step + if len(str) == 0 || str == "#" { + return Pointer{}, nil + } else if str[0] == '/' { + return parse(str) + } + + u, err := url.Parse(str) + if err != nil { + return nil, err + } + return parse(u.Fragment) +} + +// IsEmpty is a utility function to check if the Pointer +// is empty / nil equivalent +func (p Pointer) IsEmpty() bool { + return len(p) == 0 +} + +// Head returns the root of the Pointer +func (p Pointer) Head() *string { + if len(p) == 0 { + return nil + } + return &p[0] +} + +// Tail returns everything after the Pointer head +func (p Pointer) Tail() Pointer { + return Pointer(p[1:]) +} + +// The ABNF syntax of a JSON Pointer is: +// json-pointer = *( "/" reference-token ) +// reference-token = *( unescaped / escaped ) +// unescaped = %x00-2E / %x30-7D / %x7F-10FFFF +// ; %x2F ('/') and %x7E ('~') are excluded from 'unescaped' +// escaped = "~" ( "0" / "1" ) +// ; representing '~' and '/', respectively +func parse(str string) (Pointer, error) { + if len(str) == 0 { + return Pointer{}, nil + } + + if str[0] != '/' { + return nil, fmt.Errorf("non-empty references must begin with a '/' character") + } + str = str[1:] + + toks := strings.Split(str, separator) + for i, t := range toks { + toks[i] = unescapeToken(t) + } + return Pointer(toks), nil +} + +// Pointer represents a parsed JSON pointer +type Pointer []string + +// NewPointer creates a Pointer with a pre-allocated block of memory +// to avoid repeated slice expansions +func NewPointer() Pointer { + return make([]string, 0, defaultPointerAllocationSize) +} + +// String implements the stringer interface for Pointer, +// giving the escaped string +func (p Pointer) String() (str string) { + for _, tok := range p { + str += "/" + escapeToken(tok) + } + return +} + +// Eval evaluates a json pointer against a given root JSON document +// Evaluation of a JSON Pointer begins with a reference to the root +// value of a JSON document and completes with a reference to some value +// within the document. Each reference token in the JSON Pointer is +// evaluated sequentially. +func (p Pointer) Eval(data interface{}) (result interface{}, err error) { + result = data + for _, tok := range p { + if result, err = p.evalToken(tok, result); err != nil { + return nil, err + } + } + return +} + +// Descendant returns a new pointer to a descendant of the current pointer +// parsing the input path into components +func (p Pointer) Descendant(path string) (Pointer, error) { + if !strings.HasPrefix(path, "/") { + path = "/" + path + } + dpath, err := parse(path) + if err != nil { + return p, err + } + + if p.String() == "/" { + return dpath, nil + } + + return append(p, dpath...), nil +} + +// RawDescendant extends the pointer with 1 or more path tokens +// The function itself is unsafe as it doesnt fully parse the input +// and assumes the user is directly managing the pointer +// This allows for much faster pointer management +func (p Pointer) RawDescendant(path ...string) Pointer { + return append(p, path...) +} + +// Evaluation of each reference token begins by decoding any escaped +// character sequence. This is performed by first transforming any +// occurrence of the sequence '~1' to '/', and then transforming any +// occurrence of the sequence '~0' to '~'. By performing the +// substitutions in this order, an implementation avoids the error of +// turning '~01' first into '~1' and then into '/', which would be +// incorrect (the string '~01' correctly becomes '~1' after +// transformation). +// The reference token then modifies which value is referenced according +// to the following scheme: +func (p Pointer) evalToken(tok string, data interface{}) (interface{}, error) { + switch ch := data.(type) { + case map[string]interface{}: + return ch[tok], nil + case []interface{}: + i, err := strconv.Atoi(tok) + if err != nil { + return nil, fmt.Errorf("invalid array index: %s", tok) + } + if i >= len(ch) { + return nil, fmt.Errorf("index %d exceeds array length of %d", i, len(ch)) + } + return ch[i], nil + default: + return nil, fmt.Errorf("invalid JSON pointer: %s", p.String()) + } +} + +const ( + separator = "/" + escapedSeparator = "~1" + tilde = "~" + escapedTilde = "~0" +) + +func unescapeToken(tok string) string { + tok = strings.Replace(tok, escapedSeparator, separator, -1) + return strings.Replace(tok, escapedTilde, tilde, -1) +} + +func escapeToken(tok string) string { + tok = strings.Replace(tok, tilde, escapedTilde, -1) + return strings.Replace(tok, separator, escapedSeparator, -1) +} diff --git a/vendor/github.com/qri-io/jsonpointer/traversal.go b/vendor/github.com/qri-io/jsonpointer/traversal.go new file mode 100644 index 00000000000..c7453bfe17e --- /dev/null +++ b/vendor/github.com/qri-io/jsonpointer/traversal.go @@ -0,0 +1,99 @@ +package jsonpointer + +import ( + "reflect" +) + +// JSONContainer returns any existing child value for a given JSON property string +type JSONContainer interface { + // JSONProp takes a string reference for a given JSON property. + // implementations must return any matching property of that name, + // nil if no such subproperty exists. + // Note that implementations on slice-types are expected to convert + // prop to an integer value + JSONProp(prop string) interface{} +} + +// JSONParent is an interface that enables tree traversal by listing +// all immediate children of an object +type JSONParent interface { + // JSONChildren should return all immidiate children of this element + // with json property names as keys, go types as values + // Note that implementations on slice-types are expected to convert + // integers to string keys + JSONProps() map[string]interface{} +} + +// WalkJSON calls visit on all elements in a tree of decoded json +func WalkJSON(tree interface{}, visit func(elem interface{}) error) error { + if tree == nil { + return nil + } + + if err := visit(tree); err != nil { + return err + } + + if con, ok := tree.(JSONParent); ok { + for _, ch := range con.JSONProps() { + if err := WalkJSON(ch, visit); err != nil { + return err + } + } + return nil + } + + // fast-path for common json types + switch t := tree.(type) { + case map[string]interface{}: + for _, val := range t { + if err := WalkJSON(val, visit); err != nil { + return err + } + } + return nil + case []interface{}: + for _, val := range t { + if err := WalkJSON(val, visit); err != nil { + return err + } + } + return nil + } + + return walkValue(reflect.ValueOf(tree), visit) +} + +func walkValue(v reflect.Value, visit func(elem interface{}) error) error { + switch v.Kind() { + case reflect.Invalid: + return nil + case reflect.Ptr: + if !v.IsNil() { + walkValue(v.Elem(), visit) + } + case reflect.Map: + for _, key := range v.MapKeys() { + mi := v.MapIndex(key) + if mi.CanInterface() { + WalkJSON(mi.Interface(), visit) + } + } + case reflect.Struct: + // t := v.Type() + // TypeOf returns the reflection Type that represents the dynamic type of variable. + // If variable is a nil interface value, TypeOf returns nil. + for i := 0; i < v.NumField(); i++ { + f := v.Field(i) + // fmt.Printf("%d: %s %s %s = %v\n", i, t.Field(i).Name, f.Type(), t.Field(i).Tag.Get("json"), f.CanInterface()) + if f.CanInterface() { + WalkJSON(f.Interface(), visit) + } + } + case reflect.Slice, reflect.Array: + for i := 0; i < v.Len(); i++ { + WalkJSON(v.Index(i).Interface(), visit) + } + } + return nil +} diff --git a/vendor/github.com/qri-io/jsonschema/.gitignore b/vendor/github.com/qri-io/jsonschema/.gitignore new file mode 100644 index 00000000000..c378da30b36 --- /dev/null +++ b/vendor/github.com/qri-io/jsonschema/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +coverage.txt +.vscode \ No newline at end of file diff --git a/vendor/github.com/qri-io/jsonschema/CHANGELOG.md b/vendor/github.com/qri-io/jsonschema/CHANGELOG.md new file mode 100644 index 00000000000..5f38bb2df97 --- /dev/null +++ b/vendor/github.com/qri-io/jsonschema/CHANGELOG.md @@ -0,0 +1,60 @@ +# [](https://github.com/qri-io/jsonschema/compare/v0.1.2...v) (2020-05-21) + +This is relase v0.2.0. It's a rework of the jsonschema implementation which now has better support for the spec, equal or better performance depending on the keyword, possibility to easily extend with your own keywords and finally, draft2019_09 support. + +### Features + +* **jsonschema:** reworking json schema (migration to draft2019_09) ([bb2a1cf](https://github.com/qri-io/jsonschema/commit/bb2a1cf423024a5144c05dcced8f1226fd7e65b9)) + + +# [](https://github.com/qri-io/jsonschema/compare/v0.1.1...v) (2020-05-21) + +This is a patch release of jsonschema to mark v0.1.2. The purpose of it is to provide a stable v0.1 version for managing the dependencies as the upcoming v0.2.0 will break a lot of the existing API. + +### Bug Fixes + +* Typo ([#52](https://github.com/qri-io/jsonschema/issues/52)) ([9f11b79](https://github.com/qri-io/jsonschema/commit/9f11b79125715650da0b4932b3ca66328b508ac7)) + + +### Features + +* **type:** identify custom struct as objects ([c1722b7](https://github.com/qri-io/jsonschema/commit/c1722b720fafa56f0514e08063b5a3c6baa73863)) + + + +# (2019-05-23) + +This is the first proper release of jsonschema. In preparation for go 1.13, in which `go.mod` files and go modules are the primary way to handle go dependencies, we are going to do an official release of all our modules. This will be version v0.1.1 of jsonschema. + + +### Bug Fixes + +* **jsonschema:** Handle empty url fragment "#", add unit tests. ([ca0e82f](https://github.com/qri-io/jsonschema/commit/ca0e82f)) +* An issue where if $id starts with # caused a slice bounds out of range panic while Unmarshaling ([9f6179a](https://github.com/qri-io/jsonschema/commit/9f6179a)) +* **$comment:** add support for $comment keyword, add $comment to testschema_test ExampleBasic() ([#33](https://github.com/qri-io/jsonschema/issues/33)) ([3313399](https://github.com/qri-io/jsonschema/commit/3313399)) +* **const error:** error reports what const must equal instead of supplied value ([9b9427b](https://github.com/qri-io/jsonschema/commit/9b9427b)), closes [#34](https://github.com/qri-io/jsonschema/issues/34) + + +### Features + +* **IfThenElse:** implement If/Then/Else, cleanup ([bef9c1e](https://github.com/qri-io/jsonschema/commit/bef9c1e)) +* **json.Marshaler:** marshal schemas back to json properly. ([f7d8215](https://github.com/qri-io/jsonschema/commit/f7d8215)) +* **jsonschema:** Change to TopLevelType function, more general. ([4a66928](https://github.com/qri-io/jsonschema/commit/4a66928)) +* **jsonschema:** Cleanup mistakes, test for unknown schema type. ([9ab452b](https://github.com/qri-io/jsonschema/commit/9ab452b)) +* **jsonschema:** Field to tell if RootSchema is an array or object. ([8bd68f0](https://github.com/qri-io/jsonschema/commit/8bd68f0)) +* **jsonschema format:** added iri, iri-ref, regex format validators ([06217c5](https://github.com/qri-io/jsonschema/commit/06217c5)) +* **jsonschema format:** added iri, iri-ref, regex format validators ([4e5183a](https://github.com/qri-io/jsonschema/commit/4e5183a)) +* **jsonschema format:** added jsonpointer, reljsonpointer validators ([6205399](https://github.com/qri-io/jsonschema/commit/6205399)) +* **refs:** first signs of life on refs working properly ([435c766](https://github.com/qri-io/jsonschema/commit/435c766)) +* **ValError:** overhaul and upgrade error collection & reporting ([66b03e6](https://github.com/qri-io/jsonschema/commit/66b03e6)) +* added format validators for datetime, date, email, ipv4/6 and some others ([3394369](https://github.com/qri-io/jsonschema/commit/3394369)) +* added format validators for datetime, date, email, ipv4/6 and some others ([5bf895c](https://github.com/qri-io/jsonschema/commit/5bf895c)) +* added Must func for easier schema declaration in Go. ([2874aff](https://github.com/qri-io/jsonschema/commit/2874aff)) +* **jsonschema format:** added jsonpointer, reljsonpointer validators ([d787e78](https://github.com/qri-io/jsonschema/commit/d787e78)) +* first pass of draft7 test suite passing ([263a72d](https://github.com/qri-io/jsonschema/commit/263a72d)) +* initial commit ([b620f19](https://github.com/qri-io/jsonschema/commit/b620f19)) +* initial support for local references ([a99baf2](https://github.com/qri-io/jsonschema/commit/a99baf2)) +* return multiple errors on validation call. ([00b42a8](https://github.com/qri-io/jsonschema/commit/00b42a8)), closes [#15](https://github.com/qri-io/jsonschema/issues/15) + + + diff --git a/vendor/github.com/qri-io/jsonschema/DEVELOPERS.md b/vendor/github.com/qri-io/jsonschema/DEVELOPERS.md new file mode 100644 index 00000000000..1f37c649a1e --- /dev/null +++ b/vendor/github.com/qri-io/jsonschema/DEVELOPERS.md @@ -0,0 +1,125 @@ +# Developing go-jsonschema + +* [Development Setup](#setup) +* [Commit Message Guidelines](#commits) +* [Writing Documentation](#documentation) + +## Development Setup + +This document describes how to set up your development environment to build and test jsonschema + +### Installing Dependencies + +Before you can build jsonschema, you must install and configure the following dependencies on your +machine: + +* [Git](http://git-scm.com/): The [Github Guide to + Installing Git][git-setup] is a good source of information. + +* [The Go Programming Language](https://golang.org): see golang.org to get started + +### Forking jsonschema on Github + +To contribute code to jsonschema, you must have a GitHub account so you can push code to your own +fork of jsonschema and open Pull Requests in the [GitHub Repository][github]. + +To create a Github account, follow the instructions [here](https://github.com/signup/free). +Afterwards, go ahead and [fork](http://help.github.com/forking) the +[jsonschema frontend repository][github]. + +### Building jsonschema + +To build jsonschema, you clone the source code repository and use Yarn to run the electron app: + +```shell +# Clone your Github repository: +git clone https://github.com//jsonschema.git + +# Go to the jsonschema directory: +cd jsonschema + +# Build the qri binary +go install +``` + + +## Git Commit Guidelines + +We have very precise rules over how our git commit messages can be formatted. This leads to **more +readable messages** that are easy to follow when looking through the **project history**. But also, +we use the git commit messages to **generate the Qri change log**. + +### Commit Message Format +Each commit message consists of a **header**, a **body** and a **footer**. The header has a special +format that includes a **type**, a **scope** and a **subject**: + +``` +(): + + + +