From d08875261e97939bfe4718d00cd29258ced0ab0a Mon Sep 17 00:00:00 2001 From: David Subiros Date: Tue, 3 Nov 2020 12:02:26 +0000 Subject: [PATCH 1/8] Added patch struct and validation --- request/patch.go | 72 +++++++++++++++++++++++++++++++++++++++++++ request/patch_test.go | 60 ++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 request/patch.go create mode 100644 request/patch_test.go diff --git a/request/patch.go b/request/patch.go new file mode 100644 index 0000000..8503fd9 --- /dev/null +++ b/request/patch.go @@ -0,0 +1,72 @@ +package request + +import ( + "fmt" +) + +// PatchOp - iota enum of possible patch operations +type PatchOp int + +// Possible patch operations +const ( + OpAdd PatchOp = iota + OpRemove + OpReplace + OpMove + OpCopy + OpTest +) + +var validOps = []string{"add", "remove", "replace", "move", "copy", "test"} + +// ErrInvalidOp is an error returned when a patch contains a wrong 'op' +var ErrInvalidOp = fmt.Errorf("operation is missing or not valid. Please, provide one of the following: %v", validOps) + +// ErrMissingMember generates an error for a missing member +func ErrMissingMember(members []string) error { + return fmt.Errorf("missing member(s) in patch: %v", members) +} + +func (o PatchOp) String() string { + return validOps[o] +} + +// Patch models an HTTP patch operation request, according to RFC 6902 +type Patch struct { + Op string `json:"op"` + Path string `json:"path"` + From string `json:"from"` + Value []string `json:"value"` +} + +// Validate checks that the provided operation is correct and the expected members are provided +func (p *Patch) Validate() error { + missing := []string{} + switch p.Op { + case OpAdd.String(), OpReplace.String(), OpTest.String(): + if p.Path == "" { + missing = append(missing, "path") + } + if p.Value == nil { + missing = append(missing, "value") + } + case OpRemove.String(): + if p.Path == "" { + missing = append(missing, "path") + } + case OpMove.String(), OpCopy.String(): + if p.Path == "" { + missing = append(missing, "path") + } + if p.From == "" { + missing = append(missing, "from") + } + default: + return ErrInvalidOp + } + + if len(missing) > 0 { + return ErrMissingMember(missing) + } + return nil +} diff --git a/request/patch_test.go b/request/patch_test.go new file mode 100644 index 0000000..260a5a4 --- /dev/null +++ b/request/patch_test.go @@ -0,0 +1,60 @@ +package request + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestPatch(t *testing.T) { + + Convey("Validating a valid patch struct is successful", t, func() { + patch := Patch{ + Op: "add", + Path: "/a/b/c", + Value: []string{"foo"}, + } + So(patch.Validate(), ShouldBeNil) + }) + + Convey("Validating a patch struct with an invalid op fails with the expected error", t, func() { + patch := Patch{ + Op: "wrong", + Path: "/a/b/c", + Value: []string{"foo"}, + } + So(patch.Validate(), ShouldResemble, ErrInvalidOp) + }) + + Convey("Validating a patch struct with missing members for an operation results in the expected error being returned", t, func() { + patch := Patch{ + Op: "add", + Path: "/a/b/c", + } + So(patch.Validate(), ShouldResemble, ErrMissingMember([]string{"value"})) + patch = Patch{ + Op: "replace", + Value: []string{"foo"}, + } + So(patch.Validate(), ShouldResemble, ErrMissingMember([]string{"path"})) + patch = Patch{ + Op: "test", + } + So(patch.Validate(), ShouldResemble, ErrMissingMember([]string{"path", "value"})) + patch = Patch{ + Op: "remove", + } + So(patch.Validate(), ShouldResemble, ErrMissingMember([]string{"path"})) + patch = Patch{ + Op: "move", + Path: "/a/b/c", + } + So(patch.Validate(), ShouldResemble, ErrMissingMember([]string{"from"})) + patch = Patch{ + Op: "copy", + From: "/c/b/a", + } + So(patch.Validate(), ShouldResemble, ErrMissingMember([]string{"path"})) + }) + +} From 248bdd245fd6adb0b1c5dc0c306d5ea2638267c6 Mon Sep 17 00:00:00 2001 From: David Subiros Date: Wed, 4 Nov 2020 12:23:29 +0000 Subject: [PATCH 2/8] Added suppoted Ops to patch validation (i.e. not only we can validate generic patch ops, but we now provide a list of supported ops for a specific patch) --- request/patch.go | 25 ++++++++++++++++++++++++- request/patch_test.go | 25 +++++++++++++++++-------- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/request/patch.go b/request/patch.go index 8503fd9..297879e 100644 --- a/request/patch.go +++ b/request/patch.go @@ -27,6 +27,15 @@ func ErrMissingMember(members []string) error { return fmt.Errorf("missing member(s) in patch: %v", members) } +// ErrUnsupportedOp generates an error for unsupported ops +func ErrUnsupportedOp(op string, supportedOps []PatchOp) error { + supported := []string{} + for _, op := range supportedOps { + supported = append(supported, op.String()) + } + return fmt.Errorf("op '%s' not supported. Supported op(s): %v", op, supported) +} + func (o PatchOp) String() string { return validOps[o] } @@ -40,7 +49,7 @@ type Patch struct { } // Validate checks that the provided operation is correct and the expected members are provided -func (p *Patch) Validate() error { +func (p *Patch) Validate(supportedOps ...PatchOp) error { missing := []string{} switch p.Op { case OpAdd.String(), OpReplace.String(), OpTest.String(): @@ -65,8 +74,22 @@ func (p *Patch) Validate() error { return ErrInvalidOp } + if p.isOpSupported(supportedOps) == false { + return ErrUnsupportedOp(p.Op, supportedOps) + } + if len(missing) > 0 { return ErrMissingMember(missing) } return nil } + +// isOpSupported checks that the patch op is in the provided list of supported Ops +func (p *Patch) isOpSupported(supportedOps []PatchOp) bool { + for _, op := range supportedOps { + if p.Op == op.String() { + return true + } + } + return false +} diff --git a/request/patch_test.go b/request/patch_test.go index 260a5a4..0031bef 100644 --- a/request/patch_test.go +++ b/request/patch_test.go @@ -8,13 +8,13 @@ import ( func TestPatch(t *testing.T) { - Convey("Validating a valid patch struct is successful", t, func() { + Convey("Validating a valid patch with a supported op is successful", t, func() { patch := Patch{ Op: "add", Path: "/a/b/c", Value: []string{"foo"}, } - So(patch.Validate(), ShouldBeNil) + So(patch.Validate(OpAdd), ShouldBeNil) }) Convey("Validating a patch struct with an invalid op fails with the expected error", t, func() { @@ -26,35 +26,44 @@ func TestPatch(t *testing.T) { So(patch.Validate(), ShouldResemble, ErrInvalidOp) }) + Convey("Validating a valid patch with an unsupported op fails with the expected error", t, func() { + patch := Patch{ + Op: "add", + Path: "/a/b/c", + Value: []string{"foo"}, + } + So(patch.Validate(OpRemove), ShouldResemble, ErrUnsupportedOp("add", []PatchOp{OpRemove})) + }) + Convey("Validating a patch struct with missing members for an operation results in the expected error being returned", t, func() { patch := Patch{ Op: "add", Path: "/a/b/c", } - So(patch.Validate(), ShouldResemble, ErrMissingMember([]string{"value"})) + So(patch.Validate(OpAdd), ShouldResemble, ErrMissingMember([]string{"value"})) patch = Patch{ Op: "replace", Value: []string{"foo"}, } - So(patch.Validate(), ShouldResemble, ErrMissingMember([]string{"path"})) + So(patch.Validate(OpReplace), ShouldResemble, ErrMissingMember([]string{"path"})) patch = Patch{ Op: "test", } - So(patch.Validate(), ShouldResemble, ErrMissingMember([]string{"path", "value"})) + So(patch.Validate(OpTest), ShouldResemble, ErrMissingMember([]string{"path", "value"})) patch = Patch{ Op: "remove", } - So(patch.Validate(), ShouldResemble, ErrMissingMember([]string{"path"})) + So(patch.Validate(OpRemove), ShouldResemble, ErrMissingMember([]string{"path"})) patch = Patch{ Op: "move", Path: "/a/b/c", } - So(patch.Validate(), ShouldResemble, ErrMissingMember([]string{"from"})) + So(patch.Validate(OpMove), ShouldResemble, ErrMissingMember([]string{"from"})) patch = Patch{ Op: "copy", From: "/c/b/a", } - So(patch.Validate(), ShouldResemble, ErrMissingMember([]string{"path"})) + So(patch.Validate(OpCopy), ShouldResemble, ErrMissingMember([]string{"path"})) }) } From 90e85e2c348859c1329d4c4aa55e50e800051499 Mon Sep 17 00:00:00 2001 From: red Date: Mon, 7 Dec 2020 15:41:30 +0000 Subject: [PATCH 3/8] Remove redundant log.Event() Remove log.Event() as agreed with Eleanor. --- handlers/authentication.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/handlers/authentication.go b/handlers/authentication.go index bfc5cbe..437ae0d 100644 --- a/handlers/authentication.go +++ b/handlers/authentication.go @@ -27,8 +27,6 @@ func CheckIdentity(handle func(http.ResponseWriter, *http.Request)) http.Handler vars := mux.Vars(r) logData := getLogData(ctx, r.URL.EscapedPath(), vars) - log.Event(ctx, "checking for an identity in request context", log.HTTP(r, 0, 0, nil, nil), logData) - // just checking if an identity exists until permissions are being provided. if !request.IsCallerPresent(ctx) { log.Event(ctx, "no identity found in context of request", log.HTTP(r, 0, 0, nil, nil), logData) From 751c612178367d41a212a4b85980011bd2af56d7 Mon Sep 17 00:00:00 2001 From: David Subiros Date: Tue, 2 Mar 2021 17:01:00 +0000 Subject: [PATCH 4/8] made patch value a generic interface{}. The type will need to be validated by the caller according to what they need --- request/patch.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/request/patch.go b/request/patch.go index 297879e..2536ae3 100644 --- a/request/patch.go +++ b/request/patch.go @@ -42,10 +42,10 @@ func (o PatchOp) String() string { // Patch models an HTTP patch operation request, according to RFC 6902 type Patch struct { - Op string `json:"op"` - Path string `json:"path"` - From string `json:"from"` - Value []string `json:"value"` + Op string `json:"op"` + Path string `json:"path"` + From string `json:"from"` + Value interface{} `json:"value"` } // Validate checks that the provided operation is correct and the expected members are provided From 124b267e41c21a0d1cb7c479113c1463a82b9840 Mon Sep 17 00:00:00 2001 From: David Subiros Date: Wed, 3 Mar 2021 11:57:22 +0000 Subject: [PATCH 5/8] fixed server tests to use mocks instead of listening for real connections --- http/server.go | 22 +++- http/server_test.go | 272 +++++++++++++++++++++++++++----------------- 2 files changed, 184 insertions(+), 110 deletions(-) diff --git a/http/server.go b/http/server.go index d90b660..5aee06c 100644 --- a/http/server.go +++ b/http/server.go @@ -101,17 +101,17 @@ func (s *Server) Shutdown(ctx context.Context) error { ctx, _ = context.WithTimeout(context.Background(), s.DefaultShutdownTimeout) } - return s.Server.Shutdown(ctx) + return doShutdown(ctx, &s.Server) } func (s *Server) listenAndServe() error { s.prep() if len(s.CertFile) > 0 || len(s.KeyFile) > 0 { - return s.Server.ListenAndServeTLS(s.CertFile, s.KeyFile) + return doListenAndServeTLS(&s.Server, s.CertFile, s.KeyFile) } - return s.Server.ListenAndServe() + return doListenAndServe(&s.Server) } func (s *Server) listenAndServeHandleOSSignals() error { @@ -131,17 +131,29 @@ func (s *Server) listenAndServeAsync() { s.prep() if len(s.CertFile) > 0 || len(s.KeyFile) > 0 { go func() { - if err := s.Server.ListenAndServeTLS(s.CertFile, s.KeyFile); err != nil { + if err := doListenAndServeTLS(&s.Server, s.CertFile, s.KeyFile); err != nil { log.Event(nil, "http server returned error", log.Error(err)) os.Exit(1) } }() } else { go func() { - if err := s.Server.ListenAndServe(); err != nil { + if err := doListenAndServe(&s.Server); err != nil { log.Event(nil, "http server returned error", log.Error(err)) os.Exit(1) } }() } } + +var doListenAndServe = func(httpServer *http.Server) error { + return httpServer.ListenAndServe() +} + +var doListenAndServeTLS = func(httpServer *http.Server, certFile, keyFile string) error { + return httpServer.ListenAndServeTLS(certFile, keyFile) +} + +var doShutdown = func(ctx context.Context, httpServer *http.Server) error { + return httpServer.Shutdown(ctx) +} diff --git a/http/server_test.go b/http/server_test.go index ccdda5c..717ceab 100644 --- a/http/server_test.go +++ b/http/server_test.go @@ -1,151 +1,213 @@ package http import ( - "fmt" + "errors" "net/http" + "sync" "testing" "time" - "github.com/facebookgo/freeport" . "github.com/smartystreets/goconvey/convey" + "golang.org/x/net/context" ) -func newWithPort(h http.Handler) (string, *Server) { - port, err := freeport.Get() - So(err, ShouldBeNil) - So(port, ShouldBeGreaterThan, 0) - - sPort := fmt.Sprintf(":%d", port) +// listenAndServeTLSCalls keeps track of a listenAndServeTLS call +type listenAndServeTLSCalls struct { + httpServer *http.Server + certFile string + keyFile string +} - return sPort, NewServer(sPort, h) +// listenAndServeCalls keeps track of a listenAndServe call +type listenAndServeCalls struct { + httpServer *http.Server } func TestNew(t *testing.T) { - Convey("New should return a new server with sensible defaults", t, func() { - h := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {}) - s := NewServer(":0", h) - - So(s, ShouldNotBeNil) - So(s.Handler, ShouldEqual, h) - So(s.Alice, ShouldBeNil) - So(s.Addr, ShouldEqual, ":0") - So(s.MaxHeaderBytes, ShouldEqual, 0) - - Convey("TLS should not be configured by default", func() { - So(s.CertFile, ShouldBeEmpty) - So(s.KeyFile, ShouldBeEmpty) - }) - Convey("Default middleware should include RequestID and Log", func() { - So(s.middleware, ShouldContainKey, RequestIDHandlerKey) - So(s.middleware, ShouldContainKey, LogHandlerKey) - So(s.middlewareOrder, ShouldResemble, []string{RequestIDHandlerKey, LogHandlerKey}) - }) + Convey("Given mocked network calls", t, func() { - Convey("Default timeouts should be sensible", func() { - So(s.ReadTimeout, ShouldEqual, time.Second*5) - So(s.WriteTimeout, ShouldEqual, time.Second*10) - So(s.ReadHeaderTimeout, ShouldEqual, 0) - So(s.IdleTimeout, ShouldEqual, 0) - }) + doListenAndServe = func(httpServer *http.Server) error { + return errors.New("unexpected ListenAndServe call") + } - Convey("Handle OS signals by default", func() { - So(s.HandleOSSignals, ShouldEqual, true) - }) + doListenAndServeTLS = func(httpServer *http.Server, certFile, keyFile string) error { + return errors.New("unexpected ListenAndServeTLS call") - Convey("A default shutdown context is initialised", func() { - So(s.DefaultShutdownTimeout, ShouldEqual, 10*time.Second) - }) - }) + } - Convey("prep should prepare the server correctly", t, func() { - Convey("prep should create a valid Server instance", func() { - h := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {}) - s := NewServer(":0", h) + doShutdown = func(ctx context.Context, httpServer *http.Server) error { + return errors.New("unexpected Shutdown call") - s.prep() - So(s.Server.Addr, ShouldEqual, ":0") - }) + } - Convey("invalid middleware should panic", func() { + Convey("New should return a new server with sensible defaults", func() { h := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {}) s := NewServer(":0", h) - s.middlewareOrder = []string{"foo"} + So(s, ShouldNotBeNil) + So(s.Handler, ShouldEqual, h) + So(s.Alice, ShouldBeNil) + So(s.Addr, ShouldEqual, ":0") + So(s.MaxHeaderBytes, ShouldEqual, 0) + + Convey("TLS should not be configured by default", func() { + So(s.CertFile, ShouldBeEmpty) + So(s.KeyFile, ShouldBeEmpty) + }) + + Convey("Default middleware should include RequestID and Log", func() { + So(s.middleware, ShouldContainKey, RequestIDHandlerKey) + So(s.middleware, ShouldContainKey, LogHandlerKey) + So(s.middlewareOrder, ShouldResemble, []string{RequestIDHandlerKey, LogHandlerKey}) + }) + + Convey("Default timeouts should be sensible", func() { + So(s.ReadTimeout, ShouldEqual, time.Second*5) + So(s.WriteTimeout, ShouldEqual, time.Second*10) + So(s.ReadHeaderTimeout, ShouldEqual, 0) + So(s.IdleTimeout, ShouldEqual, 0) + }) + + Convey("Handle OS signals by default", func() { + So(s.HandleOSSignals, ShouldEqual, true) + }) + + Convey("A default shutdown context is initialised", func() { + So(s.DefaultShutdownTimeout, ShouldEqual, 10*time.Second) + }) + }) + + Convey("prep should prepare the server correctly", func() { + Convey("prep should create a valid Server instance", func() { + h := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {}) + s := NewServer(":0", h) - So(func() { s.prep() - }, ShouldPanicWith, "middleware not found: foo") - }) + So(s.Server.Addr, ShouldEqual, ":0") + }) - Convey("ListenAndServe with invalid middleware should panic", func() { - h := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {}) - s := NewServer(":0", h) + Convey("invalid middleware should panic", func() { + h := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {}) + s := NewServer(":0", h) - s.middlewareOrder = []string{"foo"} + s.middlewareOrder = []string{"foo"} - So(func() { - s.ListenAndServe() - }, ShouldPanicWith, "middleware not found: foo") - }) + So(func() { + s.prep() + }, ShouldPanicWith, "middleware not found: foo") + }) - Convey("ListenAndServeTLS with invalid middleware should panic", func() { - h := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {}) - s := NewServer(":0", h) + Convey("ListenAndServe with invalid middleware should panic", func() { + h := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {}) + s := NewServer(":0", h) - s.middlewareOrder = []string{"foo"} + s.middlewareOrder = []string{"foo"} - So(func() { - s.ListenAndServeTLS("testdata/certFile", "testdata/keyFile") - }, ShouldPanicWith, "middleware not found: foo") - }) - }) + So(func() { + s.ListenAndServe() + }, ShouldPanicWith, "middleware not found: foo") + }) - Convey("ListenAndServeTLS", t, func() { - Convey("ListenAndServeTLS should set CertFile/KeyFile", func() { - h := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {}) + Convey("ListenAndServeTLS with invalid middleware should panic", func() { + h := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {}) + s := NewServer(":0", h) - sPort, s := newWithPort(h) - go func() { - s.ListenAndServeTLS("testdata/certFile", "testdata/keyFile") - }() - http.Get("http://localhost" + sPort) // ensure above is responding before we check below - So(s.CertFile, ShouldEqual, "testdata/certFile") - So(s.KeyFile, ShouldEqual, "testdata/keyFile") - }) + s.middlewareOrder = []string{"foo"} - Convey("ListenAndServeTLS with only CertFile should panic", func() { - h := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {}) - s := NewServer(":0", h) + So(func() { + s.ListenAndServeTLS("testdata/certFile", "testdata/keyFile") + }, ShouldPanicWith, "middleware not found: foo") + }) + }) - So(func() { - s.ListenAndServeTLS("certFile", "") - }, ShouldPanicWith, "either CertFile/KeyFile must be blank, or both provided") + Convey("ListenAndServeTLS", func() { + Convey("ListenAndServeTLS should set CertFile/KeyFile", func() { + wg := &sync.WaitGroup{} + calls := []listenAndServeTLSCalls{} + doListenAndServeTLS = func(httpServer *http.Server, certFile, keyFile string) error { + defer wg.Done() + calls = append(calls, listenAndServeTLSCalls{ + httpServer: httpServer, + certFile: certFile, + keyFile: keyFile}) + return nil + } + + h := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {}) + s := NewServer(":0", h) + + // execute ListenAndServer and wait for it to finish + wg.Add(1) + go func() { + s.ListenAndServeTLS("testdata/certFile", "testdata/keyFile") + }() + wg.Wait() + + So(s.CertFile, ShouldEqual, "testdata/certFile") + So(s.KeyFile, ShouldEqual, "testdata/keyFile") + So(calls, ShouldHaveLength, 1) + So(calls[0].httpServer, ShouldNotBeNil) + So(calls[0].certFile, ShouldEqual, "testdata/certFile") + So(calls[0].keyFile, ShouldEqual, "testdata/keyFile") + }) + + Convey("ListenAndServeTLS with only CertFile should panic", func() { + h := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {}) + s := NewServer(":0", h) + + So(func() { + s.ListenAndServeTLS("certFile", "") + }, ShouldPanicWith, "either CertFile/KeyFile must be blank, or both provided") + }) + + Convey("ListenAndServeTLS with only KeyFile should panic", func() { + h := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {}) + s := NewServer(":0", h) + + So(func() { + s.ListenAndServeTLS("", "keyFile") + }, ShouldPanicWith, "either CertFile/KeyFile must be blank, or both provided") + }) }) - Convey("ListenAndServeTLS with only KeyFile should panic", func() { + Convey("Given a mocked ListenAndServe", func() { + wg := &sync.WaitGroup{} + calls := []listenAndServeCalls{} + doListenAndServe = func(httpServer *http.Server) error { + defer wg.Done() + calls = append(calls, listenAndServeCalls{httpServer: httpServer}) + return nil + } + h := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {}) s := NewServer(":0", h) - So(func() { - s.ListenAndServeTLS("", "keyFile") - }, ShouldPanicWith, "either CertFile/KeyFile must be blank, or both provided") - }) - }) + Convey("then ListenAndServe starts a working HTTP server", func() { + So(s.HandleOSSignals, ShouldBeTrue) + + wg.Add(1) + go func() { + s.ListenAndServe() + }() + wg.Wait() - Convey("ListenAndServe starts a working HTTP server", t, func() { - h := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {}) + So(calls, ShouldHaveLength, 1) + So(calls[0].httpServer, ShouldNotBeNil) + }) - sPort, s := newWithPort(h) - go func() { - s.ListenAndServe() - }() - time.Sleep(time.Millisecond * 20) + Convey("then if HandleOSSignals is disabled, ListenAndServe starts a working HTTP server", func() { + s.HandleOSSignals = false - res, err := http.Get("http://localhost" + sPort) - So(err, ShouldBeNil) - So(res, ShouldNotBeNil) - res.Body.Close() - So(res.StatusCode, ShouldEqual, 200) + wg.Add(1) + go func() { + s.ListenAndServe() + }() + wg.Wait() + + So(calls, ShouldHaveLength, 1) + So(calls[0].httpServer, ShouldNotBeNil) + }) + }) }) } From ce4e050d9fd2dcff4e9d5c0299d2d541cc1dd705 Mon Sep 17 00:00:00 2001 From: David Subiros Date: Wed, 3 Mar 2021 12:02:33 +0000 Subject: [PATCH 6/8] added extra patch test to validate a different value type --- request/patch_test.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/request/patch_test.go b/request/patch_test.go index 0031bef..a93f1fb 100644 --- a/request/patch_test.go +++ b/request/patch_test.go @@ -8,7 +8,7 @@ import ( func TestPatch(t *testing.T) { - Convey("Validating a valid patch with a supported op is successful", t, func() { + Convey("Validating a valid patch with a supported op and array of strings value is successful", t, func() { patch := Patch{ Op: "add", Path: "/a/b/c", @@ -17,6 +17,15 @@ func TestPatch(t *testing.T) { So(patch.Validate(OpAdd), ShouldBeNil) }) + Convey("Validating a valid patch with a supported op and float64 value is successful", t, func() { + patch := Patch{ + Op: "add", + Path: "/a/b/c", + Value: float64(123.321), + } + So(patch.Validate(OpAdd), ShouldBeNil) + }) + Convey("Validating a patch struct with an invalid op fails with the expected error", t, func() { patch := Patch{ Op: "wrong", From a99849fbbe17780f28b5cf17ea100aad3ec20b0b Mon Sep 17 00:00:00 2001 From: Geraint Edwards Date: Wed, 11 Aug 2021 07:50:12 +0100 Subject: [PATCH 7/8] update dp-api-clients-go --- go.mod | 3 +-- go.sum | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 05d9497..94bf506 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,8 @@ module github.com/ONSdigital/dp-net go 1.13 require ( - github.com/ONSdigital/dp-api-clients-go v1.34.3 + github.com/ONSdigital/dp-api-clients-go v1.41.1 github.com/ONSdigital/log.go/v2 v2.0.5 - github.com/facebookgo/freeport v0.0.0-20150612182905-d4adf43b75b9 github.com/gorilla/mux v1.8.0 github.com/justinas/alice v1.2.0 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 9d4ab15..998fd6d 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,7 @@ github.com/ONSdigital/dp-api-clients-go v1.28.0/go.mod h1:iyJy6uRL4B6OYOJA0XMr5UHt6+Q8XmN9uwmURO+9Oj4= -github.com/ONSdigital/dp-api-clients-go v1.34.3 h1:nS3ZG3Eql9T68Q0IFehpnqkUy2AFoTDktVgaD90Yj+s= github.com/ONSdigital/dp-api-clients-go v1.34.3/go.mod h1:kX+YKuoLYLfkeLHMvQKRRydZVxO7ZEYyYiwG2xhV51E= +github.com/ONSdigital/dp-api-clients-go v1.41.1 h1:xkeT6dCTFSAoBpZxgiJUiuqgcfjCX+c52CIiZo1Y2iU= +github.com/ONSdigital/dp-api-clients-go v1.41.1/go.mod h1:Ga1+ANjviu21NFJI9wp5NctJIdB4TJLDGbpQFl2V8Wc= github.com/ONSdigital/dp-healthcheck v1.0.5 h1:DXnohGIqXaLLeYGdaGOhgkZjAbWMNoLAjQ3EgZeMT3M= github.com/ONSdigital/dp-healthcheck v1.0.5/go.mod h1:2wbVAUHMl9+4tWhUlxYUuA1dnf2+NrwzC+So5f5BMLk= github.com/ONSdigital/dp-mocking v0.0.0-20190905163309-fee2702ad1b9 h1:+WXVfTDyWXY1DQRDFSmt1b/ORKk5c7jGiPu7NoeaM/0= @@ -16,9 +17,9 @@ github.com/ONSdigital/log.go v1.0.1-0.20200805084515-ee61165ea36a/go.mod h1:dDnQ github.com/ONSdigital/log.go v1.0.1-0.20200805145532-1f25087a0744/go.mod h1:y4E9MYC+cV9VfjRD0UBGj8PA7H3wABqQi87/ejrDhYc= github.com/ONSdigital/log.go v1.0.1 h1:SZ5wRZAwlt2jQUZ9AUzBB/PL+iG15KapfQpJUdA18/4= github.com/ONSdigital/log.go v1.0.1/go.mod h1:dIwSXuvFB5EsZG5x44JhsXZKMd80zlb0DZxmiAtpL4M= +github.com/ONSdigital/log.go/v2 v2.0.0/go.mod h1:PR7vXrv9dZKUc7SI/0toxBbStk84snmybBnWpe+xY2o= github.com/ONSdigital/log.go/v2 v2.0.5 h1:kl2lF0vr3BQDwPTAUcarDvX4Um3uE3fjVRfLoHAarBc= github.com/ONSdigital/log.go/v2 v2.0.5/go.mod h1:PR7vXrv9dZKUc7SI/0toxBbStk84snmybBnWpe+xY2o= -github.com/facebookgo/freeport v0.0.0-20150612182905-d4adf43b75b9 h1:wWke/RUCl7VRjQhwPlR/v0glZXNYzBHdNUzf/Am2Nmg= github.com/facebookgo/freeport v0.0.0-20150612182905-d4adf43b75b9/go.mod h1:uPmAp6Sws4L7+Q/OokbWDAK1ibXYhB3PXFP1kol5hPg= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= From 7c0439fa5efffc49ea152b8899bb396424bafd9a Mon Sep 17 00:00:00 2001 From: Geraint Edwards Date: Wed, 11 Aug 2021 07:56:50 +0100 Subject: [PATCH 8/8] update go mods --- go.mod | 7 ++++++- go.sum | 35 +++++++++++++++++++++++++++-------- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 94bf506..1b15df1 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,15 @@ go 1.13 require ( github.com/ONSdigital/dp-api-clients-go v1.41.1 + github.com/ONSdigital/dp-healthcheck v1.1.0 // indirect + github.com/ONSdigital/log.go v1.1.0 // indirect github.com/ONSdigital/log.go/v2 v2.0.5 + github.com/fatih/color v1.12.0 // indirect github.com/gorilla/mux v1.8.0 github.com/justinas/alice v1.2.0 + github.com/mattn/go-isatty v0.0.13 // indirect github.com/pkg/errors v0.9.1 github.com/smartystreets/goconvey v1.6.4 - golang.org/x/net v0.0.0-20201110031124-69a78807bb2b + golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d + golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e // indirect ) diff --git a/go.sum b/go.sum index 998fd6d..ff79211 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,9 @@ github.com/ONSdigital/dp-api-clients-go v1.28.0/go.mod h1:iyJy6uRL4B6OYOJA0XMr5U github.com/ONSdigital/dp-api-clients-go v1.34.3/go.mod h1:kX+YKuoLYLfkeLHMvQKRRydZVxO7ZEYyYiwG2xhV51E= github.com/ONSdigital/dp-api-clients-go v1.41.1 h1:xkeT6dCTFSAoBpZxgiJUiuqgcfjCX+c52CIiZo1Y2iU= github.com/ONSdigital/dp-api-clients-go v1.41.1/go.mod h1:Ga1+ANjviu21NFJI9wp5NctJIdB4TJLDGbpQFl2V8Wc= -github.com/ONSdigital/dp-healthcheck v1.0.5 h1:DXnohGIqXaLLeYGdaGOhgkZjAbWMNoLAjQ3EgZeMT3M= github.com/ONSdigital/dp-healthcheck v1.0.5/go.mod h1:2wbVAUHMl9+4tWhUlxYUuA1dnf2+NrwzC+So5f5BMLk= +github.com/ONSdigital/dp-healthcheck v1.1.0 h1:fKOf8MMe8l4EW28ljX0wNZ5oZTgx/slAs7lyEx4eq2c= +github.com/ONSdigital/dp-healthcheck v1.1.0/go.mod h1:vZwyjMJiCHjp/sJ2R1ZEqzZT0rJ0+uHVGwxqdP4J5vg= github.com/ONSdigital/dp-mocking v0.0.0-20190905163309-fee2702ad1b9 h1:+WXVfTDyWXY1DQRDFSmt1b/ORKk5c7jGiPu7NoeaM/0= github.com/ONSdigital/dp-mocking v0.0.0-20190905163309-fee2702ad1b9/go.mod h1:BcIRgitUju//qgNePRBmNjATarTtynAgc0yV29VpLEk= github.com/ONSdigital/dp-net v1.0.5-0.20200805082802-e518bc287596/go.mod h1:wDVhk2pYosQ1q6PXxuFIRYhYk2XX5+1CeRRnXpSczPY= @@ -15,18 +16,23 @@ github.com/ONSdigital/go-ns v0.0.0-20191104121206-f144c4ec2e58/go.mod h1:iWos35i github.com/ONSdigital/log.go v1.0.0/go.mod h1:UnGu9Q14gNC+kz0DOkdnLYGoqugCvnokHBRBxFRpVoQ= github.com/ONSdigital/log.go v1.0.1-0.20200805084515-ee61165ea36a/go.mod h1:dDnQATFXCBOknvj6ZQuKfmDhbOWf3e8mtV+dPEfWJqs= github.com/ONSdigital/log.go v1.0.1-0.20200805145532-1f25087a0744/go.mod h1:y4E9MYC+cV9VfjRD0UBGj8PA7H3wABqQi87/ejrDhYc= -github.com/ONSdigital/log.go v1.0.1 h1:SZ5wRZAwlt2jQUZ9AUzBB/PL+iG15KapfQpJUdA18/4= github.com/ONSdigital/log.go v1.0.1/go.mod h1:dIwSXuvFB5EsZG5x44JhsXZKMd80zlb0DZxmiAtpL4M= +github.com/ONSdigital/log.go v1.1.0 h1:XFE8U5lPeiXyujgUtbh+pKCotiICeIGFEAauNk9c24A= +github.com/ONSdigital/log.go v1.1.0/go.mod h1:0hOVuYR3bDUI30VRo48d5KHfJIoe+spuPXqgt6UF78o= github.com/ONSdigital/log.go/v2 v2.0.0/go.mod h1:PR7vXrv9dZKUc7SI/0toxBbStk84snmybBnWpe+xY2o= github.com/ONSdigital/log.go/v2 v2.0.5 h1:kl2lF0vr3BQDwPTAUcarDvX4Um3uE3fjVRfLoHAarBc= github.com/ONSdigital/log.go/v2 v2.0.5/go.mod h1:PR7vXrv9dZKUc7SI/0toxBbStk84snmybBnWpe+xY2o= github.com/facebookgo/freeport v0.0.0-20150612182905-d4adf43b75b9/go.mod h1:uPmAp6Sws4L7+Q/OokbWDAK1ibXYhB3PXFP1kol5hPg= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= +github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20210202160940-bed99a852dfe h1:rcf1P0fm+1l0EjG16p06mYLj9gW9X36KgdHJ/88hS4g= +github.com/gopherjs/gopherjs v0.0.0-20210202160940-bed99a852dfe/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/hokaccha/go-prettyjson v0.0.0-20190818114111-108c894c2c0e/go.mod h1:pFlLw2CfqZiIBOx6BuCeRLCrfxBJipTY0nIOF/VbGcI= @@ -41,24 +47,29 @@ github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= +github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= +github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -68,10 +79,18 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210414055047-fe65e336abe0 h1:g9s1Ppvvun/fI+BptTMj909BBIcGrzQ32k9FNlcevOE= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210414055047-fe65e336abe0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=