From e3bd7dc118afc48768e926df8d83f32a6a3cadc3 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Mon, 1 Apr 2024 13:49:50 -0700 Subject: [PATCH 1/9] Add analytics --- cmd/cmdtest.go | 2 + cmd/root.go | 16 +- go.mod | 4 + go.sum | 8 + internal/analytics/client.go | 6 + internal/analytics/mock.go | 13 + internal/analytics/segmentio.go | 26 ++ main.go | 14 +- vendor/github.com/google/uuid/CHANGELOG.md | 21 + vendor/github.com/google/uuid/CONTRIBUTING.md | 26 ++ vendor/github.com/google/uuid/CONTRIBUTORS | 9 + vendor/github.com/google/uuid/LICENSE | 27 ++ vendor/github.com/google/uuid/README.md | 21 + vendor/github.com/google/uuid/dce.go | 80 ++++ vendor/github.com/google/uuid/doc.go | 12 + vendor/github.com/google/uuid/hash.go | 53 +++ vendor/github.com/google/uuid/marshal.go | 38 ++ vendor/github.com/google/uuid/node.go | 90 ++++ vendor/github.com/google/uuid/node_js.go | 12 + vendor/github.com/google/uuid/node_net.go | 33 ++ vendor/github.com/google/uuid/null.go | 118 +++++ vendor/github.com/google/uuid/sql.go | 59 +++ vendor/github.com/google/uuid/time.go | 123 +++++ vendor/github.com/google/uuid/util.go | 43 ++ vendor/github.com/google/uuid/uuid.go | 312 +++++++++++++ vendor/github.com/google/uuid/version1.go | 44 ++ vendor/github.com/google/uuid/version4.go | 76 +++ .../segmentio/analytics-go/v3/.gitignore | 32 ++ .../segmentio/analytics-go/v3/.gitmodules | 6 + .../segmentio/analytics-go/v3/History.md | 93 ++++ .../segmentio/analytics-go/v3/License.md | 21 + .../segmentio/analytics-go/v3/Makefile | 31 ++ .../segmentio/analytics-go/v3/Readme.md | 55 +++ .../segmentio/analytics-go/v3/alias.go | 40 ++ .../segmentio/analytics-go/v3/analytics.go | 431 ++++++++++++++++++ .../segmentio/analytics-go/v3/config.go | 173 +++++++ .../segmentio/analytics-go/v3/context.go | 150 ++++++ .../segmentio/analytics-go/v3/error.go | 60 +++ .../segmentio/analytics-go/v3/executor.go | 53 +++ .../segmentio/analytics-go/v3/group.go | 42 ++ .../segmentio/analytics-go/v3/identify.go | 33 ++ .../segmentio/analytics-go/v3/integrations.go | 44 ++ .../segmentio/analytics-go/v3/json.go | 87 ++++ .../segmentio/analytics-go/v3/logger.go | 47 ++ .../segmentio/analytics-go/v3/message.go | 128 ++++++ .../segmentio/analytics-go/v3/page.go | 34 ++ .../segmentio/analytics-go/v3/properties.go | 117 +++++ .../segmentio/analytics-go/v3/screen.go | 34 ++ .../segmentio/analytics-go/v3/timeout_15.go | 16 + .../segmentio/analytics-go/v3/timeout_16.go | 10 + .../segmentio/analytics-go/v3/track.go | 42 ++ .../segmentio/analytics-go/v3/traits.go | 89 ++++ .../segmentio/analytics-go/v3/validate.go | 65 +++ .../github.com/segmentio/backo-go/.gitmodules | 3 + .../github.com/segmentio/backo-go/README.md | 80 ++++ vendor/github.com/segmentio/backo-go/backo.go | 83 ++++ vendor/modules.txt | 11 + 57 files changed, 3393 insertions(+), 3 deletions(-) create mode 100644 internal/analytics/client.go create mode 100644 internal/analytics/mock.go create mode 100644 internal/analytics/segmentio.go create mode 100644 vendor/github.com/google/uuid/CHANGELOG.md create mode 100644 vendor/github.com/google/uuid/CONTRIBUTING.md create mode 100644 vendor/github.com/google/uuid/CONTRIBUTORS create mode 100644 vendor/github.com/google/uuid/LICENSE create mode 100644 vendor/github.com/google/uuid/README.md create mode 100644 vendor/github.com/google/uuid/dce.go create mode 100644 vendor/github.com/google/uuid/doc.go create mode 100644 vendor/github.com/google/uuid/hash.go create mode 100644 vendor/github.com/google/uuid/marshal.go create mode 100644 vendor/github.com/google/uuid/node.go create mode 100644 vendor/github.com/google/uuid/node_js.go create mode 100644 vendor/github.com/google/uuid/node_net.go create mode 100644 vendor/github.com/google/uuid/null.go create mode 100644 vendor/github.com/google/uuid/sql.go create mode 100644 vendor/github.com/google/uuid/time.go create mode 100644 vendor/github.com/google/uuid/util.go create mode 100644 vendor/github.com/google/uuid/uuid.go create mode 100644 vendor/github.com/google/uuid/version1.go create mode 100644 vendor/github.com/google/uuid/version4.go create mode 100644 vendor/github.com/segmentio/analytics-go/v3/.gitignore create mode 100644 vendor/github.com/segmentio/analytics-go/v3/.gitmodules create mode 100644 vendor/github.com/segmentio/analytics-go/v3/History.md create mode 100644 vendor/github.com/segmentio/analytics-go/v3/License.md create mode 100644 vendor/github.com/segmentio/analytics-go/v3/Makefile create mode 100644 vendor/github.com/segmentio/analytics-go/v3/Readme.md create mode 100644 vendor/github.com/segmentio/analytics-go/v3/alias.go create mode 100644 vendor/github.com/segmentio/analytics-go/v3/analytics.go create mode 100644 vendor/github.com/segmentio/analytics-go/v3/config.go create mode 100644 vendor/github.com/segmentio/analytics-go/v3/context.go create mode 100644 vendor/github.com/segmentio/analytics-go/v3/error.go create mode 100644 vendor/github.com/segmentio/analytics-go/v3/executor.go create mode 100644 vendor/github.com/segmentio/analytics-go/v3/group.go create mode 100644 vendor/github.com/segmentio/analytics-go/v3/identify.go create mode 100644 vendor/github.com/segmentio/analytics-go/v3/integrations.go create mode 100644 vendor/github.com/segmentio/analytics-go/v3/json.go create mode 100644 vendor/github.com/segmentio/analytics-go/v3/logger.go create mode 100644 vendor/github.com/segmentio/analytics-go/v3/message.go create mode 100644 vendor/github.com/segmentio/analytics-go/v3/page.go create mode 100644 vendor/github.com/segmentio/analytics-go/v3/properties.go create mode 100644 vendor/github.com/segmentio/analytics-go/v3/screen.go create mode 100644 vendor/github.com/segmentio/analytics-go/v3/timeout_15.go create mode 100644 vendor/github.com/segmentio/analytics-go/v3/timeout_16.go create mode 100644 vendor/github.com/segmentio/analytics-go/v3/track.go create mode 100644 vendor/github.com/segmentio/analytics-go/v3/traits.go create mode 100644 vendor/github.com/segmentio/analytics-go/v3/validate.go create mode 100644 vendor/github.com/segmentio/backo-go/.gitmodules create mode 100644 vendor/github.com/segmentio/backo-go/README.md create mode 100644 vendor/github.com/segmentio/backo-go/backo.go diff --git a/cmd/cmdtest.go b/cmd/cmdtest.go index e2abf68a..240558e9 100644 --- a/cmd/cmdtest.go +++ b/cmd/cmdtest.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/require" + "ldcli/internal/analytics" "ldcli/internal/environments" "ldcli/internal/flags" "ldcli/internal/members" @@ -24,6 +25,7 @@ func CallCmd( args []string, ) ([]byte, error) { rootCmd, err := NewRootCommand( + analytics.MockClient{}, environmentsClient, flagsClient, membersClient, diff --git a/cmd/root.go b/cmd/root.go index 6f82a71c..feb57084 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -4,6 +4,7 @@ import ( "fmt" "log" "os" + "time" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -13,6 +14,7 @@ import ( flagscmd "ldcli/cmd/flags" mbrscmd "ldcli/cmd/members" projcmd "ldcli/cmd/projects" + "ldcli/internal/analytics" "ldcli/internal/environments" "ldcli/internal/flags" "ldcli/internal/members" @@ -20,6 +22,7 @@ import ( ) func NewRootCommand( + client analytics.AnalyticsTracker, environmentsClient environments.Client, flagsClient flags.Client, membersClient members.Client, @@ -96,8 +99,9 @@ func NewRootCommand( return cmd, nil } -func Execute(version string) { +func Execute(client analytics.SegmentioClient, version string) { rootCmd, err := NewRootCommand( + client, environments.NewClient(version), flags.NewClient(version), members.NewClient(version), @@ -108,6 +112,16 @@ func Execute(version string) { log.Fatal(err) } + err = client.Track( + "user-123", + map[string]interface{}{ + "event1": time.Now().String(), + }, + ) + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + } + err = rootCmd.Execute() if err != nil { fmt.Fprintln(os.Stderr, err.Error()) diff --git a/go.mod b/go.mod index b6406b96..3c1d5cfb 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/launchdarkly/api-client-go/v14 v14.0.0 github.com/muesli/reflow v0.3.0 github.com/pkg/errors v0.9.1 + github.com/segmentio/analytics-go/v3 v3.3.0 github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.18.2 github.com/stretchr/testify v1.9.0 @@ -20,11 +21,13 @@ require ( github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect + github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dlclark/regexp2 v1.4.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.4.0 // indirect github.com/gorilla/css v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -45,6 +48,7 @@ require ( github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f // indirect + github.com/segmentio/backo-go v1.0.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect diff --git a/go.sum b/go.sum index c40b262f..a7da1680 100644 --- a/go.sum +++ b/go.sum @@ -42,6 +42,8 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= @@ -123,6 +125,8 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= @@ -195,6 +199,10 @@ github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6g github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f h1:MvTmaQdww/z0Q4wrYjDSCcZ78NoftLQyHBSLW/Cx79Y= github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= +github.com/segmentio/analytics-go/v3 v3.3.0 h1:8VOMaVGBW03pdBrj1CMFfY9o/rnjJC+1wyQHlVxjw5o= +github.com/segmentio/analytics-go/v3 v3.3.0/go.mod h1:p8owAF8X+5o27jmvUognuXxdtqvSGtD0ZrfY2kcS9bE= +github.com/segmentio/backo-go v1.0.0 h1:kbOAtGJY2DqOR0jfRkYEorx/b18RgtepGtY3+Cpe6qA= +github.com/segmentio/backo-go v1.0.0/go.mod h1:kJ9mm9YmoWSkk+oQ+5Cj8DEoRCX2JT6As4kEtIIOp1M= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= diff --git a/internal/analytics/client.go b/internal/analytics/client.go new file mode 100644 index 00000000..d06360bd --- /dev/null +++ b/internal/analytics/client.go @@ -0,0 +1,6 @@ +package analytics + +type AnalyticsTracker interface { + Track(userID string, traits map[string]interface{}) error + Close() error +} diff --git a/internal/analytics/mock.go b/internal/analytics/mock.go new file mode 100644 index 00000000..a709405c --- /dev/null +++ b/internal/analytics/mock.go @@ -0,0 +1,13 @@ +package analytics + +type MockClient struct{} + +func (c MockClient) Track(userID string, traits map[string]interface{}) error { + return nil +} + +func (c MockClient) Close() error { + return nil +} + +var _ AnalyticsTracker = &MockClient{} diff --git a/internal/analytics/segmentio.go b/internal/analytics/segmentio.go new file mode 100644 index 00000000..57bcd783 --- /dev/null +++ b/internal/analytics/segmentio.go @@ -0,0 +1,26 @@ +package analytics + +import "github.com/segmentio/analytics-go/v3" + +type SegmentioClient struct { + client analytics.Client +} + +func NewSegmentioClient(client analytics.Client) SegmentioClient { + return SegmentioClient{ + client: client, + } +} + +func (c SegmentioClient) Track(userID string, traits map[string]interface{}) error { + return c.client.Enqueue(analytics.Identify{ + UserId: userID, + Traits: traits, + }) +} + +func (c SegmentioClient) Close() error { + return c.client.Close() +} + +var _ AnalyticsTracker = &SegmentioClient{} diff --git a/main.go b/main.go index f47607db..ebb81ea6 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,11 @@ package main -import "ldcli/cmd" +import ( + segmentio "github.com/segmentio/analytics-go/v3" + + "ldcli/cmd" + "ldcli/internal/analytics" +) // main.version is set at build time via ldflags by go releaser https://goreleaser.com/cookbooks/using-main.version/ var ( @@ -8,5 +13,10 @@ var ( ) func main() { - cmd.Execute(version) + client := analytics.NewSegmentioClient( + segmentio.New("TODO"), + ) + defer client.Close() + + cmd.Execute(client, version) } diff --git a/vendor/github.com/google/uuid/CHANGELOG.md b/vendor/github.com/google/uuid/CHANGELOG.md new file mode 100644 index 00000000..7ed347d3 --- /dev/null +++ b/vendor/github.com/google/uuid/CHANGELOG.md @@ -0,0 +1,21 @@ +# Changelog + +## [1.4.0](https://github.com/google/uuid/compare/v1.3.1...v1.4.0) (2023-10-26) + + +### Features + +* UUIDs slice type with Strings() convenience method ([#133](https://github.com/google/uuid/issues/133)) ([cd5fbbd](https://github.com/google/uuid/commit/cd5fbbdd02f3e3467ac18940e07e062be1f864b4)) + +### Fixes + +* Clarify that Parse's job is to parse but not necessarily validate strings. (Documents current behavior) + +## [1.3.1](https://github.com/google/uuid/compare/v1.3.0...v1.3.1) (2023-08-18) + + +### Bug Fixes + +* Use .EqualFold() to parse urn prefixed UUIDs ([#118](https://github.com/google/uuid/issues/118)) ([574e687](https://github.com/google/uuid/commit/574e6874943741fb99d41764c705173ada5293f0)) + +## Changelog diff --git a/vendor/github.com/google/uuid/CONTRIBUTING.md b/vendor/github.com/google/uuid/CONTRIBUTING.md new file mode 100644 index 00000000..a502fdc5 --- /dev/null +++ b/vendor/github.com/google/uuid/CONTRIBUTING.md @@ -0,0 +1,26 @@ +# How to contribute + +We definitely welcome patches and contribution to this project! + +### Tips + +Commits must be formatted according to the [Conventional Commits Specification](https://www.conventionalcommits.org). + +Always try to include a test case! If it is not possible or not necessary, +please explain why in the pull request description. + +### Releasing + +Commits that would precipitate a SemVer change, as described in the Conventional +Commits Specification, will trigger [`release-please`](https://github.com/google-github-actions/release-please-action) +to create a release candidate pull request. Once submitted, `release-please` +will create a release. + +For tips on how to work with `release-please`, see its documentation. + +### Legal requirements + +In order to protect both you and ourselves, you will need to sign the +[Contributor License Agreement](https://cla.developers.google.com/clas). + +You may have already signed it for other Google projects. diff --git a/vendor/github.com/google/uuid/CONTRIBUTORS b/vendor/github.com/google/uuid/CONTRIBUTORS new file mode 100644 index 00000000..b4bb97f6 --- /dev/null +++ b/vendor/github.com/google/uuid/CONTRIBUTORS @@ -0,0 +1,9 @@ +Paul Borman +bmatsuo +shawnps +theory +jboverfelt +dsymonds +cd1 +wallclockbuilder +dansouza diff --git a/vendor/github.com/google/uuid/LICENSE b/vendor/github.com/google/uuid/LICENSE new file mode 100644 index 00000000..5dc68268 --- /dev/null +++ b/vendor/github.com/google/uuid/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009,2014 Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/google/uuid/README.md b/vendor/github.com/google/uuid/README.md new file mode 100644 index 00000000..3e9a6188 --- /dev/null +++ b/vendor/github.com/google/uuid/README.md @@ -0,0 +1,21 @@ +# uuid +The uuid package generates and inspects UUIDs based on +[RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122) +and DCE 1.1: Authentication and Security Services. + +This package is based on the github.com/pborman/uuid package (previously named +code.google.com/p/go-uuid). It differs from these earlier packages in that +a UUID is a 16 byte array rather than a byte slice. One loss due to this +change is the ability to represent an invalid UUID (vs a NIL UUID). + +###### Install +```sh +go get github.com/google/uuid +``` + +###### Documentation +[![Go Reference](https://pkg.go.dev/badge/github.com/google/uuid.svg)](https://pkg.go.dev/github.com/google/uuid) + +Full `go doc` style documentation for the package can be viewed online without +installing this package by using the GoDoc site here: +http://pkg.go.dev/github.com/google/uuid diff --git a/vendor/github.com/google/uuid/dce.go b/vendor/github.com/google/uuid/dce.go new file mode 100644 index 00000000..fa820b9d --- /dev/null +++ b/vendor/github.com/google/uuid/dce.go @@ -0,0 +1,80 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" + "fmt" + "os" +) + +// A Domain represents a Version 2 domain +type Domain byte + +// Domain constants for DCE Security (Version 2) UUIDs. +const ( + Person = Domain(0) + Group = Domain(1) + Org = Domain(2) +) + +// NewDCESecurity returns a DCE Security (Version 2) UUID. +// +// The domain should be one of Person, Group or Org. +// On a POSIX system the id should be the users UID for the Person +// domain and the users GID for the Group. The meaning of id for +// the domain Org or on non-POSIX systems is site defined. +// +// For a given domain/id pair the same token may be returned for up to +// 7 minutes and 10 seconds. +func NewDCESecurity(domain Domain, id uint32) (UUID, error) { + uuid, err := NewUUID() + if err == nil { + uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2 + uuid[9] = byte(domain) + binary.BigEndian.PutUint32(uuid[0:], id) + } + return uuid, err +} + +// NewDCEPerson returns a DCE Security (Version 2) UUID in the person +// domain with the id returned by os.Getuid. +// +// NewDCESecurity(Person, uint32(os.Getuid())) +func NewDCEPerson() (UUID, error) { + return NewDCESecurity(Person, uint32(os.Getuid())) +} + +// NewDCEGroup returns a DCE Security (Version 2) UUID in the group +// domain with the id returned by os.Getgid. +// +// NewDCESecurity(Group, uint32(os.Getgid())) +func NewDCEGroup() (UUID, error) { + return NewDCESecurity(Group, uint32(os.Getgid())) +} + +// Domain returns the domain for a Version 2 UUID. Domains are only defined +// for Version 2 UUIDs. +func (uuid UUID) Domain() Domain { + return Domain(uuid[9]) +} + +// ID returns the id for a Version 2 UUID. IDs are only defined for Version 2 +// UUIDs. +func (uuid UUID) ID() uint32 { + return binary.BigEndian.Uint32(uuid[0:4]) +} + +func (d Domain) String() string { + switch d { + case Person: + return "Person" + case Group: + return "Group" + case Org: + return "Org" + } + return fmt.Sprintf("Domain%d", int(d)) +} diff --git a/vendor/github.com/google/uuid/doc.go b/vendor/github.com/google/uuid/doc.go new file mode 100644 index 00000000..5b8a4b9a --- /dev/null +++ b/vendor/github.com/google/uuid/doc.go @@ -0,0 +1,12 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package uuid generates and inspects UUIDs. +// +// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security +// Services. +// +// A UUID is a 16 byte (128 bit) array. UUIDs may be used as keys to +// maps or compared directly. +package uuid diff --git a/vendor/github.com/google/uuid/hash.go b/vendor/github.com/google/uuid/hash.go new file mode 100644 index 00000000..b404f4be --- /dev/null +++ b/vendor/github.com/google/uuid/hash.go @@ -0,0 +1,53 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "crypto/md5" + "crypto/sha1" + "hash" +) + +// Well known namespace IDs and UUIDs +var ( + NameSpaceDNS = Must(Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8")) + NameSpaceURL = Must(Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8")) + NameSpaceOID = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8")) + NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8")) + Nil UUID // empty UUID, all zeros +) + +// NewHash returns a new UUID derived from the hash of space concatenated with +// data generated by h. The hash should be at least 16 byte in length. The +// first 16 bytes of the hash are used to form the UUID. The version of the +// UUID will be the lower 4 bits of version. NewHash is used to implement +// NewMD5 and NewSHA1. +func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID { + h.Reset() + h.Write(space[:]) //nolint:errcheck + h.Write(data) //nolint:errcheck + s := h.Sum(nil) + var uuid UUID + copy(uuid[:], s) + uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4) + uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant + return uuid +} + +// NewMD5 returns a new MD5 (Version 3) UUID based on the +// supplied name space and data. It is the same as calling: +// +// NewHash(md5.New(), space, data, 3) +func NewMD5(space UUID, data []byte) UUID { + return NewHash(md5.New(), space, data, 3) +} + +// NewSHA1 returns a new SHA1 (Version 5) UUID based on the +// supplied name space and data. It is the same as calling: +// +// NewHash(sha1.New(), space, data, 5) +func NewSHA1(space UUID, data []byte) UUID { + return NewHash(sha1.New(), space, data, 5) +} diff --git a/vendor/github.com/google/uuid/marshal.go b/vendor/github.com/google/uuid/marshal.go new file mode 100644 index 00000000..14bd3407 --- /dev/null +++ b/vendor/github.com/google/uuid/marshal.go @@ -0,0 +1,38 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import "fmt" + +// MarshalText implements encoding.TextMarshaler. +func (uuid UUID) MarshalText() ([]byte, error) { + var js [36]byte + encodeHex(js[:], uuid) + return js[:], nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (uuid *UUID) UnmarshalText(data []byte) error { + id, err := ParseBytes(data) + if err != nil { + return err + } + *uuid = id + return nil +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (uuid UUID) MarshalBinary() ([]byte, error) { + return uuid[:], nil +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (uuid *UUID) UnmarshalBinary(data []byte) error { + if len(data) != 16 { + return fmt.Errorf("invalid UUID (got %d bytes)", len(data)) + } + copy(uuid[:], data) + return nil +} diff --git a/vendor/github.com/google/uuid/node.go b/vendor/github.com/google/uuid/node.go new file mode 100644 index 00000000..d651a2b0 --- /dev/null +++ b/vendor/github.com/google/uuid/node.go @@ -0,0 +1,90 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "sync" +) + +var ( + nodeMu sync.Mutex + ifname string // name of interface being used + nodeID [6]byte // hardware for version 1 UUIDs + zeroID [6]byte // nodeID with only 0's +) + +// NodeInterface returns the name of the interface from which the NodeID was +// derived. The interface "user" is returned if the NodeID was set by +// SetNodeID. +func NodeInterface() string { + defer nodeMu.Unlock() + nodeMu.Lock() + return ifname +} + +// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs. +// If name is "" then the first usable interface found will be used or a random +// Node ID will be generated. If a named interface cannot be found then false +// is returned. +// +// SetNodeInterface never fails when name is "". +func SetNodeInterface(name string) bool { + defer nodeMu.Unlock() + nodeMu.Lock() + return setNodeInterface(name) +} + +func setNodeInterface(name string) bool { + iname, addr := getHardwareInterface(name) // null implementation for js + if iname != "" && addr != nil { + ifname = iname + copy(nodeID[:], addr) + return true + } + + // We found no interfaces with a valid hardware address. If name + // does not specify a specific interface generate a random Node ID + // (section 4.1.6) + if name == "" { + ifname = "random" + randomBits(nodeID[:]) + return true + } + return false +} + +// NodeID returns a slice of a copy of the current Node ID, setting the Node ID +// if not already set. +func NodeID() []byte { + defer nodeMu.Unlock() + nodeMu.Lock() + if nodeID == zeroID { + setNodeInterface("") + } + nid := nodeID + return nid[:] +} + +// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes +// of id are used. If id is less than 6 bytes then false is returned and the +// Node ID is not set. +func SetNodeID(id []byte) bool { + if len(id) < 6 { + return false + } + defer nodeMu.Unlock() + nodeMu.Lock() + copy(nodeID[:], id) + ifname = "user" + return true +} + +// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is +// not valid. The NodeID is only well defined for version 1 and 2 UUIDs. +func (uuid UUID) NodeID() []byte { + var node [6]byte + copy(node[:], uuid[10:]) + return node[:] +} diff --git a/vendor/github.com/google/uuid/node_js.go b/vendor/github.com/google/uuid/node_js.go new file mode 100644 index 00000000..b2a0bc87 --- /dev/null +++ b/vendor/github.com/google/uuid/node_js.go @@ -0,0 +1,12 @@ +// Copyright 2017 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build js + +package uuid + +// getHardwareInterface returns nil values for the JS version of the code. +// This removes the "net" dependency, because it is not used in the browser. +// Using the "net" library inflates the size of the transpiled JS code by 673k bytes. +func getHardwareInterface(name string) (string, []byte) { return "", nil } diff --git a/vendor/github.com/google/uuid/node_net.go b/vendor/github.com/google/uuid/node_net.go new file mode 100644 index 00000000..0cbbcddb --- /dev/null +++ b/vendor/github.com/google/uuid/node_net.go @@ -0,0 +1,33 @@ +// Copyright 2017 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !js + +package uuid + +import "net" + +var interfaces []net.Interface // cached list of interfaces + +// getHardwareInterface returns the name and hardware address of interface name. +// If name is "" then the name and hardware address of one of the system's +// interfaces is returned. If no interfaces are found (name does not exist or +// there are no interfaces) then "", nil is returned. +// +// Only addresses of at least 6 bytes are returned. +func getHardwareInterface(name string) (string, []byte) { + if interfaces == nil { + var err error + interfaces, err = net.Interfaces() + if err != nil { + return "", nil + } + } + for _, ifs := range interfaces { + if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) { + return ifs.Name, ifs.HardwareAddr + } + } + return "", nil +} diff --git a/vendor/github.com/google/uuid/null.go b/vendor/github.com/google/uuid/null.go new file mode 100644 index 00000000..d7fcbf28 --- /dev/null +++ b/vendor/github.com/google/uuid/null.go @@ -0,0 +1,118 @@ +// Copyright 2021 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "bytes" + "database/sql/driver" + "encoding/json" + "fmt" +) + +var jsonNull = []byte("null") + +// NullUUID represents a UUID that may be null. +// NullUUID implements the SQL driver.Scanner interface so +// it can be used as a scan destination: +// +// var u uuid.NullUUID +// err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&u) +// ... +// if u.Valid { +// // use u.UUID +// } else { +// // NULL value +// } +// +type NullUUID struct { + UUID UUID + Valid bool // Valid is true if UUID is not NULL +} + +// Scan implements the SQL driver.Scanner interface. +func (nu *NullUUID) Scan(value interface{}) error { + if value == nil { + nu.UUID, nu.Valid = Nil, false + return nil + } + + err := nu.UUID.Scan(value) + if err != nil { + nu.Valid = false + return err + } + + nu.Valid = true + return nil +} + +// Value implements the driver Valuer interface. +func (nu NullUUID) Value() (driver.Value, error) { + if !nu.Valid { + return nil, nil + } + // Delegate to UUID Value function + return nu.UUID.Value() +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (nu NullUUID) MarshalBinary() ([]byte, error) { + if nu.Valid { + return nu.UUID[:], nil + } + + return []byte(nil), nil +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (nu *NullUUID) UnmarshalBinary(data []byte) error { + if len(data) != 16 { + return fmt.Errorf("invalid UUID (got %d bytes)", len(data)) + } + copy(nu.UUID[:], data) + nu.Valid = true + return nil +} + +// MarshalText implements encoding.TextMarshaler. +func (nu NullUUID) MarshalText() ([]byte, error) { + if nu.Valid { + return nu.UUID.MarshalText() + } + + return jsonNull, nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (nu *NullUUID) UnmarshalText(data []byte) error { + id, err := ParseBytes(data) + if err != nil { + nu.Valid = false + return err + } + nu.UUID = id + nu.Valid = true + return nil +} + +// MarshalJSON implements json.Marshaler. +func (nu NullUUID) MarshalJSON() ([]byte, error) { + if nu.Valid { + return json.Marshal(nu.UUID) + } + + return jsonNull, nil +} + +// UnmarshalJSON implements json.Unmarshaler. +func (nu *NullUUID) UnmarshalJSON(data []byte) error { + if bytes.Equal(data, jsonNull) { + *nu = NullUUID{} + return nil // valid null UUID + } + err := json.Unmarshal(data, &nu.UUID) + nu.Valid = err == nil + return err +} diff --git a/vendor/github.com/google/uuid/sql.go b/vendor/github.com/google/uuid/sql.go new file mode 100644 index 00000000..2e02ec06 --- /dev/null +++ b/vendor/github.com/google/uuid/sql.go @@ -0,0 +1,59 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "database/sql/driver" + "fmt" +) + +// Scan implements sql.Scanner so UUIDs can be read from databases transparently. +// Currently, database types that map to string and []byte are supported. Please +// consult database-specific driver documentation for matching types. +func (uuid *UUID) Scan(src interface{}) error { + switch src := src.(type) { + case nil: + return nil + + case string: + // if an empty UUID comes from a table, we return a null UUID + if src == "" { + return nil + } + + // see Parse for required string format + u, err := Parse(src) + if err != nil { + return fmt.Errorf("Scan: %v", err) + } + + *uuid = u + + case []byte: + // if an empty UUID comes from a table, we return a null UUID + if len(src) == 0 { + return nil + } + + // assumes a simple slice of bytes if 16 bytes + // otherwise attempts to parse + if len(src) != 16 { + return uuid.Scan(string(src)) + } + copy((*uuid)[:], src) + + default: + return fmt.Errorf("Scan: unable to scan type %T into UUID", src) + } + + return nil +} + +// Value implements sql.Valuer so that UUIDs can be written to databases +// transparently. Currently, UUIDs map to strings. Please consult +// database-specific driver documentation for matching types. +func (uuid UUID) Value() (driver.Value, error) { + return uuid.String(), nil +} diff --git a/vendor/github.com/google/uuid/time.go b/vendor/github.com/google/uuid/time.go new file mode 100644 index 00000000..e6ef06cd --- /dev/null +++ b/vendor/github.com/google/uuid/time.go @@ -0,0 +1,123 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" + "sync" + "time" +) + +// A Time represents a time as the number of 100's of nanoseconds since 15 Oct +// 1582. +type Time int64 + +const ( + lillian = 2299160 // Julian day of 15 Oct 1582 + unix = 2440587 // Julian day of 1 Jan 1970 + epoch = unix - lillian // Days between epochs + g1582 = epoch * 86400 // seconds between epochs + g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs +) + +var ( + timeMu sync.Mutex + lasttime uint64 // last time we returned + clockSeq uint16 // clock sequence for this run + + timeNow = time.Now // for testing +) + +// UnixTime converts t the number of seconds and nanoseconds using the Unix +// epoch of 1 Jan 1970. +func (t Time) UnixTime() (sec, nsec int64) { + sec = int64(t - g1582ns100) + nsec = (sec % 10000000) * 100 + sec /= 10000000 + return sec, nsec +} + +// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and +// clock sequence as well as adjusting the clock sequence as needed. An error +// is returned if the current time cannot be determined. +func GetTime() (Time, uint16, error) { + defer timeMu.Unlock() + timeMu.Lock() + return getTime() +} + +func getTime() (Time, uint16, error) { + t := timeNow() + + // If we don't have a clock sequence already, set one. + if clockSeq == 0 { + setClockSequence(-1) + } + now := uint64(t.UnixNano()/100) + g1582ns100 + + // If time has gone backwards with this clock sequence then we + // increment the clock sequence + if now <= lasttime { + clockSeq = ((clockSeq + 1) & 0x3fff) | 0x8000 + } + lasttime = now + return Time(now), clockSeq, nil +} + +// ClockSequence returns the current clock sequence, generating one if not +// already set. The clock sequence is only used for Version 1 UUIDs. +// +// The uuid package does not use global static storage for the clock sequence or +// the last time a UUID was generated. Unless SetClockSequence is used, a new +// random clock sequence is generated the first time a clock sequence is +// requested by ClockSequence, GetTime, or NewUUID. (section 4.2.1.1) +func ClockSequence() int { + defer timeMu.Unlock() + timeMu.Lock() + return clockSequence() +} + +func clockSequence() int { + if clockSeq == 0 { + setClockSequence(-1) + } + return int(clockSeq & 0x3fff) +} + +// SetClockSequence sets the clock sequence to the lower 14 bits of seq. Setting to +// -1 causes a new sequence to be generated. +func SetClockSequence(seq int) { + defer timeMu.Unlock() + timeMu.Lock() + setClockSequence(seq) +} + +func setClockSequence(seq int) { + if seq == -1 { + var b [2]byte + randomBits(b[:]) // clock sequence + seq = int(b[0])<<8 | int(b[1]) + } + oldSeq := clockSeq + clockSeq = uint16(seq&0x3fff) | 0x8000 // Set our variant + if oldSeq != clockSeq { + lasttime = 0 + } +} + +// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in +// uuid. The time is only defined for version 1 and 2 UUIDs. +func (uuid UUID) Time() Time { + time := int64(binary.BigEndian.Uint32(uuid[0:4])) + time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32 + time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48 + return Time(time) +} + +// ClockSequence returns the clock sequence encoded in uuid. +// The clock sequence is only well defined for version 1 and 2 UUIDs. +func (uuid UUID) ClockSequence() int { + return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff +} diff --git a/vendor/github.com/google/uuid/util.go b/vendor/github.com/google/uuid/util.go new file mode 100644 index 00000000..5ea6c737 --- /dev/null +++ b/vendor/github.com/google/uuid/util.go @@ -0,0 +1,43 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "io" +) + +// randomBits completely fills slice b with random data. +func randomBits(b []byte) { + if _, err := io.ReadFull(rander, b); err != nil { + panic(err.Error()) // rand should never fail + } +} + +// xvalues returns the value of a byte as a hexadecimal digit or 255. +var xvalues = [256]byte{ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +} + +// xtob converts hex characters x1 and x2 into a byte. +func xtob(x1, x2 byte) (byte, bool) { + b1 := xvalues[x1] + b2 := xvalues[x2] + return (b1 << 4) | b2, b1 != 255 && b2 != 255 +} diff --git a/vendor/github.com/google/uuid/uuid.go b/vendor/github.com/google/uuid/uuid.go new file mode 100644 index 00000000..dc75f7d9 --- /dev/null +++ b/vendor/github.com/google/uuid/uuid.go @@ -0,0 +1,312 @@ +// Copyright 2018 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "bytes" + "crypto/rand" + "encoding/hex" + "errors" + "fmt" + "io" + "strings" + "sync" +) + +// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC +// 4122. +type UUID [16]byte + +// A Version represents a UUID's version. +type Version byte + +// A Variant represents a UUID's variant. +type Variant byte + +// Constants returned by Variant. +const ( + Invalid = Variant(iota) // Invalid UUID + RFC4122 // The variant specified in RFC4122 + Reserved // Reserved, NCS backward compatibility. + Microsoft // Reserved, Microsoft Corporation backward compatibility. + Future // Reserved for future definition. +) + +const randPoolSize = 16 * 16 + +var ( + rander = rand.Reader // random function + poolEnabled = false + poolMu sync.Mutex + poolPos = randPoolSize // protected with poolMu + pool [randPoolSize]byte // protected with poolMu +) + +type invalidLengthError struct{ len int } + +func (err invalidLengthError) Error() string { + return fmt.Sprintf("invalid UUID length: %d", err.len) +} + +// IsInvalidLengthError is matcher function for custom error invalidLengthError +func IsInvalidLengthError(err error) bool { + _, ok := err.(invalidLengthError) + return ok +} + +// Parse decodes s into a UUID or returns an error if it cannot be parsed. Both +// the standard UUID forms defined in RFC 4122 +// (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) are decoded. In addition, +// Parse accepts non-standard strings such as the raw hex encoding +// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx and 38 byte "Microsoft style" encodings, +// e.g. {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}. Only the middle 36 bytes are +// examined in the latter case. Parse should not be used to validate strings as +// it parses non-standard encodings as indicated above. +func Parse(s string) (UUID, error) { + var uuid UUID + switch len(s) { + // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + case 36: + + // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + case 36 + 9: + if !strings.EqualFold(s[:9], "urn:uuid:") { + return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9]) + } + s = s[9:] + + // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + case 36 + 2: + s = s[1:] + + // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + case 32: + var ok bool + for i := range uuid { + uuid[i], ok = xtob(s[i*2], s[i*2+1]) + if !ok { + return uuid, errors.New("invalid UUID format") + } + } + return uuid, nil + default: + return uuid, invalidLengthError{len(s)} + } + // s is now at least 36 bytes long + // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { + return uuid, errors.New("invalid UUID format") + } + for i, x := range [16]int{ + 0, 2, 4, 6, + 9, 11, + 14, 16, + 19, 21, + 24, 26, 28, 30, 32, 34, + } { + v, ok := xtob(s[x], s[x+1]) + if !ok { + return uuid, errors.New("invalid UUID format") + } + uuid[i] = v + } + return uuid, nil +} + +// ParseBytes is like Parse, except it parses a byte slice instead of a string. +func ParseBytes(b []byte) (UUID, error) { + var uuid UUID + switch len(b) { + case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + if !bytes.EqualFold(b[:9], []byte("urn:uuid:")) { + return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9]) + } + b = b[9:] + case 36 + 2: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + b = b[1:] + case 32: // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + var ok bool + for i := 0; i < 32; i += 2 { + uuid[i/2], ok = xtob(b[i], b[i+1]) + if !ok { + return uuid, errors.New("invalid UUID format") + } + } + return uuid, nil + default: + return uuid, invalidLengthError{len(b)} + } + // s is now at least 36 bytes long + // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' { + return uuid, errors.New("invalid UUID format") + } + for i, x := range [16]int{ + 0, 2, 4, 6, + 9, 11, + 14, 16, + 19, 21, + 24, 26, 28, 30, 32, 34, + } { + v, ok := xtob(b[x], b[x+1]) + if !ok { + return uuid, errors.New("invalid UUID format") + } + uuid[i] = v + } + return uuid, nil +} + +// MustParse is like Parse but panics if the string cannot be parsed. +// It simplifies safe initialization of global variables holding compiled UUIDs. +func MustParse(s string) UUID { + uuid, err := Parse(s) + if err != nil { + panic(`uuid: Parse(` + s + `): ` + err.Error()) + } + return uuid +} + +// FromBytes creates a new UUID from a byte slice. Returns an error if the slice +// does not have a length of 16. The bytes are copied from the slice. +func FromBytes(b []byte) (uuid UUID, err error) { + err = uuid.UnmarshalBinary(b) + return uuid, err +} + +// Must returns uuid if err is nil and panics otherwise. +func Must(uuid UUID, err error) UUID { + if err != nil { + panic(err) + } + return uuid +} + +// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +// , or "" if uuid is invalid. +func (uuid UUID) String() string { + var buf [36]byte + encodeHex(buf[:], uuid) + return string(buf[:]) +} + +// URN returns the RFC 2141 URN form of uuid, +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid. +func (uuid UUID) URN() string { + var buf [36 + 9]byte + copy(buf[:], "urn:uuid:") + encodeHex(buf[9:], uuid) + return string(buf[:]) +} + +func encodeHex(dst []byte, uuid UUID) { + hex.Encode(dst, uuid[:4]) + dst[8] = '-' + hex.Encode(dst[9:13], uuid[4:6]) + dst[13] = '-' + hex.Encode(dst[14:18], uuid[6:8]) + dst[18] = '-' + hex.Encode(dst[19:23], uuid[8:10]) + dst[23] = '-' + hex.Encode(dst[24:], uuid[10:]) +} + +// Variant returns the variant encoded in uuid. +func (uuid UUID) Variant() Variant { + switch { + case (uuid[8] & 0xc0) == 0x80: + return RFC4122 + case (uuid[8] & 0xe0) == 0xc0: + return Microsoft + case (uuid[8] & 0xe0) == 0xe0: + return Future + default: + return Reserved + } +} + +// Version returns the version of uuid. +func (uuid UUID) Version() Version { + return Version(uuid[6] >> 4) +} + +func (v Version) String() string { + if v > 15 { + return fmt.Sprintf("BAD_VERSION_%d", v) + } + return fmt.Sprintf("VERSION_%d", v) +} + +func (v Variant) String() string { + switch v { + case RFC4122: + return "RFC4122" + case Reserved: + return "Reserved" + case Microsoft: + return "Microsoft" + case Future: + return "Future" + case Invalid: + return "Invalid" + } + return fmt.Sprintf("BadVariant%d", int(v)) +} + +// SetRand sets the random number generator to r, which implements io.Reader. +// If r.Read returns an error when the package requests random data then +// a panic will be issued. +// +// Calling SetRand with nil sets the random number generator to the default +// generator. +func SetRand(r io.Reader) { + if r == nil { + rander = rand.Reader + return + } + rander = r +} + +// EnableRandPool enables internal randomness pool used for Random +// (Version 4) UUID generation. The pool contains random bytes read from +// the random number generator on demand in batches. Enabling the pool +// may improve the UUID generation throughput significantly. +// +// Since the pool is stored on the Go heap, this feature may be a bad fit +// for security sensitive applications. +// +// Both EnableRandPool and DisableRandPool are not thread-safe and should +// only be called when there is no possibility that New or any other +// UUID Version 4 generation function will be called concurrently. +func EnableRandPool() { + poolEnabled = true +} + +// DisableRandPool disables the randomness pool if it was previously +// enabled with EnableRandPool. +// +// Both EnableRandPool and DisableRandPool are not thread-safe and should +// only be called when there is no possibility that New or any other +// UUID Version 4 generation function will be called concurrently. +func DisableRandPool() { + poolEnabled = false + defer poolMu.Unlock() + poolMu.Lock() + poolPos = randPoolSize +} + +// UUIDs is a slice of UUID types. +type UUIDs []UUID + +// Strings returns a string slice containing the string form of each UUID in uuids. +func (uuids UUIDs) Strings() []string { + var uuidStrs = make([]string, len(uuids)) + for i, uuid := range uuids { + uuidStrs[i] = uuid.String() + } + return uuidStrs +} diff --git a/vendor/github.com/google/uuid/version1.go b/vendor/github.com/google/uuid/version1.go new file mode 100644 index 00000000..46310962 --- /dev/null +++ b/vendor/github.com/google/uuid/version1.go @@ -0,0 +1,44 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" +) + +// NewUUID returns a Version 1 UUID based on the current NodeID and clock +// sequence, and the current time. If the NodeID has not been set by SetNodeID +// or SetNodeInterface then it will be set automatically. If the NodeID cannot +// be set NewUUID returns nil. If clock sequence has not been set by +// SetClockSequence then it will be set automatically. If GetTime fails to +// return the current NewUUID returns nil and an error. +// +// In most cases, New should be used. +func NewUUID() (UUID, error) { + var uuid UUID + now, seq, err := GetTime() + if err != nil { + return uuid, err + } + + timeLow := uint32(now & 0xffffffff) + timeMid := uint16((now >> 32) & 0xffff) + timeHi := uint16((now >> 48) & 0x0fff) + timeHi |= 0x1000 // Version 1 + + binary.BigEndian.PutUint32(uuid[0:], timeLow) + binary.BigEndian.PutUint16(uuid[4:], timeMid) + binary.BigEndian.PutUint16(uuid[6:], timeHi) + binary.BigEndian.PutUint16(uuid[8:], seq) + + nodeMu.Lock() + if nodeID == zeroID { + setNodeInterface("") + } + copy(uuid[10:], nodeID[:]) + nodeMu.Unlock() + + return uuid, nil +} diff --git a/vendor/github.com/google/uuid/version4.go b/vendor/github.com/google/uuid/version4.go new file mode 100644 index 00000000..7697802e --- /dev/null +++ b/vendor/github.com/google/uuid/version4.go @@ -0,0 +1,76 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import "io" + +// New creates a new random UUID or panics. New is equivalent to +// the expression +// +// uuid.Must(uuid.NewRandom()) +func New() UUID { + return Must(NewRandom()) +} + +// NewString creates a new random UUID and returns it as a string or panics. +// NewString is equivalent to the expression +// +// uuid.New().String() +func NewString() string { + return Must(NewRandom()).String() +} + +// NewRandom returns a Random (Version 4) UUID. +// +// The strength of the UUIDs is based on the strength of the crypto/rand +// package. +// +// Uses the randomness pool if it was enabled with EnableRandPool. +// +// A note about uniqueness derived from the UUID Wikipedia entry: +// +// Randomly generated UUIDs have 122 random bits. One's annual risk of being +// hit by a meteorite is estimated to be one chance in 17 billion, that +// means the probability is about 0.00000000006 (6 × 10−11), +// equivalent to the odds of creating a few tens of trillions of UUIDs in a +// year and having one duplicate. +func NewRandom() (UUID, error) { + if !poolEnabled { + return NewRandomFromReader(rander) + } + return newRandomFromPool() +} + +// NewRandomFromReader returns a UUID based on bytes read from a given io.Reader. +func NewRandomFromReader(r io.Reader) (UUID, error) { + var uuid UUID + _, err := io.ReadFull(r, uuid[:]) + if err != nil { + return Nil, err + } + uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 + uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 + return uuid, nil +} + +func newRandomFromPool() (UUID, error) { + var uuid UUID + poolMu.Lock() + if poolPos == randPoolSize { + _, err := io.ReadFull(rander, pool[:]) + if err != nil { + poolMu.Unlock() + return Nil, err + } + poolPos = 0 + } + copy(uuid[:], pool[poolPos:(poolPos+16)]) + poolPos += 16 + poolMu.Unlock() + + uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 + uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 + return uuid, nil +} diff --git a/vendor/github.com/segmentio/analytics-go/v3/.gitignore b/vendor/github.com/segmentio/analytics-go/v3/.gitignore new file mode 100644 index 00000000..942678bd --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/.gitignore @@ -0,0 +1,32 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + +# Emacs +*~ +\#* +.\#* + +# Artifacts +tmp/* diff --git a/vendor/github.com/segmentio/analytics-go/v3/.gitmodules b/vendor/github.com/segmentio/analytics-go/v3/.gitmodules new file mode 100644 index 00000000..b2150b34 --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/.gitmodules @@ -0,0 +1,6 @@ +[submodule "vendor/github.com/segmentio/backo-go"] + path = vendor/github.com/segmentio/backo-go + url = https://github.com/segmentio/backo-go +[submodule "vendor/github.com/xtgo/uuid"] + path = vendor/github.com/xtgo/uuid + url = https://github.com/xtgo/uuid diff --git a/vendor/github.com/segmentio/analytics-go/v3/History.md b/vendor/github.com/segmentio/analytics-go/v3/History.md new file mode 100644 index 00000000..4d867491 --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/History.md @@ -0,0 +1,93 @@ +v3.3.0 / 2023-10-31 +=================== + +* Add groupId to context so the track events can related to both a user and distinct id and group + * Note: When updating to this version, verify the groupId is not being found using the Extra map. The new groupId field will now take precedence. + +v3.1.0 / 2019-09-20 +=================== + + * add consistent panic error message + * Expose the Message interface Validate method + * return error if a custom type is enqueued + * Handle pointer types in Enqueue() + * message: update maxMessageBytes to 32KB + +v3.0.1 / 2018-10-02 +=================== + +* Migrate from Circle V1 format to Circle V2 +* Adds CLI for sending segment events +* Vendor packages back-go and uuid instead of using gitsubmodules + + +v3.0.0 / 2016-06-02 +=================== + + * 3.0 is a significant rewrite with multiple breaking changes. + * [Quickstart](https://segment.com/docs/sources/server/go/quickstart/). + * [Documentation](https://segment.com/docs/sources/server/go/). + * [GoDocs](https://godoc.org/gopkg.in/segmentio/analytics-go.v3). + * [What's New in v3](https://segment.com/docs/sources/server/go/#what-s-new-in-v3). + + +v2.1.0 / 2015-12-28 +=================== + + * Add ability to set custom timestamps for messages. + * Add ability to set a custom `net/http` client. + * Add ability to set a custom logger. + * Fix edge case when client would try to upload no messages. + * Properly upload in-flight messages when client is asked to shutdown. + * Add ability to set `.integrations` field on messages. + * Fix resource leak with interval ticker after shutdown. + * Add retries and back-off when uploading messages. + * Add ability to set custom flush interval. + +v2.0.0 / 2015-02-03 +=================== + + * rewrite with breaking API changes + +v1.2.0 / 2014-09-03 +================== + + * add public .Flush() method + * rename .Stop() to .Close() + +v1.1.0 / 2014-09-02 +================== + + * add client.Stop() to flash/wait. Closes #7 + +v1.0.0 / 2014-08-26 +================== + + * fix response close + * change comments to be more go-like + * change uuid libraries + +0.1.2 / 2014-06-11 +================== + + * add runnable example + * fix: close body + +0.1.1 / 2014-05-31 +================== + + * refactor locking + +0.1.0 / 2014-05-22 +================== + + * replace Debug option with debug package + +0.0.2 / 2014-05-20 +================== + + * add .Start() + * add mutexes + * rename BufferSize to FlushAt and FlushInterval to FlushAfter + * lower FlushInterval to 5 seconds + * lower BufferSize to 20 to match other clients diff --git a/vendor/github.com/segmentio/analytics-go/v3/License.md b/vendor/github.com/segmentio/analytics-go/v3/License.md new file mode 100644 index 00000000..f452c5d0 --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/License.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Segment, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/segmentio/analytics-go/v3/Makefile b/vendor/github.com/segmentio/analytics-go/v3/Makefile new file mode 100644 index 00000000..9de0d1ba --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/Makefile @@ -0,0 +1,31 @@ +ifndef CIRCLE_ARTIFACTS +CIRCLE_ARTIFACTS=tmp +endif + +bootstrap: + .buildscript/bootstrap.sh + +dependencies: + @go get -v -t ./... + +vet: + @go vet ./... + +test: vet + @mkdir -p ${CIRCLE_ARTIFACTS} + @go test -race -coverprofile=${CIRCLE_ARTIFACTS}/cover.out . + @go tool cover -func ${CIRCLE_ARTIFACTS}/cover.out -o ${CIRCLE_ARTIFACTS}/cover.txt + @go tool cover -html ${CIRCLE_ARTIFACTS}/cover.out -o ${CIRCLE_ARTIFACTS}/cover.html + +build: test + @go build ./... + +e2e: + @if [ "$(RUN_E2E_TESTS)" != "true" ]; then \ + echo "Skipping end to end tests."; else \ + go get github.com/segmentio/library-e2e-tester/cmd/tester; \ + tester -segment-write-key=$(SEGMENT_WRITE_KEY) -webhook-auth-username=$(WEBHOOK_AUTH_USERNAME) -webhook-bucket=$(WEBHOOK_BUCKET) -path='cli' -concurrency=2 -skip='advance|alias'; fi + +ci: dependencies test e2e + +.PHONY: bootstrap dependencies vet test e2e ci diff --git a/vendor/github.com/segmentio/analytics-go/v3/Readme.md b/vendor/github.com/segmentio/analytics-go/v3/Readme.md new file mode 100644 index 00000000..5d3d822d --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/Readme.md @@ -0,0 +1,55 @@ +# analytics-go [![Circle CI](https://circleci.com/gh/segmentio/analytics-go/tree/v3.0.svg?style=shield)](https://circleci.com/gh/segmentio/analytics-go/tree/v3.0) [![go-doc](https://godoc.org/github.com/segmentio/analytics-go?status.svg)](https://godoc.org/github.com/segmentio/analytics-go) + +Segment analytics client for Go. + +## Installation + +The package can be simply installed via go get, we recommend that you use a +package version management system like the Go vendor directory or a tool like +Godep to avoid issues related to API breaking changes introduced between major +versions of the library. + +To install it in the GOPATH: +``` +go get https://github.com/segmentio/analytics-go +``` + +## Documentation + +The links bellow should provide all the documentation needed to make the best +use of the library and the Segment API: + +- [Documentation](https://segment.com/docs/libraries/go/) +- [godoc](https://godoc.org/gopkg.in/segmentio/analytics-go.v3) +- [API](https://segment.com/docs/libraries/http/) +- [Specs](https://segment.com/docs/spec/) + +## Usage + +```go +package main + +import ( + "os" + + "github.com/segmentio/analytics-go" +) + +func main() { + // Instantiates a client to use send messages to the segment API. + client := analytics.New(os.Getenv("SEGMENT_WRITE_KEY")) + + // Enqueues a track event that will be sent asynchronously. + client.Enqueue(analytics.Track{ + UserId: "test-user", + Event: "test-snippet", + }) + + // Flushes any queued messages and closes the client. + client.Close() +} +``` + +## License + +The library is released under the [MIT license](License.md). diff --git a/vendor/github.com/segmentio/analytics-go/v3/alias.go b/vendor/github.com/segmentio/analytics-go/v3/alias.go new file mode 100644 index 00000000..8ba0f736 --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/alias.go @@ -0,0 +1,40 @@ +package analytics + +import "time" + +var _ Message = (*Alias)(nil) + +// This type represents object sent in a alias call as described in +// https://segment.com/docs/libraries/http/#alias +type Alias struct { + // This field is exported for serialization purposes and shouldn't be set by + // the application, its value is always overwritten by the library. + Type string `json:"type,omitempty"` + + MessageId string `json:"messageId,omitempty"` + PreviousId string `json:"previousId"` + UserId string `json:"userId"` + Timestamp time.Time `json:"timestamp,omitempty"` + Context *Context `json:"context,omitempty"` + Integrations Integrations `json:"integrations,omitempty"` +} + +func (msg Alias) Validate() error { + if len(msg.UserId) == 0 { + return FieldError{ + Type: "analytics.Alias", + Name: "UserId", + Value: msg.UserId, + } + } + + if len(msg.PreviousId) == 0 { + return FieldError{ + Type: "analytics.Alias", + Name: "PreviousId", + Value: msg.PreviousId, + } + } + + return nil +} diff --git a/vendor/github.com/segmentio/analytics-go/v3/analytics.go b/vendor/github.com/segmentio/analytics-go/v3/analytics.go new file mode 100644 index 00000000..fa134344 --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/analytics.go @@ -0,0 +1,431 @@ +package analytics + +import ( + "fmt" + "io" + "io/ioutil" + "strconv" + "sync" + + "bytes" + "encoding/json" + "net/http" + "time" +) + +// Version of the client. +const Version = "3.0.0" + +// This interface is the main API exposed by the analytics package. +// Values that satsify this interface are returned by the client constructors +// provided by the package and provide a way to send messages via the HTTP API. +type Client interface { + io.Closer + + // Queues a message to be sent by the client when the conditions for a batch + // upload are met. + // This is the main method you'll be using, a typical flow would look like + // this: + // + // client := analytics.New(writeKey) + // ... + // client.Enqueue(analytics.Track{ ... }) + // ... + // client.Close() + // + // The method returns an error if the message queue not be queued, which + // happens if the client was already closed at the time the method was + // called or if the message was malformed. + Enqueue(Message) error +} + +type client struct { + Config + key string + + // This channel is where the `Enqueue` method writes messages so they can be + // picked up and pushed by the backend goroutine taking care of applying the + // batching rules. + msgs chan Message + + // These two channels are used to synchronize the client shutting down when + // `Close` is called. + // The first channel is closed to signal the backend goroutine that it has + // to stop, then the second one is closed by the backend goroutine to signal + // that it has finished flushing all queued messages. + quit chan struct{} + shutdown chan struct{} + + // This HTTP client is used to send requests to the backend, it uses the + // HTTP transport provided in the configuration. + http http.Client +} + +// Instantiate a new client that uses the write key passed as first argument to +// send messages to the backend. +// The client is created with the default configuration. +func New(writeKey string) Client { + // Here we can ignore the error because the default config is always valid. + c, _ := NewWithConfig(writeKey, Config{}) + return c +} + +// Instantiate a new client that uses the write key and configuration passed as +// arguments to send messages to the backend. +// The function will return an error if the configuration contained impossible +// values (like a negative flush interval for example). +// When the function returns an error the returned client will always be nil. +func NewWithConfig(writeKey string, config Config) (cli Client, err error) { + if err = config.validate(); err != nil { + return + } + + c := &client{ + Config: makeConfig(config), + key: writeKey, + msgs: make(chan Message, 100), + quit: make(chan struct{}), + shutdown: make(chan struct{}), + http: makeHttpClient(config.Transport), + } + + go c.loop() + + cli = c + return +} + +func makeHttpClient(transport http.RoundTripper) http.Client { + httpClient := http.Client{ + Transport: transport, + } + if supportsTimeout(transport) { + httpClient.Timeout = 10 * time.Second + } + return httpClient +} + +func dereferenceMessage(msg Message) Message { + switch m := msg.(type) { + case *Alias: + if m == nil { + return nil + } + return *m + case *Group: + if m == nil { + return nil + } + return *m + case *Identify: + if m == nil { + return nil + } + return *m + case *Page: + if m == nil { + return nil + } + return *m + case *Screen: + if m == nil { + return nil + } + return *m + case *Track: + if m == nil { + return nil + } + return *m + } + + return msg +} + +func (c *client) Enqueue(msg Message) (err error) { + msg = dereferenceMessage(msg) + if err = msg.Validate(); err != nil { + return + } + + var id = c.uid() + var ts = c.now() + + switch m := msg.(type) { + case Alias: + m.Type = "alias" + m.MessageId = makeMessageId(m.MessageId, id) + m.Timestamp = makeTimestamp(m.Timestamp, ts) + msg = m + + case Group: + m.Type = "group" + m.MessageId = makeMessageId(m.MessageId, id) + m.Timestamp = makeTimestamp(m.Timestamp, ts) + msg = m + + case Identify: + m.Type = "identify" + m.MessageId = makeMessageId(m.MessageId, id) + m.Timestamp = makeTimestamp(m.Timestamp, ts) + msg = m + + case Page: + m.Type = "page" + m.MessageId = makeMessageId(m.MessageId, id) + m.Timestamp = makeTimestamp(m.Timestamp, ts) + msg = m + + case Screen: + m.Type = "screen" + m.MessageId = makeMessageId(m.MessageId, id) + m.Timestamp = makeTimestamp(m.Timestamp, ts) + msg = m + + case Track: + m.Type = "track" + m.MessageId = makeMessageId(m.MessageId, id) + m.Timestamp = makeTimestamp(m.Timestamp, ts) + msg = m + + default: + err = fmt.Errorf("messages with custom types cannot be enqueued: %T", msg) + return + } + + defer func() { + // When the `msgs` channel is closed writing to it will trigger a panic. + // To avoid letting the panic propagate to the caller we recover from it + // and instead report that the client has been closed and shouldn't be + // used anymore. + if recover() != nil { + err = ErrClosed + } + }() + + c.msgs <- msg + return +} + +// Close and flush metrics. +func (c *client) Close() (err error) { + defer func() { + // Always recover, a panic could be raised if `c`.quit was closed which + // means the method was called more than once. + if recover() != nil { + err = ErrClosed + } + }() + close(c.quit) + <-c.shutdown + return +} + +// Asychronously send a batched requests. +func (c *client) sendAsync(msgs []message, wg *sync.WaitGroup, ex *executor) { + wg.Add(1) + + if !ex.do(func() { + defer wg.Done() + defer func() { + // In case a bug is introduced in the send function that triggers + // a panic, we don't want this to ever crash the application so we + // catch it here and log it instead. + if err := recover(); err != nil { + c.errorf("panic - %s", err) + } + }() + c.send(msgs) + }) { + wg.Done() + c.errorf("sending messages failed - %s", ErrTooManyRequests) + c.notifyFailure(msgs, ErrTooManyRequests) + } +} + +// Send batch request. +func (c *client) send(msgs []message) { + const attempts = 10 + + b, err := json.Marshal(batch{ + MessageId: c.uid(), + SentAt: c.now(), + Messages: msgs, + Context: c.DefaultContext, + }) + + if err != nil { + c.errorf("marshalling messages - %s", err) + c.notifyFailure(msgs, err) + return + } + + for i := 0; i != attempts; i++ { + if err = c.upload(b); err == nil { + c.notifySuccess(msgs) + return + } + + // Wait for either a retry timeout or the client to be closed. + select { + case <-time.After(c.RetryAfter(i)): + case <-c.quit: + c.errorf("%d messages dropped because they failed to be sent and the client was closed", len(msgs)) + c.notifyFailure(msgs, err) + return + } + } + + c.errorf("%d messages dropped because they failed to be sent after %d attempts", len(msgs), attempts) + c.notifyFailure(msgs, err) +} + +// Upload serialized batch message. +func (c *client) upload(b []byte) error { + url := c.Endpoint + "/v1/batch" + req, err := http.NewRequest("POST", url, bytes.NewReader(b)) + if err != nil { + c.errorf("creating request - %s", err) + return err + } + + req.Header.Add("User-Agent", "analytics-go (version: "+Version+")") + req.Header.Add("Content-Type", "application/json") + req.Header.Add("Content-Length", strconv.Itoa(len(b))) + req.SetBasicAuth(c.key, "") + + res, err := c.http.Do(req) + + if err != nil { + c.errorf("sending request - %s", err) + return err + } + + defer res.Body.Close() + return c.report(res) +} + +// Report on response body. +func (c *client) report(res *http.Response) (err error) { + var body []byte + + if res.StatusCode < 300 { + c.debugf("response %s", res.Status) + return + } + + if body, err = ioutil.ReadAll(res.Body); err != nil { + c.errorf("response %d %s - %s", res.StatusCode, res.Status, err) + return + } + + c.logf("response %d %s – %s", res.StatusCode, res.Status, string(body)) + return fmt.Errorf("%d %s", res.StatusCode, res.Status) +} + +// Batch loop. +func (c *client) loop() { + defer close(c.shutdown) + + wg := &sync.WaitGroup{} + defer wg.Wait() + + tick := time.NewTicker(c.Interval) + defer tick.Stop() + + ex := newExecutor(c.maxConcurrentRequests) + defer ex.close() + + mq := messageQueue{ + maxBatchSize: c.BatchSize, + maxBatchBytes: c.maxBatchBytes(), + } + + for { + select { + case msg := <-c.msgs: + c.push(&mq, msg, wg, ex) + + case <-tick.C: + c.flush(&mq, wg, ex) + + case <-c.quit: + c.debugf("exit requested – draining messages") + + // Drain the msg channel, we have to close it first so no more + // messages can be pushed and otherwise the loop would never end. + close(c.msgs) + for msg := range c.msgs { + c.push(&mq, msg, wg, ex) + } + + c.flush(&mq, wg, ex) + c.debugf("exit") + return + } + } +} + +func (c *client) push(q *messageQueue, m Message, wg *sync.WaitGroup, ex *executor) { + var msg message + var err error + + if msg, err = makeMessage(m, maxMessageBytes); err != nil { + c.errorf("%s - %v", err, m) + c.notifyFailure([]message{{m, nil}}, err) + return + } + + c.debugf("buffer (%d/%d) %v", len(q.pending), c.BatchSize, m) + + if msgs := q.push(msg); msgs != nil { + c.debugf("exceeded messages batch limit with batch of %d messages – flushing", len(msgs)) + c.sendAsync(msgs, wg, ex) + } +} + +func (c *client) flush(q *messageQueue, wg *sync.WaitGroup, ex *executor) { + if msgs := q.flush(); msgs != nil { + c.debugf("flushing %d messages", len(msgs)) + c.sendAsync(msgs, wg, ex) + } +} + +func (c *client) debugf(format string, args ...interface{}) { + if c.Verbose { + c.logf(format, args...) + } +} + +func (c *client) logf(format string, args ...interface{}) { + c.Logger.Logf(format, args...) +} + +func (c *client) errorf(format string, args ...interface{}) { + c.Logger.Errorf(format, args...) +} + +func (c *client) maxBatchBytes() int { + b, _ := json.Marshal(batch{ + MessageId: c.uid(), + SentAt: c.now(), + Context: c.DefaultContext, + }) + return maxBatchBytes - len(b) +} + +func (c *client) notifySuccess(msgs []message) { + if c.Callback != nil { + for _, m := range msgs { + c.Callback.Success(m.msg) + } + } +} + +func (c *client) notifyFailure(msgs []message, err error) { + if c.Callback != nil { + for _, m := range msgs { + c.Callback.Failure(m.msg, err) + } + } +} diff --git a/vendor/github.com/segmentio/analytics-go/v3/config.go b/vendor/github.com/segmentio/analytics-go/v3/config.go new file mode 100644 index 00000000..2672d86b --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/config.go @@ -0,0 +1,173 @@ +package analytics + +import ( + "net/http" + "time" + + "github.com/google/uuid" + "github.com/segmentio/backo-go" +) + +// Instances of this type carry the different configuration options that may +// be set when instantiating a client. +// +// Each field's zero-value is either meaningful or interpreted as using the +// default value defined by the library. +type Config struct { + + // The endpoint to which the client connect and send their messages, set to + // `DefaultEndpoint` by default. + Endpoint string + + // The flushing interval of the client. Messages will be sent when they've + // been queued up to the maximum batch size or when the flushing interval + // timer triggers. + Interval time.Duration + + // The HTTP transport used by the client, this allows an application to + // redefine how requests are being sent at the HTTP level (for example, + // to change the connection pooling policy). + // If none is specified the client uses `http.DefaultTransport`. + Transport http.RoundTripper + + // The logger used by the client to output info or error messages when that + // are generated by background operations. + // If none is specified the client uses a standard logger that outputs to + // `os.Stderr`. + Logger Logger + + // The callback object that will be used by the client to notify the + // application when messages sends to the backend API succeeded or failed. + Callback Callback + + // The maximum number of messages that will be sent in one API call. + // Messages will be sent when they've been queued up to the maximum batch + // size or when the flushing interval timer triggers. + // Note that the API will still enforce a 500KB limit on each HTTP request + // which is independent from the number of embedded messages. + BatchSize int + + // When set to true the client will send more frequent and detailed messages + // to its logger. + Verbose bool + + // The default context set on each message sent by the client. + DefaultContext *Context + + // The retry policy used by the client to resend requests that have failed. + // The function is called with how many times the operation has been retried + // and is expected to return how long the client should wait before trying + // again. + // If not set the client will fallback to use a default retry policy. + RetryAfter func(int) time.Duration + + // A function called by the client to generate unique message identifiers. + // The client uses a UUID generator if none is provided. + // This field is not exported and only exposed internally to let unit tests + // mock the id generation. + uid func() string + + // A function called by the client to get the current time, `time.Now` is + // used by default. + // This field is not exported and only exposed internally to let unit tests + // mock the current time. + now func() time.Time + + // The maximum number of goroutines that will be spawned by a client to send + // requests to the backend API. + // This field is not exported and only exposed internally to let unit tests + // mock the current time. + maxConcurrentRequests int +} + +// This constant sets the default endpoint to which client instances send +// messages if none was explictly set. +const DefaultEndpoint = "https://api.segment.io" + +// This constant sets the default flush interval used by client instances if +// none was explicitly set. +const DefaultInterval = 5 * time.Second + +// This constant sets the default batch size used by client instances if none +// was explicitly set. +const DefaultBatchSize = 250 + +// Verifies that fields that don't have zero-values are set to valid values, +// returns an error describing the problem if a field was invalid. +func (c *Config) validate() error { + if c.Interval < 0 { + return ConfigError{ + Reason: "negative time intervals are not supported", + Field: "Interval", + Value: c.Interval, + } + } + + if c.BatchSize < 0 { + return ConfigError{ + Reason: "negative batch sizes are not supported", + Field: "BatchSize", + Value: c.BatchSize, + } + } + + return nil +} + +// Given a config object as argument the function will set all zero-values to +// their defaults and return the modified object. +func makeConfig(c Config) Config { + if len(c.Endpoint) == 0 { + c.Endpoint = DefaultEndpoint + } + + if c.Interval == 0 { + c.Interval = DefaultInterval + } + + if c.Transport == nil { + c.Transport = http.DefaultTransport + } + + if c.Logger == nil { + c.Logger = newDefaultLogger() + } + + if c.BatchSize == 0 { + c.BatchSize = DefaultBatchSize + } + + if c.DefaultContext == nil { + c.DefaultContext = &Context{} + } + + if c.RetryAfter == nil { + c.RetryAfter = backo.DefaultBacko().Duration + } + + if c.uid == nil { + c.uid = uid + } + + if c.now == nil { + c.now = time.Now + } + + if c.maxConcurrentRequests == 0 { + c.maxConcurrentRequests = 1000 + } + + // We always overwrite the 'library' field of the default context set on the + // client because we want this information to be accurate. + c.DefaultContext.Library = LibraryInfo{ + Name: "analytics-go", + Version: Version, + } + return c +} + +// This function returns a string representation of a UUID, it's the default +// function used for generating unique IDs. +func uid() string { + return uuid.NewString() +} diff --git a/vendor/github.com/segmentio/analytics-go/v3/context.go b/vendor/github.com/segmentio/analytics-go/v3/context.go new file mode 100644 index 00000000..94926050 --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/context.go @@ -0,0 +1,150 @@ +package analytics + +import ( + "encoding/json" + "net" + "reflect" +) + +// This type provides the representation of the `context` object as defined in +// https://segment.com/docs/spec/common/#context +type Context struct { + App AppInfo `json:"app,omitempty"` + Campaign CampaignInfo `json:"campaign,omitempty"` + Device DeviceInfo `json:"device,omitempty"` + Library LibraryInfo `json:"library,omitempty"` + Location LocationInfo `json:"location,omitempty"` + Network NetworkInfo `json:"network,omitempty"` + OS OSInfo `json:"os,omitempty"` + Page PageInfo `json:"page,omitempty"` + Referrer ReferrerInfo `json:"referrer,omitempty"` + Screen ScreenInfo `json:"screen,omitempty"` + IP net.IP `json:"ip,omitempty"` + Direct bool `json:"direct,omitempty"` + Locale string `json:"locale,omitempty"` + GroupID string `json:"groupId,omitempty"` + Timezone string `json:"timezone,omitempty"` + UserAgent string `json:"userAgent,omitempty"` + Traits Traits `json:"traits,omitempty"` + + // This map is used to allow extensions to the context specifications that + // may not be documented or could be introduced in the future. + // The fields of this map are inlined in the serialized context object, + // there is no actual "extra" field in the JSON representation. + Extra map[string]interface{} `json:"-"` +} + +// This type provides the representation of the `context.app` object as defined +// in https://segment.com/docs/spec/common/#context +type AppInfo struct { + Name string `json:"name,omitempty"` + Version string `json:"version,omitempty"` + Build string `json:"build,omitempty"` + Namespace string `json:"namespace,omitempty"` +} + +// This type provides the representation of the `context.campaign` object as +// defined in https://segment.com/docs/spec/common/#context +type CampaignInfo struct { + Name string `json:"name,omitempty"` + Source string `json:"source,omitempty"` + Medium string `json:"medium,omitempty"` + Term string `json:"term,omitempty"` + Content string `json:"content,omitempty"` +} + +// This type provides the representation of the `context.device` object as +// defined in https://segment.com/docs/spec/common/#context +type DeviceInfo struct { + Id string `json:"id,omitempty"` + Manufacturer string `json:"manufacturer,omitempty"` + Model string `json:"model,omitempty"` + Name string `json:"name,omitempty"` + Type string `json:"type,omitempty"` + Version string `json:"version,omitempty"` + AdvertisingID string `json:"advertisingId,omitempty"` +} + +// This type provides the representation of the `context.library` object as +// defined in https://segment.com/docs/spec/common/#context +type LibraryInfo struct { + Name string `json:"name,omitempty"` + Version string `json:"version,omitempty"` +} + +// This type provides the representation of the `context.location` object as +// defined in https://segment.com/docs/spec/common/#context +type LocationInfo struct { + City string `json:"city,omitempty"` + Country string `json:"country,omitempty"` + Region string `json:"region,omitempty"` + Latitude float64 `json:"latitude,omitempty"` + Longitude float64 `json:"longitude,omitempty"` + Speed float64 `json:"speed,omitempty"` +} + +// This type provides the representation of the `context.network` object as +// defined in https://segment.com/docs/spec/common/#context +type NetworkInfo struct { + Bluetooth bool `json:"bluetooth,omitempty"` + Cellular bool `json:"cellular,omitempty"` + WIFI bool `json:"wifi,omitempty"` + Carrier string `json:"carrier,omitempty"` +} + +// This type provides the representation of the `context.os` object as defined +// in https://segment.com/docs/spec/common/#context +type OSInfo struct { + Name string `json:"name,omitempty"` + Version string `json:"version,omitempty"` +} + +// This type provides the representation of the `context.page` object as +// defined in https://segment.com/docs/spec/common/#context +type PageInfo struct { + Hash string `json:"hash,omitempty"` + Path string `json:"path,omitempty"` + Referrer string `json:"referrer,omitempty"` + Search string `json:"search,omitempty"` + Title string `json:"title,omitempty"` + URL string `json:"url,omitempty"` +} + +// This type provides the representation of the `context.referrer` object as +// defined in https://segment.com/docs/spec/common/#context +type ReferrerInfo struct { + Type string `json:"type,omitempty"` + Name string `json:"name,omitempty"` + URL string `json:"url,omitempty"` + Link string `json:"link,omitempty"` +} + +// This type provides the representation of the `context.screen` object as +// defined in https://segment.com/docs/spec/common/#context +type ScreenInfo struct { + Density int `json:"density,omitempty"` + Width int `json:"width,omitempty"` + Height int `json:"height,omitempty"` +} + +// Satisfy the `json.Marshaler` interface. We have to flatten out the `Extra` +// field but the standard json package doesn't support it yet. +// Implementing this interface allows us to override the default marshaling of +// the context object and to the inlining ourselves. +// +// Related discussion: https://github.com/golang/go/issues/6213 +func (ctx Context) MarshalJSON() ([]byte, error) { + v := reflect.ValueOf(ctx) + n := v.NumField() + m := make(map[string]interface{}, n+len(ctx.Extra)) + + // Copy the `Extra` map into the map representation of the context, it is + // important to do this operation before going through the actual struct + // fields so the latter take precendence and override duplicated values + // that would be set in the extensions. + for name, value := range ctx.Extra { + m[name] = value + } + + return json.Marshal(structToMap(v, m)) +} diff --git a/vendor/github.com/segmentio/analytics-go/v3/error.go b/vendor/github.com/segmentio/analytics-go/v3/error.go new file mode 100644 index 00000000..d5503864 --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/error.go @@ -0,0 +1,60 @@ +package analytics + +import ( + "errors" + "fmt" +) + +// Returned by the `NewWithConfig` function when the one of the configuration +// fields was set to an impossible value (like a negative duration). +type ConfigError struct { + + // A human-readable message explaining why the configuration field's value + // is invalid. + Reason string + + // The name of the configuration field that was carrying an invalid value. + Field string + + // The value of the configuration field that caused the error. + Value interface{} +} + +func (e ConfigError) Error() string { + return fmt.Sprintf("analytics.NewWithConfig: %s (analytics.Config.%s: %#v)", e.Reason, e.Field, e.Value) +} + +// Instances of this type are used to represent errors returned when a field was +// no initialize properly in a structure passed as argument to one of the +// functions of this package. +type FieldError struct { + + // The human-readable representation of the type of structure that wasn't + // initialized properly. + Type string + + // The name of the field that wasn't properly initialized. + Name string + + // The value of the field that wasn't properly initialized. + Value interface{} +} + +func (e FieldError) Error() string { + return fmt.Sprintf("%s.%s: invalid field value: %#v", e.Type, e.Name, e.Value) +} + +var ( + // This error is returned by methods of the `Client` interface when they are + // called after the client was already closed. + ErrClosed = errors.New("the client was already closed") + + // This error is used to notify the application that too many requests are + // already being sent and no more messages can be accepted. + ErrTooManyRequests = errors.New("too many requests are already in-flight") + + // This error is used to notify the client callbacks that a message send + // failed because the JSON representation of a message exceeded the upper + // limit. + ErrMessageTooBig = errors.New("the message exceeds the maximum allowed size") +) diff --git a/vendor/github.com/segmentio/analytics-go/v3/executor.go b/vendor/github.com/segmentio/analytics-go/v3/executor.go new file mode 100644 index 00000000..405ee98e --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/executor.go @@ -0,0 +1,53 @@ +package analytics + +import "sync" + +type executor struct { + queue chan func() + mutex sync.Mutex + size int + cap int +} + +func newExecutor(cap int) *executor { + e := &executor{ + queue: make(chan func(), 1), + cap: cap, + } + go e.loop() + return e +} + +func (e *executor) do(task func()) (ok bool) { + e.mutex.Lock() + + if e.size != e.cap { + e.queue <- task + e.size++ + ok = true + } + + e.mutex.Unlock() + return +} + +func (e *executor) close() { + close(e.queue) +} + +func (e *executor) loop() { + for task := range e.queue { + go e.run(task) + } +} + +func (e *executor) run(task func()) { + defer e.done() + task() +} + +func (e *executor) done() { + e.mutex.Lock() + e.size-- + e.mutex.Unlock() +} diff --git a/vendor/github.com/segmentio/analytics-go/v3/group.go b/vendor/github.com/segmentio/analytics-go/v3/group.go new file mode 100644 index 00000000..352c8e44 --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/group.go @@ -0,0 +1,42 @@ +package analytics + +import "time" + +var _ Message = (*Group)(nil) + +// This type represents object sent in a group call as described in +// https://segment.com/docs/libraries/http/#group +type Group struct { + // This field is exported for serialization purposes and shouldn't be set by + // the application, its value is always overwritten by the library. + Type string `json:"type,omitempty"` + + MessageId string `json:"messageId,omitempty"` + AnonymousId string `json:"anonymousId,omitempty"` + UserId string `json:"userId,omitempty"` + GroupId string `json:"groupId"` + Timestamp time.Time `json:"timestamp,omitempty"` + Context *Context `json:"context,omitempty"` + Traits Traits `json:"traits,omitempty"` + Integrations Integrations `json:"integrations,omitempty"` +} + +func (msg Group) Validate() error { + if len(msg.GroupId) == 0 { + return FieldError{ + Type: "analytics.Group", + Name: "GroupId", + Value: msg.GroupId, + } + } + + if len(msg.UserId) == 0 && len(msg.AnonymousId) == 0 { + return FieldError{ + Type: "analytics.Group", + Name: "UserId", + Value: msg.UserId, + } + } + + return nil +} diff --git a/vendor/github.com/segmentio/analytics-go/v3/identify.go b/vendor/github.com/segmentio/analytics-go/v3/identify.go new file mode 100644 index 00000000..c3e594f7 --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/identify.go @@ -0,0 +1,33 @@ +package analytics + +import "time" + +var _ Message = (*Identify)(nil) + +// This type represents object sent in an identify call as described in +// https://segment.com/docs/libraries/http/#identify +type Identify struct { + // This field is exported for serialization purposes and shouldn't be set by + // the application, its value is always overwritten by the library. + Type string `json:"type,omitempty"` + + MessageId string `json:"messageId,omitempty"` + AnonymousId string `json:"anonymousId,omitempty"` + UserId string `json:"userId,omitempty"` + Timestamp time.Time `json:"timestamp,omitempty"` + Context *Context `json:"context,omitempty"` + Traits Traits `json:"traits,omitempty"` + Integrations Integrations `json:"integrations,omitempty"` +} + +func (msg Identify) Validate() error { + if len(msg.UserId) == 0 && len(msg.AnonymousId) == 0 { + return FieldError{ + Type: "analytics.Identify", + Name: "UserId", + Value: msg.UserId, + } + } + + return nil +} diff --git a/vendor/github.com/segmentio/analytics-go/v3/integrations.go b/vendor/github.com/segmentio/analytics-go/v3/integrations.go new file mode 100644 index 00000000..407b4912 --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/integrations.go @@ -0,0 +1,44 @@ +package analytics + +// This type is used to represent integrations in messages that support it. +// It is a free-form where values are most often booleans that enable or +// disable integrations. +// Here's a quick example of how this type is meant to be used: +// +// analytics.Track{ +// UserId: "0123456789", +// Integrations: analytics.NewIntegrations() +// .EnableAll() +// .Disable("Salesforce") +// .Disable("Marketo"), +// } +// +// The specifications can be found at https://segment.com/docs/spec/common/#integrations +type Integrations map[string]interface{} + +func NewIntegrations() Integrations { + return make(Integrations, 10) +} + +func (i Integrations) EnableAll() Integrations { + return i.Enable("all") +} + +func (i Integrations) DisableAll() Integrations { + return i.Disable("all") +} + +func (i Integrations) Enable(name string) Integrations { + return i.Set(name, true) +} + +func (i Integrations) Disable(name string) Integrations { + return i.Set(name, false) +} + +// Sets an integration named by the first argument to the specified value, any +// value other than `false` will be interpreted as enabling the integration. +func (i Integrations) Set(name string, value interface{}) Integrations { + i[name] = value + return i +} diff --git a/vendor/github.com/segmentio/analytics-go/v3/json.go b/vendor/github.com/segmentio/analytics-go/v3/json.go new file mode 100644 index 00000000..cd3b1752 --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/json.go @@ -0,0 +1,87 @@ +package analytics + +import ( + "reflect" + "strings" +) + +// Imitate what what the JSON package would do when serializing a struct value, +// the only difference is we we don't serialize zero-value struct fields as well. +// Note that this function doesn't recursively convert structures to maps, only +// the value passed as argument is transformed. +func structToMap(v reflect.Value, m map[string]interface{}) map[string]interface{} { + t := v.Type() + n := t.NumField() + + if m == nil { + m = make(map[string]interface{}, n) + } + + for i := 0; i != n; i++ { + field := t.Field(i) + value := v.Field(i) + name, omitempty := parseJsonTag(field.Tag.Get("json"), field.Name) + + if name != "-" && !(omitempty && isZeroValue(value)) { + m[name] = value.Interface() + } + } + + return m +} + +// Parses a JSON tag the way the json package would do it, returing the expected +// name of the field once serialized and if empty values should be omitted. +func parseJsonTag(tag string, defName string) (name string, omitempty bool) { + args := strings.Split(tag, ",") + + if len(args) == 0 || len(args[0]) == 0 { + name = defName + } else { + name = args[0] + } + + if len(args) > 1 && args[1] == "omitempty" { + omitempty = true + } + + return +} + +// Checks if the value given as argument is a zero-value, it is based on the +// isEmptyValue function in https://golang.org/src/encoding/json/encode.go +// but also checks struct types recursively. +func isZeroValue(v reflect.Value) bool { + switch v.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + + case reflect.Bool: + return !v.Bool() + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + + case reflect.Interface, reflect.Ptr: + return v.IsNil() + + case reflect.Struct: + for i, n := 0, v.NumField(); i != n; i++ { + if !isZeroValue(v.Field(i)) { + return false + } + } + return true + + case reflect.Invalid: + return true + } + + return false +} diff --git a/vendor/github.com/segmentio/analytics-go/v3/logger.go b/vendor/github.com/segmentio/analytics-go/v3/logger.go new file mode 100644 index 00000000..54190c5f --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/logger.go @@ -0,0 +1,47 @@ +package analytics + +import ( + "log" + "os" +) + +// Instances of types implementing this interface can be used to define where +// the analytics client logs are written. +type Logger interface { + + // Analytics clients call this method to log regular messages about the + // operations they perform. + // Messages logged by this method are usually tagged with an `INFO` log + // level in common logging libraries. + Logf(format string, args ...interface{}) + + // Analytics clients call this method to log errors they encounter while + // sending events to the backend servers. + // Messages logged by this method are usually tagged with an `ERROR` log + // level in common logging libraries. + Errorf(format string, args ...interface{}) +} + +// This function instantiate an object that statisfies the analytics.Logger +// interface and send logs to standard logger passed as argument. +func StdLogger(logger *log.Logger) Logger { + return stdLogger{ + logger: logger, + } +} + +type stdLogger struct { + logger *log.Logger +} + +func (l stdLogger) Logf(format string, args ...interface{}) { + l.logger.Printf("INFO: "+format, args...) +} + +func (l stdLogger) Errorf(format string, args ...interface{}) { + l.logger.Printf("ERROR: "+format, args...) +} + +func newDefaultLogger() Logger { + return StdLogger(log.New(os.Stderr, "segment ", log.LstdFlags)) +} diff --git a/vendor/github.com/segmentio/analytics-go/v3/message.go b/vendor/github.com/segmentio/analytics-go/v3/message.go new file mode 100644 index 00000000..d46d246e --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/message.go @@ -0,0 +1,128 @@ +package analytics + +import ( + "encoding/json" + "time" +) + +// Values implementing this interface are used by analytics clients to notify +// the application when a message send succeeded or failed. +// +// Callback methods are called by a client's internal goroutines, there are no +// guarantees on which goroutine will trigger the callbacks, the calls can be +// made sequentially or in parallel, the order doesn't depend on the order of +// messages were queued to the client. +// +// Callback methods must return quickly and not cause long blocking operations +// to avoid interferring with the client's internal work flow. +type Callback interface { + + // This method is called for every message that was successfully sent to + // the API. + Success(Message) + + // This method is called for every message that failed to be sent to the + // API and will be discarded by the client. + Failure(Message, error) +} + +// This interface is used to represent analytics objects that can be sent via +// a client. +// +// Types like analytics.Track, analytics.Page, etc... implement this interface +// and therefore can be passed to the analytics.Client.Send method. +type Message interface { + + // Validate validates the internal structure of the message, the method must return + // nil if the message is valid, or an error describing what went wrong. + Validate() error +} + +// Takes a message id as first argument and returns it, unless it's the zero- +// value, in that case the default id passed as second argument is returned. +func makeMessageId(id string, def string) string { + if len(id) == 0 { + return def + } + return id +} + +// Returns the time value passed as first argument, unless it's the zero-value, +// in that case the default value passed as second argument is returned. +func makeTimestamp(t time.Time, def time.Time) time.Time { + if t == (time.Time{}) { + return def + } + return t +} + +// This structure represents objects sent to the /v1/batch endpoint. We don't +// export this type because it's only meant to be used internally to send groups +// of messages in one API call. +type batch struct { + MessageId string `json:"messageId"` + SentAt time.Time `json:"sentAt"` + Messages []message `json:"batch"` + Context *Context `json:"context"` +} + +type message struct { + msg Message + json []byte +} + +func makeMessage(m Message, maxBytes int) (msg message, err error) { + if msg.json, err = json.Marshal(m); err == nil { + if len(msg.json) > maxBytes { + err = ErrMessageTooBig + } else { + msg.msg = m + } + } + return +} + +func (m message) MarshalJSON() ([]byte, error) { + return m.json, nil +} + +func (m message) size() int { + // The `+ 1` is for the comma that sits between each items of a JSON array. + return len(m.json) + 1 +} + +type messageQueue struct { + pending []message + bytes int + maxBatchSize int + maxBatchBytes int +} + +func (q *messageQueue) push(m message) (b []message) { + if (q.bytes + m.size()) > q.maxBatchBytes { + b = q.flush() + } + + if q.pending == nil { + q.pending = make([]message, 0, q.maxBatchSize) + } + + q.pending = append(q.pending, m) + q.bytes += len(m.json) + + if b == nil && len(q.pending) == q.maxBatchSize { + b = q.flush() + } + + return +} + +func (q *messageQueue) flush() (msgs []message) { + msgs, q.pending, q.bytes = q.pending, nil, 0 + return +} + +const ( + maxBatchBytes = 500000 + maxMessageBytes = 32000 +) diff --git a/vendor/github.com/segmentio/analytics-go/v3/page.go b/vendor/github.com/segmentio/analytics-go/v3/page.go new file mode 100644 index 00000000..11b278c5 --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/page.go @@ -0,0 +1,34 @@ +package analytics + +import "time" + +var _ Message = (*Page)(nil) + +// This type represents object sent in a page call as described in +// https://segment.com/docs/libraries/http/#page +type Page struct { + // This field is exported for serialization purposes and shouldn't be set by + // the application, its value is always overwritten by the library. + Type string `json:"type,omitempty"` + + MessageId string `json:"messageId,omitempty"` + AnonymousId string `json:"anonymousId,omitempty"` + UserId string `json:"userId,omitempty"` + Name string `json:"name,omitempty"` + Timestamp time.Time `json:"timestamp,omitempty"` + Context *Context `json:"context,omitempty"` + Properties Properties `json:"properties,omitempty"` + Integrations Integrations `json:"integrations,omitempty"` +} + +func (msg Page) Validate() error { + if len(msg.UserId) == 0 && len(msg.AnonymousId) == 0 { + return FieldError{ + Type: "analytics.Page", + Name: "UserId", + Value: msg.UserId, + } + } + + return nil +} diff --git a/vendor/github.com/segmentio/analytics-go/v3/properties.go b/vendor/github.com/segmentio/analytics-go/v3/properties.go new file mode 100644 index 00000000..8b218aeb --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/properties.go @@ -0,0 +1,117 @@ +package analytics + +// This type is used to represent properties in messages that support it. +// It is a free-form object so the application can set any value it sees fit but +// a few helper method are defined to make it easier to instantiate properties with +// common fields. +// Here's a quick example of how this type is meant to be used: +// +// analytics.Page{ +// UserId: "0123456789", +// Properties: analytics.NewProperties() +// .SetRevenue(10.0) +// .SetCurrency("USD"), +// } +// +type Properties map[string]interface{} + +func NewProperties() Properties { + return make(Properties, 10) +} + +func (p Properties) SetRevenue(revenue float64) Properties { + return p.Set("revenue", revenue) +} + +func (p Properties) SetCurrency(currency string) Properties { + return p.Set("currency", currency) +} + +func (p Properties) SetValue(value float64) Properties { + return p.Set("value", value) +} + +func (p Properties) SetPath(path string) Properties { + return p.Set("path", path) +} + +func (p Properties) SetReferrer(referrer string) Properties { + return p.Set("referrer", referrer) +} + +func (p Properties) SetTitle(title string) Properties { + return p.Set("title", title) +} + +func (p Properties) SetURL(url string) Properties { + return p.Set("url", url) +} + +func (p Properties) SetName(name string) Properties { + return p.Set("name", name) +} + +func (p Properties) SetCategory(category string) Properties { + return p.Set("category", category) +} + +func (p Properties) SetSKU(sku string) Properties { + return p.Set("sku", sku) +} + +func (p Properties) SetPrice(price float64) Properties { + return p.Set("price", price) +} + +func (p Properties) SetProductId(id string) Properties { + return p.Set("id", id) +} + +func (p Properties) SetOrderId(id string) Properties { + return p.Set("orderId", id) +} + +func (p Properties) SetTotal(total float64) Properties { + return p.Set("total", total) +} + +func (p Properties) SetSubtotal(subtotal float64) Properties { + return p.Set("subtotal", subtotal) +} + +func (p Properties) SetShipping(shipping float64) Properties { + return p.Set("shipping", shipping) +} + +func (p Properties) SetTax(tax float64) Properties { + return p.Set("tax", tax) +} + +func (p Properties) SetDiscount(discount float64) Properties { + return p.Set("discount", discount) +} + +func (p Properties) SetCoupon(coupon string) Properties { + return p.Set("coupon", coupon) +} + +func (p Properties) SetProducts(products ...Product) Properties { + return p.Set("products", products) +} + +func (p Properties) SetRepeat(repeat bool) Properties { + return p.Set("repeat", repeat) +} + +func (p Properties) Set(name string, value interface{}) Properties { + p[name] = value + return p +} + +// This type represents products in the E-commerce API. +type Product struct { + ID string `json:"id,omitempty"` + SKU string `json:"sky,omitempty"` + Name string `json:"name,omitempty"` + Price float64 `json:"price"` +} diff --git a/vendor/github.com/segmentio/analytics-go/v3/screen.go b/vendor/github.com/segmentio/analytics-go/v3/screen.go new file mode 100644 index 00000000..2ee18e2a --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/screen.go @@ -0,0 +1,34 @@ +package analytics + +import "time" + +var _ Message = (*Screen)(nil) + +// This type represents object sent in a screen call as described in +// https://segment.com/docs/libraries/http/#screen +type Screen struct { + // This field is exported for serialization purposes and shouldn't be set by + // the application, its value is always overwritten by the library. + Type string `json:"type,omitempty"` + + MessageId string `json:"messageId,omitempty"` + AnonymousId string `json:"anonymousId,omitempty"` + UserId string `json:"userId,omitempty"` + Name string `json:"name,omitempty"` + Timestamp time.Time `json:"timestamp,omitempty"` + Context *Context `json:"context,omitempty"` + Properties Properties `json:"properties,omitempty"` + Integrations Integrations `json:"integrations,omitempty"` +} + +func (msg Screen) Validate() error { + if len(msg.UserId) == 0 && len(msg.AnonymousId) == 0 { + return FieldError{ + Type: "analytics.Screen", + Name: "UserId", + Value: msg.UserId, + } + } + + return nil +} diff --git a/vendor/github.com/segmentio/analytics-go/v3/timeout_15.go b/vendor/github.com/segmentio/analytics-go/v3/timeout_15.go new file mode 100644 index 00000000..12b963ed --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/timeout_15.go @@ -0,0 +1,16 @@ +// +build !go1.6 + +package analytics + +import "net/http" + +// http clients on versions of go before 1.6 only support timeout if the +// transport implements the `CancelRequest` method. +func supportsTimeout(transport http.RoundTripper) bool { + _, ok := transport.(requestCanceler) + return ok +} + +type requestCanceler interface { + CancelRequest(*http.Request) +} diff --git a/vendor/github.com/segmentio/analytics-go/v3/timeout_16.go b/vendor/github.com/segmentio/analytics-go/v3/timeout_16.go new file mode 100644 index 00000000..1115cafb --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/timeout_16.go @@ -0,0 +1,10 @@ +// +build go1.6 + +package analytics + +import "net/http" + +// http clients on versions of go after 1.6 always support timeout. +func supportsTimeout(transport http.RoundTripper) bool { + return true +} diff --git a/vendor/github.com/segmentio/analytics-go/v3/track.go b/vendor/github.com/segmentio/analytics-go/v3/track.go new file mode 100644 index 00000000..a803c8eb --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/track.go @@ -0,0 +1,42 @@ +package analytics + +import "time" + +var _ Message = (*Track)(nil) + +// This type represents object sent in a track call as described in +// https://segment.com/docs/libraries/http/#track +type Track struct { + // This field is exported for serialization purposes and shouldn't be set by + // the application, its value is always overwritten by the library. + Type string `json:"type,omitempty"` + + MessageId string `json:"messageId,omitempty"` + AnonymousId string `json:"anonymousId,omitempty"` + UserId string `json:"userId,omitempty"` + Event string `json:"event"` + Timestamp time.Time `json:"timestamp,omitempty"` + Context *Context `json:"context,omitempty"` + Properties Properties `json:"properties,omitempty"` + Integrations Integrations `json:"integrations,omitempty"` +} + +func (msg Track) Validate() error { + if len(msg.Event) == 0 { + return FieldError{ + Type: "analytics.Track", + Name: "Event", + Value: msg.Event, + } + } + + if len(msg.UserId) == 0 && len(msg.AnonymousId) == 0 { + return FieldError{ + Type: "analytics.Track", + Name: "UserId", + Value: msg.UserId, + } + } + + return nil +} diff --git a/vendor/github.com/segmentio/analytics-go/v3/traits.go b/vendor/github.com/segmentio/analytics-go/v3/traits.go new file mode 100644 index 00000000..d4e82f07 --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/traits.go @@ -0,0 +1,89 @@ +package analytics + +import "time" + +// This type is used to represent traits in messages that support it. +// It is a free-form object so the application can set any value it sees fit but +// a few helper method are defined to make it easier to instantiate traits with +// common fields. +// Here's a quick example of how this type is meant to be used: +// +// analytics.Identify{ +// UserId: "0123456789", +// Traits: analytics.NewTraits() +// .SetFirstName("Luke") +// .SetLastName("Skywalker") +// .Set("Role", "Jedi"), +// } +// +// The specifications can be found at https://segment.com/docs/spec/identify/#traits +type Traits map[string]interface{} + +func NewTraits() Traits { + return make(Traits, 10) +} + +func (t Traits) SetAddress(address string) Traits { + return t.Set("address", address) +} + +func (t Traits) SetAge(age int) Traits { + return t.Set("age", age) +} + +func (t Traits) SetAvatar(url string) Traits { + return t.Set("avatar", url) +} + +func (t Traits) SetBirthday(date time.Time) Traits { + return t.Set("birthday", date) +} + +func (t Traits) SetCreatedAt(date time.Time) Traits { + return t.Set("createdAt", date) +} + +func (t Traits) SetDescription(desc string) Traits { + return t.Set("description", desc) +} + +func (t Traits) SetEmail(email string) Traits { + return t.Set("email", email) +} + +func (t Traits) SetFirstName(firstName string) Traits { + return t.Set("firstName", firstName) +} + +func (t Traits) SetGender(gender string) Traits { + return t.Set("gender", gender) +} + +func (t Traits) SetLastName(lastName string) Traits { + return t.Set("lastName", lastName) +} + +func (t Traits) SetName(name string) Traits { + return t.Set("name", name) +} + +func (t Traits) SetPhone(phone string) Traits { + return t.Set("phone", phone) +} + +func (t Traits) SetTitle(title string) Traits { + return t.Set("title", title) +} + +func (t Traits) SetUsername(username string) Traits { + return t.Set("username", username) +} + +func (t Traits) SetWebsite(url string) Traits { + return t.Set("website", url) +} + +func (t Traits) Set(field string, value interface{}) Traits { + t[field] = value + return t +} diff --git a/vendor/github.com/segmentio/analytics-go/v3/validate.go b/vendor/github.com/segmentio/analytics-go/v3/validate.go new file mode 100644 index 00000000..442c1267 --- /dev/null +++ b/vendor/github.com/segmentio/analytics-go/v3/validate.go @@ -0,0 +1,65 @@ +package analytics + +type FieldGetter interface { + GetField(field string) (interface{}, bool) +} + +func getString(msg FieldGetter, field string) string { + if val, ok := msg.GetField(field); ok { + if str, ok := val.(string); ok { + return str + } + } + return "" +} + +func ValidateFields(msg FieldGetter) error { + typ, _ := msg.GetField("type") + if str, ok := typ.(string); ok { + switch str { + case "alias": + return Alias{ + Type: "alias", + UserId: getString(msg, "userId"), + PreviousId: getString(msg, "previousId"), + }.Validate() + case "group": + return Group{ + Type: "group", + UserId: getString(msg, "userId"), + AnonymousId: getString(msg, "anonymousId"), + GroupId: getString(msg, "groupId"), + }.Validate() + case "identify": + return Identify{ + Type: "identify", + UserId: getString(msg, "userId"), + AnonymousId: getString(msg, "anonymousId"), + }.Validate() + case "page": + return Page{ + Type: "page", + UserId: getString(msg, "userId"), + AnonymousId: getString(msg, "anonymousId"), + }.Validate() + case "screen": + return Screen{ + Type: "screen", + UserId: getString(msg, "userId"), + AnonymousId: getString(msg, "anonymousId"), + }.Validate() + case "track": + return Track{ + Type: "track", + UserId: getString(msg, "userId"), + AnonymousId: getString(msg, "anonymousId"), + Event: getString(msg, "event"), + }.Validate() + } + } + return FieldError{ + Type: "analytics.Event", + Name: "Type", + Value: typ, + } +} diff --git a/vendor/github.com/segmentio/backo-go/.gitmodules b/vendor/github.com/segmentio/backo-go/.gitmodules new file mode 100644 index 00000000..36de9297 --- /dev/null +++ b/vendor/github.com/segmentio/backo-go/.gitmodules @@ -0,0 +1,3 @@ +[submodule "vendor/github.com/bmizerany/assert"] + path = vendor/github.com/bmizerany/assert + url = https://github.com/bmizerany/assert diff --git a/vendor/github.com/segmentio/backo-go/README.md b/vendor/github.com/segmentio/backo-go/README.md new file mode 100644 index 00000000..1362becf --- /dev/null +++ b/vendor/github.com/segmentio/backo-go/README.md @@ -0,0 +1,80 @@ +Backo [![GoDoc](http://godoc.org/github.com/segmentio/backo-go?status.png)](http://godoc.org/github.com/segmentio/backo-go) +----- + +Exponential backoff for Go (Go port of segmentio/backo). + + +Usage +----- + +```go +import "github.com/segmentio/backo-go" + +// Create a Backo instance. +backo := backo.NewBacko(milliseconds(100), 2, 1, milliseconds(10*1000)) +// OR with defaults. +backo := backo.DefaultBacko() + +// Use the ticker API. +ticker := b.NewTicker() +for { + timeout := time.After(5 * time.Minute) + select { + case <-ticker.C: + fmt.Println("ticked") + case <- timeout: + fmt.Println("timed out") + } +} + +// Or simply work with backoff intervals directly. +for i := 0; i < n; i++ { + // Sleep the current goroutine. + backo.Sleep(i) + // Retrieve the duration manually. + duration := backo.Duration(i) +} +``` + +License +------- + +``` +WWWWWW||WWWWWW + W W W||W W W + || + ( OO )__________ + / | \ + /o o| MIT \ + \___/||_||__||_|| * + || || || || + _||_|| _||_|| + (__|__|(__|__| + +The MIT License (MIT) + +Copyright (c) 2015 Segment, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` + + + + [1]: http://github.com/segmentio/backo-java + [2]: http://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=com.segment.backo&a=backo&v=LATEST \ No newline at end of file diff --git a/vendor/github.com/segmentio/backo-go/backo.go b/vendor/github.com/segmentio/backo-go/backo.go new file mode 100644 index 00000000..6f7b6d5e --- /dev/null +++ b/vendor/github.com/segmentio/backo-go/backo.go @@ -0,0 +1,83 @@ +package backo + +import ( + "math" + "math/rand" + "time" +) + +type Backo struct { + base time.Duration + factor uint8 + jitter float64 + cap time.Duration +} + +// Creates a backo instance with the given parameters +func NewBacko(base time.Duration, factor uint8, jitter float64, cap time.Duration) *Backo { + return &Backo{base, factor, jitter, cap} +} + +// Creates a backo instance with the following defaults: +// base: 100 milliseconds +// factor: 2 +// jitter: 0 +// cap: 10 seconds +func DefaultBacko() *Backo { + return NewBacko(time.Millisecond*100, 2, 0, time.Second*10) +} + +// Duration returns the backoff interval for the given attempt. +func (backo *Backo) Duration(attempt int) time.Duration { + duration := float64(backo.base) * math.Pow(float64(backo.factor), float64(attempt)) + + if backo.jitter != 0 { + random := rand.Float64() + deviation := math.Floor(random * backo.jitter * duration) + if (int(math.Floor(random*10)) & 1) == 0 { + duration = duration - deviation + } else { + duration = duration + deviation + } + } + + duration = math.Min(float64(duration), float64(backo.cap)) + return time.Duration(duration) +} + +// Sleep pauses the current goroutine for the backoff interval for the given attempt. +func (backo *Backo) Sleep(attempt int) { + duration := backo.Duration(attempt) + time.Sleep(duration) +} + +type Ticker struct { + done chan struct{} + C <-chan time.Time +} + +func (b *Backo) NewTicker() *Ticker { + c := make(chan time.Time, 1) + ticker := &Ticker{ + done: make(chan struct{}, 1), + C: c, + } + + go func() { + for i := 0; ; i++ { + select { + case t := <-time.After(b.Duration(i)): + c <- t + case <-ticker.done: + close(c) + return + } + } + }() + + return ticker +} + +func (t *Ticker) Stop() { + t.done <- struct{}{} +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 5d069622..c3d1067d 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -44,6 +44,8 @@ github.com/aymanbagabas/go-osc52/v2 ## explicit github.com/aymerick/douceur/css github.com/aymerick/douceur/parser +# github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 +## explicit # github.com/charmbracelet/bubbles v0.18.0 ## explicit; go 1.18 github.com/charmbracelet/bubbles/cursor @@ -80,6 +82,9 @@ github.com/fsnotify/fsnotify # github.com/golang/protobuf v1.5.3 ## explicit; go 1.9 github.com/golang/protobuf/proto +# github.com/google/uuid v1.4.0 +## explicit +github.com/google/uuid # github.com/gorilla/css v1.0.0 ## explicit github.com/gorilla/css/scanner @@ -169,6 +174,12 @@ github.com/sagikazarmark/slog-shim # github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f ## explicit github.com/sahilm/fuzzy +# github.com/segmentio/analytics-go/v3 v3.3.0 +## explicit; go 1.17 +github.com/segmentio/analytics-go/v3 +# github.com/segmentio/backo-go v1.0.0 +## explicit +github.com/segmentio/backo-go # github.com/sourcegraph/conc v0.3.0 ## explicit; go 1.19 github.com/sourcegraph/conc From 7a034c1f991ef2540a89fe320e45842a48fb7fdf Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Mon, 8 Apr 2024 15:07:02 -0700 Subject: [PATCH 2/9] WIP: adding build step to release --- .github/actions/publish/action.yml | 6 +++--- .github/workflows/release-please.yml | 12 +++++++++--- .goreleaser.yaml | 4 ++++ cmd/root.go | 3 ++- main.go | 8 ++++++-- 5 files changed, 24 insertions(+), 9 deletions(-) diff --git a/.github/actions/publish/action.yml b/.github/actions/publish/action.yml index 5aec8c88..37defa9f 100644 --- a/.github/actions/publish/action.yml +++ b/.github/actions/publish/action.yml @@ -12,7 +12,7 @@ inputs: description: 'Tag to upload artifacts to.' required: true outputs: - hashes: + hashes: description: sha256sum hashes of built artifacts value: ${{ steps.hash.outputs.hashes }} @@ -36,8 +36,8 @@ runs: args: release ${{ inputs.dry-run == 'true' && '--skip=publish' || '' }} env: GITHUB_TOKEN: ${{ inputs.token }} - - name: Hash build artifacts for provenance + - name: Hash build artifacts for provenance id: hash shell: bash - run: | + run: | echo "hashes=$(sha256sum dist/* | base64 -w0)" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 751cd3b2..71187ccf 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -25,20 +25,26 @@ jobs: needs: [ release-please ] if: ${{ needs.release-please.outputs.release_created == 'true' }} runs-on: ubuntu-latest - outputs: + outputs: hashes: ${{ steps.publish.outputs.hashes }} steps: - uses: actions/checkout@v4 name: Checkout with: fetch-depth: 0 - + - uses: launchdarkly/gh-actions/actions/release-secrets@release-secrets-v1.0.1 name: 'Get Docker token' with: aws_assume_role: ${{ vars.AWS_ROLE_ARN }} ssm_parameter_pairs: '/global/services/docker/public/username = DOCKER_USERNAME, /global/services/docker/public/token = DOCKER_TOKEN' - + + - uses: ./actions/release-secrets + name: 'Get segment.io token' + with: + aws_assume_role: ${{ inputs.aws_assume_role }} + ssm_parameter_pairs: '/production/common/services/ldcli/segment_write_key = SEGMENT_WRITE_KEY' + - uses: ./.github/actions/publish with: dry-run: 'false' diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 5655a4f2..9ecb7551 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -1,4 +1,8 @@ # .goreleaser.yaml +builds: + - ldflags: + - -s -w -X main.version={{ .Version }} -X main.segmentWriteKey={{ .Env.SEGMENT_WRITE_KEY }} + dockers: # AMD64 - image_templates: diff --git a/cmd/root.go b/cmd/root.go index cb4fadba..ab8d64ec 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -111,8 +111,9 @@ func Execute(client analytics.SegmentioClient, version string) { log.Fatal(err) } + fmt.Println(">>> tracking event") err = client.Track( - "user-123", + "user-234", map[string]interface{}{ "event1": time.Now().String(), }, diff --git a/main.go b/main.go index ebb81ea6..ea01eacc 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,8 @@ package main import ( + "fmt" + segmentio "github.com/segmentio/analytics-go/v3" "ldcli/cmd" @@ -9,12 +11,14 @@ import ( // main.version is set at build time via ldflags by go releaser https://goreleaser.com/cookbooks/using-main.version/ var ( - version = "dev" + version = "dev" + segmentWriteKey = "" ) func main() { + fmt.Println(">>> segmentWriteKey", segmentWriteKey) client := analytics.NewSegmentioClient( - segmentio.New("TODO"), + segmentio.New(segmentWriteKey), ) defer client.Close() From eba4c8f55250f454c638a263dfcb549b2e7b4b4c Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Mon, 8 Apr 2024 15:25:29 -0700 Subject: [PATCH 3/9] Add changes to run manual build --- .github/workflows/manual-publish.yml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/manual-publish.yml b/.github/workflows/manual-publish.yml index 7b1267d2..c4aa9a82 100644 --- a/.github/workflows/manual-publish.yml +++ b/.github/workflows/manual-publish.yml @@ -18,20 +18,26 @@ jobs: id-token: write # Needed to obtain Docker tokens contents: write # Needed to upload release artifacts runs-on: ubuntu-latest - outputs: + outputs: hashes: ${{ steps.publish.outputs.hashes }} steps: - uses: actions/checkout@v4 name: Checkout with: fetch-depth: 0 - + - uses: launchdarkly/gh-actions/actions/release-secrets@release-secrets-v1.0.1 name: 'Get Docker token' with: aws_assume_role: ${{ vars.AWS_ROLE_ARN }} ssm_parameter_pairs: '/global/services/docker/public/username = DOCKER_USERNAME, /global/services/docker/public/token = DOCKER_TOKEN' - + + - uses: ./actions/release-secrets + name: 'Get segment.io token' + with: + aws_assume_role: ${{ inputs.aws_assume_role }} + ssm_parameter_pairs: '/production/common/services/ldcli/segment_write_key = SEGMENT_WRITE_KEY' + - uses: ./.github/actions/publish id: publish with: @@ -49,4 +55,3 @@ jobs: base64-subjects: "${{ needs.release-ldcli.outputs.hashes }}" upload-assets: true upload-tag-name: ${{ inputs.tag }} - \ No newline at end of file From 2045e868ee0c2ba79e678225708cae1db09afc8d Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Mon, 8 Apr 2024 15:29:27 -0700 Subject: [PATCH 4/9] Find release-secrets --- .github/workflows/manual-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/manual-publish.yml b/.github/workflows/manual-publish.yml index c4aa9a82..18225494 100644 --- a/.github/workflows/manual-publish.yml +++ b/.github/workflows/manual-publish.yml @@ -32,7 +32,7 @@ jobs: aws_assume_role: ${{ vars.AWS_ROLE_ARN }} ssm_parameter_pairs: '/global/services/docker/public/username = DOCKER_USERNAME, /global/services/docker/public/token = DOCKER_TOKEN' - - uses: ./actions/release-secrets + - uses: launchdarkly/gh-actions/actions/release-secrets@release-secrets-v1.0.1 name: 'Get segment.io token' with: aws_assume_role: ${{ inputs.aws_assume_role }} From 33633dff8edb24ec425b5c90290441bd95dc6cb6 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Tue, 9 Apr 2024 16:14:45 -0700 Subject: [PATCH 5/9] Set up analytics client --- cmd/cmdtest.go | 2 +- cmd/environments/environments.go | 8 ++- cmd/environments/get.go | 22 +++++++-- cmd/root.go | 20 ++------ internal/analytics/client.go | 83 +++++++++++++++++++++++++++++++- internal/analytics/mock.go | 13 ----- internal/analytics/segmentio.go | 26 ---------- main.go | 21 ++++---- 8 files changed, 120 insertions(+), 75 deletions(-) delete mode 100644 internal/analytics/mock.go delete mode 100644 internal/analytics/segmentio.go diff --git a/cmd/cmdtest.go b/cmd/cmdtest.go index 240558e9..9ac56658 100644 --- a/cmd/cmdtest.go +++ b/cmd/cmdtest.go @@ -25,7 +25,7 @@ func CallCmd( args []string, ) ([]byte, error) { rootCmd, err := NewRootCommand( - analytics.MockClient{}, + &analytics.NoopClient{}, environmentsClient, flagsClient, membersClient, diff --git a/cmd/environments/environments.go b/cmd/environments/environments.go index 7f1bf9e9..a5cd4de8 100644 --- a/cmd/environments/environments.go +++ b/cmd/environments/environments.go @@ -3,17 +3,21 @@ package environments import ( "github.com/spf13/cobra" + "ldcli/internal/analytics" "ldcli/internal/environments" ) -func NewEnvironmentsCmd(client environments.Client) (*cobra.Command, error) { +func NewEnvironmentsCmd( + analyticsTracker analytics.AnalyticsTracker, + client environments.Client, +) (*cobra.Command, error) { cmd := &cobra.Command{ Use: "environments", Short: "Make requests (list, create, etc.) on environments", Long: "Make requests (list, create, etc.) on environments", } - getCmd, err := NewGetCmd(client) + getCmd, err := NewGetCmd(analyticsTracker, client) if err != nil { return nil, err } diff --git a/cmd/environments/get.go b/cmd/environments/get.go index 67fc491a..92bb92e7 100644 --- a/cmd/environments/get.go +++ b/cmd/environments/get.go @@ -9,14 +9,18 @@ import ( "ldcli/cmd/cliflags" "ldcli/cmd/validators" + "ldcli/internal/analytics" "ldcli/internal/environments" ) -func NewGetCmd(client environments.Client) (*cobra.Command, error) { +func NewGetCmd( + analyticsTracker analytics.AnalyticsTracker, + client environments.Client, +) (*cobra.Command, error) { cmd := &cobra.Command{ Args: validators.Validate(), Long: "Return an environment", - RunE: runGet(client), + RunE: runGet(analyticsTracker, client), Short: "Return an environment", Use: "get", } @@ -45,7 +49,10 @@ func NewGetCmd(client environments.Client) (*cobra.Command, error) { return cmd, nil } -func runGet(client environments.Client) func(*cobra.Command, []string) error { +func runGet( + analyticsTracker analytics.AnalyticsTracker, + client environments.Client, +) func(*cobra.Command, []string) error { return func(cmd *cobra.Command, args []string) error { _ = viper.BindPFlag(cliflags.EnvironmentFlag, cmd.Flags().Lookup(cliflags.EnvironmentFlag)) _ = viper.BindPFlag(cliflags.ProjectFlag, cmd.Flags().Lookup(cliflags.ProjectFlag)) @@ -61,6 +68,15 @@ func runGet(client environments.Client) func(*cobra.Command, []string) error { return err } + analyticsTracker.SendEvent( + viper.GetString(cliflags.AccessTokenFlag), + viper.GetString(cliflags.BaseURIFlag), + "environment_get", + map[string]interface{}{ + "key": viper.GetString(cliflags.EnvironmentFlag), + "projectKey": viper.GetString(cliflags.ProjectFlag), + }) + fmt.Fprintf(cmd.OutOrStdout(), string(response)+"\n") return nil diff --git a/cmd/root.go b/cmd/root.go index ab8d64ec..eb56474a 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -4,7 +4,6 @@ import ( "fmt" "log" "os" - "time" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -22,7 +21,7 @@ import ( ) func NewRootCommand( - client analytics.AnalyticsTracker, + analyticsTracker analytics.AnalyticsTracker, environmentsClient environments.Client, flagsClient flags.Client, membersClient members.Client, @@ -72,7 +71,7 @@ func NewRootCommand( return nil, err } - environmentsCmd, err := envscmd.NewEnvironmentsCmd(environmentsClient) + environmentsCmd, err := envscmd.NewEnvironmentsCmd(analyticsTracker, environmentsClient) if err != nil { return nil, err } @@ -98,9 +97,9 @@ func NewRootCommand( return cmd, nil } -func Execute(client analytics.SegmentioClient, version string) { +func Execute(analyticsTracker analytics.AnalyticsTracker, version string) { rootCmd, err := NewRootCommand( - client, + analyticsTracker, environments.NewClient(version), flags.NewClient(version), members.NewClient(version), @@ -111,17 +110,6 @@ func Execute(client analytics.SegmentioClient, version string) { log.Fatal(err) } - fmt.Println(">>> tracking event") - err = client.Track( - "user-234", - map[string]interface{}{ - "event1": time.Now().String(), - }, - ) - if err != nil { - fmt.Fprintln(os.Stderr, err.Error()) - } - err = rootCmd.Execute() if err != nil { fmt.Fprintln(os.Stderr, err.Error()) diff --git a/internal/analytics/client.go b/internal/analytics/client.go index d06360bd..d633a950 100644 --- a/internal/analytics/client.go +++ b/internal/analytics/client.go @@ -1,6 +1,85 @@ package analytics +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "sync" +) + type AnalyticsTracker interface { - Track(userID string, traits map[string]interface{}) error - Close() error + SendEvent( + accessToken string, + baseURI string, + eventName string, + properties map[string]interface{}, + ) +} + +type AnalyticsClient struct { + HTTPClient *http.Client + wg sync.WaitGroup +} + +// SendEvent makes an async request to track the given event with properties. +func (c *AnalyticsClient) SendEvent( + accessToken string, + baseURI string, + eventName string, + properties map[string]interface{}, +) { + input := struct { + Event string `json:"event"` + Properties map[string]interface{} `json:"properties"` + }{ + Event: eventName, + Properties: properties, + } + + c.wg.Add(1) + body, err := json.Marshal(input) + if err != nil { //nolint:staticcheck + // TODO: log error + } + + req, err := http.NewRequest("POST", fmt.Sprintf("%s/api/v2/tracking", baseURI), bytes.NewBuffer(body)) + if err != nil { //nolint:staticcheck + // TODO: log error + } + + req.Header.Add("Authorization", accessToken) + req.Header.Add("Content-Type", "application/json") + req.Header.Add("User-Agent", "launchdarkly-cli/v0.1.1") + var resp *http.Response + go func() { + resp, err = c.HTTPClient.Do(req) + if err != nil { //nolint:staticcheck + // TODO: log error + } + if resp != nil { + resp.Body.Close() + } + + _, err := io.ReadAll(resp.Body) + if err != nil { //nolint:staticcheck + // TODO: log error + } + c.wg.Done() + }() +} + +func (a *AnalyticsClient) Wait() { + a.wg.Wait() +} + +type NoopClient struct{} + +func (c *NoopClient) SendEvent( + accessToken string, + baseURI string, + eventName string, + properties map[string]interface{}, +) { } diff --git a/internal/analytics/mock.go b/internal/analytics/mock.go deleted file mode 100644 index a709405c..00000000 --- a/internal/analytics/mock.go +++ /dev/null @@ -1,13 +0,0 @@ -package analytics - -type MockClient struct{} - -func (c MockClient) Track(userID string, traits map[string]interface{}) error { - return nil -} - -func (c MockClient) Close() error { - return nil -} - -var _ AnalyticsTracker = &MockClient{} diff --git a/internal/analytics/segmentio.go b/internal/analytics/segmentio.go deleted file mode 100644 index 57bcd783..00000000 --- a/internal/analytics/segmentio.go +++ /dev/null @@ -1,26 +0,0 @@ -package analytics - -import "github.com/segmentio/analytics-go/v3" - -type SegmentioClient struct { - client analytics.Client -} - -func NewSegmentioClient(client analytics.Client) SegmentioClient { - return SegmentioClient{ - client: client, - } -} - -func (c SegmentioClient) Track(userID string, traits map[string]interface{}) error { - return c.client.Enqueue(analytics.Identify{ - UserId: userID, - Traits: traits, - }) -} - -func (c SegmentioClient) Close() error { - return c.client.Close() -} - -var _ AnalyticsTracker = &SegmentioClient{} diff --git a/main.go b/main.go index ea01eacc..19c4d9bc 100644 --- a/main.go +++ b/main.go @@ -1,9 +1,8 @@ package main import ( - "fmt" - - segmentio "github.com/segmentio/analytics-go/v3" + "net/http" + "time" "ldcli/cmd" "ldcli/internal/analytics" @@ -11,16 +10,14 @@ import ( // main.version is set at build time via ldflags by go releaser https://goreleaser.com/cookbooks/using-main.version/ var ( - version = "dev" - segmentWriteKey = "" + version = "dev" ) func main() { - fmt.Println(">>> segmentWriteKey", segmentWriteKey) - client := analytics.NewSegmentioClient( - segmentio.New(segmentWriteKey), - ) - defer client.Close() - - cmd.Execute(client, version) + httpClient := &http.Client{ + Timeout: time.Second * 3, + } + analyticsClient := &analytics.AnalyticsClient{HTTPClient: httpClient} + cmd.Execute(analyticsClient, version) + analyticsClient.Wait() } From 3ca7563b9c35b83660d74a85b2e596ae40fea120 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Tue, 9 Apr 2024 16:18:17 -0700 Subject: [PATCH 6/9] Rename --- .github/workflows/manual-publish.yml | 6 ------ .github/workflows/release-please.yml | 6 ------ .goreleaser.yaml | 4 ---- cmd/environments/environments.go | 2 +- cmd/environments/get.go | 4 ++-- cmd/root.go | 4 ++-- internal/analytics/client.go | 8 ++++---- main.go | 2 +- 8 files changed, 10 insertions(+), 26 deletions(-) diff --git a/.github/workflows/manual-publish.yml b/.github/workflows/manual-publish.yml index 71d46e74..3568def1 100644 --- a/.github/workflows/manual-publish.yml +++ b/.github/workflows/manual-publish.yml @@ -32,12 +32,6 @@ jobs: aws_assume_role: ${{ vars.AWS_ROLE_ARN }} ssm_parameter_pairs: '/global/services/docker/public/username = DOCKER_USERNAME, /global/services/docker/public/token = DOCKER_TOKEN' - - uses: launchdarkly/gh-actions/actions/release-secrets@release-secrets-v1.0.1 - name: 'Get segment.io token' - with: - aws_assume_role: ${{ inputs.aws_assume_role }} - ssm_parameter_pairs: '/production/common/services/ldcli/segment_write_key = SEGMENT_WRITE_KEY' - - uses: ./.github/actions/publish id: publish with: diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index c84da6eb..6503a352 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -39,12 +39,6 @@ jobs: aws_assume_role: ${{ vars.AWS_ROLE_ARN }} ssm_parameter_pairs: '/global/services/docker/public/username = DOCKER_USERNAME, /global/services/docker/public/token = DOCKER_TOKEN' - - uses: ./actions/release-secrets - name: 'Get segment.io token' - with: - aws_assume_role: ${{ inputs.aws_assume_role }} - ssm_parameter_pairs: '/production/common/services/ldcli/segment_write_key = SEGMENT_WRITE_KEY' - - uses: ./.github/actions/publish with: dry-run: 'false' diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 56f0a83d..3c7d5bf0 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -1,8 +1,4 @@ # .goreleaser.yaml -builds: - - ldflags: - - -s -w -X main.version={{ .Version }} -X main.segmentWriteKey={{ .Env.SEGMENT_WRITE_KEY }} - dockers: # AMD64 - image_templates: diff --git a/cmd/environments/environments.go b/cmd/environments/environments.go index a5cd4de8..2e41bb5b 100644 --- a/cmd/environments/environments.go +++ b/cmd/environments/environments.go @@ -8,7 +8,7 @@ import ( ) func NewEnvironmentsCmd( - analyticsTracker analytics.AnalyticsTracker, + analyticsTracker analytics.Tracker, client environments.Client, ) (*cobra.Command, error) { cmd := &cobra.Command{ diff --git a/cmd/environments/get.go b/cmd/environments/get.go index 92bb92e7..1f633e9b 100644 --- a/cmd/environments/get.go +++ b/cmd/environments/get.go @@ -14,7 +14,7 @@ import ( ) func NewGetCmd( - analyticsTracker analytics.AnalyticsTracker, + analyticsTracker analytics.Tracker, client environments.Client, ) (*cobra.Command, error) { cmd := &cobra.Command{ @@ -50,7 +50,7 @@ func NewGetCmd( } func runGet( - analyticsTracker analytics.AnalyticsTracker, + analyticsTracker analytics.Tracker, client environments.Client, ) func(*cobra.Command, []string) error { return func(cmd *cobra.Command, args []string) error { diff --git a/cmd/root.go b/cmd/root.go index eb56474a..feabce2a 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -21,7 +21,7 @@ import ( ) func NewRootCommand( - analyticsTracker analytics.AnalyticsTracker, + analyticsTracker analytics.Tracker, environmentsClient environments.Client, flagsClient flags.Client, membersClient members.Client, @@ -97,7 +97,7 @@ func NewRootCommand( return cmd, nil } -func Execute(analyticsTracker analytics.AnalyticsTracker, version string) { +func Execute(analyticsTracker analytics.Tracker, version string) { rootCmd, err := NewRootCommand( analyticsTracker, environments.NewClient(version), diff --git a/internal/analytics/client.go b/internal/analytics/client.go index d633a950..1e985bd6 100644 --- a/internal/analytics/client.go +++ b/internal/analytics/client.go @@ -9,7 +9,7 @@ import ( "sync" ) -type AnalyticsTracker interface { +type Tracker interface { SendEvent( accessToken string, baseURI string, @@ -18,13 +18,13 @@ type AnalyticsTracker interface { ) } -type AnalyticsClient struct { +type Client struct { HTTPClient *http.Client wg sync.WaitGroup } // SendEvent makes an async request to track the given event with properties. -func (c *AnalyticsClient) SendEvent( +func (c *Client) SendEvent( accessToken string, baseURI string, eventName string, @@ -70,7 +70,7 @@ func (c *AnalyticsClient) SendEvent( }() } -func (a *AnalyticsClient) Wait() { +func (a *Client) Wait() { a.wg.Wait() } diff --git a/main.go b/main.go index 19c4d9bc..24e5bf64 100644 --- a/main.go +++ b/main.go @@ -17,7 +17,7 @@ func main() { httpClient := &http.Client{ Timeout: time.Second * 3, } - analyticsClient := &analytics.AnalyticsClient{HTTPClient: httpClient} + analyticsClient := &analytics.Client{HTTPClient: httpClient} cmd.Execute(analyticsClient, version) analyticsClient.Wait() } From 250512dcc90ea98bbdaf2364d31b8c32a6322dfb Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Tue, 9 Apr 2024 16:25:04 -0700 Subject: [PATCH 7/9] Fix wg waiting --- internal/analytics/client.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/analytics/client.go b/internal/analytics/client.go index 1e985bd6..39e13920 100644 --- a/internal/analytics/client.go +++ b/internal/analytics/client.go @@ -42,11 +42,15 @@ func (c *Client) SendEvent( body, err := json.Marshal(input) if err != nil { //nolint:staticcheck // TODO: log error + c.wg.Done() + return } req, err := http.NewRequest("POST", fmt.Sprintf("%s/api/v2/tracking", baseURI), bytes.NewBuffer(body)) if err != nil { //nolint:staticcheck // TODO: log error + c.wg.Done() + return } req.Header.Add("Authorization", accessToken) From e1daf8c478a498bbb36f1e9d1c8e530a6ad9cff6 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Wed, 10 Apr 2024 06:04:45 -0700 Subject: [PATCH 8/9] Remove segment lib --- go.mod | 4 - go.sum | 8 - vendor/github.com/google/uuid/CHANGELOG.md | 21 - vendor/github.com/google/uuid/CONTRIBUTING.md | 26 -- vendor/github.com/google/uuid/CONTRIBUTORS | 9 - vendor/github.com/google/uuid/LICENSE | 27 -- vendor/github.com/google/uuid/README.md | 21 - vendor/github.com/google/uuid/dce.go | 80 ---- vendor/github.com/google/uuid/doc.go | 12 - vendor/github.com/google/uuid/hash.go | 53 --- vendor/github.com/google/uuid/marshal.go | 38 -- vendor/github.com/google/uuid/node.go | 90 ---- vendor/github.com/google/uuid/node_js.go | 12 - vendor/github.com/google/uuid/node_net.go | 33 -- vendor/github.com/google/uuid/null.go | 118 ----- vendor/github.com/google/uuid/sql.go | 59 --- vendor/github.com/google/uuid/time.go | 123 ----- vendor/github.com/google/uuid/util.go | 43 -- vendor/github.com/google/uuid/uuid.go | 312 ------------- vendor/github.com/google/uuid/version1.go | 44 -- vendor/github.com/google/uuid/version4.go | 76 --- .../segmentio/analytics-go/v3/.gitignore | 32 -- .../segmentio/analytics-go/v3/.gitmodules | 6 - .../segmentio/analytics-go/v3/History.md | 93 ---- .../segmentio/analytics-go/v3/License.md | 21 - .../segmentio/analytics-go/v3/Makefile | 31 -- .../segmentio/analytics-go/v3/Readme.md | 55 --- .../segmentio/analytics-go/v3/alias.go | 40 -- .../segmentio/analytics-go/v3/analytics.go | 431 ------------------ .../segmentio/analytics-go/v3/config.go | 173 ------- .../segmentio/analytics-go/v3/context.go | 150 ------ .../segmentio/analytics-go/v3/error.go | 60 --- .../segmentio/analytics-go/v3/executor.go | 53 --- .../segmentio/analytics-go/v3/group.go | 42 -- .../segmentio/analytics-go/v3/identify.go | 33 -- .../segmentio/analytics-go/v3/integrations.go | 44 -- .../segmentio/analytics-go/v3/json.go | 87 ---- .../segmentio/analytics-go/v3/logger.go | 47 -- .../segmentio/analytics-go/v3/message.go | 128 ------ .../segmentio/analytics-go/v3/page.go | 34 -- .../segmentio/analytics-go/v3/properties.go | 117 ----- .../segmentio/analytics-go/v3/screen.go | 34 -- .../segmentio/analytics-go/v3/timeout_15.go | 16 - .../segmentio/analytics-go/v3/timeout_16.go | 10 - .../segmentio/analytics-go/v3/track.go | 42 -- .../segmentio/analytics-go/v3/traits.go | 89 ---- .../segmentio/analytics-go/v3/validate.go | 65 --- .../github.com/segmentio/backo-go/.gitmodules | 3 - .../github.com/segmentio/backo-go/README.md | 80 ---- vendor/github.com/segmentio/backo-go/backo.go | 83 ---- vendor/modules.txt | 11 - 51 files changed, 3319 deletions(-) delete mode 100644 vendor/github.com/google/uuid/CHANGELOG.md delete mode 100644 vendor/github.com/google/uuid/CONTRIBUTING.md delete mode 100644 vendor/github.com/google/uuid/CONTRIBUTORS delete mode 100644 vendor/github.com/google/uuid/LICENSE delete mode 100644 vendor/github.com/google/uuid/README.md delete mode 100644 vendor/github.com/google/uuid/dce.go delete mode 100644 vendor/github.com/google/uuid/doc.go delete mode 100644 vendor/github.com/google/uuid/hash.go delete mode 100644 vendor/github.com/google/uuid/marshal.go delete mode 100644 vendor/github.com/google/uuid/node.go delete mode 100644 vendor/github.com/google/uuid/node_js.go delete mode 100644 vendor/github.com/google/uuid/node_net.go delete mode 100644 vendor/github.com/google/uuid/null.go delete mode 100644 vendor/github.com/google/uuid/sql.go delete mode 100644 vendor/github.com/google/uuid/time.go delete mode 100644 vendor/github.com/google/uuid/util.go delete mode 100644 vendor/github.com/google/uuid/uuid.go delete mode 100644 vendor/github.com/google/uuid/version1.go delete mode 100644 vendor/github.com/google/uuid/version4.go delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/.gitignore delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/.gitmodules delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/History.md delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/License.md delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/Makefile delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/Readme.md delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/alias.go delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/analytics.go delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/config.go delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/context.go delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/error.go delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/executor.go delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/group.go delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/identify.go delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/integrations.go delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/json.go delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/logger.go delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/message.go delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/page.go delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/properties.go delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/screen.go delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/timeout_15.go delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/timeout_16.go delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/track.go delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/traits.go delete mode 100644 vendor/github.com/segmentio/analytics-go/v3/validate.go delete mode 100644 vendor/github.com/segmentio/backo-go/.gitmodules delete mode 100644 vendor/github.com/segmentio/backo-go/README.md delete mode 100644 vendor/github.com/segmentio/backo-go/backo.go diff --git a/go.mod b/go.mod index 3c1d5cfb..b6406b96 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,6 @@ require ( github.com/launchdarkly/api-client-go/v14 v14.0.0 github.com/muesli/reflow v0.3.0 github.com/pkg/errors v0.9.1 - github.com/segmentio/analytics-go/v3 v3.3.0 github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.18.2 github.com/stretchr/testify v1.9.0 @@ -21,13 +20,11 @@ require ( github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect - github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dlclark/regexp2 v1.4.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/google/uuid v1.4.0 // indirect github.com/gorilla/css v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -48,7 +45,6 @@ require ( github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f // indirect - github.com/segmentio/backo-go v1.0.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect diff --git a/go.sum b/go.sum index a7da1680..c40b262f 100644 --- a/go.sum +++ b/go.sum @@ -42,8 +42,6 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= @@ -125,8 +123,6 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= @@ -199,10 +195,6 @@ github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6g github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f h1:MvTmaQdww/z0Q4wrYjDSCcZ78NoftLQyHBSLW/Cx79Y= github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= -github.com/segmentio/analytics-go/v3 v3.3.0 h1:8VOMaVGBW03pdBrj1CMFfY9o/rnjJC+1wyQHlVxjw5o= -github.com/segmentio/analytics-go/v3 v3.3.0/go.mod h1:p8owAF8X+5o27jmvUognuXxdtqvSGtD0ZrfY2kcS9bE= -github.com/segmentio/backo-go v1.0.0 h1:kbOAtGJY2DqOR0jfRkYEorx/b18RgtepGtY3+Cpe6qA= -github.com/segmentio/backo-go v1.0.0/go.mod h1:kJ9mm9YmoWSkk+oQ+5Cj8DEoRCX2JT6As4kEtIIOp1M= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= diff --git a/vendor/github.com/google/uuid/CHANGELOG.md b/vendor/github.com/google/uuid/CHANGELOG.md deleted file mode 100644 index 7ed347d3..00000000 --- a/vendor/github.com/google/uuid/CHANGELOG.md +++ /dev/null @@ -1,21 +0,0 @@ -# Changelog - -## [1.4.0](https://github.com/google/uuid/compare/v1.3.1...v1.4.0) (2023-10-26) - - -### Features - -* UUIDs slice type with Strings() convenience method ([#133](https://github.com/google/uuid/issues/133)) ([cd5fbbd](https://github.com/google/uuid/commit/cd5fbbdd02f3e3467ac18940e07e062be1f864b4)) - -### Fixes - -* Clarify that Parse's job is to parse but not necessarily validate strings. (Documents current behavior) - -## [1.3.1](https://github.com/google/uuid/compare/v1.3.0...v1.3.1) (2023-08-18) - - -### Bug Fixes - -* Use .EqualFold() to parse urn prefixed UUIDs ([#118](https://github.com/google/uuid/issues/118)) ([574e687](https://github.com/google/uuid/commit/574e6874943741fb99d41764c705173ada5293f0)) - -## Changelog diff --git a/vendor/github.com/google/uuid/CONTRIBUTING.md b/vendor/github.com/google/uuid/CONTRIBUTING.md deleted file mode 100644 index a502fdc5..00000000 --- a/vendor/github.com/google/uuid/CONTRIBUTING.md +++ /dev/null @@ -1,26 +0,0 @@ -# How to contribute - -We definitely welcome patches and contribution to this project! - -### Tips - -Commits must be formatted according to the [Conventional Commits Specification](https://www.conventionalcommits.org). - -Always try to include a test case! If it is not possible or not necessary, -please explain why in the pull request description. - -### Releasing - -Commits that would precipitate a SemVer change, as described in the Conventional -Commits Specification, will trigger [`release-please`](https://github.com/google-github-actions/release-please-action) -to create a release candidate pull request. Once submitted, `release-please` -will create a release. - -For tips on how to work with `release-please`, see its documentation. - -### Legal requirements - -In order to protect both you and ourselves, you will need to sign the -[Contributor License Agreement](https://cla.developers.google.com/clas). - -You may have already signed it for other Google projects. diff --git a/vendor/github.com/google/uuid/CONTRIBUTORS b/vendor/github.com/google/uuid/CONTRIBUTORS deleted file mode 100644 index b4bb97f6..00000000 --- a/vendor/github.com/google/uuid/CONTRIBUTORS +++ /dev/null @@ -1,9 +0,0 @@ -Paul Borman -bmatsuo -shawnps -theory -jboverfelt -dsymonds -cd1 -wallclockbuilder -dansouza diff --git a/vendor/github.com/google/uuid/LICENSE b/vendor/github.com/google/uuid/LICENSE deleted file mode 100644 index 5dc68268..00000000 --- a/vendor/github.com/google/uuid/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009,2014 Google Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/google/uuid/README.md b/vendor/github.com/google/uuid/README.md deleted file mode 100644 index 3e9a6188..00000000 --- a/vendor/github.com/google/uuid/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# uuid -The uuid package generates and inspects UUIDs based on -[RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122) -and DCE 1.1: Authentication and Security Services. - -This package is based on the github.com/pborman/uuid package (previously named -code.google.com/p/go-uuid). It differs from these earlier packages in that -a UUID is a 16 byte array rather than a byte slice. One loss due to this -change is the ability to represent an invalid UUID (vs a NIL UUID). - -###### Install -```sh -go get github.com/google/uuid -``` - -###### Documentation -[![Go Reference](https://pkg.go.dev/badge/github.com/google/uuid.svg)](https://pkg.go.dev/github.com/google/uuid) - -Full `go doc` style documentation for the package can be viewed online without -installing this package by using the GoDoc site here: -http://pkg.go.dev/github.com/google/uuid diff --git a/vendor/github.com/google/uuid/dce.go b/vendor/github.com/google/uuid/dce.go deleted file mode 100644 index fa820b9d..00000000 --- a/vendor/github.com/google/uuid/dce.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package uuid - -import ( - "encoding/binary" - "fmt" - "os" -) - -// A Domain represents a Version 2 domain -type Domain byte - -// Domain constants for DCE Security (Version 2) UUIDs. -const ( - Person = Domain(0) - Group = Domain(1) - Org = Domain(2) -) - -// NewDCESecurity returns a DCE Security (Version 2) UUID. -// -// The domain should be one of Person, Group or Org. -// On a POSIX system the id should be the users UID for the Person -// domain and the users GID for the Group. The meaning of id for -// the domain Org or on non-POSIX systems is site defined. -// -// For a given domain/id pair the same token may be returned for up to -// 7 minutes and 10 seconds. -func NewDCESecurity(domain Domain, id uint32) (UUID, error) { - uuid, err := NewUUID() - if err == nil { - uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2 - uuid[9] = byte(domain) - binary.BigEndian.PutUint32(uuid[0:], id) - } - return uuid, err -} - -// NewDCEPerson returns a DCE Security (Version 2) UUID in the person -// domain with the id returned by os.Getuid. -// -// NewDCESecurity(Person, uint32(os.Getuid())) -func NewDCEPerson() (UUID, error) { - return NewDCESecurity(Person, uint32(os.Getuid())) -} - -// NewDCEGroup returns a DCE Security (Version 2) UUID in the group -// domain with the id returned by os.Getgid. -// -// NewDCESecurity(Group, uint32(os.Getgid())) -func NewDCEGroup() (UUID, error) { - return NewDCESecurity(Group, uint32(os.Getgid())) -} - -// Domain returns the domain for a Version 2 UUID. Domains are only defined -// for Version 2 UUIDs. -func (uuid UUID) Domain() Domain { - return Domain(uuid[9]) -} - -// ID returns the id for a Version 2 UUID. IDs are only defined for Version 2 -// UUIDs. -func (uuid UUID) ID() uint32 { - return binary.BigEndian.Uint32(uuid[0:4]) -} - -func (d Domain) String() string { - switch d { - case Person: - return "Person" - case Group: - return "Group" - case Org: - return "Org" - } - return fmt.Sprintf("Domain%d", int(d)) -} diff --git a/vendor/github.com/google/uuid/doc.go b/vendor/github.com/google/uuid/doc.go deleted file mode 100644 index 5b8a4b9a..00000000 --- a/vendor/github.com/google/uuid/doc.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package uuid generates and inspects UUIDs. -// -// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security -// Services. -// -// A UUID is a 16 byte (128 bit) array. UUIDs may be used as keys to -// maps or compared directly. -package uuid diff --git a/vendor/github.com/google/uuid/hash.go b/vendor/github.com/google/uuid/hash.go deleted file mode 100644 index b404f4be..00000000 --- a/vendor/github.com/google/uuid/hash.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package uuid - -import ( - "crypto/md5" - "crypto/sha1" - "hash" -) - -// Well known namespace IDs and UUIDs -var ( - NameSpaceDNS = Must(Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8")) - NameSpaceURL = Must(Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8")) - NameSpaceOID = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8")) - NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8")) - Nil UUID // empty UUID, all zeros -) - -// NewHash returns a new UUID derived from the hash of space concatenated with -// data generated by h. The hash should be at least 16 byte in length. The -// first 16 bytes of the hash are used to form the UUID. The version of the -// UUID will be the lower 4 bits of version. NewHash is used to implement -// NewMD5 and NewSHA1. -func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID { - h.Reset() - h.Write(space[:]) //nolint:errcheck - h.Write(data) //nolint:errcheck - s := h.Sum(nil) - var uuid UUID - copy(uuid[:], s) - uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4) - uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant - return uuid -} - -// NewMD5 returns a new MD5 (Version 3) UUID based on the -// supplied name space and data. It is the same as calling: -// -// NewHash(md5.New(), space, data, 3) -func NewMD5(space UUID, data []byte) UUID { - return NewHash(md5.New(), space, data, 3) -} - -// NewSHA1 returns a new SHA1 (Version 5) UUID based on the -// supplied name space and data. It is the same as calling: -// -// NewHash(sha1.New(), space, data, 5) -func NewSHA1(space UUID, data []byte) UUID { - return NewHash(sha1.New(), space, data, 5) -} diff --git a/vendor/github.com/google/uuid/marshal.go b/vendor/github.com/google/uuid/marshal.go deleted file mode 100644 index 14bd3407..00000000 --- a/vendor/github.com/google/uuid/marshal.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package uuid - -import "fmt" - -// MarshalText implements encoding.TextMarshaler. -func (uuid UUID) MarshalText() ([]byte, error) { - var js [36]byte - encodeHex(js[:], uuid) - return js[:], nil -} - -// UnmarshalText implements encoding.TextUnmarshaler. -func (uuid *UUID) UnmarshalText(data []byte) error { - id, err := ParseBytes(data) - if err != nil { - return err - } - *uuid = id - return nil -} - -// MarshalBinary implements encoding.BinaryMarshaler. -func (uuid UUID) MarshalBinary() ([]byte, error) { - return uuid[:], nil -} - -// UnmarshalBinary implements encoding.BinaryUnmarshaler. -func (uuid *UUID) UnmarshalBinary(data []byte) error { - if len(data) != 16 { - return fmt.Errorf("invalid UUID (got %d bytes)", len(data)) - } - copy(uuid[:], data) - return nil -} diff --git a/vendor/github.com/google/uuid/node.go b/vendor/github.com/google/uuid/node.go deleted file mode 100644 index d651a2b0..00000000 --- a/vendor/github.com/google/uuid/node.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package uuid - -import ( - "sync" -) - -var ( - nodeMu sync.Mutex - ifname string // name of interface being used - nodeID [6]byte // hardware for version 1 UUIDs - zeroID [6]byte // nodeID with only 0's -) - -// NodeInterface returns the name of the interface from which the NodeID was -// derived. The interface "user" is returned if the NodeID was set by -// SetNodeID. -func NodeInterface() string { - defer nodeMu.Unlock() - nodeMu.Lock() - return ifname -} - -// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs. -// If name is "" then the first usable interface found will be used or a random -// Node ID will be generated. If a named interface cannot be found then false -// is returned. -// -// SetNodeInterface never fails when name is "". -func SetNodeInterface(name string) bool { - defer nodeMu.Unlock() - nodeMu.Lock() - return setNodeInterface(name) -} - -func setNodeInterface(name string) bool { - iname, addr := getHardwareInterface(name) // null implementation for js - if iname != "" && addr != nil { - ifname = iname - copy(nodeID[:], addr) - return true - } - - // We found no interfaces with a valid hardware address. If name - // does not specify a specific interface generate a random Node ID - // (section 4.1.6) - if name == "" { - ifname = "random" - randomBits(nodeID[:]) - return true - } - return false -} - -// NodeID returns a slice of a copy of the current Node ID, setting the Node ID -// if not already set. -func NodeID() []byte { - defer nodeMu.Unlock() - nodeMu.Lock() - if nodeID == zeroID { - setNodeInterface("") - } - nid := nodeID - return nid[:] -} - -// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes -// of id are used. If id is less than 6 bytes then false is returned and the -// Node ID is not set. -func SetNodeID(id []byte) bool { - if len(id) < 6 { - return false - } - defer nodeMu.Unlock() - nodeMu.Lock() - copy(nodeID[:], id) - ifname = "user" - return true -} - -// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is -// not valid. The NodeID is only well defined for version 1 and 2 UUIDs. -func (uuid UUID) NodeID() []byte { - var node [6]byte - copy(node[:], uuid[10:]) - return node[:] -} diff --git a/vendor/github.com/google/uuid/node_js.go b/vendor/github.com/google/uuid/node_js.go deleted file mode 100644 index b2a0bc87..00000000 --- a/vendor/github.com/google/uuid/node_js.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build js - -package uuid - -// getHardwareInterface returns nil values for the JS version of the code. -// This removes the "net" dependency, because it is not used in the browser. -// Using the "net" library inflates the size of the transpiled JS code by 673k bytes. -func getHardwareInterface(name string) (string, []byte) { return "", nil } diff --git a/vendor/github.com/google/uuid/node_net.go b/vendor/github.com/google/uuid/node_net.go deleted file mode 100644 index 0cbbcddb..00000000 --- a/vendor/github.com/google/uuid/node_net.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !js - -package uuid - -import "net" - -var interfaces []net.Interface // cached list of interfaces - -// getHardwareInterface returns the name and hardware address of interface name. -// If name is "" then the name and hardware address of one of the system's -// interfaces is returned. If no interfaces are found (name does not exist or -// there are no interfaces) then "", nil is returned. -// -// Only addresses of at least 6 bytes are returned. -func getHardwareInterface(name string) (string, []byte) { - if interfaces == nil { - var err error - interfaces, err = net.Interfaces() - if err != nil { - return "", nil - } - } - for _, ifs := range interfaces { - if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) { - return ifs.Name, ifs.HardwareAddr - } - } - return "", nil -} diff --git a/vendor/github.com/google/uuid/null.go b/vendor/github.com/google/uuid/null.go deleted file mode 100644 index d7fcbf28..00000000 --- a/vendor/github.com/google/uuid/null.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2021 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package uuid - -import ( - "bytes" - "database/sql/driver" - "encoding/json" - "fmt" -) - -var jsonNull = []byte("null") - -// NullUUID represents a UUID that may be null. -// NullUUID implements the SQL driver.Scanner interface so -// it can be used as a scan destination: -// -// var u uuid.NullUUID -// err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&u) -// ... -// if u.Valid { -// // use u.UUID -// } else { -// // NULL value -// } -// -type NullUUID struct { - UUID UUID - Valid bool // Valid is true if UUID is not NULL -} - -// Scan implements the SQL driver.Scanner interface. -func (nu *NullUUID) Scan(value interface{}) error { - if value == nil { - nu.UUID, nu.Valid = Nil, false - return nil - } - - err := nu.UUID.Scan(value) - if err != nil { - nu.Valid = false - return err - } - - nu.Valid = true - return nil -} - -// Value implements the driver Valuer interface. -func (nu NullUUID) Value() (driver.Value, error) { - if !nu.Valid { - return nil, nil - } - // Delegate to UUID Value function - return nu.UUID.Value() -} - -// MarshalBinary implements encoding.BinaryMarshaler. -func (nu NullUUID) MarshalBinary() ([]byte, error) { - if nu.Valid { - return nu.UUID[:], nil - } - - return []byte(nil), nil -} - -// UnmarshalBinary implements encoding.BinaryUnmarshaler. -func (nu *NullUUID) UnmarshalBinary(data []byte) error { - if len(data) != 16 { - return fmt.Errorf("invalid UUID (got %d bytes)", len(data)) - } - copy(nu.UUID[:], data) - nu.Valid = true - return nil -} - -// MarshalText implements encoding.TextMarshaler. -func (nu NullUUID) MarshalText() ([]byte, error) { - if nu.Valid { - return nu.UUID.MarshalText() - } - - return jsonNull, nil -} - -// UnmarshalText implements encoding.TextUnmarshaler. -func (nu *NullUUID) UnmarshalText(data []byte) error { - id, err := ParseBytes(data) - if err != nil { - nu.Valid = false - return err - } - nu.UUID = id - nu.Valid = true - return nil -} - -// MarshalJSON implements json.Marshaler. -func (nu NullUUID) MarshalJSON() ([]byte, error) { - if nu.Valid { - return json.Marshal(nu.UUID) - } - - return jsonNull, nil -} - -// UnmarshalJSON implements json.Unmarshaler. -func (nu *NullUUID) UnmarshalJSON(data []byte) error { - if bytes.Equal(data, jsonNull) { - *nu = NullUUID{} - return nil // valid null UUID - } - err := json.Unmarshal(data, &nu.UUID) - nu.Valid = err == nil - return err -} diff --git a/vendor/github.com/google/uuid/sql.go b/vendor/github.com/google/uuid/sql.go deleted file mode 100644 index 2e02ec06..00000000 --- a/vendor/github.com/google/uuid/sql.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package uuid - -import ( - "database/sql/driver" - "fmt" -) - -// Scan implements sql.Scanner so UUIDs can be read from databases transparently. -// Currently, database types that map to string and []byte are supported. Please -// consult database-specific driver documentation for matching types. -func (uuid *UUID) Scan(src interface{}) error { - switch src := src.(type) { - case nil: - return nil - - case string: - // if an empty UUID comes from a table, we return a null UUID - if src == "" { - return nil - } - - // see Parse for required string format - u, err := Parse(src) - if err != nil { - return fmt.Errorf("Scan: %v", err) - } - - *uuid = u - - case []byte: - // if an empty UUID comes from a table, we return a null UUID - if len(src) == 0 { - return nil - } - - // assumes a simple slice of bytes if 16 bytes - // otherwise attempts to parse - if len(src) != 16 { - return uuid.Scan(string(src)) - } - copy((*uuid)[:], src) - - default: - return fmt.Errorf("Scan: unable to scan type %T into UUID", src) - } - - return nil -} - -// Value implements sql.Valuer so that UUIDs can be written to databases -// transparently. Currently, UUIDs map to strings. Please consult -// database-specific driver documentation for matching types. -func (uuid UUID) Value() (driver.Value, error) { - return uuid.String(), nil -} diff --git a/vendor/github.com/google/uuid/time.go b/vendor/github.com/google/uuid/time.go deleted file mode 100644 index e6ef06cd..00000000 --- a/vendor/github.com/google/uuid/time.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package uuid - -import ( - "encoding/binary" - "sync" - "time" -) - -// A Time represents a time as the number of 100's of nanoseconds since 15 Oct -// 1582. -type Time int64 - -const ( - lillian = 2299160 // Julian day of 15 Oct 1582 - unix = 2440587 // Julian day of 1 Jan 1970 - epoch = unix - lillian // Days between epochs - g1582 = epoch * 86400 // seconds between epochs - g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs -) - -var ( - timeMu sync.Mutex - lasttime uint64 // last time we returned - clockSeq uint16 // clock sequence for this run - - timeNow = time.Now // for testing -) - -// UnixTime converts t the number of seconds and nanoseconds using the Unix -// epoch of 1 Jan 1970. -func (t Time) UnixTime() (sec, nsec int64) { - sec = int64(t - g1582ns100) - nsec = (sec % 10000000) * 100 - sec /= 10000000 - return sec, nsec -} - -// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and -// clock sequence as well as adjusting the clock sequence as needed. An error -// is returned if the current time cannot be determined. -func GetTime() (Time, uint16, error) { - defer timeMu.Unlock() - timeMu.Lock() - return getTime() -} - -func getTime() (Time, uint16, error) { - t := timeNow() - - // If we don't have a clock sequence already, set one. - if clockSeq == 0 { - setClockSequence(-1) - } - now := uint64(t.UnixNano()/100) + g1582ns100 - - // If time has gone backwards with this clock sequence then we - // increment the clock sequence - if now <= lasttime { - clockSeq = ((clockSeq + 1) & 0x3fff) | 0x8000 - } - lasttime = now - return Time(now), clockSeq, nil -} - -// ClockSequence returns the current clock sequence, generating one if not -// already set. The clock sequence is only used for Version 1 UUIDs. -// -// The uuid package does not use global static storage for the clock sequence or -// the last time a UUID was generated. Unless SetClockSequence is used, a new -// random clock sequence is generated the first time a clock sequence is -// requested by ClockSequence, GetTime, or NewUUID. (section 4.2.1.1) -func ClockSequence() int { - defer timeMu.Unlock() - timeMu.Lock() - return clockSequence() -} - -func clockSequence() int { - if clockSeq == 0 { - setClockSequence(-1) - } - return int(clockSeq & 0x3fff) -} - -// SetClockSequence sets the clock sequence to the lower 14 bits of seq. Setting to -// -1 causes a new sequence to be generated. -func SetClockSequence(seq int) { - defer timeMu.Unlock() - timeMu.Lock() - setClockSequence(seq) -} - -func setClockSequence(seq int) { - if seq == -1 { - var b [2]byte - randomBits(b[:]) // clock sequence - seq = int(b[0])<<8 | int(b[1]) - } - oldSeq := clockSeq - clockSeq = uint16(seq&0x3fff) | 0x8000 // Set our variant - if oldSeq != clockSeq { - lasttime = 0 - } -} - -// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in -// uuid. The time is only defined for version 1 and 2 UUIDs. -func (uuid UUID) Time() Time { - time := int64(binary.BigEndian.Uint32(uuid[0:4])) - time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32 - time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48 - return Time(time) -} - -// ClockSequence returns the clock sequence encoded in uuid. -// The clock sequence is only well defined for version 1 and 2 UUIDs. -func (uuid UUID) ClockSequence() int { - return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff -} diff --git a/vendor/github.com/google/uuid/util.go b/vendor/github.com/google/uuid/util.go deleted file mode 100644 index 5ea6c737..00000000 --- a/vendor/github.com/google/uuid/util.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package uuid - -import ( - "io" -) - -// randomBits completely fills slice b with random data. -func randomBits(b []byte) { - if _, err := io.ReadFull(rander, b); err != nil { - panic(err.Error()) // rand should never fail - } -} - -// xvalues returns the value of a byte as a hexadecimal digit or 255. -var xvalues = [256]byte{ - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, - 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, -} - -// xtob converts hex characters x1 and x2 into a byte. -func xtob(x1, x2 byte) (byte, bool) { - b1 := xvalues[x1] - b2 := xvalues[x2] - return (b1 << 4) | b2, b1 != 255 && b2 != 255 -} diff --git a/vendor/github.com/google/uuid/uuid.go b/vendor/github.com/google/uuid/uuid.go deleted file mode 100644 index dc75f7d9..00000000 --- a/vendor/github.com/google/uuid/uuid.go +++ /dev/null @@ -1,312 +0,0 @@ -// Copyright 2018 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package uuid - -import ( - "bytes" - "crypto/rand" - "encoding/hex" - "errors" - "fmt" - "io" - "strings" - "sync" -) - -// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC -// 4122. -type UUID [16]byte - -// A Version represents a UUID's version. -type Version byte - -// A Variant represents a UUID's variant. -type Variant byte - -// Constants returned by Variant. -const ( - Invalid = Variant(iota) // Invalid UUID - RFC4122 // The variant specified in RFC4122 - Reserved // Reserved, NCS backward compatibility. - Microsoft // Reserved, Microsoft Corporation backward compatibility. - Future // Reserved for future definition. -) - -const randPoolSize = 16 * 16 - -var ( - rander = rand.Reader // random function - poolEnabled = false - poolMu sync.Mutex - poolPos = randPoolSize // protected with poolMu - pool [randPoolSize]byte // protected with poolMu -) - -type invalidLengthError struct{ len int } - -func (err invalidLengthError) Error() string { - return fmt.Sprintf("invalid UUID length: %d", err.len) -} - -// IsInvalidLengthError is matcher function for custom error invalidLengthError -func IsInvalidLengthError(err error) bool { - _, ok := err.(invalidLengthError) - return ok -} - -// Parse decodes s into a UUID or returns an error if it cannot be parsed. Both -// the standard UUID forms defined in RFC 4122 -// (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and -// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) are decoded. In addition, -// Parse accepts non-standard strings such as the raw hex encoding -// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx and 38 byte "Microsoft style" encodings, -// e.g. {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}. Only the middle 36 bytes are -// examined in the latter case. Parse should not be used to validate strings as -// it parses non-standard encodings as indicated above. -func Parse(s string) (UUID, error) { - var uuid UUID - switch len(s) { - // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx - case 36: - - // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx - case 36 + 9: - if !strings.EqualFold(s[:9], "urn:uuid:") { - return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9]) - } - s = s[9:] - - // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} - case 36 + 2: - s = s[1:] - - // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - case 32: - var ok bool - for i := range uuid { - uuid[i], ok = xtob(s[i*2], s[i*2+1]) - if !ok { - return uuid, errors.New("invalid UUID format") - } - } - return uuid, nil - default: - return uuid, invalidLengthError{len(s)} - } - // s is now at least 36 bytes long - // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx - if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { - return uuid, errors.New("invalid UUID format") - } - for i, x := range [16]int{ - 0, 2, 4, 6, - 9, 11, - 14, 16, - 19, 21, - 24, 26, 28, 30, 32, 34, - } { - v, ok := xtob(s[x], s[x+1]) - if !ok { - return uuid, errors.New("invalid UUID format") - } - uuid[i] = v - } - return uuid, nil -} - -// ParseBytes is like Parse, except it parses a byte slice instead of a string. -func ParseBytes(b []byte) (UUID, error) { - var uuid UUID - switch len(b) { - case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx - case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx - if !bytes.EqualFold(b[:9], []byte("urn:uuid:")) { - return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9]) - } - b = b[9:] - case 36 + 2: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} - b = b[1:] - case 32: // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - var ok bool - for i := 0; i < 32; i += 2 { - uuid[i/2], ok = xtob(b[i], b[i+1]) - if !ok { - return uuid, errors.New("invalid UUID format") - } - } - return uuid, nil - default: - return uuid, invalidLengthError{len(b)} - } - // s is now at least 36 bytes long - // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx - if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' { - return uuid, errors.New("invalid UUID format") - } - for i, x := range [16]int{ - 0, 2, 4, 6, - 9, 11, - 14, 16, - 19, 21, - 24, 26, 28, 30, 32, 34, - } { - v, ok := xtob(b[x], b[x+1]) - if !ok { - return uuid, errors.New("invalid UUID format") - } - uuid[i] = v - } - return uuid, nil -} - -// MustParse is like Parse but panics if the string cannot be parsed. -// It simplifies safe initialization of global variables holding compiled UUIDs. -func MustParse(s string) UUID { - uuid, err := Parse(s) - if err != nil { - panic(`uuid: Parse(` + s + `): ` + err.Error()) - } - return uuid -} - -// FromBytes creates a new UUID from a byte slice. Returns an error if the slice -// does not have a length of 16. The bytes are copied from the slice. -func FromBytes(b []byte) (uuid UUID, err error) { - err = uuid.UnmarshalBinary(b) - return uuid, err -} - -// Must returns uuid if err is nil and panics otherwise. -func Must(uuid UUID, err error) UUID { - if err != nil { - panic(err) - } - return uuid -} - -// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx -// , or "" if uuid is invalid. -func (uuid UUID) String() string { - var buf [36]byte - encodeHex(buf[:], uuid) - return string(buf[:]) -} - -// URN returns the RFC 2141 URN form of uuid, -// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid. -func (uuid UUID) URN() string { - var buf [36 + 9]byte - copy(buf[:], "urn:uuid:") - encodeHex(buf[9:], uuid) - return string(buf[:]) -} - -func encodeHex(dst []byte, uuid UUID) { - hex.Encode(dst, uuid[:4]) - dst[8] = '-' - hex.Encode(dst[9:13], uuid[4:6]) - dst[13] = '-' - hex.Encode(dst[14:18], uuid[6:8]) - dst[18] = '-' - hex.Encode(dst[19:23], uuid[8:10]) - dst[23] = '-' - hex.Encode(dst[24:], uuid[10:]) -} - -// Variant returns the variant encoded in uuid. -func (uuid UUID) Variant() Variant { - switch { - case (uuid[8] & 0xc0) == 0x80: - return RFC4122 - case (uuid[8] & 0xe0) == 0xc0: - return Microsoft - case (uuid[8] & 0xe0) == 0xe0: - return Future - default: - return Reserved - } -} - -// Version returns the version of uuid. -func (uuid UUID) Version() Version { - return Version(uuid[6] >> 4) -} - -func (v Version) String() string { - if v > 15 { - return fmt.Sprintf("BAD_VERSION_%d", v) - } - return fmt.Sprintf("VERSION_%d", v) -} - -func (v Variant) String() string { - switch v { - case RFC4122: - return "RFC4122" - case Reserved: - return "Reserved" - case Microsoft: - return "Microsoft" - case Future: - return "Future" - case Invalid: - return "Invalid" - } - return fmt.Sprintf("BadVariant%d", int(v)) -} - -// SetRand sets the random number generator to r, which implements io.Reader. -// If r.Read returns an error when the package requests random data then -// a panic will be issued. -// -// Calling SetRand with nil sets the random number generator to the default -// generator. -func SetRand(r io.Reader) { - if r == nil { - rander = rand.Reader - return - } - rander = r -} - -// EnableRandPool enables internal randomness pool used for Random -// (Version 4) UUID generation. The pool contains random bytes read from -// the random number generator on demand in batches. Enabling the pool -// may improve the UUID generation throughput significantly. -// -// Since the pool is stored on the Go heap, this feature may be a bad fit -// for security sensitive applications. -// -// Both EnableRandPool and DisableRandPool are not thread-safe and should -// only be called when there is no possibility that New or any other -// UUID Version 4 generation function will be called concurrently. -func EnableRandPool() { - poolEnabled = true -} - -// DisableRandPool disables the randomness pool if it was previously -// enabled with EnableRandPool. -// -// Both EnableRandPool and DisableRandPool are not thread-safe and should -// only be called when there is no possibility that New or any other -// UUID Version 4 generation function will be called concurrently. -func DisableRandPool() { - poolEnabled = false - defer poolMu.Unlock() - poolMu.Lock() - poolPos = randPoolSize -} - -// UUIDs is a slice of UUID types. -type UUIDs []UUID - -// Strings returns a string slice containing the string form of each UUID in uuids. -func (uuids UUIDs) Strings() []string { - var uuidStrs = make([]string, len(uuids)) - for i, uuid := range uuids { - uuidStrs[i] = uuid.String() - } - return uuidStrs -} diff --git a/vendor/github.com/google/uuid/version1.go b/vendor/github.com/google/uuid/version1.go deleted file mode 100644 index 46310962..00000000 --- a/vendor/github.com/google/uuid/version1.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package uuid - -import ( - "encoding/binary" -) - -// NewUUID returns a Version 1 UUID based on the current NodeID and clock -// sequence, and the current time. If the NodeID has not been set by SetNodeID -// or SetNodeInterface then it will be set automatically. If the NodeID cannot -// be set NewUUID returns nil. If clock sequence has not been set by -// SetClockSequence then it will be set automatically. If GetTime fails to -// return the current NewUUID returns nil and an error. -// -// In most cases, New should be used. -func NewUUID() (UUID, error) { - var uuid UUID - now, seq, err := GetTime() - if err != nil { - return uuid, err - } - - timeLow := uint32(now & 0xffffffff) - timeMid := uint16((now >> 32) & 0xffff) - timeHi := uint16((now >> 48) & 0x0fff) - timeHi |= 0x1000 // Version 1 - - binary.BigEndian.PutUint32(uuid[0:], timeLow) - binary.BigEndian.PutUint16(uuid[4:], timeMid) - binary.BigEndian.PutUint16(uuid[6:], timeHi) - binary.BigEndian.PutUint16(uuid[8:], seq) - - nodeMu.Lock() - if nodeID == zeroID { - setNodeInterface("") - } - copy(uuid[10:], nodeID[:]) - nodeMu.Unlock() - - return uuid, nil -} diff --git a/vendor/github.com/google/uuid/version4.go b/vendor/github.com/google/uuid/version4.go deleted file mode 100644 index 7697802e..00000000 --- a/vendor/github.com/google/uuid/version4.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package uuid - -import "io" - -// New creates a new random UUID or panics. New is equivalent to -// the expression -// -// uuid.Must(uuid.NewRandom()) -func New() UUID { - return Must(NewRandom()) -} - -// NewString creates a new random UUID and returns it as a string or panics. -// NewString is equivalent to the expression -// -// uuid.New().String() -func NewString() string { - return Must(NewRandom()).String() -} - -// NewRandom returns a Random (Version 4) UUID. -// -// The strength of the UUIDs is based on the strength of the crypto/rand -// package. -// -// Uses the randomness pool if it was enabled with EnableRandPool. -// -// A note about uniqueness derived from the UUID Wikipedia entry: -// -// Randomly generated UUIDs have 122 random bits. One's annual risk of being -// hit by a meteorite is estimated to be one chance in 17 billion, that -// means the probability is about 0.00000000006 (6 × 10−11), -// equivalent to the odds of creating a few tens of trillions of UUIDs in a -// year and having one duplicate. -func NewRandom() (UUID, error) { - if !poolEnabled { - return NewRandomFromReader(rander) - } - return newRandomFromPool() -} - -// NewRandomFromReader returns a UUID based on bytes read from a given io.Reader. -func NewRandomFromReader(r io.Reader) (UUID, error) { - var uuid UUID - _, err := io.ReadFull(r, uuid[:]) - if err != nil { - return Nil, err - } - uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 - uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 - return uuid, nil -} - -func newRandomFromPool() (UUID, error) { - var uuid UUID - poolMu.Lock() - if poolPos == randPoolSize { - _, err := io.ReadFull(rander, pool[:]) - if err != nil { - poolMu.Unlock() - return Nil, err - } - poolPos = 0 - } - copy(uuid[:], pool[poolPos:(poolPos+16)]) - poolPos += 16 - poolMu.Unlock() - - uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 - uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 - return uuid, nil -} diff --git a/vendor/github.com/segmentio/analytics-go/v3/.gitignore b/vendor/github.com/segmentio/analytics-go/v3/.gitignore deleted file mode 100644 index 942678bd..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/.gitignore +++ /dev/null @@ -1,32 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.prof - -# Emacs -*~ -\#* -.\#* - -# Artifacts -tmp/* diff --git a/vendor/github.com/segmentio/analytics-go/v3/.gitmodules b/vendor/github.com/segmentio/analytics-go/v3/.gitmodules deleted file mode 100644 index b2150b34..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/.gitmodules +++ /dev/null @@ -1,6 +0,0 @@ -[submodule "vendor/github.com/segmentio/backo-go"] - path = vendor/github.com/segmentio/backo-go - url = https://github.com/segmentio/backo-go -[submodule "vendor/github.com/xtgo/uuid"] - path = vendor/github.com/xtgo/uuid - url = https://github.com/xtgo/uuid diff --git a/vendor/github.com/segmentio/analytics-go/v3/History.md b/vendor/github.com/segmentio/analytics-go/v3/History.md deleted file mode 100644 index 4d867491..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/History.md +++ /dev/null @@ -1,93 +0,0 @@ -v3.3.0 / 2023-10-31 -=================== - -* Add groupId to context so the track events can related to both a user and distinct id and group - * Note: When updating to this version, verify the groupId is not being found using the Extra map. The new groupId field will now take precedence. - -v3.1.0 / 2019-09-20 -=================== - - * add consistent panic error message - * Expose the Message interface Validate method - * return error if a custom type is enqueued - * Handle pointer types in Enqueue() - * message: update maxMessageBytes to 32KB - -v3.0.1 / 2018-10-02 -=================== - -* Migrate from Circle V1 format to Circle V2 -* Adds CLI for sending segment events -* Vendor packages back-go and uuid instead of using gitsubmodules - - -v3.0.0 / 2016-06-02 -=================== - - * 3.0 is a significant rewrite with multiple breaking changes. - * [Quickstart](https://segment.com/docs/sources/server/go/quickstart/). - * [Documentation](https://segment.com/docs/sources/server/go/). - * [GoDocs](https://godoc.org/gopkg.in/segmentio/analytics-go.v3). - * [What's New in v3](https://segment.com/docs/sources/server/go/#what-s-new-in-v3). - - -v2.1.0 / 2015-12-28 -=================== - - * Add ability to set custom timestamps for messages. - * Add ability to set a custom `net/http` client. - * Add ability to set a custom logger. - * Fix edge case when client would try to upload no messages. - * Properly upload in-flight messages when client is asked to shutdown. - * Add ability to set `.integrations` field on messages. - * Fix resource leak with interval ticker after shutdown. - * Add retries and back-off when uploading messages. - * Add ability to set custom flush interval. - -v2.0.0 / 2015-02-03 -=================== - - * rewrite with breaking API changes - -v1.2.0 / 2014-09-03 -================== - - * add public .Flush() method - * rename .Stop() to .Close() - -v1.1.0 / 2014-09-02 -================== - - * add client.Stop() to flash/wait. Closes #7 - -v1.0.0 / 2014-08-26 -================== - - * fix response close - * change comments to be more go-like - * change uuid libraries - -0.1.2 / 2014-06-11 -================== - - * add runnable example - * fix: close body - -0.1.1 / 2014-05-31 -================== - - * refactor locking - -0.1.0 / 2014-05-22 -================== - - * replace Debug option with debug package - -0.0.2 / 2014-05-20 -================== - - * add .Start() - * add mutexes - * rename BufferSize to FlushAt and FlushInterval to FlushAfter - * lower FlushInterval to 5 seconds - * lower BufferSize to 20 to match other clients diff --git a/vendor/github.com/segmentio/analytics-go/v3/License.md b/vendor/github.com/segmentio/analytics-go/v3/License.md deleted file mode 100644 index f452c5d0..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/License.md +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 Segment, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/segmentio/analytics-go/v3/Makefile b/vendor/github.com/segmentio/analytics-go/v3/Makefile deleted file mode 100644 index 9de0d1ba..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -ifndef CIRCLE_ARTIFACTS -CIRCLE_ARTIFACTS=tmp -endif - -bootstrap: - .buildscript/bootstrap.sh - -dependencies: - @go get -v -t ./... - -vet: - @go vet ./... - -test: vet - @mkdir -p ${CIRCLE_ARTIFACTS} - @go test -race -coverprofile=${CIRCLE_ARTIFACTS}/cover.out . - @go tool cover -func ${CIRCLE_ARTIFACTS}/cover.out -o ${CIRCLE_ARTIFACTS}/cover.txt - @go tool cover -html ${CIRCLE_ARTIFACTS}/cover.out -o ${CIRCLE_ARTIFACTS}/cover.html - -build: test - @go build ./... - -e2e: - @if [ "$(RUN_E2E_TESTS)" != "true" ]; then \ - echo "Skipping end to end tests."; else \ - go get github.com/segmentio/library-e2e-tester/cmd/tester; \ - tester -segment-write-key=$(SEGMENT_WRITE_KEY) -webhook-auth-username=$(WEBHOOK_AUTH_USERNAME) -webhook-bucket=$(WEBHOOK_BUCKET) -path='cli' -concurrency=2 -skip='advance|alias'; fi - -ci: dependencies test e2e - -.PHONY: bootstrap dependencies vet test e2e ci diff --git a/vendor/github.com/segmentio/analytics-go/v3/Readme.md b/vendor/github.com/segmentio/analytics-go/v3/Readme.md deleted file mode 100644 index 5d3d822d..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/Readme.md +++ /dev/null @@ -1,55 +0,0 @@ -# analytics-go [![Circle CI](https://circleci.com/gh/segmentio/analytics-go/tree/v3.0.svg?style=shield)](https://circleci.com/gh/segmentio/analytics-go/tree/v3.0) [![go-doc](https://godoc.org/github.com/segmentio/analytics-go?status.svg)](https://godoc.org/github.com/segmentio/analytics-go) - -Segment analytics client for Go. - -## Installation - -The package can be simply installed via go get, we recommend that you use a -package version management system like the Go vendor directory or a tool like -Godep to avoid issues related to API breaking changes introduced between major -versions of the library. - -To install it in the GOPATH: -``` -go get https://github.com/segmentio/analytics-go -``` - -## Documentation - -The links bellow should provide all the documentation needed to make the best -use of the library and the Segment API: - -- [Documentation](https://segment.com/docs/libraries/go/) -- [godoc](https://godoc.org/gopkg.in/segmentio/analytics-go.v3) -- [API](https://segment.com/docs/libraries/http/) -- [Specs](https://segment.com/docs/spec/) - -## Usage - -```go -package main - -import ( - "os" - - "github.com/segmentio/analytics-go" -) - -func main() { - // Instantiates a client to use send messages to the segment API. - client := analytics.New(os.Getenv("SEGMENT_WRITE_KEY")) - - // Enqueues a track event that will be sent asynchronously. - client.Enqueue(analytics.Track{ - UserId: "test-user", - Event: "test-snippet", - }) - - // Flushes any queued messages and closes the client. - client.Close() -} -``` - -## License - -The library is released under the [MIT license](License.md). diff --git a/vendor/github.com/segmentio/analytics-go/v3/alias.go b/vendor/github.com/segmentio/analytics-go/v3/alias.go deleted file mode 100644 index 8ba0f736..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/alias.go +++ /dev/null @@ -1,40 +0,0 @@ -package analytics - -import "time" - -var _ Message = (*Alias)(nil) - -// This type represents object sent in a alias call as described in -// https://segment.com/docs/libraries/http/#alias -type Alias struct { - // This field is exported for serialization purposes and shouldn't be set by - // the application, its value is always overwritten by the library. - Type string `json:"type,omitempty"` - - MessageId string `json:"messageId,omitempty"` - PreviousId string `json:"previousId"` - UserId string `json:"userId"` - Timestamp time.Time `json:"timestamp,omitempty"` - Context *Context `json:"context,omitempty"` - Integrations Integrations `json:"integrations,omitempty"` -} - -func (msg Alias) Validate() error { - if len(msg.UserId) == 0 { - return FieldError{ - Type: "analytics.Alias", - Name: "UserId", - Value: msg.UserId, - } - } - - if len(msg.PreviousId) == 0 { - return FieldError{ - Type: "analytics.Alias", - Name: "PreviousId", - Value: msg.PreviousId, - } - } - - return nil -} diff --git a/vendor/github.com/segmentio/analytics-go/v3/analytics.go b/vendor/github.com/segmentio/analytics-go/v3/analytics.go deleted file mode 100644 index fa134344..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/analytics.go +++ /dev/null @@ -1,431 +0,0 @@ -package analytics - -import ( - "fmt" - "io" - "io/ioutil" - "strconv" - "sync" - - "bytes" - "encoding/json" - "net/http" - "time" -) - -// Version of the client. -const Version = "3.0.0" - -// This interface is the main API exposed by the analytics package. -// Values that satsify this interface are returned by the client constructors -// provided by the package and provide a way to send messages via the HTTP API. -type Client interface { - io.Closer - - // Queues a message to be sent by the client when the conditions for a batch - // upload are met. - // This is the main method you'll be using, a typical flow would look like - // this: - // - // client := analytics.New(writeKey) - // ... - // client.Enqueue(analytics.Track{ ... }) - // ... - // client.Close() - // - // The method returns an error if the message queue not be queued, which - // happens if the client was already closed at the time the method was - // called or if the message was malformed. - Enqueue(Message) error -} - -type client struct { - Config - key string - - // This channel is where the `Enqueue` method writes messages so they can be - // picked up and pushed by the backend goroutine taking care of applying the - // batching rules. - msgs chan Message - - // These two channels are used to synchronize the client shutting down when - // `Close` is called. - // The first channel is closed to signal the backend goroutine that it has - // to stop, then the second one is closed by the backend goroutine to signal - // that it has finished flushing all queued messages. - quit chan struct{} - shutdown chan struct{} - - // This HTTP client is used to send requests to the backend, it uses the - // HTTP transport provided in the configuration. - http http.Client -} - -// Instantiate a new client that uses the write key passed as first argument to -// send messages to the backend. -// The client is created with the default configuration. -func New(writeKey string) Client { - // Here we can ignore the error because the default config is always valid. - c, _ := NewWithConfig(writeKey, Config{}) - return c -} - -// Instantiate a new client that uses the write key and configuration passed as -// arguments to send messages to the backend. -// The function will return an error if the configuration contained impossible -// values (like a negative flush interval for example). -// When the function returns an error the returned client will always be nil. -func NewWithConfig(writeKey string, config Config) (cli Client, err error) { - if err = config.validate(); err != nil { - return - } - - c := &client{ - Config: makeConfig(config), - key: writeKey, - msgs: make(chan Message, 100), - quit: make(chan struct{}), - shutdown: make(chan struct{}), - http: makeHttpClient(config.Transport), - } - - go c.loop() - - cli = c - return -} - -func makeHttpClient(transport http.RoundTripper) http.Client { - httpClient := http.Client{ - Transport: transport, - } - if supportsTimeout(transport) { - httpClient.Timeout = 10 * time.Second - } - return httpClient -} - -func dereferenceMessage(msg Message) Message { - switch m := msg.(type) { - case *Alias: - if m == nil { - return nil - } - return *m - case *Group: - if m == nil { - return nil - } - return *m - case *Identify: - if m == nil { - return nil - } - return *m - case *Page: - if m == nil { - return nil - } - return *m - case *Screen: - if m == nil { - return nil - } - return *m - case *Track: - if m == nil { - return nil - } - return *m - } - - return msg -} - -func (c *client) Enqueue(msg Message) (err error) { - msg = dereferenceMessage(msg) - if err = msg.Validate(); err != nil { - return - } - - var id = c.uid() - var ts = c.now() - - switch m := msg.(type) { - case Alias: - m.Type = "alias" - m.MessageId = makeMessageId(m.MessageId, id) - m.Timestamp = makeTimestamp(m.Timestamp, ts) - msg = m - - case Group: - m.Type = "group" - m.MessageId = makeMessageId(m.MessageId, id) - m.Timestamp = makeTimestamp(m.Timestamp, ts) - msg = m - - case Identify: - m.Type = "identify" - m.MessageId = makeMessageId(m.MessageId, id) - m.Timestamp = makeTimestamp(m.Timestamp, ts) - msg = m - - case Page: - m.Type = "page" - m.MessageId = makeMessageId(m.MessageId, id) - m.Timestamp = makeTimestamp(m.Timestamp, ts) - msg = m - - case Screen: - m.Type = "screen" - m.MessageId = makeMessageId(m.MessageId, id) - m.Timestamp = makeTimestamp(m.Timestamp, ts) - msg = m - - case Track: - m.Type = "track" - m.MessageId = makeMessageId(m.MessageId, id) - m.Timestamp = makeTimestamp(m.Timestamp, ts) - msg = m - - default: - err = fmt.Errorf("messages with custom types cannot be enqueued: %T", msg) - return - } - - defer func() { - // When the `msgs` channel is closed writing to it will trigger a panic. - // To avoid letting the panic propagate to the caller we recover from it - // and instead report that the client has been closed and shouldn't be - // used anymore. - if recover() != nil { - err = ErrClosed - } - }() - - c.msgs <- msg - return -} - -// Close and flush metrics. -func (c *client) Close() (err error) { - defer func() { - // Always recover, a panic could be raised if `c`.quit was closed which - // means the method was called more than once. - if recover() != nil { - err = ErrClosed - } - }() - close(c.quit) - <-c.shutdown - return -} - -// Asychronously send a batched requests. -func (c *client) sendAsync(msgs []message, wg *sync.WaitGroup, ex *executor) { - wg.Add(1) - - if !ex.do(func() { - defer wg.Done() - defer func() { - // In case a bug is introduced in the send function that triggers - // a panic, we don't want this to ever crash the application so we - // catch it here and log it instead. - if err := recover(); err != nil { - c.errorf("panic - %s", err) - } - }() - c.send(msgs) - }) { - wg.Done() - c.errorf("sending messages failed - %s", ErrTooManyRequests) - c.notifyFailure(msgs, ErrTooManyRequests) - } -} - -// Send batch request. -func (c *client) send(msgs []message) { - const attempts = 10 - - b, err := json.Marshal(batch{ - MessageId: c.uid(), - SentAt: c.now(), - Messages: msgs, - Context: c.DefaultContext, - }) - - if err != nil { - c.errorf("marshalling messages - %s", err) - c.notifyFailure(msgs, err) - return - } - - for i := 0; i != attempts; i++ { - if err = c.upload(b); err == nil { - c.notifySuccess(msgs) - return - } - - // Wait for either a retry timeout or the client to be closed. - select { - case <-time.After(c.RetryAfter(i)): - case <-c.quit: - c.errorf("%d messages dropped because they failed to be sent and the client was closed", len(msgs)) - c.notifyFailure(msgs, err) - return - } - } - - c.errorf("%d messages dropped because they failed to be sent after %d attempts", len(msgs), attempts) - c.notifyFailure(msgs, err) -} - -// Upload serialized batch message. -func (c *client) upload(b []byte) error { - url := c.Endpoint + "/v1/batch" - req, err := http.NewRequest("POST", url, bytes.NewReader(b)) - if err != nil { - c.errorf("creating request - %s", err) - return err - } - - req.Header.Add("User-Agent", "analytics-go (version: "+Version+")") - req.Header.Add("Content-Type", "application/json") - req.Header.Add("Content-Length", strconv.Itoa(len(b))) - req.SetBasicAuth(c.key, "") - - res, err := c.http.Do(req) - - if err != nil { - c.errorf("sending request - %s", err) - return err - } - - defer res.Body.Close() - return c.report(res) -} - -// Report on response body. -func (c *client) report(res *http.Response) (err error) { - var body []byte - - if res.StatusCode < 300 { - c.debugf("response %s", res.Status) - return - } - - if body, err = ioutil.ReadAll(res.Body); err != nil { - c.errorf("response %d %s - %s", res.StatusCode, res.Status, err) - return - } - - c.logf("response %d %s – %s", res.StatusCode, res.Status, string(body)) - return fmt.Errorf("%d %s", res.StatusCode, res.Status) -} - -// Batch loop. -func (c *client) loop() { - defer close(c.shutdown) - - wg := &sync.WaitGroup{} - defer wg.Wait() - - tick := time.NewTicker(c.Interval) - defer tick.Stop() - - ex := newExecutor(c.maxConcurrentRequests) - defer ex.close() - - mq := messageQueue{ - maxBatchSize: c.BatchSize, - maxBatchBytes: c.maxBatchBytes(), - } - - for { - select { - case msg := <-c.msgs: - c.push(&mq, msg, wg, ex) - - case <-tick.C: - c.flush(&mq, wg, ex) - - case <-c.quit: - c.debugf("exit requested – draining messages") - - // Drain the msg channel, we have to close it first so no more - // messages can be pushed and otherwise the loop would never end. - close(c.msgs) - for msg := range c.msgs { - c.push(&mq, msg, wg, ex) - } - - c.flush(&mq, wg, ex) - c.debugf("exit") - return - } - } -} - -func (c *client) push(q *messageQueue, m Message, wg *sync.WaitGroup, ex *executor) { - var msg message - var err error - - if msg, err = makeMessage(m, maxMessageBytes); err != nil { - c.errorf("%s - %v", err, m) - c.notifyFailure([]message{{m, nil}}, err) - return - } - - c.debugf("buffer (%d/%d) %v", len(q.pending), c.BatchSize, m) - - if msgs := q.push(msg); msgs != nil { - c.debugf("exceeded messages batch limit with batch of %d messages – flushing", len(msgs)) - c.sendAsync(msgs, wg, ex) - } -} - -func (c *client) flush(q *messageQueue, wg *sync.WaitGroup, ex *executor) { - if msgs := q.flush(); msgs != nil { - c.debugf("flushing %d messages", len(msgs)) - c.sendAsync(msgs, wg, ex) - } -} - -func (c *client) debugf(format string, args ...interface{}) { - if c.Verbose { - c.logf(format, args...) - } -} - -func (c *client) logf(format string, args ...interface{}) { - c.Logger.Logf(format, args...) -} - -func (c *client) errorf(format string, args ...interface{}) { - c.Logger.Errorf(format, args...) -} - -func (c *client) maxBatchBytes() int { - b, _ := json.Marshal(batch{ - MessageId: c.uid(), - SentAt: c.now(), - Context: c.DefaultContext, - }) - return maxBatchBytes - len(b) -} - -func (c *client) notifySuccess(msgs []message) { - if c.Callback != nil { - for _, m := range msgs { - c.Callback.Success(m.msg) - } - } -} - -func (c *client) notifyFailure(msgs []message, err error) { - if c.Callback != nil { - for _, m := range msgs { - c.Callback.Failure(m.msg, err) - } - } -} diff --git a/vendor/github.com/segmentio/analytics-go/v3/config.go b/vendor/github.com/segmentio/analytics-go/v3/config.go deleted file mode 100644 index 2672d86b..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/config.go +++ /dev/null @@ -1,173 +0,0 @@ -package analytics - -import ( - "net/http" - "time" - - "github.com/google/uuid" - "github.com/segmentio/backo-go" -) - -// Instances of this type carry the different configuration options that may -// be set when instantiating a client. -// -// Each field's zero-value is either meaningful or interpreted as using the -// default value defined by the library. -type Config struct { - - // The endpoint to which the client connect and send their messages, set to - // `DefaultEndpoint` by default. - Endpoint string - - // The flushing interval of the client. Messages will be sent when they've - // been queued up to the maximum batch size or when the flushing interval - // timer triggers. - Interval time.Duration - - // The HTTP transport used by the client, this allows an application to - // redefine how requests are being sent at the HTTP level (for example, - // to change the connection pooling policy). - // If none is specified the client uses `http.DefaultTransport`. - Transport http.RoundTripper - - // The logger used by the client to output info or error messages when that - // are generated by background operations. - // If none is specified the client uses a standard logger that outputs to - // `os.Stderr`. - Logger Logger - - // The callback object that will be used by the client to notify the - // application when messages sends to the backend API succeeded or failed. - Callback Callback - - // The maximum number of messages that will be sent in one API call. - // Messages will be sent when they've been queued up to the maximum batch - // size or when the flushing interval timer triggers. - // Note that the API will still enforce a 500KB limit on each HTTP request - // which is independent from the number of embedded messages. - BatchSize int - - // When set to true the client will send more frequent and detailed messages - // to its logger. - Verbose bool - - // The default context set on each message sent by the client. - DefaultContext *Context - - // The retry policy used by the client to resend requests that have failed. - // The function is called with how many times the operation has been retried - // and is expected to return how long the client should wait before trying - // again. - // If not set the client will fallback to use a default retry policy. - RetryAfter func(int) time.Duration - - // A function called by the client to generate unique message identifiers. - // The client uses a UUID generator if none is provided. - // This field is not exported and only exposed internally to let unit tests - // mock the id generation. - uid func() string - - // A function called by the client to get the current time, `time.Now` is - // used by default. - // This field is not exported and only exposed internally to let unit tests - // mock the current time. - now func() time.Time - - // The maximum number of goroutines that will be spawned by a client to send - // requests to the backend API. - // This field is not exported and only exposed internally to let unit tests - // mock the current time. - maxConcurrentRequests int -} - -// This constant sets the default endpoint to which client instances send -// messages if none was explictly set. -const DefaultEndpoint = "https://api.segment.io" - -// This constant sets the default flush interval used by client instances if -// none was explicitly set. -const DefaultInterval = 5 * time.Second - -// This constant sets the default batch size used by client instances if none -// was explicitly set. -const DefaultBatchSize = 250 - -// Verifies that fields that don't have zero-values are set to valid values, -// returns an error describing the problem if a field was invalid. -func (c *Config) validate() error { - if c.Interval < 0 { - return ConfigError{ - Reason: "negative time intervals are not supported", - Field: "Interval", - Value: c.Interval, - } - } - - if c.BatchSize < 0 { - return ConfigError{ - Reason: "negative batch sizes are not supported", - Field: "BatchSize", - Value: c.BatchSize, - } - } - - return nil -} - -// Given a config object as argument the function will set all zero-values to -// their defaults and return the modified object. -func makeConfig(c Config) Config { - if len(c.Endpoint) == 0 { - c.Endpoint = DefaultEndpoint - } - - if c.Interval == 0 { - c.Interval = DefaultInterval - } - - if c.Transport == nil { - c.Transport = http.DefaultTransport - } - - if c.Logger == nil { - c.Logger = newDefaultLogger() - } - - if c.BatchSize == 0 { - c.BatchSize = DefaultBatchSize - } - - if c.DefaultContext == nil { - c.DefaultContext = &Context{} - } - - if c.RetryAfter == nil { - c.RetryAfter = backo.DefaultBacko().Duration - } - - if c.uid == nil { - c.uid = uid - } - - if c.now == nil { - c.now = time.Now - } - - if c.maxConcurrentRequests == 0 { - c.maxConcurrentRequests = 1000 - } - - // We always overwrite the 'library' field of the default context set on the - // client because we want this information to be accurate. - c.DefaultContext.Library = LibraryInfo{ - Name: "analytics-go", - Version: Version, - } - return c -} - -// This function returns a string representation of a UUID, it's the default -// function used for generating unique IDs. -func uid() string { - return uuid.NewString() -} diff --git a/vendor/github.com/segmentio/analytics-go/v3/context.go b/vendor/github.com/segmentio/analytics-go/v3/context.go deleted file mode 100644 index 94926050..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/context.go +++ /dev/null @@ -1,150 +0,0 @@ -package analytics - -import ( - "encoding/json" - "net" - "reflect" -) - -// This type provides the representation of the `context` object as defined in -// https://segment.com/docs/spec/common/#context -type Context struct { - App AppInfo `json:"app,omitempty"` - Campaign CampaignInfo `json:"campaign,omitempty"` - Device DeviceInfo `json:"device,omitempty"` - Library LibraryInfo `json:"library,omitempty"` - Location LocationInfo `json:"location,omitempty"` - Network NetworkInfo `json:"network,omitempty"` - OS OSInfo `json:"os,omitempty"` - Page PageInfo `json:"page,omitempty"` - Referrer ReferrerInfo `json:"referrer,omitempty"` - Screen ScreenInfo `json:"screen,omitempty"` - IP net.IP `json:"ip,omitempty"` - Direct bool `json:"direct,omitempty"` - Locale string `json:"locale,omitempty"` - GroupID string `json:"groupId,omitempty"` - Timezone string `json:"timezone,omitempty"` - UserAgent string `json:"userAgent,omitempty"` - Traits Traits `json:"traits,omitempty"` - - // This map is used to allow extensions to the context specifications that - // may not be documented or could be introduced in the future. - // The fields of this map are inlined in the serialized context object, - // there is no actual "extra" field in the JSON representation. - Extra map[string]interface{} `json:"-"` -} - -// This type provides the representation of the `context.app` object as defined -// in https://segment.com/docs/spec/common/#context -type AppInfo struct { - Name string `json:"name,omitempty"` - Version string `json:"version,omitempty"` - Build string `json:"build,omitempty"` - Namespace string `json:"namespace,omitempty"` -} - -// This type provides the representation of the `context.campaign` object as -// defined in https://segment.com/docs/spec/common/#context -type CampaignInfo struct { - Name string `json:"name,omitempty"` - Source string `json:"source,omitempty"` - Medium string `json:"medium,omitempty"` - Term string `json:"term,omitempty"` - Content string `json:"content,omitempty"` -} - -// This type provides the representation of the `context.device` object as -// defined in https://segment.com/docs/spec/common/#context -type DeviceInfo struct { - Id string `json:"id,omitempty"` - Manufacturer string `json:"manufacturer,omitempty"` - Model string `json:"model,omitempty"` - Name string `json:"name,omitempty"` - Type string `json:"type,omitempty"` - Version string `json:"version,omitempty"` - AdvertisingID string `json:"advertisingId,omitempty"` -} - -// This type provides the representation of the `context.library` object as -// defined in https://segment.com/docs/spec/common/#context -type LibraryInfo struct { - Name string `json:"name,omitempty"` - Version string `json:"version,omitempty"` -} - -// This type provides the representation of the `context.location` object as -// defined in https://segment.com/docs/spec/common/#context -type LocationInfo struct { - City string `json:"city,omitempty"` - Country string `json:"country,omitempty"` - Region string `json:"region,omitempty"` - Latitude float64 `json:"latitude,omitempty"` - Longitude float64 `json:"longitude,omitempty"` - Speed float64 `json:"speed,omitempty"` -} - -// This type provides the representation of the `context.network` object as -// defined in https://segment.com/docs/spec/common/#context -type NetworkInfo struct { - Bluetooth bool `json:"bluetooth,omitempty"` - Cellular bool `json:"cellular,omitempty"` - WIFI bool `json:"wifi,omitempty"` - Carrier string `json:"carrier,omitempty"` -} - -// This type provides the representation of the `context.os` object as defined -// in https://segment.com/docs/spec/common/#context -type OSInfo struct { - Name string `json:"name,omitempty"` - Version string `json:"version,omitempty"` -} - -// This type provides the representation of the `context.page` object as -// defined in https://segment.com/docs/spec/common/#context -type PageInfo struct { - Hash string `json:"hash,omitempty"` - Path string `json:"path,omitempty"` - Referrer string `json:"referrer,omitempty"` - Search string `json:"search,omitempty"` - Title string `json:"title,omitempty"` - URL string `json:"url,omitempty"` -} - -// This type provides the representation of the `context.referrer` object as -// defined in https://segment.com/docs/spec/common/#context -type ReferrerInfo struct { - Type string `json:"type,omitempty"` - Name string `json:"name,omitempty"` - URL string `json:"url,omitempty"` - Link string `json:"link,omitempty"` -} - -// This type provides the representation of the `context.screen` object as -// defined in https://segment.com/docs/spec/common/#context -type ScreenInfo struct { - Density int `json:"density,omitempty"` - Width int `json:"width,omitempty"` - Height int `json:"height,omitempty"` -} - -// Satisfy the `json.Marshaler` interface. We have to flatten out the `Extra` -// field but the standard json package doesn't support it yet. -// Implementing this interface allows us to override the default marshaling of -// the context object and to the inlining ourselves. -// -// Related discussion: https://github.com/golang/go/issues/6213 -func (ctx Context) MarshalJSON() ([]byte, error) { - v := reflect.ValueOf(ctx) - n := v.NumField() - m := make(map[string]interface{}, n+len(ctx.Extra)) - - // Copy the `Extra` map into the map representation of the context, it is - // important to do this operation before going through the actual struct - // fields so the latter take precendence and override duplicated values - // that would be set in the extensions. - for name, value := range ctx.Extra { - m[name] = value - } - - return json.Marshal(structToMap(v, m)) -} diff --git a/vendor/github.com/segmentio/analytics-go/v3/error.go b/vendor/github.com/segmentio/analytics-go/v3/error.go deleted file mode 100644 index d5503864..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/error.go +++ /dev/null @@ -1,60 +0,0 @@ -package analytics - -import ( - "errors" - "fmt" -) - -// Returned by the `NewWithConfig` function when the one of the configuration -// fields was set to an impossible value (like a negative duration). -type ConfigError struct { - - // A human-readable message explaining why the configuration field's value - // is invalid. - Reason string - - // The name of the configuration field that was carrying an invalid value. - Field string - - // The value of the configuration field that caused the error. - Value interface{} -} - -func (e ConfigError) Error() string { - return fmt.Sprintf("analytics.NewWithConfig: %s (analytics.Config.%s: %#v)", e.Reason, e.Field, e.Value) -} - -// Instances of this type are used to represent errors returned when a field was -// no initialize properly in a structure passed as argument to one of the -// functions of this package. -type FieldError struct { - - // The human-readable representation of the type of structure that wasn't - // initialized properly. - Type string - - // The name of the field that wasn't properly initialized. - Name string - - // The value of the field that wasn't properly initialized. - Value interface{} -} - -func (e FieldError) Error() string { - return fmt.Sprintf("%s.%s: invalid field value: %#v", e.Type, e.Name, e.Value) -} - -var ( - // This error is returned by methods of the `Client` interface when they are - // called after the client was already closed. - ErrClosed = errors.New("the client was already closed") - - // This error is used to notify the application that too many requests are - // already being sent and no more messages can be accepted. - ErrTooManyRequests = errors.New("too many requests are already in-flight") - - // This error is used to notify the client callbacks that a message send - // failed because the JSON representation of a message exceeded the upper - // limit. - ErrMessageTooBig = errors.New("the message exceeds the maximum allowed size") -) diff --git a/vendor/github.com/segmentio/analytics-go/v3/executor.go b/vendor/github.com/segmentio/analytics-go/v3/executor.go deleted file mode 100644 index 405ee98e..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/executor.go +++ /dev/null @@ -1,53 +0,0 @@ -package analytics - -import "sync" - -type executor struct { - queue chan func() - mutex sync.Mutex - size int - cap int -} - -func newExecutor(cap int) *executor { - e := &executor{ - queue: make(chan func(), 1), - cap: cap, - } - go e.loop() - return e -} - -func (e *executor) do(task func()) (ok bool) { - e.mutex.Lock() - - if e.size != e.cap { - e.queue <- task - e.size++ - ok = true - } - - e.mutex.Unlock() - return -} - -func (e *executor) close() { - close(e.queue) -} - -func (e *executor) loop() { - for task := range e.queue { - go e.run(task) - } -} - -func (e *executor) run(task func()) { - defer e.done() - task() -} - -func (e *executor) done() { - e.mutex.Lock() - e.size-- - e.mutex.Unlock() -} diff --git a/vendor/github.com/segmentio/analytics-go/v3/group.go b/vendor/github.com/segmentio/analytics-go/v3/group.go deleted file mode 100644 index 352c8e44..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/group.go +++ /dev/null @@ -1,42 +0,0 @@ -package analytics - -import "time" - -var _ Message = (*Group)(nil) - -// This type represents object sent in a group call as described in -// https://segment.com/docs/libraries/http/#group -type Group struct { - // This field is exported for serialization purposes and shouldn't be set by - // the application, its value is always overwritten by the library. - Type string `json:"type,omitempty"` - - MessageId string `json:"messageId,omitempty"` - AnonymousId string `json:"anonymousId,omitempty"` - UserId string `json:"userId,omitempty"` - GroupId string `json:"groupId"` - Timestamp time.Time `json:"timestamp,omitempty"` - Context *Context `json:"context,omitempty"` - Traits Traits `json:"traits,omitempty"` - Integrations Integrations `json:"integrations,omitempty"` -} - -func (msg Group) Validate() error { - if len(msg.GroupId) == 0 { - return FieldError{ - Type: "analytics.Group", - Name: "GroupId", - Value: msg.GroupId, - } - } - - if len(msg.UserId) == 0 && len(msg.AnonymousId) == 0 { - return FieldError{ - Type: "analytics.Group", - Name: "UserId", - Value: msg.UserId, - } - } - - return nil -} diff --git a/vendor/github.com/segmentio/analytics-go/v3/identify.go b/vendor/github.com/segmentio/analytics-go/v3/identify.go deleted file mode 100644 index c3e594f7..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/identify.go +++ /dev/null @@ -1,33 +0,0 @@ -package analytics - -import "time" - -var _ Message = (*Identify)(nil) - -// This type represents object sent in an identify call as described in -// https://segment.com/docs/libraries/http/#identify -type Identify struct { - // This field is exported for serialization purposes and shouldn't be set by - // the application, its value is always overwritten by the library. - Type string `json:"type,omitempty"` - - MessageId string `json:"messageId,omitempty"` - AnonymousId string `json:"anonymousId,omitempty"` - UserId string `json:"userId,omitempty"` - Timestamp time.Time `json:"timestamp,omitempty"` - Context *Context `json:"context,omitempty"` - Traits Traits `json:"traits,omitempty"` - Integrations Integrations `json:"integrations,omitempty"` -} - -func (msg Identify) Validate() error { - if len(msg.UserId) == 0 && len(msg.AnonymousId) == 0 { - return FieldError{ - Type: "analytics.Identify", - Name: "UserId", - Value: msg.UserId, - } - } - - return nil -} diff --git a/vendor/github.com/segmentio/analytics-go/v3/integrations.go b/vendor/github.com/segmentio/analytics-go/v3/integrations.go deleted file mode 100644 index 407b4912..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/integrations.go +++ /dev/null @@ -1,44 +0,0 @@ -package analytics - -// This type is used to represent integrations in messages that support it. -// It is a free-form where values are most often booleans that enable or -// disable integrations. -// Here's a quick example of how this type is meant to be used: -// -// analytics.Track{ -// UserId: "0123456789", -// Integrations: analytics.NewIntegrations() -// .EnableAll() -// .Disable("Salesforce") -// .Disable("Marketo"), -// } -// -// The specifications can be found at https://segment.com/docs/spec/common/#integrations -type Integrations map[string]interface{} - -func NewIntegrations() Integrations { - return make(Integrations, 10) -} - -func (i Integrations) EnableAll() Integrations { - return i.Enable("all") -} - -func (i Integrations) DisableAll() Integrations { - return i.Disable("all") -} - -func (i Integrations) Enable(name string) Integrations { - return i.Set(name, true) -} - -func (i Integrations) Disable(name string) Integrations { - return i.Set(name, false) -} - -// Sets an integration named by the first argument to the specified value, any -// value other than `false` will be interpreted as enabling the integration. -func (i Integrations) Set(name string, value interface{}) Integrations { - i[name] = value - return i -} diff --git a/vendor/github.com/segmentio/analytics-go/v3/json.go b/vendor/github.com/segmentio/analytics-go/v3/json.go deleted file mode 100644 index cd3b1752..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/json.go +++ /dev/null @@ -1,87 +0,0 @@ -package analytics - -import ( - "reflect" - "strings" -) - -// Imitate what what the JSON package would do when serializing a struct value, -// the only difference is we we don't serialize zero-value struct fields as well. -// Note that this function doesn't recursively convert structures to maps, only -// the value passed as argument is transformed. -func structToMap(v reflect.Value, m map[string]interface{}) map[string]interface{} { - t := v.Type() - n := t.NumField() - - if m == nil { - m = make(map[string]interface{}, n) - } - - for i := 0; i != n; i++ { - field := t.Field(i) - value := v.Field(i) - name, omitempty := parseJsonTag(field.Tag.Get("json"), field.Name) - - if name != "-" && !(omitempty && isZeroValue(value)) { - m[name] = value.Interface() - } - } - - return m -} - -// Parses a JSON tag the way the json package would do it, returing the expected -// name of the field once serialized and if empty values should be omitted. -func parseJsonTag(tag string, defName string) (name string, omitempty bool) { - args := strings.Split(tag, ",") - - if len(args) == 0 || len(args[0]) == 0 { - name = defName - } else { - name = args[0] - } - - if len(args) > 1 && args[1] == "omitempty" { - omitempty = true - } - - return -} - -// Checks if the value given as argument is a zero-value, it is based on the -// isEmptyValue function in https://golang.org/src/encoding/json/encode.go -// but also checks struct types recursively. -func isZeroValue(v reflect.Value) bool { - switch v.Kind() { - case reflect.Array, reflect.Map, reflect.Slice, reflect.String: - return v.Len() == 0 - - case reflect.Bool: - return !v.Bool() - - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return v.Int() == 0 - - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return v.Uint() == 0 - - case reflect.Float32, reflect.Float64: - return v.Float() == 0 - - case reflect.Interface, reflect.Ptr: - return v.IsNil() - - case reflect.Struct: - for i, n := 0, v.NumField(); i != n; i++ { - if !isZeroValue(v.Field(i)) { - return false - } - } - return true - - case reflect.Invalid: - return true - } - - return false -} diff --git a/vendor/github.com/segmentio/analytics-go/v3/logger.go b/vendor/github.com/segmentio/analytics-go/v3/logger.go deleted file mode 100644 index 54190c5f..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/logger.go +++ /dev/null @@ -1,47 +0,0 @@ -package analytics - -import ( - "log" - "os" -) - -// Instances of types implementing this interface can be used to define where -// the analytics client logs are written. -type Logger interface { - - // Analytics clients call this method to log regular messages about the - // operations they perform. - // Messages logged by this method are usually tagged with an `INFO` log - // level in common logging libraries. - Logf(format string, args ...interface{}) - - // Analytics clients call this method to log errors they encounter while - // sending events to the backend servers. - // Messages logged by this method are usually tagged with an `ERROR` log - // level in common logging libraries. - Errorf(format string, args ...interface{}) -} - -// This function instantiate an object that statisfies the analytics.Logger -// interface and send logs to standard logger passed as argument. -func StdLogger(logger *log.Logger) Logger { - return stdLogger{ - logger: logger, - } -} - -type stdLogger struct { - logger *log.Logger -} - -func (l stdLogger) Logf(format string, args ...interface{}) { - l.logger.Printf("INFO: "+format, args...) -} - -func (l stdLogger) Errorf(format string, args ...interface{}) { - l.logger.Printf("ERROR: "+format, args...) -} - -func newDefaultLogger() Logger { - return StdLogger(log.New(os.Stderr, "segment ", log.LstdFlags)) -} diff --git a/vendor/github.com/segmentio/analytics-go/v3/message.go b/vendor/github.com/segmentio/analytics-go/v3/message.go deleted file mode 100644 index d46d246e..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/message.go +++ /dev/null @@ -1,128 +0,0 @@ -package analytics - -import ( - "encoding/json" - "time" -) - -// Values implementing this interface are used by analytics clients to notify -// the application when a message send succeeded or failed. -// -// Callback methods are called by a client's internal goroutines, there are no -// guarantees on which goroutine will trigger the callbacks, the calls can be -// made sequentially or in parallel, the order doesn't depend on the order of -// messages were queued to the client. -// -// Callback methods must return quickly and not cause long blocking operations -// to avoid interferring with the client's internal work flow. -type Callback interface { - - // This method is called for every message that was successfully sent to - // the API. - Success(Message) - - // This method is called for every message that failed to be sent to the - // API and will be discarded by the client. - Failure(Message, error) -} - -// This interface is used to represent analytics objects that can be sent via -// a client. -// -// Types like analytics.Track, analytics.Page, etc... implement this interface -// and therefore can be passed to the analytics.Client.Send method. -type Message interface { - - // Validate validates the internal structure of the message, the method must return - // nil if the message is valid, or an error describing what went wrong. - Validate() error -} - -// Takes a message id as first argument and returns it, unless it's the zero- -// value, in that case the default id passed as second argument is returned. -func makeMessageId(id string, def string) string { - if len(id) == 0 { - return def - } - return id -} - -// Returns the time value passed as first argument, unless it's the zero-value, -// in that case the default value passed as second argument is returned. -func makeTimestamp(t time.Time, def time.Time) time.Time { - if t == (time.Time{}) { - return def - } - return t -} - -// This structure represents objects sent to the /v1/batch endpoint. We don't -// export this type because it's only meant to be used internally to send groups -// of messages in one API call. -type batch struct { - MessageId string `json:"messageId"` - SentAt time.Time `json:"sentAt"` - Messages []message `json:"batch"` - Context *Context `json:"context"` -} - -type message struct { - msg Message - json []byte -} - -func makeMessage(m Message, maxBytes int) (msg message, err error) { - if msg.json, err = json.Marshal(m); err == nil { - if len(msg.json) > maxBytes { - err = ErrMessageTooBig - } else { - msg.msg = m - } - } - return -} - -func (m message) MarshalJSON() ([]byte, error) { - return m.json, nil -} - -func (m message) size() int { - // The `+ 1` is for the comma that sits between each items of a JSON array. - return len(m.json) + 1 -} - -type messageQueue struct { - pending []message - bytes int - maxBatchSize int - maxBatchBytes int -} - -func (q *messageQueue) push(m message) (b []message) { - if (q.bytes + m.size()) > q.maxBatchBytes { - b = q.flush() - } - - if q.pending == nil { - q.pending = make([]message, 0, q.maxBatchSize) - } - - q.pending = append(q.pending, m) - q.bytes += len(m.json) - - if b == nil && len(q.pending) == q.maxBatchSize { - b = q.flush() - } - - return -} - -func (q *messageQueue) flush() (msgs []message) { - msgs, q.pending, q.bytes = q.pending, nil, 0 - return -} - -const ( - maxBatchBytes = 500000 - maxMessageBytes = 32000 -) diff --git a/vendor/github.com/segmentio/analytics-go/v3/page.go b/vendor/github.com/segmentio/analytics-go/v3/page.go deleted file mode 100644 index 11b278c5..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/page.go +++ /dev/null @@ -1,34 +0,0 @@ -package analytics - -import "time" - -var _ Message = (*Page)(nil) - -// This type represents object sent in a page call as described in -// https://segment.com/docs/libraries/http/#page -type Page struct { - // This field is exported for serialization purposes and shouldn't be set by - // the application, its value is always overwritten by the library. - Type string `json:"type,omitempty"` - - MessageId string `json:"messageId,omitempty"` - AnonymousId string `json:"anonymousId,omitempty"` - UserId string `json:"userId,omitempty"` - Name string `json:"name,omitempty"` - Timestamp time.Time `json:"timestamp,omitempty"` - Context *Context `json:"context,omitempty"` - Properties Properties `json:"properties,omitempty"` - Integrations Integrations `json:"integrations,omitempty"` -} - -func (msg Page) Validate() error { - if len(msg.UserId) == 0 && len(msg.AnonymousId) == 0 { - return FieldError{ - Type: "analytics.Page", - Name: "UserId", - Value: msg.UserId, - } - } - - return nil -} diff --git a/vendor/github.com/segmentio/analytics-go/v3/properties.go b/vendor/github.com/segmentio/analytics-go/v3/properties.go deleted file mode 100644 index 8b218aeb..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/properties.go +++ /dev/null @@ -1,117 +0,0 @@ -package analytics - -// This type is used to represent properties in messages that support it. -// It is a free-form object so the application can set any value it sees fit but -// a few helper method are defined to make it easier to instantiate properties with -// common fields. -// Here's a quick example of how this type is meant to be used: -// -// analytics.Page{ -// UserId: "0123456789", -// Properties: analytics.NewProperties() -// .SetRevenue(10.0) -// .SetCurrency("USD"), -// } -// -type Properties map[string]interface{} - -func NewProperties() Properties { - return make(Properties, 10) -} - -func (p Properties) SetRevenue(revenue float64) Properties { - return p.Set("revenue", revenue) -} - -func (p Properties) SetCurrency(currency string) Properties { - return p.Set("currency", currency) -} - -func (p Properties) SetValue(value float64) Properties { - return p.Set("value", value) -} - -func (p Properties) SetPath(path string) Properties { - return p.Set("path", path) -} - -func (p Properties) SetReferrer(referrer string) Properties { - return p.Set("referrer", referrer) -} - -func (p Properties) SetTitle(title string) Properties { - return p.Set("title", title) -} - -func (p Properties) SetURL(url string) Properties { - return p.Set("url", url) -} - -func (p Properties) SetName(name string) Properties { - return p.Set("name", name) -} - -func (p Properties) SetCategory(category string) Properties { - return p.Set("category", category) -} - -func (p Properties) SetSKU(sku string) Properties { - return p.Set("sku", sku) -} - -func (p Properties) SetPrice(price float64) Properties { - return p.Set("price", price) -} - -func (p Properties) SetProductId(id string) Properties { - return p.Set("id", id) -} - -func (p Properties) SetOrderId(id string) Properties { - return p.Set("orderId", id) -} - -func (p Properties) SetTotal(total float64) Properties { - return p.Set("total", total) -} - -func (p Properties) SetSubtotal(subtotal float64) Properties { - return p.Set("subtotal", subtotal) -} - -func (p Properties) SetShipping(shipping float64) Properties { - return p.Set("shipping", shipping) -} - -func (p Properties) SetTax(tax float64) Properties { - return p.Set("tax", tax) -} - -func (p Properties) SetDiscount(discount float64) Properties { - return p.Set("discount", discount) -} - -func (p Properties) SetCoupon(coupon string) Properties { - return p.Set("coupon", coupon) -} - -func (p Properties) SetProducts(products ...Product) Properties { - return p.Set("products", products) -} - -func (p Properties) SetRepeat(repeat bool) Properties { - return p.Set("repeat", repeat) -} - -func (p Properties) Set(name string, value interface{}) Properties { - p[name] = value - return p -} - -// This type represents products in the E-commerce API. -type Product struct { - ID string `json:"id,omitempty"` - SKU string `json:"sky,omitempty"` - Name string `json:"name,omitempty"` - Price float64 `json:"price"` -} diff --git a/vendor/github.com/segmentio/analytics-go/v3/screen.go b/vendor/github.com/segmentio/analytics-go/v3/screen.go deleted file mode 100644 index 2ee18e2a..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/screen.go +++ /dev/null @@ -1,34 +0,0 @@ -package analytics - -import "time" - -var _ Message = (*Screen)(nil) - -// This type represents object sent in a screen call as described in -// https://segment.com/docs/libraries/http/#screen -type Screen struct { - // This field is exported for serialization purposes and shouldn't be set by - // the application, its value is always overwritten by the library. - Type string `json:"type,omitempty"` - - MessageId string `json:"messageId,omitempty"` - AnonymousId string `json:"anonymousId,omitempty"` - UserId string `json:"userId,omitempty"` - Name string `json:"name,omitempty"` - Timestamp time.Time `json:"timestamp,omitempty"` - Context *Context `json:"context,omitempty"` - Properties Properties `json:"properties,omitempty"` - Integrations Integrations `json:"integrations,omitempty"` -} - -func (msg Screen) Validate() error { - if len(msg.UserId) == 0 && len(msg.AnonymousId) == 0 { - return FieldError{ - Type: "analytics.Screen", - Name: "UserId", - Value: msg.UserId, - } - } - - return nil -} diff --git a/vendor/github.com/segmentio/analytics-go/v3/timeout_15.go b/vendor/github.com/segmentio/analytics-go/v3/timeout_15.go deleted file mode 100644 index 12b963ed..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/timeout_15.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build !go1.6 - -package analytics - -import "net/http" - -// http clients on versions of go before 1.6 only support timeout if the -// transport implements the `CancelRequest` method. -func supportsTimeout(transport http.RoundTripper) bool { - _, ok := transport.(requestCanceler) - return ok -} - -type requestCanceler interface { - CancelRequest(*http.Request) -} diff --git a/vendor/github.com/segmentio/analytics-go/v3/timeout_16.go b/vendor/github.com/segmentio/analytics-go/v3/timeout_16.go deleted file mode 100644 index 1115cafb..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/timeout_16.go +++ /dev/null @@ -1,10 +0,0 @@ -// +build go1.6 - -package analytics - -import "net/http" - -// http clients on versions of go after 1.6 always support timeout. -func supportsTimeout(transport http.RoundTripper) bool { - return true -} diff --git a/vendor/github.com/segmentio/analytics-go/v3/track.go b/vendor/github.com/segmentio/analytics-go/v3/track.go deleted file mode 100644 index a803c8eb..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/track.go +++ /dev/null @@ -1,42 +0,0 @@ -package analytics - -import "time" - -var _ Message = (*Track)(nil) - -// This type represents object sent in a track call as described in -// https://segment.com/docs/libraries/http/#track -type Track struct { - // This field is exported for serialization purposes and shouldn't be set by - // the application, its value is always overwritten by the library. - Type string `json:"type,omitempty"` - - MessageId string `json:"messageId,omitempty"` - AnonymousId string `json:"anonymousId,omitempty"` - UserId string `json:"userId,omitempty"` - Event string `json:"event"` - Timestamp time.Time `json:"timestamp,omitempty"` - Context *Context `json:"context,omitempty"` - Properties Properties `json:"properties,omitempty"` - Integrations Integrations `json:"integrations,omitempty"` -} - -func (msg Track) Validate() error { - if len(msg.Event) == 0 { - return FieldError{ - Type: "analytics.Track", - Name: "Event", - Value: msg.Event, - } - } - - if len(msg.UserId) == 0 && len(msg.AnonymousId) == 0 { - return FieldError{ - Type: "analytics.Track", - Name: "UserId", - Value: msg.UserId, - } - } - - return nil -} diff --git a/vendor/github.com/segmentio/analytics-go/v3/traits.go b/vendor/github.com/segmentio/analytics-go/v3/traits.go deleted file mode 100644 index d4e82f07..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/traits.go +++ /dev/null @@ -1,89 +0,0 @@ -package analytics - -import "time" - -// This type is used to represent traits in messages that support it. -// It is a free-form object so the application can set any value it sees fit but -// a few helper method are defined to make it easier to instantiate traits with -// common fields. -// Here's a quick example of how this type is meant to be used: -// -// analytics.Identify{ -// UserId: "0123456789", -// Traits: analytics.NewTraits() -// .SetFirstName("Luke") -// .SetLastName("Skywalker") -// .Set("Role", "Jedi"), -// } -// -// The specifications can be found at https://segment.com/docs/spec/identify/#traits -type Traits map[string]interface{} - -func NewTraits() Traits { - return make(Traits, 10) -} - -func (t Traits) SetAddress(address string) Traits { - return t.Set("address", address) -} - -func (t Traits) SetAge(age int) Traits { - return t.Set("age", age) -} - -func (t Traits) SetAvatar(url string) Traits { - return t.Set("avatar", url) -} - -func (t Traits) SetBirthday(date time.Time) Traits { - return t.Set("birthday", date) -} - -func (t Traits) SetCreatedAt(date time.Time) Traits { - return t.Set("createdAt", date) -} - -func (t Traits) SetDescription(desc string) Traits { - return t.Set("description", desc) -} - -func (t Traits) SetEmail(email string) Traits { - return t.Set("email", email) -} - -func (t Traits) SetFirstName(firstName string) Traits { - return t.Set("firstName", firstName) -} - -func (t Traits) SetGender(gender string) Traits { - return t.Set("gender", gender) -} - -func (t Traits) SetLastName(lastName string) Traits { - return t.Set("lastName", lastName) -} - -func (t Traits) SetName(name string) Traits { - return t.Set("name", name) -} - -func (t Traits) SetPhone(phone string) Traits { - return t.Set("phone", phone) -} - -func (t Traits) SetTitle(title string) Traits { - return t.Set("title", title) -} - -func (t Traits) SetUsername(username string) Traits { - return t.Set("username", username) -} - -func (t Traits) SetWebsite(url string) Traits { - return t.Set("website", url) -} - -func (t Traits) Set(field string, value interface{}) Traits { - t[field] = value - return t -} diff --git a/vendor/github.com/segmentio/analytics-go/v3/validate.go b/vendor/github.com/segmentio/analytics-go/v3/validate.go deleted file mode 100644 index 442c1267..00000000 --- a/vendor/github.com/segmentio/analytics-go/v3/validate.go +++ /dev/null @@ -1,65 +0,0 @@ -package analytics - -type FieldGetter interface { - GetField(field string) (interface{}, bool) -} - -func getString(msg FieldGetter, field string) string { - if val, ok := msg.GetField(field); ok { - if str, ok := val.(string); ok { - return str - } - } - return "" -} - -func ValidateFields(msg FieldGetter) error { - typ, _ := msg.GetField("type") - if str, ok := typ.(string); ok { - switch str { - case "alias": - return Alias{ - Type: "alias", - UserId: getString(msg, "userId"), - PreviousId: getString(msg, "previousId"), - }.Validate() - case "group": - return Group{ - Type: "group", - UserId: getString(msg, "userId"), - AnonymousId: getString(msg, "anonymousId"), - GroupId: getString(msg, "groupId"), - }.Validate() - case "identify": - return Identify{ - Type: "identify", - UserId: getString(msg, "userId"), - AnonymousId: getString(msg, "anonymousId"), - }.Validate() - case "page": - return Page{ - Type: "page", - UserId: getString(msg, "userId"), - AnonymousId: getString(msg, "anonymousId"), - }.Validate() - case "screen": - return Screen{ - Type: "screen", - UserId: getString(msg, "userId"), - AnonymousId: getString(msg, "anonymousId"), - }.Validate() - case "track": - return Track{ - Type: "track", - UserId: getString(msg, "userId"), - AnonymousId: getString(msg, "anonymousId"), - Event: getString(msg, "event"), - }.Validate() - } - } - return FieldError{ - Type: "analytics.Event", - Name: "Type", - Value: typ, - } -} diff --git a/vendor/github.com/segmentio/backo-go/.gitmodules b/vendor/github.com/segmentio/backo-go/.gitmodules deleted file mode 100644 index 36de9297..00000000 --- a/vendor/github.com/segmentio/backo-go/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "vendor/github.com/bmizerany/assert"] - path = vendor/github.com/bmizerany/assert - url = https://github.com/bmizerany/assert diff --git a/vendor/github.com/segmentio/backo-go/README.md b/vendor/github.com/segmentio/backo-go/README.md deleted file mode 100644 index 1362becf..00000000 --- a/vendor/github.com/segmentio/backo-go/README.md +++ /dev/null @@ -1,80 +0,0 @@ -Backo [![GoDoc](http://godoc.org/github.com/segmentio/backo-go?status.png)](http://godoc.org/github.com/segmentio/backo-go) ------ - -Exponential backoff for Go (Go port of segmentio/backo). - - -Usage ------ - -```go -import "github.com/segmentio/backo-go" - -// Create a Backo instance. -backo := backo.NewBacko(milliseconds(100), 2, 1, milliseconds(10*1000)) -// OR with defaults. -backo := backo.DefaultBacko() - -// Use the ticker API. -ticker := b.NewTicker() -for { - timeout := time.After(5 * time.Minute) - select { - case <-ticker.C: - fmt.Println("ticked") - case <- timeout: - fmt.Println("timed out") - } -} - -// Or simply work with backoff intervals directly. -for i := 0; i < n; i++ { - // Sleep the current goroutine. - backo.Sleep(i) - // Retrieve the duration manually. - duration := backo.Duration(i) -} -``` - -License -------- - -``` -WWWWWW||WWWWWW - W W W||W W W - || - ( OO )__________ - / | \ - /o o| MIT \ - \___/||_||__||_|| * - || || || || - _||_|| _||_|| - (__|__|(__|__| - -The MIT License (MIT) - -Copyright (c) 2015 Segment, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -``` - - - - [1]: http://github.com/segmentio/backo-java - [2]: http://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=com.segment.backo&a=backo&v=LATEST \ No newline at end of file diff --git a/vendor/github.com/segmentio/backo-go/backo.go b/vendor/github.com/segmentio/backo-go/backo.go deleted file mode 100644 index 6f7b6d5e..00000000 --- a/vendor/github.com/segmentio/backo-go/backo.go +++ /dev/null @@ -1,83 +0,0 @@ -package backo - -import ( - "math" - "math/rand" - "time" -) - -type Backo struct { - base time.Duration - factor uint8 - jitter float64 - cap time.Duration -} - -// Creates a backo instance with the given parameters -func NewBacko(base time.Duration, factor uint8, jitter float64, cap time.Duration) *Backo { - return &Backo{base, factor, jitter, cap} -} - -// Creates a backo instance with the following defaults: -// base: 100 milliseconds -// factor: 2 -// jitter: 0 -// cap: 10 seconds -func DefaultBacko() *Backo { - return NewBacko(time.Millisecond*100, 2, 0, time.Second*10) -} - -// Duration returns the backoff interval for the given attempt. -func (backo *Backo) Duration(attempt int) time.Duration { - duration := float64(backo.base) * math.Pow(float64(backo.factor), float64(attempt)) - - if backo.jitter != 0 { - random := rand.Float64() - deviation := math.Floor(random * backo.jitter * duration) - if (int(math.Floor(random*10)) & 1) == 0 { - duration = duration - deviation - } else { - duration = duration + deviation - } - } - - duration = math.Min(float64(duration), float64(backo.cap)) - return time.Duration(duration) -} - -// Sleep pauses the current goroutine for the backoff interval for the given attempt. -func (backo *Backo) Sleep(attempt int) { - duration := backo.Duration(attempt) - time.Sleep(duration) -} - -type Ticker struct { - done chan struct{} - C <-chan time.Time -} - -func (b *Backo) NewTicker() *Ticker { - c := make(chan time.Time, 1) - ticker := &Ticker{ - done: make(chan struct{}, 1), - C: c, - } - - go func() { - for i := 0; ; i++ { - select { - case t := <-time.After(b.Duration(i)): - c <- t - case <-ticker.done: - close(c) - return - } - } - }() - - return ticker -} - -func (t *Ticker) Stop() { - t.done <- struct{}{} -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 37988e94..57260e4f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -44,8 +44,6 @@ github.com/aymanbagabas/go-osc52/v2 ## explicit github.com/aymerick/douceur/css github.com/aymerick/douceur/parser -# github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 -## explicit # github.com/charmbracelet/bubbles v0.18.0 ## explicit; go 1.18 github.com/charmbracelet/bubbles/cursor @@ -83,9 +81,6 @@ github.com/fsnotify/fsnotify # github.com/golang/protobuf v1.5.3 ## explicit; go 1.9 github.com/golang/protobuf/proto -# github.com/google/uuid v1.4.0 -## explicit -github.com/google/uuid # github.com/gorilla/css v1.0.0 ## explicit github.com/gorilla/css/scanner @@ -175,12 +170,6 @@ github.com/sagikazarmark/slog-shim # github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f ## explicit github.com/sahilm/fuzzy -# github.com/segmentio/analytics-go/v3 v3.3.0 -## explicit; go 1.17 -github.com/segmentio/analytics-go/v3 -# github.com/segmentio/backo-go v1.0.0 -## explicit -github.com/segmentio/backo-go # github.com/sourcegraph/conc v0.3.0 ## explicit; go 1.19 github.com/sourcegraph/conc From 2dda61b00cf35d53d904d9f46198d006f74784ed Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Wed, 10 Apr 2024 10:13:35 -0700 Subject: [PATCH 9/9] Add in line accidentally removed --- .github/actions/publish/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/publish/action.yml b/.github/actions/publish/action.yml index edb1a65f..1d8d0873 100644 --- a/.github/actions/publish/action.yml +++ b/.github/actions/publish/action.yml @@ -39,6 +39,7 @@ runs: args: release ${{ inputs.dry-run == 'true' && '--skip=publish' || '' }} env: GITHUB_TOKEN: ${{ inputs.token }} + HOMEBREW_DEPLOY_KEY: ${{ inputs.homebrew-gh-secret }} - name: Hash build artifacts for provenance id: hash shell: bash