From d5e81c2e9fe66485c6b89bbede3ac7e1e4b0d10d Mon Sep 17 00:00:00 2001 From: Rob Galanakis Date: Wed, 31 Jul 2024 10:22:51 -0700 Subject: [PATCH 1/6] Upgrade to go 1.22 and run fmt --- .github/workflows/pr-checks.yml | 4 +- api/api.go | 18 ++-- api/apiparams/doc.go | 34 +++---- api/apiparams/paramfield.go | 10 +- api/apiparams/reflector.go | 38 ++++---- go.mod | 26 +++++- go.sum | 159 ++++++-------------------------- kronos/kronos.go | 6 +- mariobros/mariobros.go | 28 +++--- validator/doc.go | 17 ++-- validator/validators.go | 7 +- 11 files changed, 132 insertions(+), 215 deletions(-) diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index 3c0189d..73f1d63 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -10,10 +10,10 @@ jobs: uses: actions/checkout@v2 with: ref: ${{ github.head_ref }} - - name: Set up Go 1.16.x + - name: Set up Go 1.22.x uses: actions/setup-go@v1 with: - go-version: 1.16.x + go-version: 1.22.x - uses: actions/cache@v1 with: path: ~/go/pkg/mod diff --git a/api/api.go b/api/api.go index d4304ab..5b9f128 100644 --- a/api/api.go +++ b/api/api.go @@ -4,15 +4,15 @@ It sets up /statusz and /healthz endpoints, and sets up logging middleware that takes care of the following important, and fundamentally (in Go) interconnected tasks: -- Extract (or add) a trace ID header to the request and response. -- The trace ID can be retrieved through api.TraceID(context) of the echo.Context for the request. -- Use that trace ID header as context for the logrus logger. -- Handle request logging (metadata about the request and response, - and log at the level appropriate for the status code). -- The request logger can be retrieved api.Logger(echo.Context). -- Recover from panics. -- Coerce all errors into api.Error types, and marshal them. -- Override echo's HTTPErrorHandler to pass through api.Error types. + - Extract (or add) a trace ID header to the request and response. + - The trace ID can be retrieved through api.TraceID(context) of the echo.Context for the request. + - Use that trace ID header as context for the logrus logger. + - Handle request logging (metadata about the request and response, + and log at the level appropriate for the status code). + - The request logger can be retrieved api.Logger(echo.Context). + - Recover from panics. + - Coerce all errors into api.Error types, and marshal them. + - Override echo's HTTPErrorHandler to pass through api.Error types. */ package api diff --git a/api/apiparams/doc.go b/api/apiparams/doc.go index 41cef2f..a489645 100644 --- a/api/apiparams/doc.go +++ b/api/apiparams/doc.go @@ -34,24 +34,24 @@ For example, consider the following test: Note all the benefits: -- Data is pulled from path parameters, query parameters, any JSON body, - and defaults defined in struct tags. The variable names used for values - is specified via the appropriate struct tag. - See ParamSource for more details, but possible tags are "path", "query", "header", "form", and "json". - The "json" tag will bind from any source, not just a JSON request body. - This makes it clear at the endpoint and model definitions where data comes from and - how an endpoint is supposed to be called. -- Path and query param coercion is done from the basic JSON types, - depending on the struct field type (int/float, string, bool). -- Validation is done using the validator package. - Custom validators can be registered as we need to express more - sophisticated validations. - -Validations + - Data is pulled from path parameters, query parameters, any JSON body, + and defaults defined in struct tags. The variable names used for values + is specified via the appropriate struct tag. + See ParamSource for more details, but possible tags are "path", "query", "header", "form", and "json". + The "json" tag will bind from any source, not just a JSON request body. + This makes it clear at the endpoint and model definitions where data comes from and + how an endpoint is supposed to be called. + - Path and query param coercion is done from the basic JSON types, + depending on the struct field type (int/float, string, bool). + - Validation is done using the validator package. + Custom validators can be registered as we need to express more + sophisticated validations. + +# Validations See validator for a list of available validators and usage examples. -Adapters +# Adapters The only non-obvious prerequisite to using apiparams.BindAndValidate is to create a apiparams.Adapter for your HTTP framework of choice. @@ -125,7 +125,7 @@ chi pulls data out of there to figure out a URL Param, like when chi.URLParam is Note again that in general only one of these need to be defined and once per-project (or you can put them into a library, whatever floats your boat). -Errors +# Errors apiparams.BindAndValidate returns a apiparams.HTTPError. Nil result means no error. The HTTPError can be one of various error codes (415, 422, 400, 500) @@ -136,7 +136,7 @@ parseable-but-invalid value (like a too-high number), or malformed JSON. Callers should wrap the result in the appropriate error for their framework, or can write the Code and Message to the HTTP response. -Custom Types +# Custom Types Custom types can be used in an API by providing a CustomTypeDef and passing it to RegisterCustomType. A CustomTypeDef consists of a _defaulter_ and a _parser_. diff --git a/api/apiparams/paramfield.go b/api/apiparams/paramfield.go index ca1b0ab..1aacf41 100644 --- a/api/apiparams/paramfield.go +++ b/api/apiparams/paramfield.go @@ -7,7 +7,9 @@ import ( // ParamSource is a struct tag name that can define where a field is set by. // For example, a field of: -// Wibble string `path:"wibble"` +// +// Wibble string `path:"wibble"` +// // would be said to have a Source of "path". // In general, fields can only be set from their parameter source, // so that the Wibble field can only be set from the path and not a query parameter. @@ -36,7 +38,7 @@ var AllParamSources = []ParamSource{ // whether via query, path, header, or json/body parameters. // For a struct field of: // -// Field string `header:"x-my-field"` +// Field string `header:"x-my-field"` // // - Name is "x-my-field" // - Source is "header" @@ -51,7 +53,9 @@ type paramField struct { // that indicates how the parameter is supposed to be set: its Source (header, query, path, json) // the Name used to set the parameter, and a reference back to the parsed StructField. // This means parsing the struct field: -// Field string `query:"pretty"` +// +// Field string `query:"pretty"` +// // would return a paramField with a Source of "query" and Name of "pretty". // This also resolves json field naming rules (like `query:"-"` indicating not to set the field). // If no paramField can be parsed (it has no tags, or the tags indicate not to export the field), diff --git a/api/apiparams/reflector.go b/api/apiparams/reflector.go index 7667526..30f012b 100644 --- a/api/apiparams/reflector.go +++ b/api/apiparams/reflector.go @@ -114,9 +114,9 @@ func (f *fieldMapper) mapAndFlushRun() { // mapping the reflect.StructField to the name we should expect // it to be called in parameters. In other words, this struct: // -// type Params struct { -// Foo string `json:"foo"` -// } +// type Params struct { +// Foo string `json:"foo"` +// } // // would set a map of // {"foo": } for paramFieldsByJsonName and @@ -126,13 +126,13 @@ func (f *fieldMapper) mapAndFlushRun() { // // For nested params, the mapping is still flat. For example, this struct: // -// type Params struct { -// []struct { -// A string `json:"a"` -// } -// Nest struct { -// B int `json:"b"` -// } `json:"nest"` +// type Params struct { +// []struct { +// A string `json:"a"` +// } +// Nest struct { +// B int `json:"b"` +// } `json:"nest"` // // would set a map of // {"a": , "nest":, "b":} for paramFieldsByJsonName and @@ -143,8 +143,8 @@ func (f *fieldMapper) mapAndFlushRun() { // Since path/query params are a flat list of key-value pairs, we don't need // deep parameters from the struct. // - Mapping validation field errors (like "Foo" or "Nest[0].B" to JSON names. -// The only alternative is to map names back after the fact, -// or write yet-another-validator that is consistent with the way we parse names +// The only alternative is to map names back after the fact, +// or write yet-another-validator that is consistent with the way we parse names // from struct tags. // See the MapFieldNameToParamName method doc for more details on how this works. func (r reflector) parseStructTags(underlyingType reflect.Type) { @@ -183,8 +183,8 @@ func (r reflector) parseStructTags(underlyingType reflect.Type) { // or because a field is being set of a type that isn't supported. // For the latter case, imagine a struct with: // -// D time.Time `json:"d"` -// Foo MyFooType `json:"foo"` +// D time.Time `json:"d"` +// Foo MyFooType `json:"foo"` // // time.Time is a supported type, so would be fine, but MyFooType // is not a supported type so this code would panic. @@ -202,11 +202,11 @@ func (r reflector) setField(fieldDef reflect.StructField, field reflect.Value, v // parseValue parses a string value into a reflect.Value that can be set via reflection. // -// - t is the reflect.Type of the field that the value will be parsed into, -// such as a basic type like string or int, a slice type like []string or []int, or a struct type. -// - field is the reflect.Value of the existing struct field- -// this is only used for slice types, which need to append to the field. -// - value is the string value to parse. +// - t is the reflect.Type of the field that the value will be parsed into, +// such as a basic type like string or int, a slice type like []string or []int, or a struct type. +// - field is the reflect.Value of the existing struct field- +// this is only used for slice types, which need to append to the field. +// - value is the string value to parse. // // This is verbose, if generally straightforward. // If t is not a pointer type, the reflect.Value returned points to the new field value. diff --git a/go.mod b/go.mod index d799210..e0d7eae 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/lithictech/go-aperitif -go 1.16 +go 1.22 require ( github.com/auth0/go-jwt-middleware v0.0.0-20200507191422-d30d7b9ece63 @@ -8,15 +8,31 @@ require ( github.com/google/uuid v1.1.1 github.com/hashicorp/go-multierror v1.1.0 github.com/jmoiron/sqlx v1.2.0 - github.com/labstack/echo v3.3.10+incompatible - github.com/labstack/gommon v0.3.0 // indirect + github.com/labstack/echo/v4 v4.12.0 github.com/onsi/ginkgo/v2 v2.3.1 github.com/onsi/gomega v1.22.0 github.com/pkg/errors v0.9.1 github.com/rgalanakis/golangal v1.1.0 github.com/rgalanakis/validator v0.0.0-20180731224108-4a34a8927f7c github.com/sirupsen/logrus v1.6.0 - github.com/valyala/fasttemplate v1.1.0 // indirect - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 + golang.org/x/crypto v0.22.0 +) + +require ( + github.com/golang-jwt/jwt v3.2.2+incompatible // indirect + github.com/google/go-cmp v0.5.8 // indirect + github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect + github.com/labstack/gommon v0.4.2 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/term v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.5.0 // indirect google.golang.org/appengine v1.6.6 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 1740b47..b998998 100644 --- a/go.sum +++ b/go.sum @@ -1,38 +1,20 @@ github.com/auth0/go-jwt-middleware v0.0.0-20200507191422-d30d7b9ece63 h1:LY/kRH+fCqA090FsM2VfZ+oocD99ogm3HrT1r0WDnCk= github.com/auth0/go-jwt-middleware v0.0.0-20200507191422-d30d7b9ece63/go.mod h1:mF0ip7kTEFtnhBJbd/gJe62US3jykNN+dcZoZakJCCA= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= -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/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -44,43 +26,27 @@ github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/U github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg= -github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= -github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= -github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= +github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= +github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= +github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= -github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= github.com/onsi/ginkgo/v2 v2.3.1 h1:8SbseP7qM32WcvE6VaN6vfXxv698izmsJ1UQX9ve7T8= github.com/onsi/ginkgo/v2 v2.3.1/go.mod h1:Sv4yQXwG5VmF7tm3Q5Z+RWUpPo24LF1mpnz2crUb8Ys= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= github.com/onsi/gomega v1.22.0 h1:AIg2/OntwkBiCg5Tt1ayyiF1ArFrWFoCSMtMi/wdApk= github.com/onsi/gomega v1.22.0/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -98,110 +64,43 @@ github.com/smartystreets/assertions v1.1.0 h1:MkTeG1DMwsrdH7QtLXy5W+fUxWq+vmb6cL github.com/smartystreets/assertions v1.1.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= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/valyala/fasttemplate v1.1.0 h1:RZqt0yGBsps8NGvLSGW804QQqCUYYLsaOjTVHy1Ocw4= -github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= 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-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/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-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= 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= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -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/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -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= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/kronos/kronos.go b/kronos/kronos.go index b1544e9..e9f1c0e 100644 --- a/kronos/kronos.go +++ b/kronos/kronos.go @@ -119,9 +119,9 @@ func RollMonth(t time.Time, months int) time.Time { // Get the new month after offsetting month m by offset. // -// offsetMonth(January, 1) => February -// offsetMonth(January, 13) => February -// offsetMonth(January, -1) => December +// offsetMonth(January, 1) => February +// offsetMonth(January, 13) => February +// offsetMonth(January, -1) => December func offsetMonth(m time.Month, offset int) time.Month { zeroBased := int(m - 1) offsetMonth := zeroBased + offset diff --git a/mariobros/mariobros.go b/mariobros/mariobros.go index f6f8cd2..14bdcb2 100644 --- a/mariobros/mariobros.go +++ b/mariobros/mariobros.go @@ -7,11 +7,11 @@ // the operation, like 'level1.area4.lavapit'. Then defer the callback returned from that function, // like: // -// go func() { -// mario := mariobros.Yo("level1.area4.lavapit") -// defer mario() -// // Do more stuff... -// } +// go func() { +// mario := mariobros.Yo("level1.area4.lavapit") +// defer mario() +// // Do more stuff... +// } // // Every 5 seconds (or whatever is configured), mariobros will report on the active goroutines. // @@ -22,7 +22,6 @@ // // If Mariobros is not active, calls to Yo noop and the timer that prints does not run. // It's important to call Mariobros.Start() early, or import mariobros/autoload. -// package mariobros import ( @@ -42,10 +41,9 @@ type Writer func(totalActive uint, activePerName map[string][]GoroutineId) // StreamWriter is used when you want to write mariobros output to a stream. // The output you get is like: // -// active goroutines (1): -// my.job: 1, 5 -// other.job: 6, 7 -// +// active goroutines (1): +// my.job: 1, 5 +// other.job: 6, 7 func StreamWriter(w io.Writer) Writer { return func(totalActive uint, activePerName map[string][]GoroutineId) { w := func(s string, a ...interface{}) { @@ -67,11 +65,11 @@ func StreamWriter(w io.Writer) Writer { // KeyValueWriter is helpful when you want to log a structured message. // For example: // -// mariobros.Start(mariobros.NewOptions(func(o *mariobros.Options) { -// o.Writer = mariobros.KeyValueWriter("mariobros_", func(m map[string]interface{}) { -// logger.WithFields(m).Info("mariobros") -// }) -// })) +// mariobros.Start(mariobros.NewOptions(func(o *mariobros.Options) { +// o.Writer = mariobros.KeyValueWriter("mariobros_", func(m map[string]interface{}) { +// logger.WithFields(m).Info("mariobros") +// }) +// })) func KeyValueWriter(keyPrefix string, write func(map[string]interface{})) Writer { return func(totalActive uint, activePerName map[string][]GoroutineId) { result := make(map[string]interface{}, len(activePerName)+1) diff --git a/validator/doc.go b/validator/doc.go index 340d56a..77ae845 100644 --- a/validator/doc.go +++ b/validator/doc.go @@ -91,7 +91,7 @@ Available validators include: (validation will only be done if a value is provided). (Usage: comparenow=hour|gte comparenow=day|lt|opt) -Optional validations +# Optional validations Most validators support a way to specify they are optional. Usually that is something like providing "opt" as a value, like `intid=opt`, @@ -100,7 +100,7 @@ See example usages for details. Nil pointers are generally considered valid. See Pointers section for more details. -Pointers +# Pointers If validator is validating a pointer field, it will generally validate the underlying type the same as non-pointer fields. The only real difference is that a nil pointer will be considered valid, @@ -109,15 +109,14 @@ because pointer fields generally specify a value is optional. If a nil pointer isn't valid for a pointer field, you can use the "nonzero" validation. For example, a nil pointer is acceptable here, even though there is no trailing "|opt" flag: - type d struct { - D *time.Time `json:"d" validate:"comparenow=lte|day"` - } + type d struct { + D *time.Time `json:"d" validate:"comparenow=lte|day"` + } However, a nil pointer is not acceptable here, because of the "nonzero" validation: - type d struct { - D *time.Time `json:"d" validate:"comparenow=lte|day,nonzero"` - } - + type d struct { + D *time.Time `json:"d" validate:"comparenow=lte|day,nonzero"` + } */ package validator diff --git a/validator/validators.go b/validator/validators.go index 38ef98a..223ac2b 100644 --- a/validator/validators.go +++ b/validator/validators.go @@ -29,9 +29,10 @@ const optional = "opt" // Split the param string on |, // and return a type of (other args, if param ends in |opt, error in the case of empty args). // Examples: -// "a|b" -> (["a", "b"], false, nil) -// "a|opt" -> (["a"], true, nil) -// "|opt" -> ([], false, ) +// +// "a|b" -> (["a", "b"], false, nil) +// "a|opt" -> (["a"], true, nil) +// "|opt" -> ([], false, ) func splitOptionalVal(param string) ([]string, bool, error) { params := strings.Split(param, "|") if len(params) == 0 { From 84c78bf6858854e095e9ef7e1013ae6abc1f8f09 Mon Sep 17 00:00:00 2001 From: Rob Galanakis Date: Wed, 31 Jul 2024 10:22:59 -0700 Subject: [PATCH 2/6] Upgrade to echo v4 --- api/adapter.go | 2 +- api/api.go | 4 ++-- api/api_test.go | 2 +- api/apiparams/apiparams_test.go | 2 +- api/auth0jwt/auth0jwt.go | 2 +- api/cache_control.go | 2 +- api/debug.go | 4 ++-- api/echoapitest/echoapitest.go | 2 +- api/logging.go | 2 +- api/preflight/preflight.go | 2 +- api/preflight/preflight_test.go | 2 +- api/spa/spa.go | 2 +- api/spa/spa_test.go | 2 +- api/trace_id.go | 2 +- 14 files changed, 16 insertions(+), 16 deletions(-) diff --git a/api/adapter.go b/api/adapter.go index 07de7b4..8f7a9a9 100644 --- a/api/adapter.go +++ b/api/adapter.go @@ -2,7 +2,7 @@ package api import ( "context" - "github.com/labstack/echo" + "github.com/labstack/echo/v4" "github.com/lithictech/go-aperitif/logctx" ) diff --git a/api/api.go b/api/api.go index 5b9f128..c577962 100644 --- a/api/api.go +++ b/api/api.go @@ -17,8 +17,8 @@ and fundamentally (in Go) interconnected tasks: package api import ( - "github.com/labstack/echo" - "github.com/labstack/echo/middleware" + "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" "github.com/sirupsen/logrus" "net/http" "os" diff --git a/api/api_test.go b/api/api_test.go index 6879105..4e7dc9d 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -2,7 +2,7 @@ package api_test import ( "errors" - "github.com/labstack/echo" + "github.com/labstack/echo/v4" "github.com/lithictech/go-aperitif/api" "github.com/lithictech/go-aperitif/api/apiparams" . "github.com/lithictech/go-aperitif/api/echoapitest" diff --git a/api/apiparams/apiparams_test.go b/api/apiparams/apiparams_test.go index d390298..079383d 100644 --- a/api/apiparams/apiparams_test.go +++ b/api/apiparams/apiparams_test.go @@ -2,7 +2,7 @@ package apiparams_test import ( "fmt" - "github.com/labstack/echo" + "github.com/labstack/echo/v4" "github.com/lithictech/go-aperitif/api/apiparams" . "github.com/lithictech/go-aperitif/api/echoapitest" . "github.com/lithictech/go-aperitif/apitest" diff --git a/api/auth0jwt/auth0jwt.go b/api/auth0jwt/auth0jwt.go index 1e19f1b..941abf5 100644 --- a/api/auth0jwt/auth0jwt.go +++ b/api/auth0jwt/auth0jwt.go @@ -9,7 +9,7 @@ import ( "errors" "github.com/auth0/go-jwt-middleware" "github.com/dgrijalva/jwt-go" - "github.com/labstack/echo" + "github.com/labstack/echo/v4" "net/http" ) diff --git a/api/cache_control.go b/api/cache_control.go index cfe9068..1c502dd 100644 --- a/api/cache_control.go +++ b/api/cache_control.go @@ -1,7 +1,7 @@ package api import ( - "github.com/labstack/echo" + "github.com/labstack/echo/v4" ) func WithCacheControl(enabled bool, value string) echo.MiddlewareFunc { diff --git a/api/debug.go b/api/debug.go index 312517e..3183b75 100644 --- a/api/debug.go +++ b/api/debug.go @@ -1,8 +1,8 @@ package api import ( - "github.com/labstack/echo" - "github.com/labstack/echo/middleware" + "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" "github.com/lithictech/go-aperitif/logctx" "net/http" "runtime" diff --git a/api/echoapitest/echoapitest.go b/api/echoapitest/echoapitest.go index 45d5ffb..18cab8e 100644 --- a/api/echoapitest/echoapitest.go +++ b/api/echoapitest/echoapitest.go @@ -1,7 +1,7 @@ package echoapitest import ( - "github.com/labstack/echo" + "github.com/labstack/echo/v4" "net/http" "net/http/httptest" ) diff --git a/api/logging.go b/api/logging.go index 65ac53b..ee078f0 100644 --- a/api/logging.go +++ b/api/logging.go @@ -2,7 +2,7 @@ package api import ( "fmt" - "github.com/labstack/echo" + "github.com/labstack/echo/v4" "github.com/lithictech/go-aperitif/api/apiparams" "github.com/lithictech/go-aperitif/logctx" "github.com/sirupsen/logrus" diff --git a/api/preflight/preflight.go b/api/preflight/preflight.go index 4f174ac..fdf05f5 100644 --- a/api/preflight/preflight.go +++ b/api/preflight/preflight.go @@ -1,7 +1,7 @@ package preflight import ( - "github.com/labstack/echo" + "github.com/labstack/echo/v4" "github.com/pkg/errors" "time" ) diff --git a/api/preflight/preflight_test.go b/api/preflight/preflight_test.go index f3a8858..f2b3500 100644 --- a/api/preflight/preflight_test.go +++ b/api/preflight/preflight_test.go @@ -2,7 +2,7 @@ package preflight_test import ( "errors" - "github.com/labstack/echo" + "github.com/labstack/echo/v4" "github.com/lithictech/go-aperitif/api" . "github.com/lithictech/go-aperitif/api/echoapitest" "github.com/lithictech/go-aperitif/api/preflight" diff --git a/api/spa/spa.go b/api/spa/spa.go index ede7b5e..b232f1d 100644 --- a/api/spa/spa.go +++ b/api/spa/spa.go @@ -6,7 +6,7 @@ package spa import ( - "github.com/labstack/echo" + "github.com/labstack/echo/v4" "log" "net/http" ) diff --git a/api/spa/spa_test.go b/api/spa/spa_test.go index db55a39..2367b5b 100644 --- a/api/spa/spa_test.go +++ b/api/spa/spa_test.go @@ -1,7 +1,7 @@ package spa_test import ( - "github.com/labstack/echo" + "github.com/labstack/echo/v4" . "github.com/lithictech/go-aperitif/api/echoapitest" "github.com/lithictech/go-aperitif/api/spa" . "github.com/lithictech/go-aperitif/apitest" diff --git a/api/trace_id.go b/api/trace_id.go index 59b0cab..0d2ee2d 100644 --- a/api/trace_id.go +++ b/api/trace_id.go @@ -2,7 +2,7 @@ package api import ( "github.com/google/uuid" - "github.com/labstack/echo" + "github.com/labstack/echo/v4" "github.com/lithictech/go-aperitif/logctx" ) From f0b7ff29d061729e273ba8457b96255594888e0d Mon Sep 17 00:00:00 2001 From: Rob Galanakis Date: Wed, 31 Jul 2024 10:29:22 -0700 Subject: [PATCH 3/6] Remove JWT packages These depended on an outdated library, and were meant mostly as aids to that library. Since the libraries are newer, they have different and ideally better ways of doing things, so don't bother trying to do a direct upgrade. --- api/auth0jwt/auth0jwt.go | 139 ------------------------ api/auth0jwt/auth0jwt_test.go | 30 ------ go.mod | 33 +++--- go.sum | 85 +++++++++------ jwtee/jwtee.go | 149 ------------------------- jwtee/jwtee_test.go | 197 ---------------------------------- 6 files changed, 73 insertions(+), 560 deletions(-) delete mode 100644 api/auth0jwt/auth0jwt.go delete mode 100644 api/auth0jwt/auth0jwt_test.go delete mode 100644 jwtee/jwtee.go delete mode 100644 jwtee/jwtee_test.go diff --git a/api/auth0jwt/auth0jwt.go b/api/auth0jwt/auth0jwt.go deleted file mode 100644 index 941abf5..0000000 --- a/api/auth0jwt/auth0jwt.go +++ /dev/null @@ -1,139 +0,0 @@ -// Package auth0jwt is a modification of the Auth0 provided Go tutorial: -// https://auth0.com/docs/quickstart/backend/golang -// As you may guess that has several issues, but a lot of what's here has been taken verbatim. -package auth0jwt - -import ( - "crypto/subtle" - "encoding/json" - "errors" - "github.com/auth0/go-jwt-middleware" - "github.com/dgrijalva/jwt-go" - "github.com/labstack/echo/v4" - "net/http" -) - -type Jwks struct { - Keys []JSONWebKeys `json:"keys"` -} - -type JSONWebKeys struct { - Kty string `json:"kty"` - Kid string `json:"kid"` - Use string `json:"use"` - N string `json:"n"` - E string `json:"e"` - X5c []string `json:"x5c"` -} - -type Config struct { - // Aud is used to veirfy the 'aud' claim. It's the identifier of the API in Auth0. - Aud string - // Iss is used to verify the 'iss' claim. - Iss string - // JwksPath is the path to the file like "https://my-application.auth0.com/.well-known/jwks.json". - // See https://auth0.com/docs/tokens/concepts/jwks - JwksPath string -} - -func NewMiddleware(cfg Config) echo.MiddlewareFunc { - mw := jwtmiddleware.New(jwtmiddleware.Options{ - ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) { - checkAud := verifyArrayAudience(token.Claims.(jwt.MapClaims), cfg.Aud, true) - if !checkAud { - return token, echo.NewHTTPError(401, "invalid audience") - } - checkIss := token.Claims.(jwt.MapClaims).VerifyIssuer(cfg.Iss, true) - if !checkIss { - return token, echo.NewHTTPError(401, "invalid issuer") - } - - cert, err := getPemCert(cfg.JwksPath, token) - if err != nil { - return nil, err - } - - result, _ := jwt.ParseRSAPublicKeyFromPEM([]byte(cert)) - return result, nil - }, - UserProperty: "user", - CredentialsOptional: false, - Debug: false, - }) - - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - req := c.Request() - // req gets stomped by CheckJWT, to have a new context - err := mw.CheckJWT(c.Response().Writer, req) - if err != nil { - return err - } - user := req.Context().Value(mw.Options.UserProperty) - if user == nil { - panic("why is 'user' nil in jwt context!?") - } - c.Set(mw.Options.UserProperty, user) - return next(c) - } - } -} - -func getPemCert(jwksPath string, token *jwt.Token) (string, error) { - cert := "" - resp, err := http.Get(jwksPath) - - if err != nil { - return cert, err - } - defer resp.Body.Close() - - var jwks = Jwks{} - err = json.NewDecoder(resp.Body).Decode(&jwks) - - if err != nil { - return cert, err - } - - for k := range jwks.Keys { - if token.Header["kid"] == jwks.Keys[k].Kid { - cert = "-----BEGIN CERTIFICATE-----\n" + jwks.Keys[k].X5c[0] + "\n-----END CERTIFICATE-----" - } - } - - if cert == "" { - err := errors.New("unable to find appropriate key") - return cert, err - } - - return cert, nil -} - -// Seehttps://github.com/dgrijalva/jwt-go/pull/308 -// These two methods are straight copy paste -func verifyArrayAudience(m jwt.MapClaims, cmp string, req bool) bool { - switch m["aud"].(type) { - case string: - aud := m["aud"].(string) - return verifyAudHelper(aud, cmp, req) - default: - auds := m["aud"].([]interface{}) - for _, aud := range auds { - if verifyAudHelper(aud.(string), cmp, req) { - return true - } - } - return false - } -} - -func verifyAudHelper(aud string, cmp string, required bool) bool { - if aud == "" { - return !required - } - if subtle.ConstantTimeCompare([]byte(aud), []byte(cmp)) != 0 { - return true - } else { - return false - } -} diff --git a/api/auth0jwt/auth0jwt_test.go b/api/auth0jwt/auth0jwt_test.go deleted file mode 100644 index d5eca98..0000000 --- a/api/auth0jwt/auth0jwt_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package auth0jwt_test - -import ( - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "testing" -) - -func TestAuth0Jwt(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "API Suite") -} - -var _ = Describe("auth0jwt", func() { - //var e *echo.Echo - - BeforeEach(func() { - //e = echo.New() - }) - - It("validates against iss", func() { - }) - It("validates against aud string", func() { - }) - It("validates against aud array", func() { - }) - It("adds the user to the echo context", func() { - }) - It("validates against the PEM cert", func() {}) -}) diff --git a/go.mod b/go.mod index e0d7eae..1bb5eb7 100644 --- a/go.mod +++ b/go.mod @@ -3,24 +3,25 @@ module github.com/lithictech/go-aperitif go 1.22 require ( - github.com/auth0/go-jwt-middleware v0.0.0-20200507191422-d30d7b9ece63 - github.com/dgrijalva/jwt-go v3.2.0+incompatible - github.com/google/uuid v1.1.1 - github.com/hashicorp/go-multierror v1.1.0 - github.com/jmoiron/sqlx v1.2.0 + github.com/google/uuid v1.6.0 + github.com/hashicorp/go-multierror v1.1.1 + github.com/jmoiron/sqlx v1.4.0 github.com/labstack/echo/v4 v4.12.0 - github.com/onsi/ginkgo/v2 v2.3.1 - github.com/onsi/gomega v1.22.0 + github.com/onsi/ginkgo/v2 v2.19.1 + github.com/onsi/gomega v1.34.1 github.com/pkg/errors v0.9.1 - github.com/rgalanakis/golangal v1.1.0 + github.com/rgalanakis/golangal v1.2.0 github.com/rgalanakis/validator v0.0.0-20180731224108-4a34a8927f7c - github.com/sirupsen/logrus v1.6.0 - golang.org/x/crypto v0.22.0 + github.com/sirupsen/logrus v1.9.3 + golang.org/x/crypto v0.25.0 ) require ( + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect - github.com/google/go-cmp v0.5.8 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect github.com/labstack/gommon v0.4.2 // indirect @@ -28,11 +29,13 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect - golang.org/x/net v0.24.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/term v0.19.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect + golang.org/x/net v0.27.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/term v0.22.0 // indirect + golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.23.0 // indirect google.golang.org/appengine v1.6.6 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index b998998..55cffea 100644 --- a/go.sum +++ b/go.sum @@ -1,35 +1,41 @@ -github.com/auth0/go-jwt-middleware v0.0.0-20200507191422-d30d7b9ece63 h1:LY/kRH+fCqA090FsM2VfZ+oocD99ogm3HrT1r0WDnCk= -github.com/auth0/go-jwt-middleware v0.0.0-20200507191422-d30d7b9ece63/go.mod h1:mF0ip7kTEFtnhBJbd/gJe62US3jykNN+dcZoZakJCCA= -github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +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/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg= +github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0= -github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= -github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= +github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= @@ -38,6 +44,7 @@ github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0 github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -45,62 +52,80 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/onsi/ginkgo/v2 v2.3.1 h1:8SbseP7qM32WcvE6VaN6vfXxv698izmsJ1UQX9ve7T8= github.com/onsi/ginkgo/v2 v2.3.1/go.mod h1:Sv4yQXwG5VmF7tm3Q5Z+RWUpPo24LF1mpnz2crUb8Ys= +github.com/onsi/ginkgo/v2 v2.19.1 h1:QXgq3Z8Crl5EL1WBAC98A5sEBHARrAJNzAmMxzLcRF0= +github.com/onsi/ginkgo/v2 v2.19.1/go.mod h1:O3DtEWQkPa/F7fBMgmZQKKsluAy8pd3rEQdrjkPb9zA= github.com/onsi/gomega v1.22.0 h1:AIg2/OntwkBiCg5Tt1ayyiF1ArFrWFoCSMtMi/wdApk= github.com/onsi/gomega v1.22.0/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= +github.com/onsi/gomega v1.34.0 h1:eSSPsPNp6ZpsG8X1OVmOTxig+CblTc4AxpPBykhe2Os= +github.com/onsi/gomega v1.34.0/go.mod h1:MIKI8c+f+QLWk+hxbePD4i0LMJSExPaZOVfkoex4cAo= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.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/rgalanakis/golangal v1.1.0 h1:JMR5gBHMmcWQ86hYM8mRXp99AC1AeEdWKc4sNfjjA3A= github.com/rgalanakis/golangal v1.1.0/go.mod h1:DT35MZom81QtvdrIerWZ0T3moT/npNBiJmW9cXcufMM= +github.com/rgalanakis/golangal v1.2.0 h1:dDrD6sJR2JbnNl0aDNvBHjzWN6r9lDTfv3XqKTNTMmg= +github.com/rgalanakis/golangal v1.2.0/go.mod h1:DT35MZom81QtvdrIerWZ0T3moT/npNBiJmW9cXcufMM= github.com/rgalanakis/validator v0.0.0-20180731224108-4a34a8927f7c h1:z1J+SUwpje5zEyPfHBIv6N++OxjETBV5wGBwkmO/Wzk= github.com/rgalanakis/validator v0.0.0-20180731224108-4a34a8927f7c/go.mod h1:qIw2dAd/4wMHHnINySB5+y9HPKQXUKmga0oZHSluPKE= github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.1.0 h1:MkTeG1DMwsrdH7QtLXy5W+fUxWq+vmb6cLmyJ7aRtF0= -github.com/smartystreets/assertions v1.1.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= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= -github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= -golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 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.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= +golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= 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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/jwtee/jwtee.go b/jwtee/jwtee.go deleted file mode 100644 index 025454d..0000000 --- a/jwtee/jwtee.go +++ /dev/null @@ -1,149 +0,0 @@ -// Package jwtee wraps github.com/dgrijalva/jwt-go -// with some tooling that makes it easier to use -// in most practical usage. -package jwtee - -import ( - "crypto/subtle" - "errors" - "github.com/dgrijalva/jwt-go" - "time" -) - -type Error struct { - msg string -} - -func (e Error) Error() string { - return e.msg -} - -type Jwtee struct { - Secret []byte - Aud string - Iss string - Alg jwt.SigningMethod -} - -type Input struct { - Secret string - Aud string - Iss string - Alg string -} - -func New(input Input) (Jwtee, error) { - j := Jwtee{ - Secret: []byte(input.Secret), - Aud: input.Aud, - Iss: input.Iss, - Alg: jwt.GetSigningMethod(input.Alg), - } - if len(j.Secret) == 0 { - return j, errors.New("secret is required") - } - if j.Aud == "" { - return j, errors.New("aud is required") - } - if j.Iss == "" { - return j, errors.New("iss is required") - } - if j.Alg == nil { - return j, errors.New("valid alg is required") - } - return j, nil -} - -func (j Jwtee) Parse(tokenString string) (*jwt.Token, error) { - return jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { - if token.Method != j.Alg { - return token, Error{msg: "invalid alg"} - } - checkAud := verifyArrayAudience(token.Claims.(jwt.MapClaims), j.Aud, true) - if !checkAud { - return token, Error{msg: "invalid aud"} - } - checkIss := token.Claims.(jwt.MapClaims).VerifyIssuer(j.Iss, true) - if !checkIss { - return token, Error{msg: "invalid iss"} - } - return j.Secret, nil - }) -} - -func (j Jwtee) ParseMapClaims(tokenString string) (jwt.MapClaims, error) { - tok, err := j.Parse(tokenString) - if tok == nil { - panic("token should never be nil") - } - return tok.Claims.(jwt.MapClaims), err -} - -func (j Jwtee) BuildTtl(ttl time.Duration, moreClaims map[string]interface{}) (string, error) { - tok := jwt.New(j.Alg) - mc := tok.Claims.(jwt.MapClaims) - mc["iss"] = j.Iss - mc["aud"] = j.Aud - mc["exp"] = jwt.TimeFunc().Add(ttl).Unix() - for k, v := range moreClaims { - mc[k] = v - } - return tok.SignedString(j.Secret) -} - -func (j Jwtee) Dup(input Input) Jwtee { - if len(input.Secret) > 0 { - j.Secret = []byte(input.Secret) - } - if input.Aud != "" { - j.Aud = input.Aud - } - if input.Iss != "" { - j.Iss = input.Iss - } - if input.Alg != "" { - j.Alg = jwt.GetSigningMethod(input.Alg) - } - return j -} - -// See https://github.com/dgrijalva/jwt-go/pull/308 -// These two methods are straight copy paste -func verifyArrayAudience(m jwt.MapClaims, cmp string, req bool) bool { - switch m["aud"].(type) { - case string: - aud := m["aud"].(string) - return verifyAudHelper(aud, cmp, req) - default: - auds := m["aud"].([]interface{}) - for _, aud := range auds { - if verifyAudHelper(aud.(string), cmp, req) { - return true - } - } - return false - } -} - -func verifyAudHelper(aud string, cmp string, required bool) bool { - if aud == "" { - return !required - } - if subtle.ConstantTimeCompare([]byte(aud), []byte(cmp)) != 0 { - return true - } else { - return false - } -} - -func StringClaim(claims jwt.MapClaims, key string) (string, bool) { - v, ok := claims[key] - if !ok { - return "", false - } - s, ok := v.(string) - if !ok { - return "", false - } - return s, len(s) > 0 -} diff --git a/jwtee/jwtee_test.go b/jwtee/jwtee_test.go deleted file mode 100644 index 3455b93..0000000 --- a/jwtee/jwtee_test.go +++ /dev/null @@ -1,197 +0,0 @@ -package jwtee_test - -import ( - "github.com/dgrijalva/jwt-go" - "github.com/lithictech/go-aperitif/jwtee" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - . "github.com/rgalanakis/golangal" - "testing" - "time" -) - -func TestJwtee(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "jwtee package Suite") -} - -var _ = Describe("jwtee", func() { - secret := "xyz" - aud := "hi" - iss := "there" - alg := "HS256" - - validInput := func() jwtee.Input { - return jwtee.Input{ - Secret: secret, - Aud: aud, - Iss: iss, - Alg: alg, - } - } - - newJwtee := func() jwtee.Jwtee { - j, err := jwtee.New(validInput()) - Expect(err).ToNot(HaveOccurred()) - return j - } - - It("requires secret, aud, iss, and alg", func() { - var err error - var jw jwtee.Jwtee - jw, err = jwtee.New(validInput()) - Expect(err).ToNot(HaveOccurred()) - Expect(jw).To(And( - MatchField("Aud", aud), - MatchField("Iss", iss), - MatchField("Alg", jwt.SigningMethodHS256), - )) - _, err = jwtee.New(jwtee.Input{ - Secret: "", - Aud: aud, - Iss: iss, - Alg: alg, - }) - Expect(err).To(MatchError(ContainSubstring("secret is required"))) - _, err = jwtee.New(jwtee.Input{ - Secret: secret, - Aud: "", - Iss: iss, - Alg: alg, - }) - Expect(err).To(MatchError(ContainSubstring("aud is required"))) - _, err = jwtee.New(jwtee.Input{ - Secret: secret, - Aud: aud, - Iss: "", - Alg: alg, - }) - Expect(err).To(MatchError(ContainSubstring("iss is required"))) - _, err = jwtee.New(jwtee.Input{ - Secret: secret, - Aud: aud, - Iss: iss, - Alg: "", - }) - Expect(err).To(MatchError(ContainSubstring("alg is required"))) - }) - It("can dup itself with non-empty input values", func() { - jw := newJwtee() - jw2 := jw.Dup(jwtee.Input{Aud: "another"}) - Expect(jw.Aud).To(Equal(aud)) - Expect(jw.Iss).To(Equal(iss)) - Expect(jw2.Aud).To(Equal("another")) - Expect(jw2.Iss).To(Equal(iss)) - }) - Describe("parsing", func() { - It("can verify with a string aud claim", func() { - jw := newJwtee() - s := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJoaSIsImlzcyI6InRoZXJlIiwiaWF0IjoxNTE2MjM5MDIyfQ.kTgZa43Zq9LrjDAEerD8feT2_TrIhzCPO1UC4bBXzgQ` - cl, err := jw.ParseMapClaims(s) - Expect(err).ToNot(HaveOccurred()) - Expect(cl["aud"]).To(Equal("hi")) - }) - - It("can fail with an invalid aud claim", func() { - jw := newJwtee() - s := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ5byIsImlzcyI6InRoZXJlIiwiaWF0IjoxNTE2MjM5MDIyfQ.BG7D0kCIcdgTfhOFNxArgubEL_2_WQmxE4vpnOv_AlU` - cl, err := jw.ParseMapClaims(s) - Expect(err).To(MatchError("invalid aud")) - Expect(cl["aud"]).To(Equal("yo")) - }) - It("can verify with an array aud claim", func() { - jw := newJwtee() - s := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiaGkiLCJoZWxsbyJdLCJpc3MiOiJ0aGVyZSIsImlhdCI6MTUxNjIzOTAyMn0.37-1H6f20flFs2vjJ6u2nzh7BQ51kyQyELEX0y_xE3c` - cl, err := jw.ParseMapClaims(s) - Expect(err).ToNot(HaveOccurred()) - Expect(cl["aud"]).To(BeEquivalentTo([]interface{}{"hi", "hello"})) - }) - It("can fail if aud not in array aud claim", func() { - jw := newJwtee() - s := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsieW8iXSwiaXNzIjoidGhlcmUiLCJpYXQiOjE1MTYyMzkwMjJ9.u-WkwjTF4kxdGB2wtinAtC1usOnIqeTPnDKg2HQ2gJw` - cl, err := jw.ParseMapClaims(s) - Expect(err).To(MatchError("invalid aud")) - Expect(cl["aud"]).To(BeEquivalentTo([]interface{}{"yo"})) - }) - It("can verify against an issuer", func() { - jw := newJwtee() - s := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJoaSIsImlzcyI6InRoZXJlIiwiaWF0IjoxNTE2MjM5MDIyfQ.kTgZa43Zq9LrjDAEerD8feT2_TrIhzCPO1UC4bBXzgQ` - cl, err := jw.ParseMapClaims(s) - Expect(err).ToNot(HaveOccurred()) - Expect(cl["iss"]).To(Equal("there")) - }) - It("can fail with an invalid issuer", func() { - jw := newJwtee() - s := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJoaSIsImlzcyI6InlvbmRlciIsImlhdCI6MTUxNjIzOTAyMn0.Wo0zf5P9H4HAnOWTgdUKNN0W-jTTJot0lEl5kE1r3YY` - cl, err := jw.ParseMapClaims(s) - Expect(err).To(MatchError("invalid iss")) - Expect(cl["iss"]).To(Equal("yonder")) - }) - It("validates against the signing method", func() { - jw := newJwtee() - s := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJoaSIsImlzcyI6InRoZXJlIiwiaWF0IjoxNTE2MjM5MDIyfQ.kTgZa43Zq9LrjDAEerD8feT2_TrIhzCPO1UC4bBXzgQ` - tok, err := jw.Parse(s) - Expect(err).ToNot(HaveOccurred()) - Expect(tok.Header["alg"]).To(Equal("HS256")) - }) - It("can fail with an unexpected signing method", func() { - jw := newJwtee() - s := `eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJoaSIsImlzcyI6InlvbmRlciIsImlhdCI6MTUxNjIzOTAyMn0.7q_DMegJbTO9uxWPy7n2mfDrBgAO3xBSpVmGjqHG6-ubve8QH2Y1d2noYWMk-wjSwkfbVB1K98FCfVDvZxhfGA` - tok, err := jw.Parse(s) - Expect(err).To(MatchError("invalid alg")) - Expect(tok.Header["alg"]).To(Equal("HS512")) - }) - It("can verify an unexpired exp", func() { - jw := newJwtee() - s := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJoaSIsImlzcyI6InRoZXJlIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1OTc5ODkyMzM1MTR9.bpLset-GCKbOlish900xGamrRCqQzmX06A2e2BtAdJE` - cl, err := jw.ParseMapClaims(s) - Expect(err).ToNot(HaveOccurred()) - Expect(cl["exp"]).To(BeEquivalentTo(1597989233514)) - }) - It("fails expired exp", func() { - jw := newJwtee() - s := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJoaSIsImlzcyI6InRoZXJlIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjV9.XsqW1BORkEZBwYXzDVgPJmSV-6wDzkFaZ7NacIfDjNY` - cl, err := jw.ParseMapClaims(s) - Expect(err).To(MatchError("Token is expired")) - Expect(cl["exp"]).To(BeEquivalentTo(5)) - }) - }) - Describe("building", func() { - origTime := jwt.TimeFunc - AfterEach(func() { - jwt.TimeFunc = origTime - }) - It("builds a token with the default fields and additional fields", func() { - jwt.TimeFunc = func() time.Time { - return time.Unix(10000, 0) - } - jw := newJwtee() - js, err := jw.BuildTtl(654*time.Second, map[string]interface{}{"sub": 1234}) - Expect(err).ToNot(HaveOccurred()) - expected := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJoaSIsImV4cCI6MTA2NTQsImlzcyI6InRoZXJlIiwic3ViIjoxMjM0fQ.OgPwnSrNaEpCgSMcILAdATor2NGlupnt7ggbqr32NL0` - Expect(js).To(Equal(expected)) - }) - }) - Describe("StringClaim", func() { - It("extracts a non-empty string claim", func() { - c := jwt.MapClaims{"s": "", "s2": "a", "i": 1} - var s string - var ok bool - s, ok = jwtee.StringClaim(c, "s2") - Expect(ok).To(BeTrue()) - Expect(s).To(Equal("a")) - - s, ok = jwtee.StringClaim(c, "s") - Expect(ok).To(BeFalse()) - Expect(s).To(BeEmpty()) - - s, ok = jwtee.StringClaim(c, "x") - Expect(ok).To(BeFalse()) - Expect(s).To(BeEmpty()) - - s, ok = jwtee.StringClaim(c, "i") - Expect(ok).To(BeFalse()) - Expect(s).To(BeEmpty()) - }) - }) -}) From 490ca5855aef4c636f917383af2411e43ca6da47 Mon Sep 17 00:00:00 2001 From: Rob Galanakis Date: Wed, 31 Jul 2024 11:52:36 -0700 Subject: [PATCH 4/6] Remove logrus, use slog Required replacing logrus' testing tools with a new `logctx.Hook` type. Also remove sqlw since alternatives like pgx have become so popular and use different logging patterns, which now that there is slog as a common library, won't require so many workarounds like sqlw. --- api/api.go | 11 ++-- api/api_test.go | 110 +++++++++++++++---------------- api/debug.go | 48 +++++++------- api/logging.go | 90 ++++++++++++-------------- api/sqlw/dblog.go | 92 -------------------------- api/sqlw/mockdb.go | 88 ------------------------- api/sqlw/sqlw.go | 92 -------------------------- go.mod | 5 +- go.sum | 66 +------------------ logctx/log_hook.go | 108 +++++++++++++++++++++++++++++++ logctx/logctx.go | 126 +++++++++++++++++++----------------- logctx/logctx_test.go | 50 ++++++++------ stopwatch/stopwatch.go | 45 ++++++------- stopwatch/stopwatch_test.go | 86 ++++++++++++------------ 14 files changed, 395 insertions(+), 622 deletions(-) delete mode 100644 api/sqlw/dblog.go delete mode 100644 api/sqlw/mockdb.go delete mode 100644 api/sqlw/sqlw.go create mode 100644 logctx/log_hook.go diff --git a/api/api.go b/api/api.go index c577962..e99b162 100644 --- a/api/api.go +++ b/api/api.go @@ -1,12 +1,12 @@ /* -Package api is a standalone API package/pattern built on echo and logrus. +Package api is a standalone API package/pattern built on echo. It sets up /statusz and /healthz endpoints, and sets up logging middleware that takes care of the following important, and fundamentally (in Go) interconnected tasks: - Extract (or add) a trace ID header to the request and response. - The trace ID can be retrieved through api.TraceID(context) of the echo.Context for the request. - - Use that trace ID header as context for the logrus logger. + - Use that trace ID header as context for the logger. - Handle request logging (metadata about the request and response, and log at the level appropriate for the status code). - The request logger can be retrieved api.Logger(echo.Context). @@ -19,7 +19,8 @@ package api import ( "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" - "github.com/sirupsen/logrus" + "github.com/lithictech/go-aperitif/logctx" + "log/slog" "net/http" "os" ) @@ -27,7 +28,7 @@ import ( type Config struct { // If not provided, create an echo.New. App *echo.Echo - Logger *logrus.Entry + Logger *slog.Logger LoggingMiddlwareConfig LoggingMiddlwareConfig // Origins for echo's CORS middleware. // If it and CorsConfig are empty, do not add the middleware. @@ -57,7 +58,7 @@ type Config struct { func New(cfg Config) *echo.Echo { if cfg.Logger == nil { - cfg.Logger = unconfiguredLogger() + cfg.Logger = logctx.UnconfiguredLogger() } if cfg.HealthHandler == nil { if cfg.HealthResponse == nil { diff --git a/api/api_test.go b/api/api_test.go index 4e7dc9d..fb52796 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -11,8 +11,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" . "github.com/rgalanakis/golangal" - "github.com/sirupsen/logrus" - "github.com/sirupsen/logrus/hooks/test" + "log/slog" "net/http" "net/http/httptest" "testing" @@ -25,15 +24,14 @@ func TestAPI(t *testing.T) { var _ = Describe("API", func() { var e *echo.Echo - var logger *logrus.Logger - var logHook *test.Hook - var logEntry *logrus.Entry + + var logger *slog.Logger + var logHook *logctx.Hook BeforeEach(func() { - logger, logHook = test.NewNullLogger() - logEntry = logger.WithFields(nil) + logger, logHook = logctx.NewNullLogger() e = api.New(api.Config{ - Logger: logEntry, + Logger: logger, HealthResponse: map[string]interface{}{"o": "k"}, StatusResponse: map[string]interface{}{"it": "me"}, }) @@ -48,7 +46,7 @@ var _ = Describe("API", func() { It("can use custom health and status fields", func() { e = api.New(api.Config{ - Logger: logEntry, + Logger: logger, HealthHandler: func(c echo.Context) error { return c.String(200, "yo") }, @@ -117,58 +115,56 @@ var _ = Describe("API", func() { Describe("logging", func() { It("does not corrupt the input logger (by reassigning the closure)", func() { e.GET("/before-first-call", func(c echo.Context) error { - Expect(api.Logger(c).Data).ToNot(HaveKey("request_status")) + Expect(api.Logger(c).Handler().(*logctx.Hook).AttrMap()).ToNot(HaveKey("request_status")) return c.String(401, "ok") }) e.GET("/after-first-call", func(c echo.Context) error { - Expect(api.Logger(c).Data).ToNot(HaveKey("request_status")) + Expect(api.Logger(c).Handler().(*logctx.Hook).AttrMap()).ToNot(HaveKey("request_status")) return c.String(403, "ok") }) Expect(Serve(e, GetRequest("/before-first-call"))).To(HaveResponseCode(401)) Expect(Serve(e, GetRequest("/after-first-call"))).To(HaveResponseCode(403)) - Expect(logHook.Entries).To(HaveLen(2)) + Expect(logHook.Records()).To(HaveLen(2)) }) It("logs normal requests at info", func() { e.GET("/", func(c echo.Context) error { return c.String(200, "ok") }) Expect(Serve(e, GetRequest("/"))).To(HaveResponseCode(200)) - Expect(logHook.Entries).To(HaveLen(1)) - Expect(logHook.Entries[0].Level).To(Equal(logrus.InfoLevel)) + Expect(logHook.Records()).To(HaveLen(1)) + Expect(logHook.Records()[0].Record.Level).To(Equal(slog.LevelInfo)) }) It("logs 500+ at error", func() { e.GET("/", func(c echo.Context) error { return c.String(500, "oh") }) Expect(Serve(e, GetRequest("/"))).To(HaveResponseCode(500)) - Expect(logHook.Entries).To(HaveLen(1)) - Expect(logHook.Entries[0].Level).To(Equal(logrus.ErrorLevel)) + Expect(logHook.Records()).To(HaveLen(1)) + Expect(logHook.Records()[0].Record.Level).To(Equal(slog.LevelError)) }) It("logs 400 to 499 as warn", func() { e.GET("/", func(c echo.Context) error { return c.String(400, "client err") }) Expect(Serve(e, GetRequest("/"))).To(HaveResponseCode(400)) - Expect(logHook.Entries).To(HaveLen(1)) - Expect(logHook.Entries[0].Level).To(Equal(logrus.WarnLevel)) + Expect(logHook.Records()).To(HaveLen(1)) + Expect(logHook.Records()[0].Record.Level).To(Equal(slog.LevelWarn)) }) It("logs status and health as debug", func() { - logger.SetLevel(logrus.DebugLevel) Expect(Serve(e, GetRequest("/healthz"))).To(HaveResponseCode(200)) Expect(Serve(e, GetRequest("/statusz"))).To(HaveResponseCode(200)) - Expect(logHook.Entries).To(HaveLen(2)) - Expect(logHook.Entries[0].Level).To(Equal(logrus.DebugLevel)) - Expect(logHook.Entries[1].Level).To(Equal(logrus.DebugLevel)) + Expect(logHook.Records()).To(HaveLen(2)) + Expect(logHook.Records()[0].Record.Level).To(Equal(slog.LevelDebug)) + Expect(logHook.Records()[1].Record.Level).To(Equal(slog.LevelDebug)) }) It("logs options as debug", func() { - logger.SetLevel(logrus.DebugLevel) Expect(Serve(e, NewRequest("OPTIONS", "/foo", nil))).To(HaveResponseCode(404)) - Expect(logHook.Entries).To(HaveLen(1)) - Expect(logHook.Entries[0].Level).To(Equal(logrus.DebugLevel)) + Expect(logHook.Records()).To(HaveLen(1)) + Expect(logHook.Records()[0].Record.Level).To(Equal(slog.LevelDebug)) }) It("can log request and response headers", func() { e = api.New(api.Config{ - Logger: logEntry, + Logger: logger, LoggingMiddlwareConfig: api.LoggingMiddlwareConfig{ RequestHeaders: true, ResponseHeaders: true, @@ -179,8 +175,8 @@ var _ = Describe("API", func() { return c.String(200, "ok") }) Expect(Serve(e, GetRequest("/", SetReqHeader("ReqHead", "ReqHeadVal")))).To(HaveResponseCode(200)) - Expect(logHook.Entries).To(HaveLen(1)) - Expect(logHook.Entries[0].Data).To(And( + Expect(logHook.Records()).To(HaveLen(1)) + Expect(logHook.Records()[0].AttrMap()).To(And( HaveKeyWithValue("request_header.Reqhead", "ReqHeadVal"), HaveKeyWithValue("response_header.Reshead", "ResHeadVal"), )) @@ -188,15 +184,15 @@ var _ = Describe("API", func() { It("can use custom DoLog, BeforeRequest, and AfterRequest hooks", func() { doLogCalled := false e = api.New(api.Config{ - Logger: logEntry, + Logger: logger, LoggingMiddlwareConfig: api.LoggingMiddlwareConfig{ - BeforeRequest: func(_ echo.Context, e *logrus.Entry) *logrus.Entry { - return e.WithField("before", 1) + BeforeRequest: func(_ echo.Context, e *slog.Logger) *slog.Logger { + return e.With("before", 1) }, - AfterRequest: func(_ echo.Context, e *logrus.Entry) *logrus.Entry { - return e.WithField("after", 2) + AfterRequest: func(_ echo.Context, e *slog.Logger) *slog.Logger { + return e.With("after", 2) }, - DoLog: func(c echo.Context, e *logrus.Entry) { + DoLog: func(c echo.Context, e *slog.Logger) { doLogCalled = true api.LoggingMiddlewareDefaultDoLog(c, e) }, @@ -207,9 +203,9 @@ var _ = Describe("API", func() { }) Expect(Serve(e, GetRequest("/"))).To(HaveResponseCode(400)) Expect(doLogCalled).To(BeTrue()) - Expect(logHook.Entries[len(logHook.Entries)-1].Data).To(And( - HaveKeyWithValue("before", 1), - HaveKeyWithValue("after", 2), + Expect(logHook.LastRecord().AttrMap()).To(And( + HaveKeyWithValue("before", BeEquivalentTo(1)), + HaveKeyWithValue("after", BeEquivalentTo(2)), )) }) }) @@ -284,7 +280,8 @@ var _ = Describe("API", func() { r, err := http.NewRequest("GET", "", nil) Expect(err).ToNot(HaveOccurred()) ctx := e.NewContext(r, httptest.NewRecorder()) - logger := logrus.New().WithField("a", 2) + logger, _ := logctx.NewNullLogger() + logger = logger.With("a", 2) api.SetLogger(ctx, logger) tid := api.TraceId(ctx) @@ -292,8 +289,8 @@ var _ = Describe("API", func() { tkey, tval := logctx.ActiveTraceId(c) Expect(tkey).To(Equal(logctx.RequestTraceIdKey)) Expect(tval).To(Equal(tid)) - Expect(logctx.Logger(c).Data).To(And( - HaveKeyWithValue("a", 2), + Expect(logctx.Logger(c).Handler().(*logctx.Hook).AttrMap()).To(And( + HaveKeyWithValue("a", BeEquivalentTo(2)), HaveKeyWithValue(BeEquivalentTo(logctx.RequestTraceIdKey), tid), )) }) @@ -320,17 +317,14 @@ var _ = Describe("API", func() { }) Describe("DebugMiddleware", func() { - BeforeEach(func() { - logger.SetLevel(logrus.DebugLevel) - }) It("noops if not enabled", func() { e.Use(api.DebugMiddleware(api.DebugMiddlewareConfig{Enabled: false, DumpResponseBody: true})) e.GET("/foo", func(c echo.Context) error { return c.String(200, "ok") }) Serve(e, NewRequest("POST", "/endpoint", nil)) - Expect(logHook.Entries).To(HaveLen(1)) - Expect(logHook.Entries[0].Message).To(Equal("request_finished")) + Expect(logHook.Records()).To(HaveLen(1)) + Expect(logHook.Records()[0].Record.Message).To(Equal("request_finished")) }) It("dumps what is enabled", func() { e.Use(api.DebugMiddleware(api.DebugMiddlewareConfig{Enabled: true, DumpResponseBody: true, DumpResponseHeaders: true})) @@ -338,9 +332,9 @@ var _ = Describe("API", func() { return c.String(200, "ok") }) Serve(e, NewRequest("GET", "/endpoint", nil)) - Expect(logHook.Entries).To(HaveLen(2)) - Expect(logHook.Entries[0].Message).To(Equal("request_debug")) - Expect(logHook.Entries[0].Data).To(And( + Expect(logHook.Records()).To(HaveLen(2)) + Expect(logHook.Records()[0].Record.Message).To(Equal("request_debug")) + Expect(logHook.Records()[0].AttrMap()).To(And( HaveKeyWithValue("debug_response_headers", HaveKey("Content-Type")), HaveKeyWithValue("debug_response_body", ContainSubstring("ok")), )) @@ -351,9 +345,9 @@ var _ = Describe("API", func() { return c.String(200, "ok") }) Serve(e, NewRequest("GET", "/endpoint", nil, SetReqHeader("Foo", "x"))) - Expect(logHook.Entries).To(HaveLen(2)) - Expect(logHook.Entries[0].Message).To(Equal("request_debug")) - Expect(logHook.Entries[0].Data).To(And( + Expect(logHook.Records()).To(HaveLen(2)) + Expect(logHook.Records()[0].Record.Message).To(Equal("request_debug")) + Expect(logHook.Records()[0].AttrMap()).To(And( HaveKeyWithValue("debug_request_headers", HaveKey("Foo")), HaveKeyWithValue("debug_response_headers", HaveKey("Content-Type")), HaveKeyWithValue("debug_request_body", ""), @@ -367,13 +361,13 @@ var _ = Describe("API", func() { }) Serve(e, NewRequest("GET", "/endpoint", nil, SetReqHeader("Foo", "x"))) Serve(e, NewRequest("GET", "/endpoint", nil, SetReqHeader("Foo", "x"))) - Expect(logHook.Entries).To(HaveLen(4)) - Expect(logHook.Entries[0].Message).To(Equal("request_debug")) - Expect(logHook.Entries[0].Data).ToNot(HaveKey("memory_sys")) - Expect(logHook.Entries[1].Message).To(Equal("request_finished")) - Expect(logHook.Entries[2].Message).To(Equal("request_debug")) - Expect(logHook.Entries[2].Data).To(HaveKey("memory_sys")) - Expect(logHook.Entries[3].Message).To(Equal("request_finished")) + Expect(logHook.Records()).To(HaveLen(4)) + Expect(logHook.Records()[0].Record.Message).To(Equal("request_debug")) + Expect(logHook.Records()[0].AttrMap()).ToNot(HaveKey("memory_sys")) + Expect(logHook.Records()[1].Record.Message).To(Equal("request_finished")) + Expect(logHook.Records()[2].Record.Message).To(Equal("request_debug")) + Expect(logHook.Records()[2].AttrMap()).To(HaveKey("memory_sys")) + Expect(logHook.Records()[3].Record.Message).To(Equal("request_finished")) }) }) }) diff --git a/api/debug.go b/api/debug.go index 3183b75..8cbdd41 100644 --- a/api/debug.go +++ b/api/debug.go @@ -41,40 +41,40 @@ func DebugMiddleware(cfg DebugMiddlewareConfig) echo.MiddlewareFunc { atomic.AddUint64(&requestCounter, 1) log := logctx.Logger(StdContext(c)) if cfg.DumpRequestBody { - log = log.WithField("debug_request_body", string(reqBody)) + log = log.With("debug_request_body", string(reqBody)) } if cfg.DumpResponseBody { - log = log.WithField("debug_response_body", string(resBody)) + log = log.With("debug_response_body", string(resBody)) } if cfg.DumpRequestHeaders { - log = log.WithField("debug_request_headers", headerToMap(c.Request().Header)) + log = log.With("debug_request_headers", headerToMap(c.Request().Header)) } if cfg.DumpResponseHeaders { - log = log.WithField("debug_response_headers", headerToMap(c.Response().Header())) + log = log.With("debug_response_headers", headerToMap(c.Response().Header())) } if cfg.DumpMemoryEvery > 0 && (requestCounter%dumpEveryUint) == 0 { var ms runtime.MemStats runtime.ReadMemStats(&ms) - log = log.WithFields(map[string]interface{}{ - "memory_alloc": ms.Alloc, - "memory_total_alloc": ms.TotalAlloc, - "memory_sys": ms.Sys, - "memory_mallocs": ms.Mallocs, - "memory_frees": ms.Frees, - "memory_heap_alloc": ms.HeapAlloc, - "memory_heap_sys": ms.HeapSys, - "memory_heap_idle": ms.HeapIdle, - "memory_heap_inuse": ms.HeapInuse, - "memory_heap_released": ms.HeapReleased, - "memory_heap_objects": ms.HeapObjects, - "memory_stack_inuse": ms.StackInuse, - "memory_stack_sys": ms.StackSys, - "memory_other_sys": ms.OtherSys, - "memory_next_gc": ms.NextGC, - "memory_last_gc": ms.LastGC, - "memory_pause_total_ns": ms.PauseTotalNs, - "memory_num_gc": ms.NumGC, - }) + log = log.With( + "memory_alloc", ms.Alloc, + "memory_total_alloc", ms.TotalAlloc, + "memory_sys", ms.Sys, + "memory_mallocs", ms.Mallocs, + "memory_frees", ms.Frees, + "memory_heap_alloc", ms.HeapAlloc, + "memory_heap_sys", ms.HeapSys, + "memory_heap_idle", ms.HeapIdle, + "memory_heap_inuse", ms.HeapInuse, + "memory_heap_released", ms.HeapReleased, + "memory_heap_objects", ms.HeapObjects, + "memory_stack_inuse", ms.StackInuse, + "memory_stack_sys", ms.StackSys, + "memory_other_sys", ms.OtherSys, + "memory_next_gc", ms.NextGC, + "memory_last_gc", ms.LastGC, + "memory_pause_total_ns", ms.PauseTotalNs, + "memory_num_gc", ms.NumGC, + ) } log.Debug("request_debug") }) diff --git a/api/logging.go b/api/logging.go index ee078f0..f6d8fe0 100644 --- a/api/logging.go +++ b/api/logging.go @@ -5,27 +5,23 @@ import ( "github.com/labstack/echo/v4" "github.com/lithictech/go-aperitif/api/apiparams" "github.com/lithictech/go-aperitif/logctx" - "github.com/sirupsen/logrus" + "log/slog" "net/http" "runtime" "strconv" "time" ) -func unconfiguredLogger() *logrus.Entry { - return logrus.New().WithField("unconfigured_logger", "true") -} - -func Logger(c echo.Context) *logrus.Entry { - logger, ok := c.Get(logctx.LoggerKey).(*logrus.Entry) +func Logger(c echo.Context) *slog.Logger { + logger, ok := c.Get(logctx.LoggerKey).(*slog.Logger) if !ok { - logger = unconfiguredLogger() + logger = logctx.UnconfiguredLogger() logger.Error("No logger configured for request!") } return logger } -func SetLogger(c echo.Context, logger *logrus.Entry) { +func SetLogger(c echo.Context, logger *slog.Logger) { c.Set(logctx.LoggerKey, logger) } @@ -37,20 +33,20 @@ type LoggingMiddlwareConfig struct { // If provided, the returned logger is stored in the context // which is eventually passed to the handler. // Use to add additional fields to the logger based on the request. - BeforeRequest func(echo.Context, *logrus.Entry) *logrus.Entry + BeforeRequest func(echo.Context, *slog.Logger) *slog.Logger // If provided, the returned logger is used for response logging. // Use to add additional fields to the logger based on the request or response. - AfterRequest func(echo.Context, *logrus.Entry) *logrus.Entry + AfterRequest func(echo.Context, *slog.Logger) *slog.Logger // The function that does the actual logging. // By default, it will log at a certain level based on the status code of the response. - DoLog func(echo.Context, *logrus.Entry) + DoLog func(echo.Context, *slog.Logger) } -func LoggingMiddleware(outerLogger *logrus.Entry) echo.MiddlewareFunc { +func LoggingMiddleware(outerLogger *slog.Logger) echo.MiddlewareFunc { return LoggingMiddlewareWithConfig(outerLogger, LoggingMiddlwareConfig{}) } -func LoggingMiddlewareWithConfig(outerLogger *logrus.Entry, cfg LoggingMiddlwareConfig) echo.MiddlewareFunc { +func LoggingMiddlewareWithConfig(outerLogger *slog.Logger, cfg LoggingMiddlwareConfig) echo.MiddlewareFunc { if cfg.DoLog == nil { cfg.DoLog = LoggingMiddlewareDefaultDoLog } @@ -67,24 +63,24 @@ func LoggingMiddlewareWithConfig(outerLogger *logrus.Entry, cfg LoggingMiddlware bytesIn = "0" } - logger := outerLogger.WithFields(logrus.Fields{ - "request_started_at": start.Format(time.RFC3339), - "request_remote_ip": c.RealIP(), - "request_method": req.Method, - "request_uri": req.RequestURI, - "request_protocol": req.Proto, - "request_host": req.Host, - "request_path": path, - "request_query": req.URL.RawQuery, - "request_referer": req.Referer(), - "request_user_agent": req.UserAgent(), - "request_bytes_in": bytesIn, - string(logctx.RequestTraceIdKey): TraceId(c), - }) + logger := outerLogger.With( + "request_started_at", start.Format(time.RFC3339), + "request_remote_ip", c.RealIP(), + "request_method", req.Method, + "request_uri", req.RequestURI, + "request_protocol", req.Proto, + "request_host", req.Host, + "request_path", path, + "request_query", req.URL.RawQuery, + "request_referer", req.Referer(), + "request_user_agent", req.UserAgent(), + "request_bytes_in", bytesIn, + string(logctx.RequestTraceIdKey), TraceId(c), + ) if cfg.RequestHeaders { for k, v := range req.Header { if len(v) > 0 && k != "Authorization" && k != "Cookie" { - logger = logger.WithField("request_header."+k, v[0]) + logger = logger.With("request_header."+k, v[0]) } } } @@ -103,21 +99,21 @@ func LoggingMiddlewareWithConfig(outerLogger *logrus.Entry, cfg LoggingMiddlware stop := time.Now() res := c.Response() - logger = Logger(c).WithFields(logrus.Fields{ - "request_finished_at": stop.Format(time.RFC3339), - "request_status": res.Status, - "request_latency_ms": int(stop.Sub(start)) / 1000 / 1000, - "request_bytes_out": strconv.FormatInt(res.Size, 10), - }) + logger = Logger(c).With( + "request_finished_at", stop.Format(time.RFC3339), + "request_status", res.Status, + "request_latency_ms", int(stop.Sub(start))/1000/1000, + "request_bytes_out", strconv.FormatInt(res.Size, 10), + ) if cfg.ResponseHeaders { for k, v := range res.Header() { if len(v) > 0 && k != "Set-Cookie" { - logger = logger.WithField("response_header."+k, v[0]) + logger = logger.With("response_header."+k, v[0]) } } } if err != nil { - logger = logger.WithField("request_error", err) + logger = logger.With("request_error", err) } if cfg.BeforeRequest != nil { logger = cfg.AfterRequest(c, logger) @@ -129,7 +125,7 @@ func LoggingMiddlewareWithConfig(outerLogger *logrus.Entry, cfg LoggingMiddlware } } -func LoggingMiddlewareDefaultDoLog(c echo.Context, logger *logrus.Entry) { +func LoggingMiddlewareDefaultDoLog(c echo.Context, logger *slog.Logger) { req := c.Request() res := c.Response() logMethod := logger.Info @@ -149,7 +145,7 @@ func LoggingMiddlewareDefaultDoLog(c echo.Context, logger *logrus.Entry) { // so that if it panics, we can recover from it and pass on a 500. // Use the "named return parameter can be set in defer" trick so we can // return the error we create from the panic. -func safeInvokeNext(logger *logrus.Entry, next echo.HandlerFunc, c echo.Context) (err error) { +func safeInvokeNext(logger *slog.Logger, next echo.HandlerFunc, c echo.Context) (err error) { defer func() { if r := recover(); r != nil { if e, ok := r.(error); ok { @@ -159,10 +155,10 @@ func safeInvokeNext(logger *logrus.Entry, next echo.HandlerFunc, c echo.Context) } stack := make([]byte, 4<<10) // 4kb length := runtime.Stack(stack, true) - logger.WithFields(logrus.Fields{ - "error": err, - "stack": string(stack[:length]), - }).Error("panic_recover") + logger.With( + "error", err, + "stack", string(stack[:length]), + ).Error("panic_recover") } }() err = next(c) @@ -189,12 +185,6 @@ func adaptToError(e error) error { return NewInternalError(e) } -// Deprecated: Use NewHTTPErrorHandler instead. -func HTTPErrorHandler(err error, c echo.Context) { - e := echo.New() - NewHTTPErrorHandler(e)(err, c) -} - func NewHTTPErrorHandler(e *echo.Echo) echo.HTTPErrorHandler { return func(err error, c echo.Context) { apiErr, ok := err.(Error) @@ -211,7 +201,7 @@ func NewHTTPErrorHandler(e *echo.Echo) echo.HTTPErrorHandler { err = c.JSON(apiErr.HTTPStatus, apiErr) } if err != nil { - Logger(c).WithField("error", err).Error("http_error_handler_error") + Logger(c).With("error", err).Error("http_error_handler_error") } } } diff --git a/api/sqlw/dblog.go b/api/sqlw/dblog.go deleted file mode 100644 index 99ff60d..0000000 --- a/api/sqlw/dblog.go +++ /dev/null @@ -1,92 +0,0 @@ -package sqlw - -import ( - "context" - "database/sql" - "github.com/lithictech/go-aperitif/logctx" - - "github.com/jmoiron/sqlx" - "github.com/sirupsen/logrus" -) - -// WithLogging adds logging around all calls. -func WithLogging(db Interface, defaultLogger *logrus.Entry) Interface { - if db == nil { - panic("must provide db") - } - if defaultLogger == nil { - panic("must provide logger") - } - return &dblogger{ - defaultLogger: defaultLogger, - db: db, - } -} - -type dblogger struct { - defaultLogger *logrus.Entry - db Interface -} - -func (p *dblogger) DBX() *sqlx.DB { - return p.db.DBX() -} - -func (p *dblogger) logger(ctx context.Context) *logrus.Entry { - if ctx == nil { - return p.defaultLogger - } - logger := logctx.LoggerOrNil(ctx) - if logger != nil { - return logger - } - return p.defaultLogger -} - -func (p *dblogger) log(ctx context.Context, cmd, q string, args []interface{}) { - logger := p.logger(ctx) - logger.WithFields(logrus.Fields{ - "sql_statement": q, - "sql_args": args, - }).Debug("sql_" + cmd) -} - -func (p *dblogger) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) { - p.log(ctx, "exec", query, args) - return p.db.ExecContext(ctx, query, args...) -} - -func (p *dblogger) Exec(query string, args ...interface{}) (sql.Result, error) { - p.log(nil, "exec", query, args) - return p.db.Exec(query, args...) -} - -func (p *dblogger) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) { - p.log(ctx, "query", query, args) - return p.db.QueryContext(ctx, query, args...) -} - -func (p *dblogger) QueryxContext(ctx context.Context, query string, args ...interface{}) (*sqlx.Rows, error) { - p.log(ctx, "queryx", query, args) - return p.db.QueryxContext(ctx, query, args...) -} - -func (p *dblogger) QueryRowxContext(ctx context.Context, query string, args ...interface{}) *sqlx.Row { - p.log(ctx, "queryxrow", query, args) - return p.db.QueryRowxContext(ctx, query, args...) -} - -func (p *dblogger) Query(query string, args ...interface{}) (*sql.Rows, error) { - p.log(nil, "query", query, args) - return p.db.Query(query, args...) -} - -func (p *dblogger) Queryx(query string, args ...interface{}) (*sqlx.Rows, error) { - p.log(nil, "queryx", query, args) - return p.db.Queryx(query, args...) -} - -func (p *dblogger) QueryRowx(query string, args ...interface{}) *sqlx.Row { - p.log(nil, "queryxrow", query, args) - return p.db.QueryRowx(query, args...) -} diff --git a/api/sqlw/mockdb.go b/api/sqlw/mockdb.go deleted file mode 100644 index 6250f21..0000000 --- a/api/sqlw/mockdb.go +++ /dev/null @@ -1,88 +0,0 @@ -package sqlw - -import ( - "context" - "database/sql" - "github.com/jmoiron/sqlx" -) - -type Interceptor func(context.Context, string, []interface{}) error - -// WithInterceptor will call interceptor before each DB call. -// If interceptor returns an error, it will be returned. -// If the DB method does not return an error (like QueryRow), but Interceptor does, -// panic with the error. -// Usually this is used for mocking. -func WithInterceptor(db Interface, interceptor Interceptor) Interface { - return &dbintercept{ - Interceptor: interceptor, - DB: db, - } -} - -type dbintercept struct { - Interceptor Interceptor - DB Interface -} - -func (p *dbintercept) DBX() *sqlx.DB { - return p.DB.DBX() -} - -func (p *dbintercept) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) { - if err := p.Interceptor(ctx, query, args); err != nil { - return nil, err - } - return p.DB.ExecContext(ctx, query, args...) -} - -func (p *dbintercept) Exec(query string, args ...interface{}) (sql.Result, error) { - if err := p.Interceptor(nil, query, args); err != nil { - return nil, err - } - return p.DB.Exec(query, args...) -} - -func (p *dbintercept) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) { - if err := p.Interceptor(ctx, query, args); err != nil { - return nil, err - } - return p.DB.QueryContext(ctx, query, args...) -} - -func (p *dbintercept) QueryxContext(ctx context.Context, query string, args ...interface{}) (*sqlx.Rows, error) { - if err := p.Interceptor(ctx, query, args); err != nil { - return nil, err - } - return p.DB.QueryxContext(ctx, query, args...) -} - -func (p *dbintercept) QueryRowxContext(ctx context.Context, query string, args ...interface{}) *sqlx.Row { - if err := p.Interceptor(ctx, query, args); err != nil { - panic(err) - } - return p.DB.QueryRowxContext(ctx, query, args...) -} - -func (p *dbintercept) Query(query string, args ...interface{}) (*sql.Rows, error) { - if err := p.Interceptor(nil, query, args); err != nil { - return nil, err - } - return p.DB.Query(query, args...) -} - -func (p *dbintercept) Queryx(query string, args ...interface{}) (*sqlx.Rows, error) { - if err := p.Interceptor(nil, query, args); err != nil { - return nil, err - } - return p.DB.Queryx(query, args...) -} - -func (p *dbintercept) QueryRowx(query string, args ...interface{}) *sqlx.Row { - if err := p.Interceptor(nil, query, args); err != nil { - panic(err) - } - return p.DB.QueryRowx(query, args...) -} - -var _ Interface = &dbintercept{} diff --git a/api/sqlw/sqlw.go b/api/sqlw/sqlw.go deleted file mode 100644 index b922102..0000000 --- a/api/sqlw/sqlw.go +++ /dev/null @@ -1,92 +0,0 @@ -package sqlw - -import ( - "context" - "database/sql" - "github.com/jmoiron/sqlx" -) - -// Interface is a common wrapper over sqlx so we can compose functionality. -type Interface interface { - sqlx.Queryer - sqlx.QueryerContext - sqlx.Execer - sqlx.ExecerContext - DBX() *sqlx.DB -} - -type AddRow func([]interface{}) - -func CopyFrom(ctx context.Context, db *sql.DB, copyIn string, rowAdder func(cb AddRow)) error { - txn, err := db.Begin() - if err != nil { - return err - } - stmt, err := txn.Prepare(copyIn) - if err != nil { - return err - } - rowAdder(func(i []interface{}) { - if _, e := stmt.ExecContext(ctx, i...); e != nil { - err = e - } - }) - if err != nil { - return err - } - if _, err = stmt.ExecContext(ctx); err != nil { - return err - } - if err := stmt.Close(); err != nil { - return err - } - if err := txn.Commit(); err != nil { - return err - } - return nil -} - -// Wrap wraps a real sqlx.DB connection into one that can be composed. -func Wrap(db *sqlx.DB) Interface { - return &sqlxWrapper{db: db} -} - -type sqlxWrapper struct { - db *sqlx.DB -} - -func (s *sqlxWrapper) Query(query string, args ...interface{}) (*sql.Rows, error) { - return s.db.Query(query, args...) -} - -func (s *sqlxWrapper) Queryx(query string, args ...interface{}) (*sqlx.Rows, error) { - return s.db.Queryx(query, args...) -} - -func (s *sqlxWrapper) QueryRowx(query string, args ...interface{}) *sqlx.Row { - return s.db.QueryRowx(query, args...) -} - -func (s *sqlxWrapper) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) { - return s.db.QueryContext(ctx, query, args...) -} - -func (s *sqlxWrapper) QueryxContext(ctx context.Context, query string, args ...interface{}) (*sqlx.Rows, error) { - return s.db.QueryxContext(ctx, query, args...) -} - -func (s *sqlxWrapper) QueryRowxContext(ctx context.Context, query string, args ...interface{}) *sqlx.Row { - return s.db.QueryRowxContext(ctx, query, args...) -} - -func (s *sqlxWrapper) Exec(query string, args ...interface{}) (sql.Result, error) { - return s.db.Exec(query, args...) -} - -func (s *sqlxWrapper) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) { - return s.db.ExecContext(ctx, query, args...) -} - -func (s *sqlxWrapper) DBX() *sqlx.DB { - return s.db -} diff --git a/go.mod b/go.mod index 1bb5eb7..ed68eda 100644 --- a/go.mod +++ b/go.mod @@ -5,14 +5,13 @@ go 1.22 require ( github.com/google/uuid v1.6.0 github.com/hashicorp/go-multierror v1.1.1 - github.com/jmoiron/sqlx v1.4.0 github.com/labstack/echo/v4 v4.12.0 github.com/onsi/ginkgo/v2 v2.19.1 github.com/onsi/gomega v1.34.1 + github.com/phsym/console-slog v0.3.1 github.com/pkg/errors v0.9.1 github.com/rgalanakis/golangal v1.2.0 github.com/rgalanakis/validator v0.0.0-20180731224108-4a34a8927f7c - github.com/sirupsen/logrus v1.9.3 golang.org/x/crypto v0.25.0 ) @@ -23,7 +22,6 @@ require ( github.com/google/go-cmp v0.6.0 // indirect github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect - github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect github.com/labstack/gommon v0.4.2 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -36,6 +34,5 @@ require ( golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.23.0 // indirect - google.golang.org/appengine v1.6.6 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 55cffea..96eb469 100644 --- a/go.sum +++ b/go.sum @@ -1,131 +1,71 @@ -filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -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/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= -github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg= github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= -github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= -github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= -github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= -github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= -github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4= -github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= -github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/onsi/ginkgo/v2 v2.3.1 h1:8SbseP7qM32WcvE6VaN6vfXxv698izmsJ1UQX9ve7T8= -github.com/onsi/ginkgo/v2 v2.3.1/go.mod h1:Sv4yQXwG5VmF7tm3Q5Z+RWUpPo24LF1mpnz2crUb8Ys= github.com/onsi/ginkgo/v2 v2.19.1 h1:QXgq3Z8Crl5EL1WBAC98A5sEBHARrAJNzAmMxzLcRF0= github.com/onsi/ginkgo/v2 v2.19.1/go.mod h1:O3DtEWQkPa/F7fBMgmZQKKsluAy8pd3rEQdrjkPb9zA= -github.com/onsi/gomega v1.22.0 h1:AIg2/OntwkBiCg5Tt1ayyiF1ArFrWFoCSMtMi/wdApk= -github.com/onsi/gomega v1.22.0/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= -github.com/onsi/gomega v1.34.0 h1:eSSPsPNp6ZpsG8X1OVmOTxig+CblTc4AxpPBykhe2Os= -github.com/onsi/gomega v1.34.0/go.mod h1:MIKI8c+f+QLWk+hxbePD4i0LMJSExPaZOVfkoex4cAo= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/phsym/console-slog v0.3.1 h1:Fuzcrjr40xTc004S9Kni8XfNsk+qrptQmyR+wZw9/7A= +github.com/phsym/console-slog v0.3.1/go.mod h1:oJskjp/X6e6c0mGpfP8ELkfKUsrkDifYRAqJQgmdDS0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.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/rgalanakis/golangal v1.1.0 h1:JMR5gBHMmcWQ86hYM8mRXp99AC1AeEdWKc4sNfjjA3A= -github.com/rgalanakis/golangal v1.1.0/go.mod h1:DT35MZom81QtvdrIerWZ0T3moT/npNBiJmW9cXcufMM= github.com/rgalanakis/golangal v1.2.0 h1:dDrD6sJR2JbnNl0aDNvBHjzWN6r9lDTfv3XqKTNTMmg= github.com/rgalanakis/golangal v1.2.0/go.mod h1:DT35MZom81QtvdrIerWZ0T3moT/npNBiJmW9cXcufMM= github.com/rgalanakis/validator v0.0.0-20180731224108-4a34a8927f7c h1:z1J+SUwpje5zEyPfHBIv6N++OxjETBV5wGBwkmO/Wzk= github.com/rgalanakis/validator v0.0.0-20180731224108-4a34a8927f7c/go.mod h1:qIw2dAd/4wMHHnINySB5+y9HPKQXUKmga0oZHSluPKE= -github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= -google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/logctx/log_hook.go b/logctx/log_hook.go new file mode 100644 index 0000000..cbd7400 --- /dev/null +++ b/logctx/log_hook.go @@ -0,0 +1,108 @@ +package logctx + +import ( + "context" + "log/slog" + "sync" +) + +type HookRecord struct { + Record slog.Record + Attrs []slog.Attr + Group string +} + +func (r HookRecord) AttrMap() map[string]any { + return attrMap(r.Attrs) +} + +func NewHook() *Hook { + return &Hook{records: &hookRecords{r: make([]HookRecord, 0, 4)}} +} + +// Hook is a hook designed for dealing with logs in test scenarios. +type Hook struct { + records *hookRecords + attrs []slog.Attr + group string +} + +var _ slog.Handler = &Hook{} + +func (t *Hook) Enabled(context.Context, slog.Level) bool { + return true +} + +func (t *Hook) Handle(_ context.Context, r slog.Record) error { + t.records.Add(HookRecord{Record: r, Attrs: t.attrs, Group: t.group}) + return nil +} + +func (t *Hook) WithAttrs(attrs []slog.Attr) slog.Handler { + return &Hook{ + records: t.records, + attrs: append(t.attrs, attrs...), + group: t.group, + } +} + +func (t *Hook) WithGroup(group string) slog.Handler { + return &Hook{ + records: t.records, + attrs: t.attrs, + group: group, + } +} + +// LastRecord returns the last record that was logged or nil. +func (t *Hook) LastRecord() *HookRecord { + return t.records.LastRecord() +} + +// Records returns all records that were logged. +func (t *Hook) Records() []HookRecord { + return t.records.Records() +} + +func (t *Hook) AttrMap() map[string]any { + return attrMap(t.attrs) +} + +type hookRecords struct { + r []HookRecord + mux sync.RWMutex +} + +func (h *hookRecords) Add(r HookRecord) { + h.mux.Lock() + defer h.mux.Unlock() + h.r = append(h.r, r) +} + +func (h *hookRecords) Records() []HookRecord { + h.mux.RLock() + defer h.mux.RUnlock() + entries := make([]HookRecord, len(h.r)) + for i, rec := range h.r { + entries[i] = rec + } + return entries +} + +func (h *hookRecords) LastRecord() *HookRecord { + h.mux.RLock() + defer h.mux.RUnlock() + i := len(h.r) - 1 + if i < 0 { + return nil + } + return &h.r[i] +} + +func attrMap(attrs []slog.Attr) map[string]any { + result := make(map[string]any, len(attrs)) + for _, a := range attrs { + result[a.Key] = a.Value.Any() + } + return result +} diff --git a/logctx/logctx.go b/logctx/logctx.go index 545ac42..e42bfc7 100644 --- a/logctx/logctx.go +++ b/logctx/logctx.go @@ -3,9 +3,10 @@ package logctx import ( "context" "github.com/google/uuid" - "github.com/sirupsen/logrus" - "github.com/sirupsen/logrus/hooks/test" + "github.com/phsym/console-slog" "golang.org/x/crypto/ssh/terminal" + "io" + "log/slog" "os" ) @@ -25,13 +26,13 @@ const ProcessTraceIdKey TraceIdKey = "process_trace_id" // MissingTraceIdKey is the key that will be present to indicate tracing is misconfigured. const MissingTraceIdKey TraceIdKey = "missing_trace_id" -func unconfiguredLogger() *logrus.Entry { - return logrus.New().WithField("unconfigured_logger", "true") +func UnconfiguredLogger() *slog.Logger { + return slog.Default().With("unconfigured_logger", "true") } // WithLogger returns a new context that adds a logger which // can be retrieved with Logger(Context). -func WithLogger(c context.Context, logger *logrus.Entry) context.Context { +func WithLogger(c context.Context, logger *slog.Logger) context.Context { return context.WithValue(c, LoggerKey, logger) } @@ -43,7 +44,7 @@ func WithLogger(c context.Context, logger *logrus.Entry) context.Context { func WithTracingLogger(c context.Context) context.Context { logger := Logger(c) tkey, trace := ActiveTraceId(c) - logger = logger.WithField(string(tkey), trace) + logger = logger.With(string(tkey), trace) return context.WithValue(c, LoggerKey, logger) } @@ -51,16 +52,16 @@ func WithTraceId(c context.Context, key TraceIdKey) context.Context { return context.WithValue(c, key, uuid.New().String()) } -func LoggerOrNil(c context.Context) *logrus.Entry { - logger, _ := c.Value(LoggerKey).(*logrus.Entry) +func LoggerOrNil(c context.Context) *slog.Logger { + logger, _ := c.Value(LoggerKey).(*slog.Logger) return logger } -func Logger(c context.Context) *logrus.Entry { - if logger, ok := c.Value(LoggerKey).(*logrus.Entry); ok { +func Logger(c context.Context) *slog.Logger { + if logger, ok := c.Value(LoggerKey).(*slog.Logger); ok { return logger } - logger := unconfiguredLogger() + logger := UnconfiguredLogger() logger.Warn( "Logger called with no logger in context. " + "It should always be there to ensure consistent logs from a single logger") @@ -88,23 +89,15 @@ func ActiveTraceIdValue(c context.Context) string { return v } -func AddFieldsAndGet(c context.Context, fields map[string]interface{}) (context.Context, *logrus.Entry) { - logger := Logger(c) - logger = logger.WithFields(fields) - return WithLogger(c, logger), logger -} - -func AddFieldAndGet(c context.Context, key string, value interface{}) (context.Context, *logrus.Entry) { - return AddFieldsAndGet(c, map[string]interface{}{key: value}) -} - -func AddFields(c context.Context, fields map[string]interface{}) context.Context { - ctx, _ := AddFieldsAndGet(c, fields) +func AddTo(c context.Context, args ...any) context.Context { + ctx, _ := AddToR(c, args...) return ctx } -func AddField(c context.Context, key string, value interface{}) context.Context { - return AddFields(c, map[string]interface{}{key: value}) +func AddToR(c context.Context, args ...any) (context.Context, *slog.Logger) { + logger := Logger(c) + logger = logger.With(args...) + return WithLogger(c, logger), logger } type NewLoggerInput struct { @@ -113,56 +106,58 @@ type NewLoggerInput struct { File string BuildSha string BuildTime string - Fields logrus.Fields + Fields []any } -func NewLogger(cfg NewLoggerInput) (*logrus.Entry, error) { - logger := logrus.New() +func NewLogger(cfg NewLoggerInput) (*slog.Logger, error) { + // Set output to file or stdout/stderr (stderr for tty, stdout otherwise like for 12 factor apps) + var out io.Writer + if cfg.File != "" { + file, err := os.OpenFile(cfg.File, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) + if err != nil { + return nil, err + } + out = file + } else if IsTty() { + out = os.Stderr + } else { + out = os.Stdout + } - // Parse and set level - lvl, err := logrus.ParseLevel(cfg.Level) + hopts := &slog.HandlerOptions{} + lvl, err := ParseLevel(cfg.Level) if err != nil { return nil, err } - logger.SetLevel(lvl) + hopts.Level = lvl - // Set format + var handler slog.Handler if cfg.Format == "json" { - logger.SetFormatter(&logrus.JSONFormatter{}) + handler = slog.NewJSONHandler(out, hopts) } else if cfg.Format == "text" { - logger.SetFormatter(&logrus.TextFormatter{}) + handler = slog.NewTextHandler(out, hopts) } else if cfg.File != "" { - logger.SetFormatter(&logrus.JSONFormatter{}) + handler = slog.NewJSONHandler(out, hopts) } else if IsTty() { - logger.SetFormatter(&logrus.TextFormatter{}) + handler = console.NewHandler(out, &console.HandlerOptions{ + AddSource: hopts.AddSource, + Level: hopts.Level, + }) } else { - logger.SetFormatter(&logrus.JSONFormatter{}) + handler = slog.NewJSONHandler(out, hopts) } - // Set output to file or stdout/stderr (stderr for tty, stdout otherwise like for 12 factor apps) - if cfg.File != "" { - file, err := os.OpenFile(cfg.File, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) - if err != nil { - return nil, err - } - logger.SetOutput(file) - } else if IsTty() { - logger.SetOutput(os.Stderr) - } else { - logger.SetOutput(os.Stdout) - } - - entry := logger.WithFields(nil) + logger := slog.New(handler) if len(cfg.Fields) > 0 { - entry = logger.WithFields(cfg.Fields) + logger = logger.With(cfg.Fields...) } if cfg.BuildSha != "" { - entry = entry.WithField("build_sha", cfg.BuildSha) + logger = logger.With("build_sha", cfg.BuildSha) } if cfg.BuildTime != "" { - entry = entry.WithField("build_time", cfg.BuildTime) + logger = logger.With("build_time", cfg.BuildTime) } - return entry, nil + return logger, nil } func IsTty() bool { @@ -171,12 +166,25 @@ func IsTty() bool { // WithNullLogger adds the logger from test.NewNullLogger into the given context // (default c to context.Background). Use the hook to get the log messages. -// See https://github.com/sirupsen/logrus#testing for testing with logrus. -func WithNullLogger(c context.Context) (context.Context, *test.Hook) { +// See https://github.com/sirupsen/logrus#testing for examples, +// though this doesn't use logrus the ideas still apply. +func WithNullLogger(c context.Context) (context.Context, *Hook) { if c == nil { c = context.Background() } - logger, hook := test.NewNullLogger() - c2 := WithLogger(c, logger.WithField("testlogger", true)) + logger, hook := NewNullLogger() + c2 := WithLogger(c, logger.With("testlogger", true)) return c2, hook } + +func ParseLevel(s string) (slog.Level, error) { + var level slog.Level + var err = level.UnmarshalText([]byte(s)) + return level, err +} + +func NewNullLogger() (*slog.Logger, *Hook) { + hook := NewHook() + logger := slog.New(hook) + return logger, hook +} diff --git a/logctx/logctx_test.go b/logctx/logctx_test.go index 9b107c1..482866f 100644 --- a/logctx/logctx_test.go +++ b/logctx/logctx_test.go @@ -5,7 +5,7 @@ import ( "github.com/lithictech/go-aperitif/logctx" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/sirupsen/logrus" + "log/slog" "testing" ) @@ -15,37 +15,44 @@ func TestLogtools(t *testing.T) { } var _ = Describe("logtools", func() { - bg := context.Background() + var logger *slog.Logger + var hook *logctx.Hook + var ctx context.Context + + BeforeEach(func() { + logger, hook = logctx.NewNullLogger() + ctx = logctx.WithLogger(context.Background(), logger) + }) Describe("WithTraceId", func() { It("adds a new trace id", func() { - c := logctx.WithTraceId(bg, logctx.ProcessTraceIdKey) + c := logctx.WithTraceId(ctx, logctx.ProcessTraceIdKey) Expect(c.Value(logctx.ProcessTraceIdKey)).To(HaveLen(36)) }) }) Describe("ActiveTraceId", func() { It("returns a request trace id", func() { - c := context.WithValue(bg, logctx.RequestTraceIdKey, "abc") + c := context.WithValue(ctx, logctx.RequestTraceIdKey, "abc") key, val := logctx.ActiveTraceId(c) Expect(key).To(Equal(logctx.RequestTraceIdKey)) Expect(val).To(Equal("abc")) Expect(logctx.ActiveTraceIdValue(c)).To(Equal("abc")) }) It("returns a process trace id", func() { - c := context.WithValue(bg, logctx.ProcessTraceIdKey, "abc") + c := context.WithValue(ctx, logctx.ProcessTraceIdKey, "abc") key, val := logctx.ActiveTraceId(c) Expect(key).To(Equal(logctx.ProcessTraceIdKey)) Expect(val).To(Equal("abc")) }) It("returns a process trace id", func() { - c := context.WithValue(bg, logctx.JobTraceIdKey, "abc") + c := context.WithValue(ctx, logctx.JobTraceIdKey, "abc") key, val := logctx.ActiveTraceId(c) Expect(key).To(Equal(logctx.JobTraceIdKey)) Expect(val).To(Equal("abc")) }) It("prefers request->job->process trace id", func() { - c := bg + c := ctx c = context.WithValue(c, logctx.ProcessTraceIdKey, "proc") key, _ := logctx.ActiveTraceId(c) @@ -60,7 +67,7 @@ var _ = Describe("logtools", func() { Expect(key).To(Equal(logctx.RequestTraceIdKey)) }) It("defaults to a missing trace id", func() { - key, val := logctx.ActiveTraceId(bg) + key, val := logctx.ActiveTraceId(ctx) Expect(key).To(Equal(logctx.MissingTraceIdKey)) Expect(val).To(Equal("no-trace-id-in-context")) }) @@ -68,7 +75,6 @@ var _ = Describe("logtools", func() { Describe("WithLogger", func() { It("adds the logger", func() { - logger := &logrus.Entry{} c := logctx.WithLogger(context.Background(), logger) Expect(c.Value(logctx.LoggerKey)).To(BeAssignableToTypeOf(logger)) }) @@ -76,24 +82,26 @@ var _ = Describe("logtools", func() { Describe("WithTracingLogger", func() { It("adds a trace id to the logger", func() { - c := logctx.WithTracingLogger(logctx.WithTraceId(bg, logctx.RequestTraceIdKey)) - logger := c.Value(logctx.LoggerKey).(*logrus.Entry) - Expect(logger.Data).To(HaveKeyWithValue(BeEquivalentTo(logctx.RequestTraceIdKey), BeAssignableToTypeOf(""))) + c := logctx.WithTracingLogger(logctx.WithTraceId(ctx, logctx.RequestTraceIdKey)) + logctx.Logger(c).Info("hi") + Expect(hook.LastRecord().AttrMap()).To(HaveKeyWithValue(BeEquivalentTo(logctx.RequestTraceIdKey), BeAssignableToTypeOf(""))) }) }) - Describe("AddFields", func() { + Describe("AddTo", func() { It("returns a new context where the given fields have been added to the context logger", func() { - c := logctx.AddField(bg, "x", "y") - logger := logctx.Logger(c) - Expect(logger.Data).To(HaveKeyWithValue("x", "y")) + c := logctx.AddTo(ctx, "x", "y") + Expect(logctx.Logger(c).Handler().(*logctx.Hook).AttrMap()).To(HaveKeyWithValue("x", "y")) + logctx.Logger(c).Info("hi") + Expect(hook.LastRecord().AttrMap()).To(HaveKeyWithValue("x", "y")) }) }) - Describe("AddFieldsAndGet", func() { + Describe("AddToR", func() { It("returns the new context, and the logger that was added", func() { - c, logger := logctx.AddFieldAndGet(bg, "x", "y") - Expect(logger.Data).To(HaveKeyWithValue("x", "y")) + c, logger := logctx.AddToR(ctx, "x", "y") + logctx.Logger(c).Info("hi") + Expect(hook.LastRecord().AttrMap()).To(HaveKeyWithValue("x", "y")) Expect(logger).To(BeIdenticalTo(logctx.Logger(c))) }) }) @@ -102,8 +110,8 @@ var _ = Describe("logtools", func() { It("inserts the null logger", func() { c, hook := logctx.WithNullLogger(nil) logctx.Logger(c).Info("hi") - Expect(hook.Entries).To(HaveLen(1)) - Expect(hook.LastEntry().Message).To(Equal("hi")) + Expect(hook.Records()).To(HaveLen(1)) + Expect(hook.LastRecord().Record.Message).To(Equal("hi")) }) }) }) diff --git a/stopwatch/stopwatch.go b/stopwatch/stopwatch.go index f6639a2..4283a62 100644 --- a/stopwatch/stopwatch.go +++ b/stopwatch/stopwatch.go @@ -10,27 +10,28 @@ since they can have vastly different timings package stopwatch import ( - "github.com/sirupsen/logrus" + "context" + "log/slog" "time" ) type Stopwatch struct { start time.Time operation string - logger *logrus.Entry + logger *slog.Logger } type StartOpts struct { Key string - Level logrus.Level + Level slog.Level } -func StartWith(logger *logrus.Entry, operation string, opts StartOpts) *Stopwatch { +func StartWith(ctx context.Context, logger *slog.Logger, operation string, opts StartOpts) *Stopwatch { if opts.Key == "" { opts.Key = "_started" } if opts.Level == 0 { - opts.Level = logrus.DebugLevel + opts.Level = slog.LevelDebug } sw := &Stopwatch{ start: time.Now(), @@ -38,23 +39,23 @@ func StartWith(logger *logrus.Entry, operation string, opts StartOpts) *Stopwatc logger: logger, } - sw.logger.Log(opts.Level, operation+opts.Key) + sw.logger.Log(ctx, opts.Level, operation+opts.Key) return sw } -func Start(logger *logrus.Entry, operation string) *Stopwatch { - return StartWith(logger, operation, StartOpts{}) +func Start(ctx context.Context, logger *slog.Logger, operation string) *Stopwatch { + return StartWith(ctx, logger, operation, StartOpts{}) } type FinishOpts struct { - Logger *logrus.Entry + Logger *slog.Logger Key string ElapsedKey string Milliseconds bool - Level logrus.Level + Level slog.Level } -func (sw *Stopwatch) FinishWith(opts FinishOpts) { +func (sw *Stopwatch) FinishWith(ctx context.Context, opts FinishOpts) { if opts.Key == "" { opts.Key = "_finished" } @@ -62,27 +63,27 @@ func (sw *Stopwatch) FinishWith(opts FinishOpts) { opts.ElapsedKey = "elapsed" } if opts.Level == 0 { - opts.Level = logrus.InfoLevel + opts.Level = slog.LevelInfo } if opts.Logger == nil { opts.Logger = sw.logger } logger := opts.Logger if opts.Milliseconds { - logger = logger.WithField(opts.ElapsedKey, time.Since(sw.start).Milliseconds()) + logger = logger.With(opts.ElapsedKey, time.Since(sw.start).Milliseconds()) } else { - logger = logger.WithField(opts.ElapsedKey, time.Since(sw.start).Seconds()) + logger = logger.With(opts.ElapsedKey, time.Since(sw.start).Seconds()) } - logger.Log(opts.Level, sw.operation+opts.Key) + logger.Log(ctx, opts.Level, sw.operation+opts.Key) } -func (sw *Stopwatch) Finish() { - sw.FinishWith(FinishOpts{}) +func (sw *Stopwatch) Finish(ctx context.Context) { + sw.FinishWith(ctx, FinishOpts{}) } type LapOpts FinishOpts -func (sw *Stopwatch) LapWith(opts LapOpts) { +func (sw *Stopwatch) LapWith(ctx context.Context, opts LapOpts) { if opts.Key == "" { opts.Key = "_lap" } @@ -90,14 +91,14 @@ func (sw *Stopwatch) LapWith(opts LapOpts) { opts.ElapsedKey = "elapsed" } if opts.Level == 0 { - opts.Level = logrus.InfoLevel + opts.Level = slog.LevelInfo } if opts.Logger == nil { opts.Logger = sw.logger } - sw.FinishWith(FinishOpts(opts)) + sw.FinishWith(ctx, FinishOpts(opts)) } -func (sw *Stopwatch) Lap() { - sw.LapWith(LapOpts{}) +func (sw *Stopwatch) Lap(ctx context.Context) { + sw.LapWith(ctx, LapOpts{}) } diff --git a/stopwatch/stopwatch_test.go b/stopwatch/stopwatch_test.go index 5d3e892..0725347 100644 --- a/stopwatch/stopwatch_test.go +++ b/stopwatch/stopwatch_test.go @@ -1,11 +1,12 @@ package stopwatch_test import ( + "context" + "github.com/lithictech/go-aperitif/logctx" "github.com/lithictech/go-aperitif/stopwatch" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/sirupsen/logrus" - "github.com/sirupsen/logrus/hooks/test" + "log/slog" "testing" ) @@ -15,67 +16,64 @@ func TestStopwatch(t *testing.T) { } var _ = Describe("Stopwatch", func() { - var logger *logrus.Logger - var entry *logrus.Entry - var hook *test.Hook + var logger *slog.Logger + var hook *logctx.Hook + var ctx context.Context BeforeEach(func() { - logger, hook = test.NewNullLogger() - logger.SetLevel(logrus.DebugLevel) - entry = logger.WithFields(nil) + logger, hook = logctx.NewNullLogger() + ctx = logctx.WithLogger(context.Background(), logger) }) + It("logs start and stop", func() { - sw := stopwatch.Start(entry, "test") - sw.Finish() - Expect(hook.Entries).To(HaveLen(2)) + sw := stopwatch.Start(ctx, logger, "test") + sw.Finish(ctx) + Expect(hook.Records()).To(HaveLen(2)) - Expect(hook.Entries[0].Level).To(Equal(logrus.DebugLevel)) - Expect(hook.Entries[0].Message).To(ContainSubstring("test_started")) + Expect(hook.Records()[0].Record.Level).To(Equal(slog.LevelDebug)) + Expect(hook.Records()[0].Record.Message).To(ContainSubstring("test_started")) - Expect(hook.Entries[1].Level).To(Equal(logrus.InfoLevel)) - Expect(hook.Entries[1].Message).To(ContainSubstring("test_finished")) + Expect(hook.Records()[1].Record.Level).To(Equal(slog.LevelInfo)) + Expect(hook.Records()[1].Record.Message).To(ContainSubstring("test_finished")) }) It("can custom start and stop", func() { - sw := stopwatch.StartWith(entry, "test", stopwatch.StartOpts{Level: logrus.WarnLevel, Key: "_begin"}) - sw.FinishWith(stopwatch.FinishOpts{Level: logrus.ErrorLevel, Key: "_end", ElapsedKey: "timing"}) - Expect(hook.Entries).To(HaveLen(2)) + sw := stopwatch.StartWith(ctx, logger, "test", stopwatch.StartOpts{Level: slog.LevelWarn, Key: "_begin"}) + sw.FinishWith(ctx, stopwatch.FinishOpts{Level: slog.LevelError, Key: "_end", ElapsedKey: "timing"}) + Expect(hook.Records()).To(HaveLen(2)) - Expect(hook.Entries[0].Level).To(Equal(logrus.WarnLevel)) - Expect(hook.Entries[0].Message).To(ContainSubstring("test_begin")) + Expect(hook.Records()[0].Record.Level).To(Equal(slog.LevelWarn)) + Expect(hook.Records()[0].Record.Message).To(ContainSubstring("test_begin")) - Expect(hook.Entries[1].Level).To(Equal(logrus.ErrorLevel)) - Expect(hook.Entries[1].Message).To(ContainSubstring("test_end")) - Expect(hook.Entries[1].Data).To(HaveKey("timing")) + Expect(hook.Records()[1].Record.Level).To(Equal(slog.LevelError)) + Expect(hook.Records()[1].Record.Message).To(ContainSubstring("test_end")) + Expect(hook.Records()[1].AttrMap()).To(HaveKey("timing")) }) It("can use a custom finish logger", func() { - startLogger, startHook := test.NewNullLogger() - startLogger.SetLevel(logrus.DebugLevel) - - finishLogger, finishHook := test.NewNullLogger() - finishLogger.SetLevel(logrus.DebugLevel) + startLogger, startHook := logctx.NewNullLogger() + finishLogger, finishHook := logctx.NewNullLogger() - sw := stopwatch.Start(startLogger.WithFields(nil), "test") + sw := stopwatch.Start(ctx, startLogger, "test") - sw.FinishWith(stopwatch.FinishOpts{Logger: finishLogger.WithFields(nil)}) + sw.FinishWith(ctx, stopwatch.FinishOpts{Logger: finishLogger}) - Expect(startHook.Entries).To(HaveLen(1)) - Expect(finishHook.Entries).To(HaveLen(1)) + Expect(startHook.Records()).To(HaveLen(1)) + Expect(finishHook.Records()).To(HaveLen(1)) }) It("can lap", func() { - sw := stopwatch.Start(entry, "test") - sw.Lap() - sw.LapWith(stopwatch.LapOpts{Key: "_split", Level: logrus.WarnLevel, ElapsedKey: "timing"}) - Expect(hook.Entries).To(HaveLen(3)) - - Expect(hook.Entries[1].Level).To(Equal(logrus.InfoLevel)) - Expect(hook.Entries[1].Message).To(ContainSubstring("test_lap")) - Expect(hook.Entries[1].Data).To(HaveKey("elapsed")) - - Expect(hook.Entries[2].Level).To(Equal(logrus.WarnLevel)) - Expect(hook.Entries[2].Message).To(ContainSubstring("test_split")) - Expect(hook.Entries[2].Data).To(HaveKey("timing")) + sw := stopwatch.Start(ctx, logger, "test") + sw.Lap(ctx) + sw.LapWith(ctx, stopwatch.LapOpts{Key: "_split", Level: slog.LevelWarn, ElapsedKey: "timing"}) + Expect(hook.Records()).To(HaveLen(3)) + + Expect(hook.Records()[1].Record.Level).To(Equal(slog.LevelInfo)) + Expect(hook.Records()[1].Record.Message).To(ContainSubstring("test_lap")) + Expect(hook.Records()[1].AttrMap()).To(HaveKey("elapsed")) + + Expect(hook.Records()[2].Record.Level).To(Equal(slog.LevelWarn)) + Expect(hook.Records()[2].Record.Message).To(ContainSubstring("test_split")) + Expect(hook.Records()[2].AttrMap()).To(HaveKey("timing")) }) }) From 572476a15e70ef023351a27a6586fffdfb91a049 Mon Sep 17 00:00:00 2001 From: Rob Galanakis Date: Wed, 31 Jul 2024 11:55:44 -0700 Subject: [PATCH 5/6] Remove stringutil Go has generics now, so such utilities are not generally useful. Libraries should have generic utilities instead. --- stringutil/stringutil.go | 21 --------------------- stringutil/stringutil_test.go | 30 ------------------------------ validator/stringutil.go | 18 ++++++++++++++++++ validator/validators.go | 7 +++---- 4 files changed, 21 insertions(+), 55 deletions(-) delete mode 100644 stringutil/stringutil.go delete mode 100644 stringutil/stringutil_test.go create mode 100644 validator/stringutil.go diff --git a/stringutil/stringutil.go b/stringutil/stringutil.go deleted file mode 100644 index aedb198..0000000 --- a/stringutil/stringutil.go +++ /dev/null @@ -1,21 +0,0 @@ -package stringutil - -// Map applies f to each string in in. -func Map(in []string, f func(string) string) []string { - res := make([]string, 0, len(in)) - for _, s := range in { - res = append(res, f(s)) - } - return res -} - -// Contains returns true if in contains element, -// false if not. -func Contains(in []string, element string) bool { - for _, a := range in { - if a == element { - return true - } - } - return false -} diff --git a/stringutil/stringutil_test.go b/stringutil/stringutil_test.go deleted file mode 100644 index 61ab494..0000000 --- a/stringutil/stringutil_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package stringutil_test - -import ( - "github.com/lithictech/go-aperitif/stringutil" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "strings" - "testing" -) - -func TestStringUtil(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "stringutil Suite") -} - -var _ = Describe("Map", func() { - It("maps the input slice", func() { - s := []string{"a", "b"} - res := stringutil.Map(s, strings.ToUpper) - Expect(res).To(Equal([]string{"A", "B"})) - }) -}) - -var _ = Describe("Contains", func() { - It("is true if the slice contains the string", func() { - s := []string{"a", "b"} - Expect(stringutil.Contains(s, "a")).To(BeTrue()) - Expect(stringutil.Contains(s, "A")).To(BeFalse()) - }) -}) diff --git a/validator/stringutil.go b/validator/stringutil.go new file mode 100644 index 0000000..8ea7f8f --- /dev/null +++ b/validator/stringutil.go @@ -0,0 +1,18 @@ +package validator + +func mapString(in []string, f func(string) string) []string { + res := make([]string, 0, len(in)) + for _, s := range in { + res = append(res, f(s)) + } + return res +} + +func containsString(in []string, element string) bool { + for _, a := range in { + if a == element { + return true + } + } + return false +} diff --git a/validator/validators.go b/validator/validators.go index 223ac2b..357a36b 100644 --- a/validator/validators.go +++ b/validator/validators.go @@ -3,7 +3,6 @@ package validator import ( "errors" "github.com/lithictech/go-aperitif/kronos" - "github.com/lithictech/go-aperitif/stringutil" "github.com/rgalanakis/validator" "net/url" "regexp" @@ -92,7 +91,7 @@ func validateEnumImpl(v interface{}, param string, mapper func(string) string) e return err } if mapper != nil { - choices = stringutil.Map(choices, mapper) + choices = mapString(choices, mapper) } if s, ok := v.(string); ok { @@ -110,7 +109,7 @@ func validateEnumImpl(v interface{}, param string, mapper func(string) string) e return validator.ErrBadParameter } if mapper != nil { - ss = stringutil.Map(ss, mapper) + ss = mapString(ss, mapper) } return validateEnumImplSlice(ss, choices) } @@ -139,7 +138,7 @@ func validateEnumImplStr(s string, choices []string, optional bool) error { func validateEnumImplSlice(ss []string, choices []string) error { for _, s := range ss { - if !stringutil.Contains(choices, s) { + if !containsString(choices, s) { return newError("element not one of " + strings.Join(choices, "|")) } } From 23d0bb97b3982c484d75b551eb79d3c0d19c276a Mon Sep 17 00:00:00 2001 From: Rob Galanakis Date: Wed, 31 Jul 2024 12:17:02 -0700 Subject: [PATCH 6/6] Bump to v2 --- api/adapter.go | 2 +- api/api.go | 2 +- api/api_test.go | 10 +++++----- api/apiparams/apiparams.go | 2 +- api/apiparams/apiparams_test.go | 6 +++--- api/apiparams/benchmark_test.go | 4 ++-- api/debug.go | 2 +- api/logging.go | 4 ++-- api/preflight/preflight_test.go | 8 ++++---- api/spa/spa_test.go | 6 +++--- api/trace_id.go | 2 +- async/async.go | 2 +- async/async_test.go | 2 +- go.mod | 2 +- kronos/kronos_test.go | 2 +- logctx/logctx_test.go | 2 +- parallel/parallel.go | 2 +- parallel/parallel_test.go | 2 +- quiz/quiz_test.go | 2 +- stopwatch/stopwatch_test.go | 4 ++-- validator/validator_test.go | 2 +- validator/validators.go | 2 +- 22 files changed, 36 insertions(+), 36 deletions(-) diff --git a/api/adapter.go b/api/adapter.go index 8f7a9a9..84ea000 100644 --- a/api/adapter.go +++ b/api/adapter.go @@ -3,7 +3,7 @@ package api import ( "context" "github.com/labstack/echo/v4" - "github.com/lithictech/go-aperitif/logctx" + "github.com/lithictech/go-aperitif/v2/logctx" ) // StdContext returns a standard context from an echo context. diff --git a/api/api.go b/api/api.go index e99b162..131cd3d 100644 --- a/api/api.go +++ b/api/api.go @@ -19,7 +19,7 @@ package api import ( "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" - "github.com/lithictech/go-aperitif/logctx" + "github.com/lithictech/go-aperitif/v2/logctx" "log/slog" "net/http" "os" diff --git a/api/api_test.go b/api/api_test.go index fb52796..4b643f4 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -3,11 +3,11 @@ package api_test import ( "errors" "github.com/labstack/echo/v4" - "github.com/lithictech/go-aperitif/api" - "github.com/lithictech/go-aperitif/api/apiparams" - . "github.com/lithictech/go-aperitif/api/echoapitest" - . "github.com/lithictech/go-aperitif/apitest" - "github.com/lithictech/go-aperitif/logctx" + "github.com/lithictech/go-aperitif/v2/api" + "github.com/lithictech/go-aperitif/v2/api/apiparams" + . "github.com/lithictech/go-aperitif/v2/api/echoapitest" + . "github.com/lithictech/go-aperitif/v2/apitest" + "github.com/lithictech/go-aperitif/v2/logctx" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" . "github.com/rgalanakis/golangal" diff --git a/api/apiparams/apiparams.go b/api/apiparams/apiparams.go index 415728c..da51234 100644 --- a/api/apiparams/apiparams.go +++ b/api/apiparams/apiparams.go @@ -2,7 +2,7 @@ package apiparams import ( "fmt" - "github.com/lithictech/go-aperitif/validator" + "github.com/lithictech/go-aperitif/v2/validator" "net/http" "reflect" "time" diff --git a/api/apiparams/apiparams_test.go b/api/apiparams/apiparams_test.go index 079383d..1f08567 100644 --- a/api/apiparams/apiparams_test.go +++ b/api/apiparams/apiparams_test.go @@ -3,9 +3,9 @@ package apiparams_test import ( "fmt" "github.com/labstack/echo/v4" - "github.com/lithictech/go-aperitif/api/apiparams" - . "github.com/lithictech/go-aperitif/api/echoapitest" - . "github.com/lithictech/go-aperitif/apitest" + "github.com/lithictech/go-aperitif/v2/api/apiparams" + . "github.com/lithictech/go-aperitif/v2/api/echoapitest" + . "github.com/lithictech/go-aperitif/v2/apitest" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" . "github.com/rgalanakis/golangal" diff --git a/api/apiparams/benchmark_test.go b/api/apiparams/benchmark_test.go index 2ee6d56..8caa3cd 100644 --- a/api/apiparams/benchmark_test.go +++ b/api/apiparams/benchmark_test.go @@ -2,8 +2,8 @@ package apiparams_test import ( "encoding/json" - "github.com/lithictech/go-aperitif/api/apiparams" - "github.com/lithictech/go-aperitif/convext" + "github.com/lithictech/go-aperitif/v2/api/apiparams" + "github.com/lithictech/go-aperitif/v2/convext" "net/http" "strings" "testing" diff --git a/api/debug.go b/api/debug.go index 8cbdd41..682c5b0 100644 --- a/api/debug.go +++ b/api/debug.go @@ -3,7 +3,7 @@ package api import ( "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" - "github.com/lithictech/go-aperitif/logctx" + "github.com/lithictech/go-aperitif/v2/logctx" "net/http" "runtime" "sync/atomic" diff --git a/api/logging.go b/api/logging.go index f6d8fe0..dab2054 100644 --- a/api/logging.go +++ b/api/logging.go @@ -3,8 +3,8 @@ package api import ( "fmt" "github.com/labstack/echo/v4" - "github.com/lithictech/go-aperitif/api/apiparams" - "github.com/lithictech/go-aperitif/logctx" + "github.com/lithictech/go-aperitif/v2/api/apiparams" + "github.com/lithictech/go-aperitif/v2/logctx" "log/slog" "net/http" "runtime" diff --git a/api/preflight/preflight_test.go b/api/preflight/preflight_test.go index f2b3500..e973c09 100644 --- a/api/preflight/preflight_test.go +++ b/api/preflight/preflight_test.go @@ -3,10 +3,10 @@ package preflight_test import ( "errors" "github.com/labstack/echo/v4" - "github.com/lithictech/go-aperitif/api" - . "github.com/lithictech/go-aperitif/api/echoapitest" - "github.com/lithictech/go-aperitif/api/preflight" - . "github.com/lithictech/go-aperitif/apitest" + "github.com/lithictech/go-aperitif/v2/api" + . "github.com/lithictech/go-aperitif/v2/api/echoapitest" + "github.com/lithictech/go-aperitif/v2/api/preflight" + . "github.com/lithictech/go-aperitif/v2/apitest" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" . "github.com/rgalanakis/golangal" diff --git a/api/spa/spa_test.go b/api/spa/spa_test.go index 2367b5b..ffe6042 100644 --- a/api/spa/spa_test.go +++ b/api/spa/spa_test.go @@ -2,9 +2,9 @@ package spa_test import ( "github.com/labstack/echo/v4" - . "github.com/lithictech/go-aperitif/api/echoapitest" - "github.com/lithictech/go-aperitif/api/spa" - . "github.com/lithictech/go-aperitif/apitest" + . "github.com/lithictech/go-aperitif/v2/api/echoapitest" + "github.com/lithictech/go-aperitif/v2/api/spa" + . "github.com/lithictech/go-aperitif/v2/apitest" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" . "github.com/rgalanakis/golangal" diff --git a/api/trace_id.go b/api/trace_id.go index 0d2ee2d..78863e1 100644 --- a/api/trace_id.go +++ b/api/trace_id.go @@ -3,7 +3,7 @@ package api import ( "github.com/google/uuid" "github.com/labstack/echo/v4" - "github.com/lithictech/go-aperitif/logctx" + "github.com/lithictech/go-aperitif/v2/logctx" ) const TraceIdHeader = "Trace-Id" diff --git a/async/async.go b/async/async.go index 30e0f91..8bcf003 100644 --- a/async/async.go +++ b/async/async.go @@ -1,6 +1,6 @@ package async -import "github.com/lithictech/go-aperitif/mariobros" +import "github.com/lithictech/go-aperitif/v2/mariobros" type Goer func(name string, f func()) diff --git a/async/async_test.go b/async/async_test.go index a56d05e..22936f9 100644 --- a/async/async_test.go +++ b/async/async_test.go @@ -1,7 +1,7 @@ package async_test import ( - "github.com/lithictech/go-aperitif/async" + "github.com/lithictech/go-aperitif/v2/async" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "testing" diff --git a/go.mod b/go.mod index ed68eda..0eca24a 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/lithictech/go-aperitif +module github.com/lithictech/go-aperitif/v2 go 1.22 diff --git a/kronos/kronos_test.go b/kronos/kronos_test.go index 7e7ef50..d7d9c69 100644 --- a/kronos/kronos_test.go +++ b/kronos/kronos_test.go @@ -2,7 +2,7 @@ package kronos_test import ( "fmt" - "github.com/lithictech/go-aperitif/kronos" + "github.com/lithictech/go-aperitif/v2/kronos" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "math/rand" diff --git a/logctx/logctx_test.go b/logctx/logctx_test.go index 482866f..0e9e493 100644 --- a/logctx/logctx_test.go +++ b/logctx/logctx_test.go @@ -2,7 +2,7 @@ package logctx_test import ( "context" - "github.com/lithictech/go-aperitif/logctx" + "github.com/lithictech/go-aperitif/v2/logctx" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "log/slog" diff --git a/parallel/parallel.go b/parallel/parallel.go index 47a48c0..fbdc8e9 100644 --- a/parallel/parallel.go +++ b/parallel/parallel.go @@ -3,7 +3,7 @@ package parallel import ( "errors" "github.com/hashicorp/go-multierror" - "github.com/lithictech/go-aperitif/mariobros" + "github.com/lithictech/go-aperitif/v2/mariobros" "sync" ) diff --git a/parallel/parallel_test.go b/parallel/parallel_test.go index 774c092..725bab9 100644 --- a/parallel/parallel_test.go +++ b/parallel/parallel_test.go @@ -1,7 +1,7 @@ package parallel_test import ( - "github.com/lithictech/go-aperitif/parallel" + "github.com/lithictech/go-aperitif/v2/parallel" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "sync" diff --git a/quiz/quiz_test.go b/quiz/quiz_test.go index 4e7157d..3378ee7 100644 --- a/quiz/quiz_test.go +++ b/quiz/quiz_test.go @@ -1,7 +1,7 @@ package quiz_test import ( - "github.com/lithictech/go-aperitif/quiz" + "github.com/lithictech/go-aperitif/v2/quiz" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "testing" diff --git a/stopwatch/stopwatch_test.go b/stopwatch/stopwatch_test.go index 0725347..466e732 100644 --- a/stopwatch/stopwatch_test.go +++ b/stopwatch/stopwatch_test.go @@ -2,8 +2,8 @@ package stopwatch_test import ( "context" - "github.com/lithictech/go-aperitif/logctx" - "github.com/lithictech/go-aperitif/stopwatch" + "github.com/lithictech/go-aperitif/v2/logctx" + "github.com/lithictech/go-aperitif/v2/stopwatch" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "log/slog" diff --git a/validator/validator_test.go b/validator/validator_test.go index f79ba31..dad41f3 100644 --- a/validator/validator_test.go +++ b/validator/validator_test.go @@ -2,7 +2,7 @@ package validator_test import ( "errors" - "github.com/lithictech/go-aperitif/validator" + "github.com/lithictech/go-aperitif/v2/validator" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "testing" diff --git a/validator/validators.go b/validator/validators.go index 357a36b..53817b3 100644 --- a/validator/validators.go +++ b/validator/validators.go @@ -2,7 +2,7 @@ package validator import ( "errors" - "github.com/lithictech/go-aperitif/kronos" + "github.com/lithictech/go-aperitif/v2/kronos" "github.com/rgalanakis/validator" "net/url" "regexp"