mirror of https://github.com/knative/pkg.git
Bump assorted dependencies (#2140)
This commit is contained in:
parent
a2869ae1dc
commit
0e61d6763d
|
@ -19,8 +19,8 @@ package duck
|
|||
import (
|
||||
"encoding/json"
|
||||
|
||||
jsonmergepatch "github.com/evanphx/json-patch/v5"
|
||||
jsonpatch "gomodules.xyz/jsonpatch/v2"
|
||||
jsonmergepatch "gopkg.in/evanphx/json-patch.v4"
|
||||
)
|
||||
|
||||
func marshallBeforeAfter(before, after interface{}) ([]byte, []byte, error) {
|
||||
|
|
7
go.mod
7
go.mod
|
@ -14,6 +14,7 @@ require (
|
|||
github.com/census-instrumentation/opencensus-proto v0.3.0
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/dgryski/go-gk v0.0.0-20200319235926-a69029f61654 // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.5.0
|
||||
github.com/go-openapi/spec v0.19.6 // indirect
|
||||
github.com/go-openapi/swag v0.19.7 // indirect
|
||||
github.com/gobuffalo/flect v0.2.2
|
||||
|
@ -43,22 +44,20 @@ require (
|
|||
go.opencensus.io v0.23.0
|
||||
go.uber.org/atomic v1.7.0
|
||||
go.uber.org/automaxprocs v1.4.0
|
||||
go.uber.org/zap v1.16.0
|
||||
go.uber.org/zap v1.17.0
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||
golang.org/x/tools v0.1.1
|
||||
golang.org/x/tools v0.1.2
|
||||
gomodules.xyz/jsonpatch/v2 v2.2.0
|
||||
google.golang.org/api v0.36.0
|
||||
google.golang.org/genproto v0.0.0-20210416161957-9910b6c460de
|
||||
google.golang.org/grpc v1.38.0
|
||||
google.golang.org/protobuf v1.26.0
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.9.0
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
honnef.co/go/tools v0.0.1-2020.1.5 // indirect
|
||||
k8s.io/api v0.19.7
|
||||
k8s.io/apiextensions-apiserver v0.19.7
|
||||
k8s.io/apimachinery v0.19.7
|
||||
|
|
24
go.sum
24
go.sum
|
@ -55,7 +55,6 @@ github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxB
|
|||
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
|
||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
|
@ -175,6 +174,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
|
|||
github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
|
||||
github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses=
|
||||
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch/v5 v5.5.0 h1:bAmFiUJ+o0o2B4OiTFeE3MqCOtyo+jjPP9iZ0VRxYUc=
|
||||
github.com/evanphx/json-patch/v5 v5.5.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
||||
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
||||
|
@ -618,8 +619,9 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
|||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
|
@ -658,21 +660,19 @@ go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
|||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/automaxprocs v1.4.0 h1:CpDZl6aOlLhReez+8S3eEotD7Jx0Os++lemPlMULQP0=
|
||||
go.uber.org/automaxprocs v1.4.0/go.mod h1:/mTEdr7LvHhs0v7mjdxDreTz1OG5zdZGqgOnhWiR/+Q=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
|
||||
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
|
||||
go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=
|
||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
|
@ -930,8 +930,8 @@ golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82u
|
|||
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.1 h1:wGiQel/hW0NnEkJUk8lbzkX2gFJU6PFxf1v5OlCfuOs=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -1056,8 +1056,6 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8X
|
|||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.9.0 h1:T7W7A7+DTEpLTC11pkf8yfaeRfqhRj/gOPf+LtaJdNY=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.9.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
|
@ -1090,8 +1088,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
|||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.5 h1:nI5egYTGJakVyOryqLs1cQO5dO0ksin5XXs2pspk75k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.5/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
k8s.io/api v0.19.7 h1:MpHhls03C2pyzoYcpbe4QqYiiZjdvW+tuWq6TbjV14Y=
|
||||
k8s.io/api v0.19.7/go.mod h1:KTryDUT3l6Mtv7K2J2486PNL9DBns3wOYTkGR+iz63Y=
|
||||
k8s.io/apiextensions-apiserver v0.19.7 h1:aV9DANMSCCYBEMbtoT/5oesrtcciQrjy9yqWVtZZL5A=
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
module github.com/evanphx/json-patch/v5
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/jessevdk/go-flags v1.4.0
|
||||
github.com/pkg/errors v0.8.1
|
||||
)
|
|
@ -0,0 +1,4 @@
|
|||
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
@ -27,21 +27,33 @@ func merge(cur, patch *lazyNode, mergeMerge bool) *lazyNode {
|
|||
}
|
||||
|
||||
func mergeDocs(doc, patch *partialDoc, mergeMerge bool) {
|
||||
for k, v := range *patch {
|
||||
for k, v := range patch.obj {
|
||||
if v == nil {
|
||||
if mergeMerge {
|
||||
(*doc)[k] = nil
|
||||
idx := -1
|
||||
for i, key := range doc.keys {
|
||||
if key == k {
|
||||
idx = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if idx == -1 {
|
||||
doc.keys = append(doc.keys, k)
|
||||
}
|
||||
doc.obj[k] = nil
|
||||
} else {
|
||||
delete(*doc, k)
|
||||
_ = doc.remove(k, &ApplyOptions{})
|
||||
}
|
||||
} else {
|
||||
cur, ok := (*doc)[k]
|
||||
cur, ok := doc.obj[k]
|
||||
|
||||
if !ok || cur == nil {
|
||||
if !mergeMerge {
|
||||
pruneNulls(v)
|
||||
(*doc)[k] = v
|
||||
}
|
||||
_ = doc.set(k, v, &ApplyOptions{})
|
||||
} else {
|
||||
(*doc)[k] = merge(cur, v, mergeMerge)
|
||||
_ = doc.set(k, merge(cur, v, mergeMerge), &ApplyOptions{})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -62,9 +74,9 @@ func pruneNulls(n *lazyNode) {
|
|||
}
|
||||
|
||||
func pruneDocNulls(doc *partialDoc) *partialDoc {
|
||||
for k, v := range *doc {
|
||||
for k, v := range doc.obj {
|
||||
if v == nil {
|
||||
delete(*doc, k)
|
||||
_ = doc.remove(k, &ApplyOptions{})
|
||||
} else {
|
||||
pruneNulls(v)
|
||||
}
|
||||
|
@ -79,8 +91,8 @@ func pruneAryNulls(ary *partialArray) *partialArray {
|
|||
for _, v := range *ary {
|
||||
if v != nil {
|
||||
pruneNulls(v)
|
||||
newAry = append(newAry, v)
|
||||
}
|
||||
newAry = append(newAry, v)
|
||||
}
|
||||
|
||||
*ary = newAry
|
||||
|
@ -113,19 +125,19 @@ func doMergePatch(docData, patchData []byte, mergeMerge bool) ([]byte, error) {
|
|||
|
||||
patchErr := json.Unmarshal(patchData, patch)
|
||||
|
||||
if _, ok := docErr.(*json.SyntaxError); ok {
|
||||
if isSyntaxError(docErr) {
|
||||
return nil, errBadJSONDoc
|
||||
}
|
||||
|
||||
if _, ok := patchErr.(*json.SyntaxError); ok {
|
||||
if isSyntaxError(patchErr) {
|
||||
return nil, errBadJSONPatch
|
||||
}
|
||||
|
||||
if docErr == nil && *doc == nil {
|
||||
if docErr == nil && doc.obj == nil {
|
||||
return nil, errBadJSONDoc
|
||||
}
|
||||
|
||||
if patchErr == nil && *patch == nil {
|
||||
if patchErr == nil && patch.obj == nil {
|
||||
return nil, errBadJSONPatch
|
||||
}
|
||||
|
||||
|
@ -162,6 +174,16 @@ func doMergePatch(docData, patchData []byte, mergeMerge bool) ([]byte, error) {
|
|||
return json.Marshal(doc)
|
||||
}
|
||||
|
||||
func isSyntaxError(err error) bool {
|
||||
if _, ok := err.(*json.SyntaxError); ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := err.(*syntaxError); ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// resemblesJSONArray indicates whether the byte-slice "appears" to be
|
||||
// a JSON array or not.
|
||||
// False-positives are possible, as this function does not check the internal
|
|
@ -24,6 +24,10 @@ var (
|
|||
// AccumulatedCopySizeLimit limits the total size increase in bytes caused by
|
||||
// "copy" operations in a patch.
|
||||
AccumulatedCopySizeLimit int64 = 0
|
||||
startObject = json.Delim('{')
|
||||
endObject = json.Delim('}')
|
||||
startArray = json.Delim('[')
|
||||
endArray = json.Delim(']')
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -32,11 +36,15 @@ var (
|
|||
ErrUnknownType = errors.New("unknown object type")
|
||||
ErrInvalid = errors.New("invalid state detected")
|
||||
ErrInvalidIndex = errors.New("invalid index referenced")
|
||||
|
||||
rawJSONArray = []byte("[]")
|
||||
rawJSONObject = []byte("{}")
|
||||
rawJSONNull = []byte("null")
|
||||
)
|
||||
|
||||
type lazyNode struct {
|
||||
raw *json.RawMessage
|
||||
doc partialDoc
|
||||
doc *partialDoc
|
||||
ary partialArray
|
||||
which int
|
||||
}
|
||||
|
@ -47,20 +55,58 @@ type Operation map[string]*json.RawMessage
|
|||
// Patch is an ordered collection of Operations.
|
||||
type Patch []Operation
|
||||
|
||||
type partialDoc map[string]*lazyNode
|
||||
type partialDoc struct {
|
||||
keys []string
|
||||
obj map[string]*lazyNode
|
||||
}
|
||||
|
||||
type partialArray []*lazyNode
|
||||
|
||||
type container interface {
|
||||
get(key string) (*lazyNode, error)
|
||||
set(key string, val *lazyNode) error
|
||||
add(key string, val *lazyNode) error
|
||||
remove(key string) error
|
||||
get(key string, options *ApplyOptions) (*lazyNode, error)
|
||||
set(key string, val *lazyNode, options *ApplyOptions) error
|
||||
add(key string, val *lazyNode, options *ApplyOptions) error
|
||||
remove(key string, options *ApplyOptions) error
|
||||
}
|
||||
|
||||
// ApplyOptions specifies options for calls to ApplyWithOptions.
|
||||
// Use NewApplyOptions to obtain default values for ApplyOptions.
|
||||
type ApplyOptions struct {
|
||||
// SupportNegativeIndices decides whether to support non-standard practice of
|
||||
// allowing negative indices to mean indices starting at the end of an array.
|
||||
// Default to true.
|
||||
SupportNegativeIndices bool
|
||||
// AccumulatedCopySizeLimit limits the total size increase in bytes caused by
|
||||
// "copy" operations in a patch.
|
||||
AccumulatedCopySizeLimit int64
|
||||
// AllowMissingPathOnRemove indicates whether to fail "remove" operations when the target path is missing.
|
||||
// Default to false.
|
||||
AllowMissingPathOnRemove bool
|
||||
// EnsurePathExistsOnAdd instructs json-patch to recursively create the missing parts of path on "add" operation.
|
||||
// Default to false.
|
||||
EnsurePathExistsOnAdd bool
|
||||
}
|
||||
|
||||
// NewApplyOptions creates a default set of options for calls to ApplyWithOptions.
|
||||
func NewApplyOptions() *ApplyOptions {
|
||||
return &ApplyOptions{
|
||||
SupportNegativeIndices: SupportNegativeIndices,
|
||||
AccumulatedCopySizeLimit: AccumulatedCopySizeLimit,
|
||||
AllowMissingPathOnRemove: false,
|
||||
EnsurePathExistsOnAdd: false,
|
||||
}
|
||||
}
|
||||
|
||||
func newLazyNode(raw *json.RawMessage) *lazyNode {
|
||||
return &lazyNode{raw: raw, doc: nil, ary: nil, which: eRaw}
|
||||
}
|
||||
|
||||
func newRawMessage(buf []byte) *json.RawMessage {
|
||||
ra := make(json.RawMessage, len(buf))
|
||||
copy(ra, buf)
|
||||
return &ra
|
||||
}
|
||||
|
||||
func (n *lazyNode) MarshalJSON() ([]byte, error) {
|
||||
switch n.which {
|
||||
case eRaw:
|
||||
|
@ -82,6 +128,109 @@ func (n *lazyNode) UnmarshalJSON(data []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (n *partialDoc) MarshalJSON() ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
if _, err := buf.WriteString("{"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i, k := range n.keys {
|
||||
if i > 0 {
|
||||
if _, err := buf.WriteString(", "); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
key, err := json.Marshal(k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := buf.Write(key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := buf.WriteString(": "); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
value, err := json.Marshal(n.obj[k])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := buf.Write(value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if _, err := buf.WriteString("}"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
type syntaxError struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
func (err *syntaxError) Error() string {
|
||||
return err.msg
|
||||
}
|
||||
|
||||
func (n *partialDoc) UnmarshalJSON(data []byte) error {
|
||||
if err := json.Unmarshal(data, &n.obj); err != nil {
|
||||
return err
|
||||
}
|
||||
buffer := bytes.NewBuffer(data)
|
||||
d := json.NewDecoder(buffer)
|
||||
if t, err := d.Token(); err != nil {
|
||||
return err
|
||||
} else if t != startObject {
|
||||
return &syntaxError{fmt.Sprintf("unexpected JSON token in document node: %s", t)}
|
||||
}
|
||||
for d.More() {
|
||||
k, err := d.Token()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key, ok := k.(string)
|
||||
if !ok {
|
||||
return &syntaxError{fmt.Sprintf("unexpected JSON token as document node key: %s", k)}
|
||||
}
|
||||
if err := skipValue(d); err != nil {
|
||||
return err
|
||||
}
|
||||
n.keys = append(n.keys, key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func skipValue(d *json.Decoder) error {
|
||||
t, err := d.Token()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if t != startObject && t != startArray {
|
||||
return nil
|
||||
}
|
||||
for d.More() {
|
||||
if t == startObject {
|
||||
// consume key token
|
||||
if _, err := d.Token(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := skipValue(d); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
end, err := d.Token()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if t == startObject && end != endObject {
|
||||
return &syntaxError{msg: "expected close object token"}
|
||||
}
|
||||
if t == startArray && end != endArray {
|
||||
return &syntaxError{msg: "expected close object token"}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deepCopy(src *lazyNode) (*lazyNode, int, error) {
|
||||
if src == nil {
|
||||
return nil, 0, nil
|
||||
|
@ -91,14 +240,12 @@ func deepCopy(src *lazyNode) (*lazyNode, int, error) {
|
|||
return nil, 0, err
|
||||
}
|
||||
sz := len(a)
|
||||
ra := make(json.RawMessage, sz)
|
||||
copy(ra, a)
|
||||
return newLazyNode(&ra), sz, nil
|
||||
return newLazyNode(newRawMessage(a)), sz, nil
|
||||
}
|
||||
|
||||
func (n *lazyNode) intoDoc() (*partialDoc, error) {
|
||||
if n.which == eDoc {
|
||||
return &n.doc, nil
|
||||
return n.doc, nil
|
||||
}
|
||||
|
||||
if n.raw == nil {
|
||||
|
@ -112,7 +259,7 @@ func (n *lazyNode) intoDoc() (*partialDoc, error) {
|
|||
}
|
||||
|
||||
n.which = eDoc
|
||||
return &n.doc, nil
|
||||
return n.doc, nil
|
||||
}
|
||||
|
||||
func (n *lazyNode) intoAry() (*partialArray, error) {
|
||||
|
@ -202,12 +349,12 @@ func (n *lazyNode) equal(o *lazyNode) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
if len(n.doc) != len(o.doc) {
|
||||
if len(n.doc.obj) != len(o.doc.obj) {
|
||||
return false
|
||||
}
|
||||
|
||||
for k, v := range n.doc {
|
||||
ov, ok := o.doc[k]
|
||||
for k, v := range n.doc.obj {
|
||||
ov, ok := o.doc.obj[k]
|
||||
|
||||
if !ok {
|
||||
return false
|
||||
|
@ -340,7 +487,7 @@ Loop:
|
|||
return false
|
||||
}
|
||||
|
||||
func findObject(pd *container, path string) (container, string) {
|
||||
func findObject(pd *container, path string, options *ApplyOptions) (container, string) {
|
||||
doc := *pd
|
||||
|
||||
split := strings.Split(path, "/")
|
||||
|
@ -357,7 +504,7 @@ func findObject(pd *container, path string) (container, string) {
|
|||
|
||||
for _, part := range parts {
|
||||
|
||||
next, ok := doc.get(decodePatchKey(part))
|
||||
next, ok := doc.get(decodePatchKey(part), options)
|
||||
|
||||
if next == nil || ok != nil {
|
||||
return nil, ""
|
||||
|
@ -381,42 +528,76 @@ func findObject(pd *container, path string) (container, string) {
|
|||
return doc, decodePatchKey(key)
|
||||
}
|
||||
|
||||
func (d *partialDoc) set(key string, val *lazyNode) error {
|
||||
(*d)[key] = val
|
||||
func (d *partialDoc) set(key string, val *lazyNode, options *ApplyOptions) error {
|
||||
found := false
|
||||
for _, k := range d.keys {
|
||||
if k == key {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
d.keys = append(d.keys, key)
|
||||
}
|
||||
d.obj[key] = val
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *partialDoc) add(key string, val *lazyNode) error {
|
||||
(*d)[key] = val
|
||||
return nil
|
||||
func (d *partialDoc) add(key string, val *lazyNode, options *ApplyOptions) error {
|
||||
return d.set(key, val, options)
|
||||
}
|
||||
|
||||
func (d *partialDoc) get(key string) (*lazyNode, error) {
|
||||
return (*d)[key], nil
|
||||
}
|
||||
|
||||
func (d *partialDoc) remove(key string) error {
|
||||
_, ok := (*d)[key]
|
||||
func (d *partialDoc) get(key string, options *ApplyOptions) (*lazyNode, error) {
|
||||
v, ok := d.obj[key]
|
||||
if !ok {
|
||||
return errors.Wrapf(ErrMissing, "Unable to remove nonexistent key: %s", key)
|
||||
return v, errors.Wrapf(ErrMissing, "unable to get nonexistent key: %s", key)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
delete(*d, key)
|
||||
func (d *partialDoc) remove(key string, options *ApplyOptions) error {
|
||||
_, ok := d.obj[key]
|
||||
if !ok {
|
||||
if options.AllowMissingPathOnRemove {
|
||||
return nil
|
||||
}
|
||||
return errors.Wrapf(ErrMissing, "unable to remove nonexistent key: %s", key)
|
||||
}
|
||||
idx := -1
|
||||
for i, k := range d.keys {
|
||||
if k == key {
|
||||
idx = i
|
||||
break
|
||||
}
|
||||
}
|
||||
d.keys = append(d.keys[0:idx], d.keys[idx+1:]...)
|
||||
delete(d.obj, key)
|
||||
return nil
|
||||
}
|
||||
|
||||
// set should only be used to implement the "replace" operation, so "key" must
|
||||
// be an already existing index in "d".
|
||||
func (d *partialArray) set(key string, val *lazyNode) error {
|
||||
func (d *partialArray) set(key string, val *lazyNode, options *ApplyOptions) error {
|
||||
idx, err := strconv.Atoi(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if idx < 0 {
|
||||
if !options.SupportNegativeIndices {
|
||||
return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
|
||||
}
|
||||
if idx < -len(*d) {
|
||||
return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
|
||||
}
|
||||
idx += len(*d)
|
||||
}
|
||||
|
||||
(*d)[idx] = val
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *partialArray) add(key string, val *lazyNode) error {
|
||||
func (d *partialArray) add(key string, val *lazyNode, options *ApplyOptions) error {
|
||||
if key == "-" {
|
||||
*d = append(*d, val)
|
||||
return nil
|
||||
|
@ -438,7 +619,7 @@ func (d *partialArray) add(key string, val *lazyNode) error {
|
|||
}
|
||||
|
||||
if idx < 0 {
|
||||
if !SupportNegativeIndices {
|
||||
if !options.SupportNegativeIndices {
|
||||
return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
|
||||
}
|
||||
if idx < -len(ary) {
|
||||
|
@ -455,13 +636,23 @@ func (d *partialArray) add(key string, val *lazyNode) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (d *partialArray) get(key string) (*lazyNode, error) {
|
||||
func (d *partialArray) get(key string, options *ApplyOptions) (*lazyNode, error) {
|
||||
idx, err := strconv.Atoi(key)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if idx < 0 {
|
||||
if !options.SupportNegativeIndices {
|
||||
return nil, errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
|
||||
}
|
||||
if idx < -len(*d) {
|
||||
return nil, errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
|
||||
}
|
||||
idx += len(*d)
|
||||
}
|
||||
|
||||
if idx >= len(*d) {
|
||||
return nil, errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
|
||||
}
|
||||
|
@ -469,7 +660,7 @@ func (d *partialArray) get(key string) (*lazyNode, error) {
|
|||
return (*d)[idx], nil
|
||||
}
|
||||
|
||||
func (d *partialArray) remove(key string) error {
|
||||
func (d *partialArray) remove(key string, options *ApplyOptions) error {
|
||||
idx, err := strconv.Atoi(key)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -478,14 +669,20 @@ func (d *partialArray) remove(key string) error {
|
|||
cur := *d
|
||||
|
||||
if idx >= len(cur) {
|
||||
if options.AllowMissingPathOnRemove {
|
||||
return nil
|
||||
}
|
||||
return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
|
||||
}
|
||||
|
||||
if idx < 0 {
|
||||
if !SupportNegativeIndices {
|
||||
if !options.SupportNegativeIndices {
|
||||
return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
|
||||
}
|
||||
if idx < -len(cur) {
|
||||
if options.AllowMissingPathOnRemove {
|
||||
return nil
|
||||
}
|
||||
return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
|
||||
}
|
||||
idx += len(cur)
|
||||
|
@ -498,22 +695,29 @@ func (d *partialArray) remove(key string) error {
|
|||
|
||||
*d = ary
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (p Patch) add(doc *container, op Operation) error {
|
||||
func (p Patch) add(doc *container, op Operation, options *ApplyOptions) error {
|
||||
path, err := op.Path()
|
||||
if err != nil {
|
||||
return errors.Wrapf(ErrMissing, "add operation failed to decode path")
|
||||
}
|
||||
|
||||
con, key := findObject(doc, path)
|
||||
if options.EnsurePathExistsOnAdd {
|
||||
err = ensurePathExists(doc, path, options)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
con, key := findObject(doc, path, options)
|
||||
|
||||
if con == nil {
|
||||
return errors.Wrapf(ErrMissing, "add operation does not apply: doc is missing path: \"%s\"", path)
|
||||
}
|
||||
|
||||
err = con.add(key, op.value())
|
||||
err = con.add(key, op.value(), options)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error in add for path: '%s'", path)
|
||||
}
|
||||
|
@ -521,19 +725,113 @@ func (p Patch) add(doc *container, op Operation) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p Patch) remove(doc *container, op Operation) error {
|
||||
// Given a document and a path to a key, walk the path and create all missing elements
|
||||
// creating objects and arrays as needed.
|
||||
func ensurePathExists(pd *container, path string, options *ApplyOptions) error {
|
||||
doc := *pd
|
||||
|
||||
var err error
|
||||
var arrIndex int
|
||||
|
||||
split := strings.Split(path, "/")
|
||||
|
||||
if len(split) < 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
parts := split[1:]
|
||||
|
||||
for pi, part := range parts {
|
||||
|
||||
// Have we reached the key part of the path?
|
||||
// If yes, we're done.
|
||||
if pi == len(parts)-1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
target, ok := doc.get(decodePatchKey(part), options)
|
||||
|
||||
if target == nil || ok != nil {
|
||||
|
||||
// If the current container is an array which has fewer elements than our target index,
|
||||
// pad the current container with nulls.
|
||||
if arrIndex, err = strconv.Atoi(part); err == nil {
|
||||
pa, ok := doc.(*partialArray)
|
||||
|
||||
if ok && arrIndex >= len(*pa)+1 {
|
||||
// Pad the array with null values up to the required index.
|
||||
for i := len(*pa); i <= arrIndex-1; i++ {
|
||||
doc.add(strconv.Itoa(i), newLazyNode(newRawMessage(rawJSONNull)), options)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the next part is a numeric index.
|
||||
// If yes, then create an array, otherwise, create an object.
|
||||
if arrIndex, err = strconv.Atoi(parts[pi+1]); err == nil {
|
||||
if arrIndex < 0 {
|
||||
|
||||
if !options.SupportNegativeIndices {
|
||||
return errors.Wrapf(ErrInvalidIndex, "Unable to ensure path for invalid index: %d", arrIndex)
|
||||
}
|
||||
|
||||
if arrIndex < -1 {
|
||||
return errors.Wrapf(ErrInvalidIndex, "Unable to ensure path for negative index other than -1: %d", arrIndex)
|
||||
}
|
||||
|
||||
arrIndex = 0
|
||||
}
|
||||
|
||||
newNode := newLazyNode(newRawMessage(rawJSONArray))
|
||||
doc.add(part, newNode, options)
|
||||
doc, _ = newNode.intoAry()
|
||||
|
||||
// Pad the new array with null values up to the required index.
|
||||
for i := 0; i < arrIndex; i++ {
|
||||
doc.add(strconv.Itoa(i), newLazyNode(newRawMessage(rawJSONNull)), options)
|
||||
}
|
||||
} else {
|
||||
newNode := newLazyNode(newRawMessage(rawJSONObject))
|
||||
|
||||
doc.add(part, newNode, options)
|
||||
doc, _ = newNode.intoDoc()
|
||||
}
|
||||
} else {
|
||||
if isArray(*target.raw) {
|
||||
doc, err = target.intoAry()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
doc, err = target.intoDoc()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p Patch) remove(doc *container, op Operation, options *ApplyOptions) error {
|
||||
path, err := op.Path()
|
||||
if err != nil {
|
||||
return errors.Wrapf(ErrMissing, "remove operation failed to decode path")
|
||||
}
|
||||
|
||||
con, key := findObject(doc, path)
|
||||
con, key := findObject(doc, path, options)
|
||||
|
||||
if con == nil {
|
||||
if options.AllowMissingPathOnRemove {
|
||||
return nil
|
||||
}
|
||||
return errors.Wrapf(ErrMissing, "remove operation does not apply: doc is missing path: \"%s\"", path)
|
||||
}
|
||||
|
||||
err = con.remove(key)
|
||||
err = con.remove(key, options)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error in remove for path: '%s'", path)
|
||||
}
|
||||
|
@ -541,24 +839,24 @@ func (p Patch) remove(doc *container, op Operation) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p Patch) replace(doc *container, op Operation) error {
|
||||
func (p Patch) replace(doc *container, op Operation, options *ApplyOptions) error {
|
||||
path, err := op.Path()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "replace operation failed to decode path")
|
||||
}
|
||||
|
||||
con, key := findObject(doc, path)
|
||||
con, key := findObject(doc, path, options)
|
||||
|
||||
if con == nil {
|
||||
return errors.Wrapf(ErrMissing, "replace operation does not apply: doc is missing path: %s", path)
|
||||
}
|
||||
|
||||
_, ok := con.get(key)
|
||||
_, ok := con.get(key, options)
|
||||
if ok != nil {
|
||||
return errors.Wrapf(ErrMissing, "replace operation does not apply: doc is missing key: %s", path)
|
||||
}
|
||||
|
||||
err = con.set(key, op.value())
|
||||
err = con.set(key, op.value(), options)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error in remove for path: '%s'", path)
|
||||
}
|
||||
|
@ -566,24 +864,24 @@ func (p Patch) replace(doc *container, op Operation) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p Patch) move(doc *container, op Operation) error {
|
||||
func (p Patch) move(doc *container, op Operation, options *ApplyOptions) error {
|
||||
from, err := op.From()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "move operation failed to decode from")
|
||||
}
|
||||
|
||||
con, key := findObject(doc, from)
|
||||
con, key := findObject(doc, from, options)
|
||||
|
||||
if con == nil {
|
||||
return errors.Wrapf(ErrMissing, "move operation does not apply: doc is missing from path: %s", from)
|
||||
}
|
||||
|
||||
val, err := con.get(key)
|
||||
val, err := con.get(key, options)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error in move for path: '%s'", key)
|
||||
}
|
||||
|
||||
err = con.remove(key)
|
||||
err = con.remove(key, options)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error in move for path: '%s'", key)
|
||||
}
|
||||
|
@ -593,13 +891,13 @@ func (p Patch) move(doc *container, op Operation) error {
|
|||
return errors.Wrapf(err, "move operation failed to decode path")
|
||||
}
|
||||
|
||||
con, key = findObject(doc, path)
|
||||
con, key = findObject(doc, path, options)
|
||||
|
||||
if con == nil {
|
||||
return errors.Wrapf(ErrMissing, "move operation does not apply: doc is missing destination path: %s", path)
|
||||
}
|
||||
|
||||
err = con.add(key, val)
|
||||
err = con.add(key, val, options)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error in move for path: '%s'", path)
|
||||
}
|
||||
|
@ -607,20 +905,20 @@ func (p Patch) move(doc *container, op Operation) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p Patch) test(doc *container, op Operation) error {
|
||||
func (p Patch) test(doc *container, op Operation, options *ApplyOptions) error {
|
||||
path, err := op.Path()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "test operation failed to decode path")
|
||||
}
|
||||
|
||||
con, key := findObject(doc, path)
|
||||
con, key := findObject(doc, path, options)
|
||||
|
||||
if con == nil {
|
||||
return errors.Wrapf(ErrMissing, "test operation does not apply: is missing path: %s", path)
|
||||
}
|
||||
|
||||
val, err := con.get(key)
|
||||
if err != nil {
|
||||
val, err := con.get(key, options)
|
||||
if err != nil && errors.Cause(err) != ErrMissing {
|
||||
return errors.Wrapf(err, "error in test for path: '%s'", path)
|
||||
}
|
||||
|
||||
|
@ -640,19 +938,19 @@ func (p Patch) test(doc *container, op Operation) error {
|
|||
return errors.Wrapf(ErrTestFailed, "testing value %s failed", path)
|
||||
}
|
||||
|
||||
func (p Patch) copy(doc *container, op Operation, accumulatedCopySize *int64) error {
|
||||
func (p Patch) copy(doc *container, op Operation, accumulatedCopySize *int64, options *ApplyOptions) error {
|
||||
from, err := op.From()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "copy operation failed to decode from")
|
||||
}
|
||||
|
||||
con, key := findObject(doc, from)
|
||||
con, key := findObject(doc, from, options)
|
||||
|
||||
if con == nil {
|
||||
return errors.Wrapf(ErrMissing, "copy operation does not apply: doc is missing from path: %s", from)
|
||||
}
|
||||
|
||||
val, err := con.get(key)
|
||||
val, err := con.get(key, options)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error in copy for from: '%s'", from)
|
||||
}
|
||||
|
@ -662,7 +960,7 @@ func (p Patch) copy(doc *container, op Operation, accumulatedCopySize *int64) er
|
|||
return errors.Wrapf(ErrMissing, "copy operation failed to decode path")
|
||||
}
|
||||
|
||||
con, key = findObject(doc, path)
|
||||
con, key = findObject(doc, path, options)
|
||||
|
||||
if con == nil {
|
||||
return errors.Wrapf(ErrMissing, "copy operation does not apply: doc is missing destination path: %s", path)
|
||||
|
@ -674,11 +972,11 @@ func (p Patch) copy(doc *container, op Operation, accumulatedCopySize *int64) er
|
|||
}
|
||||
|
||||
(*accumulatedCopySize) += int64(sz)
|
||||
if AccumulatedCopySizeLimit > 0 && *accumulatedCopySize > AccumulatedCopySizeLimit {
|
||||
return NewAccumulatedCopySizeError(AccumulatedCopySizeLimit, *accumulatedCopySize)
|
||||
if options.AccumulatedCopySizeLimit > 0 && *accumulatedCopySize > options.AccumulatedCopySizeLimit {
|
||||
return NewAccumulatedCopySizeError(options.AccumulatedCopySizeLimit, *accumulatedCopySize)
|
||||
}
|
||||
|
||||
err = con.add(key, valCopy)
|
||||
err = con.add(key, valCopy, options)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error while adding value during copy")
|
||||
}
|
||||
|
@ -688,13 +986,8 @@ func (p Patch) copy(doc *container, op Operation, accumulatedCopySize *int64) er
|
|||
|
||||
// Equal indicates if 2 JSON documents have the same structural equality.
|
||||
func Equal(a, b []byte) bool {
|
||||
ra := make(json.RawMessage, len(a))
|
||||
copy(ra, a)
|
||||
la := newLazyNode(&ra)
|
||||
|
||||
rb := make(json.RawMessage, len(b))
|
||||
copy(rb, b)
|
||||
lb := newLazyNode(&rb)
|
||||
la := newLazyNode(newRawMessage(a))
|
||||
lb := newLazyNode(newRawMessage(b))
|
||||
|
||||
return la.equal(lb)
|
||||
}
|
||||
|
@ -715,12 +1008,28 @@ func DecodePatch(buf []byte) (Patch, error) {
|
|||
// Apply mutates a JSON document according to the patch, and returns the new
|
||||
// document.
|
||||
func (p Patch) Apply(doc []byte) ([]byte, error) {
|
||||
return p.ApplyIndent(doc, "")
|
||||
return p.ApplyWithOptions(doc, NewApplyOptions())
|
||||
}
|
||||
|
||||
// ApplyWithOptions mutates a JSON document according to the patch and the passed in ApplyOptions.
|
||||
// It returns the new document.
|
||||
func (p Patch) ApplyWithOptions(doc []byte, options *ApplyOptions) ([]byte, error) {
|
||||
return p.ApplyIndentWithOptions(doc, "", options)
|
||||
}
|
||||
|
||||
// ApplyIndent mutates a JSON document according to the patch, and returns the new
|
||||
// document indented.
|
||||
func (p Patch) ApplyIndent(doc []byte, indent string) ([]byte, error) {
|
||||
return p.ApplyIndentWithOptions(doc, indent, NewApplyOptions())
|
||||
}
|
||||
|
||||
// ApplyIndentWithOptions mutates a JSON document according to the patch and the passed in ApplyOptions.
|
||||
// It returns the new document indented.
|
||||
func (p Patch) ApplyIndentWithOptions(doc []byte, indent string, options *ApplyOptions) ([]byte, error) {
|
||||
if len(doc) == 0 {
|
||||
return doc, nil
|
||||
}
|
||||
|
||||
var pd container
|
||||
if doc[0] == '[' {
|
||||
pd = &partialArray{}
|
||||
|
@ -741,17 +1050,17 @@ func (p Patch) ApplyIndent(doc []byte, indent string) ([]byte, error) {
|
|||
for _, op := range p {
|
||||
switch op.Kind() {
|
||||
case "add":
|
||||
err = p.add(&pd, op)
|
||||
err = p.add(&pd, op, options)
|
||||
case "remove":
|
||||
err = p.remove(&pd, op)
|
||||
err = p.remove(&pd, op, options)
|
||||
case "replace":
|
||||
err = p.replace(&pd, op)
|
||||
err = p.replace(&pd, op, options)
|
||||
case "move":
|
||||
err = p.move(&pd, op)
|
||||
err = p.move(&pd, op, options)
|
||||
case "test":
|
||||
err = p.test(&pd, op)
|
||||
err = p.test(&pd, op, options)
|
||||
case "copy":
|
||||
err = p.copy(&pd, op, &accumulatedCopySize)
|
||||
err = p.copy(&pd, op, &accumulatedCopySize, options)
|
||||
default:
|
||||
err = fmt.Errorf("Unexpected kind: %s", op.Kind())
|
||||
}
|
|
@ -4,17 +4,11 @@ go_import_path: go.uber.org/multierr
|
|||
|
||||
env:
|
||||
global:
|
||||
- GO15VENDOREXPERIMENT=1
|
||||
- GO111MODULE=on
|
||||
|
||||
go:
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- vendor
|
||||
- oldstable
|
||||
- stable
|
||||
|
||||
before_install:
|
||||
- go version
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
Releases
|
||||
========
|
||||
|
||||
v1.6.0 (2020-09-14)
|
||||
===================
|
||||
|
||||
- Actually drop library dependency on development-time tooling.
|
||||
|
||||
|
||||
v1.5.0 (2020-02-24)
|
||||
===================
|
||||
|
||||
|
|
|
@ -21,12 +21,12 @@ gofmt:
|
|||
|
||||
.PHONY: golint
|
||||
golint:
|
||||
@go install golang.org/x/lint/golint
|
||||
@cd tools && go install golang.org/x/lint/golint
|
||||
@$(GOBIN)/golint ./...
|
||||
|
||||
.PHONY: staticcheck
|
||||
staticcheck:
|
||||
@go install honnef.co/go/tools/cmd/staticcheck
|
||||
@cd tools && go install honnef.co/go/tools/cmd/staticcheck
|
||||
@$(GOBIN)/staticcheck ./...
|
||||
|
||||
.PHONY: lint
|
||||
|
@ -38,5 +38,5 @@ cover:
|
|||
go tool cover -html=cover.out -o cover.html
|
||||
|
||||
update-license:
|
||||
@go install go.uber.org/tools/update-license
|
||||
@cd tools && go install go.uber.org/tools/update-license
|
||||
@$(GOBIN)/update-license $(GO_FILES)
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
//
|
||||
// errors := multierr.Errors(err)
|
||||
// if len(errors) > 0 {
|
||||
// fmt.Println("The following errors occurred:")
|
||||
// fmt.Println("The following errors occurred:", errors)
|
||||
// }
|
||||
//
|
||||
// Advanced Usage
|
||||
|
|
|
@ -4,9 +4,5 @@ go 1.12
|
|||
|
||||
require (
|
||||
github.com/stretchr/testify v1.3.0
|
||||
go.uber.org/atomic v1.6.0
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 // indirect
|
||||
honnef.co/go/tools v0.0.1-2019.2.3
|
||||
go.uber.org/atomic v1.7.0
|
||||
)
|
||||
|
|
|
@ -1,45 +1,11 @@
|
|||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
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-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c h1:IGkKhmfzcztjm6gYkykvu/NiS8kaqbCWAEWWAyf8J5U=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
language: go
|
||||
sudo: false
|
||||
|
||||
go_import_path: go.uber.org/zap
|
||||
env:
|
||||
global:
|
||||
- TEST_TIMEOUT_SCALE=10
|
||||
- GO111MODULE=on
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- go: 1.13.x
|
||||
- go: 1.14.x
|
||||
env: LINT=1
|
||||
|
||||
script:
|
||||
- test -z "$LINT" || make lint
|
||||
- make test
|
||||
- make bench
|
||||
|
||||
after_success:
|
||||
- make cover
|
||||
- bash <(curl -s https://codecov.io/bash)
|
|
@ -1,5 +1,24 @@
|
|||
# Changelog
|
||||
|
||||
## 1.17.0 (25 May 2021)
|
||||
|
||||
Bugfixes:
|
||||
* [#867][]: Encode `<nil>` for nil `error` instead of a panic.
|
||||
* [#931][], [#936][]: Update minimum version constraints to address
|
||||
vulnerabilities in dependencies.
|
||||
|
||||
Enhancements:
|
||||
* [#865][]: Improve alignment of fields of the Logger struct, reducing its
|
||||
size from 96 to 80 bytes.
|
||||
* [#881][]: Support `grpclog.LoggerV2` in zapgrpc.
|
||||
* [#903][]: Support URL-encoded POST requests to the AtomicLevel HTTP handler
|
||||
with the `application/x-www-form-urlencoded` content type.
|
||||
* [#912][]: Support multi-field encoding with `zap.Inline`.
|
||||
* [#913][]: Speed up SugaredLogger for calls with a single string.
|
||||
* [#928][]: Add support for filtering by field name to `zaptest/observer`.
|
||||
|
||||
Thanks to @ash2k, @FMLS, @jimmystewpot, @Oncilla, @tsoslow, @tylitianrui, @withshubh, and @wziww for their contributions to this release.
|
||||
|
||||
## 1.16.0 (1 Sep 2020)
|
||||
|
||||
Bugfixes:
|
||||
|
@ -430,3 +449,12 @@ upgrade to the upcoming stable release.
|
|||
[#854]: https://github.com/uber-go/zap/pull/854
|
||||
[#861]: https://github.com/uber-go/zap/pull/861
|
||||
[#862]: https://github.com/uber-go/zap/pull/862
|
||||
[#865]: https://github.com/uber-go/zap/pull/865
|
||||
[#867]: https://github.com/uber-go/zap/pull/867
|
||||
[#881]: https://github.com/uber-go/zap/pull/881
|
||||
[#903]: https://github.com/uber-go/zap/pull/903
|
||||
[#912]: https://github.com/uber-go/zap/pull/912
|
||||
[#913]: https://github.com/uber-go/zap/pull/913
|
||||
[#928]: https://github.com/uber-go/zap/pull/928
|
||||
[#931]: https://github.com/uber-go/zap/pull/931
|
||||
[#936]: https://github.com/uber-go/zap/pull/936
|
||||
|
|
|
@ -25,12 +25,6 @@ git remote add upstream https://github.com/uber-go/zap.git
|
|||
git fetch upstream
|
||||
```
|
||||
|
||||
Install zap's dependencies:
|
||||
|
||||
```
|
||||
make dependencies
|
||||
```
|
||||
|
||||
Make sure that the tests and the linters pass:
|
||||
|
||||
```
|
||||
|
|
|
@ -27,6 +27,13 @@ abstraction, and it lets us add methods without introducing breaking changes.
|
|||
Your applications should define and depend upon an interface that includes
|
||||
just the methods you use.
|
||||
|
||||
### Why are some of my logs missing?
|
||||
|
||||
Logs are dropped intentionally by zap when sampling is enabled. The production
|
||||
configuration (as returned by `NewProductionConfig()` enables sampling which will
|
||||
cause repeated logs within a second to be sampled. See more details on why sampling
|
||||
is enabled in [Why sample application logs](https://github.com/uber-go/zap/blob/master/FAQ.md#why-sample-application-logs).
|
||||
|
||||
### Why sample application logs?
|
||||
|
||||
Applications often experience runs of errors, either because of a bug or
|
||||
|
@ -150,6 +157,7 @@ We're aware of the following extensions, but haven't used them ourselves:
|
|||
| `github.com/fgrosse/zaptest` | Ginkgo |
|
||||
| `github.com/blendle/zapdriver` | Stackdriver |
|
||||
| `github.com/moul/zapgorm` | Gorm |
|
||||
| `github.com/moul/zapfilter` | Advanced filtering rules |
|
||||
|
||||
[go-proverbs]: https://go-proverbs.github.io/
|
||||
[import-path]: https://golang.org/cmd/go/#hdr-Remote_import_paths
|
||||
|
|
|
@ -7,7 +7,7 @@ BENCH_FLAGS ?= -cpuprofile=cpu.pprof -memprofile=mem.pprof -benchmem
|
|||
# Directories containing independent Go modules.
|
||||
#
|
||||
# We track coverage only for the main module.
|
||||
MODULE_DIRS = . ./benchmarks
|
||||
MODULE_DIRS = . ./benchmarks ./zapgrpc/internal/test
|
||||
|
||||
# Many Go tools take file globs or directories as arguments instead of packages.
|
||||
GO_FILES := $(shell \
|
||||
|
@ -33,12 +33,18 @@ lint: $(GOLINT) $(STATICCHECK)
|
|||
@echo "Checking for license headers..."
|
||||
@./checklicense.sh | tee -a lint.log
|
||||
@[ ! -s lint.log ]
|
||||
@echo "Checking 'go mod tidy'..."
|
||||
@make tidy
|
||||
@if ! git diff --quiet; then \
|
||||
echo "'go mod tidy' resulted in changes or working tree is dirty:"; \
|
||||
git --no-pager diff; \
|
||||
fi
|
||||
|
||||
$(GOLINT):
|
||||
go install golang.org/x/lint/golint
|
||||
cd tools && go install golang.org/x/lint/golint
|
||||
|
||||
$(STATICCHECK):
|
||||
go install honnef.co/go/tools/cmd/staticcheck
|
||||
cd tools && go install honnef.co/go/tools/cmd/staticcheck
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
|
@ -61,3 +67,7 @@ bench:
|
|||
updatereadme:
|
||||
rm -f README.md
|
||||
cat .readme.tmpl | go run internal/readme/readme.go > README.md
|
||||
|
||||
.PHONY: tidy
|
||||
tidy:
|
||||
@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && go mod tidy) &&) true
|
||||
|
|
|
@ -123,10 +123,10 @@ Released under the [MIT License](LICENSE.txt).
|
|||
benchmarking against slightly older versions of other packages. Versions are
|
||||
pinned in the [benchmarks/go.mod][] file. [↩](#anchor-versions)
|
||||
|
||||
[doc-img]: https://godoc.org/go.uber.org/zap?status.svg
|
||||
[doc]: https://godoc.org/go.uber.org/zap
|
||||
[ci-img]: https://travis-ci.com/uber-go/zap.svg?branch=master
|
||||
[ci]: https://travis-ci.com/uber-go/zap
|
||||
[doc-img]: https://pkg.go.dev/badge/go.uber.org/zap
|
||||
[doc]: https://pkg.go.dev/go.uber.org/zap
|
||||
[ci-img]: https://github.com/uber-go/zap/actions/workflows/go.yml/badge.svg
|
||||
[ci]: https://github.com/uber-go/zap/actions/workflows/go.yml
|
||||
[cov-img]: https://codecov.io/gh/uber-go/zap/branch/master/graph/badge.svg
|
||||
[cov]: https://codecov.io/gh/uber-go/zap
|
||||
[benchmarking suite]: https://github.com/uber-go/zap/tree/master/benchmarks
|
||||
|
|
|
@ -400,6 +400,16 @@ func Object(key string, val zapcore.ObjectMarshaler) Field {
|
|||
return Field{Key: key, Type: zapcore.ObjectMarshalerType, Interface: val}
|
||||
}
|
||||
|
||||
// Inline constructs a Field that is similar to Object, but it
|
||||
// will add the elements of the provided ObjectMarshaler to the
|
||||
// current namespace.
|
||||
func Inline(val zapcore.ObjectMarshaler) Field {
|
||||
return zapcore.Field{
|
||||
Type: zapcore.InlineMarshalerType,
|
||||
Interface: val,
|
||||
}
|
||||
}
|
||||
|
||||
// Any takes a key and an arbitrary value and chooses the best way to represent
|
||||
// them as a field, falling back to a reflection-based approach only if
|
||||
// necessary.
|
||||
|
|
|
@ -4,10 +4,9 @@ go 1.13
|
|||
|
||||
require (
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/stretchr/testify v1.4.0
|
||||
go.uber.org/atomic v1.6.0
|
||||
go.uber.org/multierr v1.5.0
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de
|
||||
gopkg.in/yaml.v2 v2.2.2
|
||||
honnef.co/go/tools v0.0.1-2019.2.3
|
||||
github.com/stretchr/testify v1.7.0
|
||||
go.uber.org/atomic v1.7.0
|
||||
go.uber.org/multierr v1.6.0
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
)
|
||||
|
|
|
@ -1,56 +1,22 @@
|
|||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c h1:IGkKhmfzcztjm6gYkykvu/NiS8kaqbCWAEWWAyf8J5U=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
@ -23,6 +23,7 @@ package zap
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"go.uber.org/zap/zapcore"
|
||||
|
@ -31,47 +32,63 @@ import (
|
|||
// ServeHTTP is a simple JSON endpoint that can report on or change the current
|
||||
// logging level.
|
||||
//
|
||||
// GET requests return a JSON description of the current logging level. PUT
|
||||
// requests change the logging level and expect a payload like:
|
||||
// GET
|
||||
//
|
||||
// The GET request returns a JSON description of the current logging level like:
|
||||
// {"level":"info"}
|
||||
//
|
||||
// It's perfectly safe to change the logging level while a program is running.
|
||||
// PUT
|
||||
//
|
||||
// The PUT request changes the logging level. It is perfectly safe to change the
|
||||
// logging level while a program is running. Two content types are supported:
|
||||
//
|
||||
// Content-Type: application/x-www-form-urlencoded
|
||||
//
|
||||
// With this content type, the level can be provided through the request body or
|
||||
// a query parameter. The log level is URL encoded like:
|
||||
//
|
||||
// level=debug
|
||||
//
|
||||
// The request body takes precedence over the query parameter, if both are
|
||||
// specified.
|
||||
//
|
||||
// This content type is the default for a curl PUT request. Following are two
|
||||
// example curl requests that both set the logging level to debug.
|
||||
//
|
||||
// curl -X PUT localhost:8080/log/level?level=debug
|
||||
// curl -X PUT localhost:8080/log/level -d level=debug
|
||||
//
|
||||
// For any other content type, the payload is expected to be JSON encoded and
|
||||
// look like:
|
||||
//
|
||||
// {"level":"info"}
|
||||
//
|
||||
// An example curl request could look like this:
|
||||
//
|
||||
// curl -X PUT localhost:8080/log/level -H "Content-Type: application/json" -d '{"level":"debug"}'
|
||||
//
|
||||
func (lvl AtomicLevel) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
type errorResponse struct {
|
||||
Error string `json:"error"`
|
||||
}
|
||||
type payload struct {
|
||||
Level *zapcore.Level `json:"level"`
|
||||
Level zapcore.Level `json:"level"`
|
||||
}
|
||||
|
||||
enc := json.NewEncoder(w)
|
||||
|
||||
switch r.Method {
|
||||
|
||||
case http.MethodGet:
|
||||
current := lvl.Level()
|
||||
enc.Encode(payload{Level: ¤t})
|
||||
|
||||
enc.Encode(payload{Level: lvl.Level()})
|
||||
case http.MethodPut:
|
||||
var req payload
|
||||
|
||||
if errmess := func() string {
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return fmt.Sprintf("Request body must be well-formed JSON: %v", err)
|
||||
}
|
||||
if req.Level == nil {
|
||||
return "Must specify a logging level."
|
||||
}
|
||||
return ""
|
||||
}(); errmess != "" {
|
||||
requestedLvl, err := decodePutRequest(r.Header.Get("Content-Type"), r)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
enc.Encode(errorResponse{Error: errmess})
|
||||
enc.Encode(errorResponse{Error: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
lvl.SetLevel(*req.Level)
|
||||
enc.Encode(req)
|
||||
|
||||
lvl.SetLevel(requestedLvl)
|
||||
enc.Encode(payload{Level: lvl.Level()})
|
||||
default:
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
enc.Encode(errorResponse{
|
||||
|
@ -79,3 +96,37 @@ func (lvl AtomicLevel) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Decodes incoming PUT requests and returns the requested logging level.
|
||||
func decodePutRequest(contentType string, r *http.Request) (zapcore.Level, error) {
|
||||
if contentType == "application/x-www-form-urlencoded" {
|
||||
return decodePutURL(r)
|
||||
}
|
||||
return decodePutJSON(r.Body)
|
||||
}
|
||||
|
||||
func decodePutURL(r *http.Request) (zapcore.Level, error) {
|
||||
lvl := r.FormValue("level")
|
||||
if lvl == "" {
|
||||
return 0, fmt.Errorf("must specify logging level")
|
||||
}
|
||||
var l zapcore.Level
|
||||
if err := l.UnmarshalText([]byte(lvl)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func decodePutJSON(body io.Reader) (zapcore.Level, error) {
|
||||
var pld struct {
|
||||
Level *zapcore.Level `json:"level"`
|
||||
}
|
||||
if err := json.NewDecoder(body).Decode(&pld); err != nil {
|
||||
return 0, fmt.Errorf("malformed request body: %v", err)
|
||||
}
|
||||
if pld.Level == nil {
|
||||
return 0, fmt.Errorf("must specify logging level")
|
||||
}
|
||||
return *pld.Level, nil
|
||||
|
||||
}
|
||||
|
|
|
@ -42,14 +42,15 @@ type Logger struct {
|
|||
core zapcore.Core
|
||||
|
||||
development bool
|
||||
addCaller bool
|
||||
onFatal zapcore.CheckWriteAction // default is WriteThenFatal
|
||||
|
||||
name string
|
||||
errorOutput zapcore.WriteSyncer
|
||||
|
||||
addCaller bool
|
||||
addStack zapcore.LevelEnabler
|
||||
|
||||
callerSkip int
|
||||
onFatal zapcore.CheckWriteAction // default is WriteThenFatal
|
||||
}
|
||||
|
||||
// New constructs a new Logger from the provided zapcore.Core and Options. If
|
||||
|
@ -334,7 +335,7 @@ func getCallerFrame(skip int) (frame runtime.Frame, ok bool) {
|
|||
const skipOffset = 2 // skip getCallerFrame and Callers
|
||||
|
||||
pc := make([]uintptr, 1)
|
||||
numFrames := runtime.Callers(skip+skipOffset, pc[:])
|
||||
numFrames := runtime.Callers(skip+skipOffset, pc)
|
||||
if numFrames < 1 {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -222,19 +222,30 @@ func (s *SugaredLogger) log(lvl zapcore.Level, template string, fmtArgs []interf
|
|||
return
|
||||
}
|
||||
|
||||
// Format with Sprint, Sprintf, or neither.
|
||||
msg := template
|
||||
if msg == "" && len(fmtArgs) > 0 {
|
||||
msg = fmt.Sprint(fmtArgs...)
|
||||
} else if msg != "" && len(fmtArgs) > 0 {
|
||||
msg = fmt.Sprintf(template, fmtArgs...)
|
||||
}
|
||||
|
||||
msg := getMessage(template, fmtArgs)
|
||||
if ce := s.base.Check(lvl, msg); ce != nil {
|
||||
ce.Write(s.sweetenFields(context)...)
|
||||
}
|
||||
}
|
||||
|
||||
// getMessage format with Sprint, Sprintf, or neither.
|
||||
func getMessage(template string, fmtArgs []interface{}) string {
|
||||
if len(fmtArgs) == 0 {
|
||||
return template
|
||||
}
|
||||
|
||||
if template != "" {
|
||||
return fmt.Sprintf(template, fmtArgs...)
|
||||
}
|
||||
|
||||
if len(fmtArgs) == 1 {
|
||||
if str, ok := fmtArgs[0].(string); ok {
|
||||
return str
|
||||
}
|
||||
}
|
||||
return fmt.Sprint(fmtArgs...)
|
||||
}
|
||||
|
||||
func (s *SugaredLogger) sweetenFields(args []interface{}) []Field {
|
||||
if len(args) == 0 {
|
||||
return nil
|
||||
|
|
|
@ -56,7 +56,7 @@ type consoleEncoder struct {
|
|||
// encoder configuration, it will omit any element whose key is set to the empty
|
||||
// string.
|
||||
func NewConsoleEncoder(cfg EncoderConfig) Encoder {
|
||||
if len(cfg.ConsoleSeparator) == 0 {
|
||||
if cfg.ConsoleSeparator == "" {
|
||||
// Use a default delimiter of '\t' for backwards compatibility
|
||||
cfg.ConsoleSeparator = "\t"
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ package zapcore
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
@ -42,7 +43,23 @@ import (
|
|||
// ...
|
||||
// ],
|
||||
// }
|
||||
func encodeError(key string, err error, enc ObjectEncoder) error {
|
||||
func encodeError(key string, err error, enc ObjectEncoder) (retErr error) {
|
||||
// Try to capture panics (from nil references or otherwise) when calling
|
||||
// the Error() method
|
||||
defer func() {
|
||||
if rerr := recover(); rerr != nil {
|
||||
// If it's a nil pointer, just say "<nil>". The likeliest causes are a
|
||||
// error that fails to guard against nil or a nil pointer for a
|
||||
// value receiver, and in either case, "<nil>" is a nice result.
|
||||
if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
enc.AddString(key, "<nil>")
|
||||
return
|
||||
}
|
||||
|
||||
retErr = fmt.Errorf("PANIC=%v", rerr)
|
||||
}
|
||||
}()
|
||||
|
||||
basic := err.Error()
|
||||
enc.AddString(key, basic)
|
||||
|
||||
|
|
|
@ -92,6 +92,10 @@ const (
|
|||
ErrorType
|
||||
// SkipType indicates that the field is a no-op.
|
||||
SkipType
|
||||
|
||||
// InlineMarshalerType indicates that the field carries an ObjectMarshaler
|
||||
// that should be inlined.
|
||||
InlineMarshalerType
|
||||
)
|
||||
|
||||
// A Field is a marshaling operation used to add a key-value pair to a logger's
|
||||
|
@ -115,6 +119,8 @@ func (f Field) AddTo(enc ObjectEncoder) {
|
|||
err = enc.AddArray(f.Key, f.Interface.(ArrayMarshaler))
|
||||
case ObjectMarshalerType:
|
||||
err = enc.AddObject(f.Key, f.Interface.(ObjectMarshaler))
|
||||
case InlineMarshalerType:
|
||||
err = f.Interface.(ObjectMarshaler).MarshalLogObject(enc)
|
||||
case BinaryType:
|
||||
enc.AddBinary(f.Key, f.Interface.([]byte))
|
||||
case BoolType:
|
||||
|
@ -167,7 +173,7 @@ func (f Field) AddTo(enc ObjectEncoder) {
|
|||
case StringerType:
|
||||
err = encodeStringer(f.Key, f.Interface, enc)
|
||||
case ErrorType:
|
||||
encodeError(f.Key, f.Interface.(error), enc)
|
||||
err = encodeError(f.Key, f.Interface.(error), enc)
|
||||
case SkipType:
|
||||
break
|
||||
default:
|
||||
|
|
|
@ -91,8 +91,7 @@ func NewMultiWriteSyncer(ws ...WriteSyncer) WriteSyncer {
|
|||
if len(ws) == 1 {
|
||||
return ws[0]
|
||||
}
|
||||
// Copy to protect against https://github.com/golang/go/issues/7809
|
||||
return multiWriteSyncer(append([]WriteSyncer(nil), ws...))
|
||||
return multiWriteSyncer(ws)
|
||||
}
|
||||
|
||||
// See https://golang.org/src/io/multi.go
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.14
|
||||
- 1.13
|
||||
|
||||
install:
|
||||
- if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
|
||||
- go get github.com/jessevdk/go-flags
|
||||
|
||||
script:
|
||||
- go get
|
||||
- go test -cover ./...
|
||||
- cd ./v5
|
||||
- go get
|
||||
- go test -cover ./...
|
||||
|
||||
notifications:
|
||||
email: false
|
|
@ -1,298 +0,0 @@
|
|||
# JSON-Patch
|
||||
`jsonpatch` is a library which provides functionality for both applying
|
||||
[RFC6902 JSON patches](http://tools.ietf.org/html/rfc6902) against documents, as
|
||||
well as for calculating & applying [RFC7396 JSON merge patches](https://tools.ietf.org/html/rfc7396).
|
||||
|
||||
[](http://godoc.org/github.com/evanphx/json-patch)
|
||||
[](https://travis-ci.org/evanphx/json-patch)
|
||||
[](https://goreportcard.com/report/github.com/evanphx/json-patch)
|
||||
|
||||
# Get It!
|
||||
|
||||
**Latest and greatest**:
|
||||
```bash
|
||||
go get -u github.com/evanphx/json-patch/v5
|
||||
```
|
||||
|
||||
**Stable Versions**:
|
||||
* Version 5: `go get -u gopkg.in/evanphx/json-patch.v5`
|
||||
* Version 4: `go get -u gopkg.in/evanphx/json-patch.v4`
|
||||
|
||||
(previous versions below `v3` are unavailable)
|
||||
|
||||
# Use It!
|
||||
* [Create and apply a merge patch](#create-and-apply-a-merge-patch)
|
||||
* [Create and apply a JSON Patch](#create-and-apply-a-json-patch)
|
||||
* [Comparing JSON documents](#comparing-json-documents)
|
||||
* [Combine merge patches](#combine-merge-patches)
|
||||
|
||||
|
||||
# Configuration
|
||||
|
||||
* There is a global configuration variable `jsonpatch.SupportNegativeIndices`.
|
||||
This defaults to `true` and enables the non-standard practice of allowing
|
||||
negative indices to mean indices starting at the end of an array. This
|
||||
functionality can be disabled by setting `jsonpatch.SupportNegativeIndices =
|
||||
false`.
|
||||
|
||||
* There is a global configuration variable `jsonpatch.AccumulatedCopySizeLimit`,
|
||||
which limits the total size increase in bytes caused by "copy" operations in a
|
||||
patch. It defaults to 0, which means there is no limit.
|
||||
|
||||
## Create and apply a merge patch
|
||||
Given both an original JSON document and a modified JSON document, you can create
|
||||
a [Merge Patch](https://tools.ietf.org/html/rfc7396) document.
|
||||
|
||||
It can describe the changes needed to convert from the original to the
|
||||
modified JSON document.
|
||||
|
||||
Once you have a merge patch, you can apply it to other JSON documents using the
|
||||
`jsonpatch.MergePatch(document, patch)` function.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Let's create a merge patch from these two documents...
|
||||
original := []byte(`{"name": "John", "age": 24, "height": 3.21}`)
|
||||
target := []byte(`{"name": "Jane", "age": 24}`)
|
||||
|
||||
patch, err := jsonpatch.CreateMergePatch(original, target)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Now lets apply the patch against a different JSON document...
|
||||
|
||||
alternative := []byte(`{"name": "Tina", "age": 28, "height": 3.75}`)
|
||||
modifiedAlternative, err := jsonpatch.MergePatch(alternative, patch)
|
||||
|
||||
fmt.Printf("patch document: %s\n", patch)
|
||||
fmt.Printf("updated alternative doc: %s\n", modifiedAlternative)
|
||||
}
|
||||
```
|
||||
|
||||
When ran, you get the following output:
|
||||
|
||||
```bash
|
||||
$ go run main.go
|
||||
patch document: {"height":null,"name":"Jane"}
|
||||
updated alternative doc: {"age":28,"name":"Jane"}
|
||||
```
|
||||
|
||||
## Create and apply a JSON Patch
|
||||
You can create patch objects using `DecodePatch([]byte)`, which can then
|
||||
be applied against JSON documents.
|
||||
|
||||
The following is an example of creating a patch from two operations, and
|
||||
applying it against a JSON document.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
)
|
||||
|
||||
func main() {
|
||||
original := []byte(`{"name": "John", "age": 24, "height": 3.21}`)
|
||||
patchJSON := []byte(`[
|
||||
{"op": "replace", "path": "/name", "value": "Jane"},
|
||||
{"op": "remove", "path": "/height"}
|
||||
]`)
|
||||
|
||||
patch, err := jsonpatch.DecodePatch(patchJSON)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
modified, err := patch.Apply(original)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("Original document: %s\n", original)
|
||||
fmt.Printf("Modified document: %s\n", modified)
|
||||
}
|
||||
```
|
||||
|
||||
When ran, you get the following output:
|
||||
|
||||
```bash
|
||||
$ go run main.go
|
||||
Original document: {"name": "John", "age": 24, "height": 3.21}
|
||||
Modified document: {"age":24,"name":"Jane"}
|
||||
```
|
||||
|
||||
## Comparing JSON documents
|
||||
Due to potential whitespace and ordering differences, one cannot simply compare
|
||||
JSON strings or byte-arrays directly.
|
||||
|
||||
As such, you can instead use `jsonpatch.Equal(document1, document2)` to
|
||||
determine if two JSON documents are _structurally_ equal. This ignores
|
||||
whitespace differences, and key-value ordering.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
)
|
||||
|
||||
func main() {
|
||||
original := []byte(`{"name": "John", "age": 24, "height": 3.21}`)
|
||||
similar := []byte(`
|
||||
{
|
||||
"age": 24,
|
||||
"height": 3.21,
|
||||
"name": "John"
|
||||
}
|
||||
`)
|
||||
different := []byte(`{"name": "Jane", "age": 20, "height": 3.37}`)
|
||||
|
||||
if jsonpatch.Equal(original, similar) {
|
||||
fmt.Println(`"original" is structurally equal to "similar"`)
|
||||
}
|
||||
|
||||
if !jsonpatch.Equal(original, different) {
|
||||
fmt.Println(`"original" is _not_ structurally equal to "different"`)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When ran, you get the following output:
|
||||
```bash
|
||||
$ go run main.go
|
||||
"original" is structurally equal to "similar"
|
||||
"original" is _not_ structurally equal to "different"
|
||||
```
|
||||
|
||||
## Combine merge patches
|
||||
Given two JSON merge patch documents, it is possible to combine them into a
|
||||
single merge patch which can describe both set of changes.
|
||||
|
||||
The resulting merge patch can be used such that applying it results in a
|
||||
document structurally similar as merging each merge patch to the document
|
||||
in succession.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
)
|
||||
|
||||
func main() {
|
||||
original := []byte(`{"name": "John", "age": 24, "height": 3.21}`)
|
||||
|
||||
nameAndHeight := []byte(`{"height":null,"name":"Jane"}`)
|
||||
ageAndEyes := []byte(`{"age":4.23,"eyes":"blue"}`)
|
||||
|
||||
// Let's combine these merge patch documents...
|
||||
combinedPatch, err := jsonpatch.MergeMergePatches(nameAndHeight, ageAndEyes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Apply each patch individual against the original document
|
||||
withoutCombinedPatch, err := jsonpatch.MergePatch(original, nameAndHeight)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
withoutCombinedPatch, err = jsonpatch.MergePatch(withoutCombinedPatch, ageAndEyes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Apply the combined patch against the original document
|
||||
|
||||
withCombinedPatch, err := jsonpatch.MergePatch(original, combinedPatch)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Do both result in the same thing? They should!
|
||||
if jsonpatch.Equal(withCombinedPatch, withoutCombinedPatch) {
|
||||
fmt.Println("Both JSON documents are structurally the same!")
|
||||
}
|
||||
|
||||
fmt.Printf("combined merge patch: %s", combinedPatch)
|
||||
}
|
||||
```
|
||||
|
||||
When ran, you get the following output:
|
||||
```bash
|
||||
$ go run main.go
|
||||
Both JSON documents are structurally the same!
|
||||
combined merge patch: {"age":4.23,"eyes":"blue","height":null,"name":"Jane"}
|
||||
```
|
||||
|
||||
# CLI for comparing JSON documents
|
||||
You can install the commandline program `json-patch`.
|
||||
|
||||
This program can take multiple JSON patch documents as arguments,
|
||||
and fed a JSON document from `stdin`. It will apply the patch(es) against
|
||||
the document and output the modified doc.
|
||||
|
||||
**patch.1.json**
|
||||
```json
|
||||
[
|
||||
{"op": "replace", "path": "/name", "value": "Jane"},
|
||||
{"op": "remove", "path": "/height"}
|
||||
]
|
||||
```
|
||||
|
||||
**patch.2.json**
|
||||
```json
|
||||
[
|
||||
{"op": "add", "path": "/address", "value": "123 Main St"},
|
||||
{"op": "replace", "path": "/age", "value": "21"}
|
||||
]
|
||||
```
|
||||
|
||||
**document.json**
|
||||
```json
|
||||
{
|
||||
"name": "John",
|
||||
"age": 24,
|
||||
"height": 3.21
|
||||
}
|
||||
```
|
||||
|
||||
You can then run:
|
||||
|
||||
```bash
|
||||
$ go install github.com/evanphx/json-patch/cmd/json-patch
|
||||
$ cat document.json | json-patch -p patch.1.json -p patch.2.json
|
||||
{"address":"123 Main St","age":"21","name":"Jane"}
|
||||
```
|
||||
|
||||
# Help It!
|
||||
Contributions are welcomed! Leave [an issue](https://github.com/evanphx/json-patch/issues)
|
||||
or [create a PR](https://github.com/evanphx/json-patch/compare).
|
||||
|
||||
|
||||
Before creating a pull request, we'd ask that you make sure tests are passing
|
||||
and that you have added new tests when applicable.
|
||||
|
||||
Contributors can run tests using:
|
||||
|
||||
```bash
|
||||
go test -cover ./...
|
||||
```
|
||||
|
||||
Builds for pull requests are tested automatically
|
||||
using [TravisCI](https://travis-ci.org/evanphx/json-patch).
|
|
@ -98,6 +98,9 @@ github.com/emicklei/go-restful
|
|||
github.com/emicklei/go-restful/log
|
||||
# github.com/evanphx/json-patch v4.9.0+incompatible
|
||||
github.com/evanphx/json-patch
|
||||
# github.com/evanphx/json-patch/v5 v5.5.0
|
||||
## explicit
|
||||
github.com/evanphx/json-patch/v5
|
||||
# github.com/go-logr/logr v0.2.0
|
||||
github.com/go-logr/logr
|
||||
# github.com/go-openapi/jsonpointer v0.19.3
|
||||
|
@ -295,9 +298,9 @@ go.uber.org/atomic
|
|||
go.uber.org/automaxprocs/internal/cgroups
|
||||
go.uber.org/automaxprocs/internal/runtime
|
||||
go.uber.org/automaxprocs/maxprocs
|
||||
# go.uber.org/multierr v1.5.0
|
||||
# go.uber.org/multierr v1.6.0
|
||||
go.uber.org/multierr
|
||||
# go.uber.org/zap v1.16.0
|
||||
# go.uber.org/zap v1.17.0
|
||||
## explicit
|
||||
go.uber.org/zap
|
||||
go.uber.org/zap/buffer
|
||||
|
@ -366,7 +369,7 @@ golang.org/x/text/width
|
|||
# golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba
|
||||
## explicit
|
||||
golang.org/x/time/rate
|
||||
# golang.org/x/tools v0.1.1
|
||||
# golang.org/x/tools v0.1.2
|
||||
## explicit
|
||||
golang.org/x/tools/cmd/goimports
|
||||
golang.org/x/tools/go/ast/astutil
|
||||
|
@ -544,9 +547,6 @@ google.golang.org/protobuf/types/pluginpb
|
|||
gopkg.in/alecthomas/kingpin.v2
|
||||
# gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f
|
||||
## explicit
|
||||
# gopkg.in/evanphx/json-patch.v4 v4.9.0
|
||||
## explicit
|
||||
gopkg.in/evanphx/json-patch.v4
|
||||
# gopkg.in/inf.v0 v0.9.1
|
||||
gopkg.in/inf.v0
|
||||
# gopkg.in/yaml.v2 v2.3.0
|
||||
|
@ -554,8 +554,6 @@ gopkg.in/yaml.v2
|
|||
# gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
## explicit
|
||||
gopkg.in/yaml.v3
|
||||
# honnef.co/go/tools v0.0.1-2020.1.5
|
||||
## explicit
|
||||
# k8s.io/api v0.19.7
|
||||
## explicit
|
||||
k8s.io/api/admission/v1
|
||||
|
|
Loading…
Reference in New Issue