From 8b25d04f9e278fc7fd87b8dfb113b9057c7816fb Mon Sep 17 00:00:00 2001 From: Julien <38018054+Julien4218@users.noreply.github.com> Date: Tue, 14 Nov 2023 11:23:01 -0800 Subject: [PATCH] chore(events): add events api (#1065) --- newrelic/newrelic.go | 151 +++-------------------- pkg/config/config.go | 22 ++++ pkg/config/main.go | 194 ++++++++++++++++++++++++++++++ pkg/events/events_service.go | 27 +++++ pkg/events/events_service_test.go | 29 +++++ 5 files changed, 291 insertions(+), 132 deletions(-) create mode 100644 pkg/config/main.go create mode 100644 pkg/events/events_service.go create mode 100644 pkg/events/events_service_test.go diff --git a/newrelic/newrelic.go b/newrelic/newrelic.go index 307876c88..abb2665fb 100644 --- a/newrelic/newrelic.go +++ b/newrelic/newrelic.go @@ -1,12 +1,9 @@ package newrelic import ( - "errors" "net/http" "time" - log "github.com/sirupsen/logrus" - "github.com/newrelic/newrelic-client-go/v2/pkg/accountmanagement" "github.com/newrelic/newrelic-client-go/v2/pkg/accounts" "github.com/newrelic/newrelic-client-go/v2/pkg/agentapplications" @@ -31,7 +28,6 @@ import ( "github.com/newrelic/newrelic-client-go/v2/pkg/nrdb" "github.com/newrelic/newrelic-client-go/v2/pkg/nrqldroprules" "github.com/newrelic/newrelic-client-go/v2/pkg/plugins" - "github.com/newrelic/newrelic-client-go/v2/pkg/region" "github.com/newrelic/newrelic-client-go/v2/pkg/servicelevel" "github.com/newrelic/newrelic-client-go/v2/pkg/synthetics" "github.com/newrelic/newrelic-client-go/v2/pkg/workflows" @@ -74,21 +70,9 @@ type NewRelic struct { func New(opts ...ConfigOption) (*NewRelic, error) { cfg := config.New() - // Loop through config options - for _, fn := range opts { - if nil != fn { - if err := fn(&cfg); err != nil { - return nil, err - } - } - } - - if cfg.PersonalAPIKey == "" && cfg.AdminAPIKey == "" && cfg.InsightsInsertKey == "" { - return nil, errors.New("must use at least one of: ConfigPersonalAPIKey, ConfigAdminAPIKey, ConfigInsightsInsertKey") - } - - if cfg.Logger == nil { - cfg.Logger = cfg.GetLogger() + err := cfg.Init(opts) + if err != nil { + return nil, err } nr := &NewRelic{ @@ -150,182 +134,85 @@ func (nr *NewRelic) TestEndpoints() error { // ConfigOption configures the Config when provided to NewApplication. // https://docs.newrelic.com/docs/apis/get-started/intro-apis/types-new-relic-api-keys -type ConfigOption func(*config.Config) error +type ConfigOption = config.ConfigOption // ConfigPersonalAPIKey sets the New Relic Admin API key this client will use. // This key should be used to create a client instance. // https://docs.newrelic.com/docs/apis/get-started/intro-apis/types-new-relic-api-keys func ConfigPersonalAPIKey(apiKey string) ConfigOption { - return func(cfg *config.Config) error { - cfg.PersonalAPIKey = apiKey - return nil - } + return config.ConfigPersonalAPIKey(apiKey) } // ConfigInsightsInsertKey sets the New Relic Insights insert key this client will use. // https://docs.newrelic.com/docs/apis/get-started/intro-apis/types-new-relic-api-keys func ConfigInsightsInsertKey(insightsInsertKey string) ConfigOption { - return func(cfg *config.Config) error { - cfg.InsightsInsertKey = insightsInsertKey - return nil - } + return config.ConfigInsightsInsertKey(insightsInsertKey) } // ConfigAdminAPIKey sets the New Relic Admin API key this client will use. // Deprecated. Use a personal API key for authentication. // https://docs.newrelic.com/docs/apis/get-started/intro-apis/types-new-relic-api-keys func ConfigAdminAPIKey(adminAPIKey string) ConfigOption { - return func(cfg *config.Config) error { - cfg.AdminAPIKey = adminAPIKey - return nil - } + return config.ConfigAdminAPIKey(adminAPIKey) } // ConfigRegion sets the New Relic Region this client will use. func ConfigRegion(r string) ConfigOption { - return func(cfg *config.Config) error { - // We can ignore this error since we will be defaulting in the next step - regName, _ := region.Parse(r) - - reg, err := region.Get(regName) - if err != nil { - if _, ok := err.(region.UnknownUsingDefaultError); ok { - // If region wasn't provided, output a warning message - // indicating the default region "US" is being used. - log.Warn(err) - return nil - } - - return err - } - - err = cfg.SetRegion(reg) - - return err - } + return config.ConfigRegion(r) } // ConfigHTTPTimeout sets the timeout for HTTP requests. func ConfigHTTPTimeout(t time.Duration) ConfigOption { - return func(cfg *config.Config) error { - var timeout = &t - cfg.Timeout = timeout - return nil - } + return config.ConfigHTTPTimeout(t) } // ConfigHTTPTransport sets the HTTP Transporter. func ConfigHTTPTransport(transport http.RoundTripper) ConfigOption { - return func(cfg *config.Config) error { - if transport != nil { - cfg.HTTPTransport = transport - return nil - } - - return errors.New("HTTP Transport can not be nil") - } + return config.ConfigHTTPTransport(transport) } // ConfigUserAgent sets the HTTP UserAgent for API requests. func ConfigUserAgent(ua string) ConfigOption { - return func(cfg *config.Config) error { - if ua != "" { - cfg.UserAgent = ua - return nil - } - - return errors.New("user-agent can not be empty") - } + return config.ConfigUserAgent(ua) } // ConfigServiceName sets the service name logged func ConfigServiceName(name string) ConfigOption { - return func(cfg *config.Config) error { - if name != "" { - cfg.ServiceName = name - } - - return nil - } + return config.ConfigServiceName(name) } // ConfigBaseURL sets the base URL used to make requests to the REST API V2. func ConfigBaseURL(url string) ConfigOption { - return func(cfg *config.Config) error { - if url != "" { - cfg.Region().SetRestBaseURL(url) - return nil - } - - return errors.New("base URL can not be empty") - } + return config.ConfigBaseURL(url) } // ConfigInfrastructureBaseURL sets the base URL used to make requests to the Infrastructure API. func ConfigInfrastructureBaseURL(url string) ConfigOption { - return func(cfg *config.Config) error { - if url != "" { - cfg.Region().SetInfrastructureBaseURL(url) - return nil - } - - return errors.New("infrastructure base URL can not be empty") - } + return config.ConfigInfrastructureBaseURL(url) } // ConfigSyntheticsBaseURL sets the base URL used to make requests to the Synthetics API. func ConfigSyntheticsBaseURL(url string) ConfigOption { - return func(cfg *config.Config) error { - if url != "" { - cfg.Region().SetSyntheticsBaseURL(url) - return nil - } - - return errors.New("synthetics base URL can not be empty") - } + return config.ConfigSyntheticsBaseURL(url) } // ConfigNerdGraphBaseURL sets the base URL used to make requests to the NerdGraph API. func ConfigNerdGraphBaseURL(url string) ConfigOption { - return func(cfg *config.Config) error { - if url != "" { - cfg.Region().SetNerdGraphBaseURL(url) - return nil - } - - return errors.New("nerdgraph base URL can not be empty") - } + return config.ConfigNerdGraphBaseURL(url) } // ConfigLogLevel sets the log level for the client. func ConfigLogLevel(logLevel string) ConfigOption { - return func(cfg *config.Config) error { - if logLevel != "" { - cfg.LogLevel = logLevel - return nil - } - - return errors.New("log level can not be empty") - } + return config.ConfigLogLevel(logLevel) } // ConfigLogJSON toggles JSON formatting on for the logger if set to true. func ConfigLogJSON(logJSON bool) ConfigOption { - return func(cfg *config.Config) error { - cfg.LogJSON = logJSON - return nil - } + return config.ConfigLogJSON(logJSON) } // ConfigLogger can be used to customize the client's logger. // Custom loggers must conform to the logging.Logger interface. func ConfigLogger(logger logging.Logger) ConfigOption { - return func(cfg *config.Config) error { - if logger != nil { - cfg.Logger = logger - return nil - } - - return errors.New("logger can not be nil") - } + return config.ConfigLogger(logger) } diff --git a/pkg/config/config.go b/pkg/config/config.go index 3aa302461..c741ece6e 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -2,6 +2,7 @@ package config import ( + "errors" "fmt" "net/http" "os" @@ -70,6 +71,27 @@ func New() Config { } } +func (cfg *Config) Init(opts []ConfigOption) error { + // Loop through config options + for _, fn := range opts { + if nil != fn { + if err := fn(cfg); err != nil { + return err + } + } + } + + if cfg.PersonalAPIKey == "" && cfg.AdminAPIKey == "" && cfg.InsightsInsertKey == "" { + return errors.New("must use at least one of: ConfigPersonalAPIKey, ConfigAdminAPIKey, ConfigInsightsInsertKey") + } + + if cfg.Logger == nil { + cfg.Logger = cfg.GetLogger() + } + + return nil +} + // Region returns the region configuration struct // if one has not been set, use the default region func (c *Config) Region() *region.Region { diff --git a/pkg/config/main.go b/pkg/config/main.go new file mode 100644 index 000000000..1b51143a4 --- /dev/null +++ b/pkg/config/main.go @@ -0,0 +1,194 @@ +package config + +import ( + "errors" + "net/http" + "time" + + log "github.com/sirupsen/logrus" + + "github.com/newrelic/newrelic-client-go/v2/pkg/logging" + "github.com/newrelic/newrelic-client-go/v2/pkg/region" +) + +// ConfigOption configures the Config when provided to NewApplication. +// https://docs.newrelic.com/docs/apis/get-started/intro-apis/types-new-relic-api-keys +type ConfigOption func(*Config) error + +// ConfigPersonalAPIKey sets the New Relic Admin API key this client will use. +// This key should be used to create a client instance. +// https://docs.newrelic.com/docs/apis/get-started/intro-apis/types-new-relic-api-keys +func ConfigPersonalAPIKey(apiKey string) ConfigOption { + return func(cfg *Config) error { + cfg.PersonalAPIKey = apiKey + return nil + } +} + +// ConfigInsightsInsertKey sets the New Relic Insights insert key this client will use. +// https://docs.newrelic.com/docs/apis/get-started/intro-apis/types-new-relic-api-keys +func ConfigInsightsInsertKey(insightsInsertKey string) ConfigOption { + return func(cfg *Config) error { + cfg.InsightsInsertKey = insightsInsertKey + return nil + } +} + +// ConfigAdminAPIKey sets the New Relic Admin API key this client will use. +// Deprecated. Use a personal API key for authentication. +// https://docs.newrelic.com/docs/apis/get-started/intro-apis/types-new-relic-api-keys +func ConfigAdminAPIKey(adminAPIKey string) ConfigOption { + return func(cfg *Config) error { + cfg.AdminAPIKey = adminAPIKey + return nil + } +} + +// ConfigRegion sets the New Relic Region this client will use. +func ConfigRegion(r string) ConfigOption { + return func(cfg *Config) error { + // We can ignore this error since we will be defaulting in the next step + regName, _ := region.Parse(r) + + reg, err := region.Get(regName) + if err != nil { + if _, ok := err.(region.UnknownUsingDefaultError); ok { + // If region wasn't provided, output a warning message + // indicating the default region "US" is being used. + log.Warn(err) + return nil + } + + return err + } + + err = cfg.SetRegion(reg) + + return err + } +} + +// ConfigHTTPTimeout sets the timeout for HTTP requests. +func ConfigHTTPTimeout(t time.Duration) ConfigOption { + return func(cfg *Config) error { + var timeout = &t + cfg.Timeout = timeout + return nil + } +} + +// ConfigHTTPTransport sets the HTTP Transporter. +func ConfigHTTPTransport(transport http.RoundTripper) ConfigOption { + return func(cfg *Config) error { + if transport != nil { + cfg.HTTPTransport = transport + return nil + } + + return errors.New("HTTP Transport can not be nil") + } +} + +// ConfigUserAgent sets the HTTP UserAgent for API requests. +func ConfigUserAgent(ua string) ConfigOption { + return func(cfg *Config) error { + if ua != "" { + cfg.UserAgent = ua + return nil + } + + return errors.New("user-agent can not be empty") + } +} + +// ConfigServiceName sets the service name logged +func ConfigServiceName(name string) ConfigOption { + return func(cfg *Config) error { + if name != "" { + cfg.ServiceName = name + } + + return nil + } +} + +// ConfigBaseURL sets the base URL used to make requests to the REST API V2. +func ConfigBaseURL(url string) ConfigOption { + return func(cfg *Config) error { + if url != "" { + cfg.Region().SetRestBaseURL(url) + return nil + } + + return errors.New("base URL can not be empty") + } +} + +// ConfigInfrastructureBaseURL sets the base URL used to make requests to the Infrastructure API. +func ConfigInfrastructureBaseURL(url string) ConfigOption { + return func(cfg *Config) error { + if url != "" { + cfg.Region().SetInfrastructureBaseURL(url) + return nil + } + + return errors.New("infrastructure base URL can not be empty") + } +} + +// ConfigSyntheticsBaseURL sets the base URL used to make requests to the Synthetics API. +func ConfigSyntheticsBaseURL(url string) ConfigOption { + return func(cfg *Config) error { + if url != "" { + cfg.Region().SetSyntheticsBaseURL(url) + return nil + } + + return errors.New("synthetics base URL can not be empty") + } +} + +// ConfigNerdGraphBaseURL sets the base URL used to make requests to the NerdGraph API. +func ConfigNerdGraphBaseURL(url string) ConfigOption { + return func(cfg *Config) error { + if url != "" { + cfg.Region().SetNerdGraphBaseURL(url) + return nil + } + + return errors.New("nerdgraph base URL can not be empty") + } +} + +// ConfigLogLevel sets the log level for the client. +func ConfigLogLevel(logLevel string) ConfigOption { + return func(cfg *Config) error { + if logLevel != "" { + cfg.LogLevel = logLevel + return nil + } + + return errors.New("log level can not be empty") + } +} + +// ConfigLogJSON toggles JSON formatting on for the logger if set to true. +func ConfigLogJSON(logJSON bool) ConfigOption { + return func(cfg *Config) error { + cfg.LogJSON = logJSON + return nil + } +} + +// ConfigLogger can be used to customize the client's logger. +// Custom loggers must conform to the logging.Logger interface. +func ConfigLogger(logger logging.Logger) ConfigOption { + return func(cfg *Config) error { + if logger != nil { + cfg.Logger = logger + return nil + } + + return errors.New("logger can not be nil") + } +} diff --git a/pkg/events/events_service.go b/pkg/events/events_service.go new file mode 100644 index 000000000..74eaf30b7 --- /dev/null +++ b/pkg/events/events_service.go @@ -0,0 +1,27 @@ +package events + +import ( + "context" + + "github.com/newrelic/newrelic-client-go/v2/pkg/config" +) + +// EventsAPI provides an interface to enable mocking the service implementation. +// You should use this interface to invoke methods and substitute with a mock having a compatible implementation for unit testing your usages. +type EventsAPI interface { + CreateEvent(accountID int, event interface{}) error + CreateEventWithContext(ctx context.Context, accountID int, event interface{}) error +} + +// Provides the EventsAPI operations for *Events +func NewEventsService(opts ...config.ConfigOption) (*Events, error) { + cfg := config.New() + + err := cfg.Init(opts) + if err != nil { + return nil, err + } + + events := New(cfg) + return &events, nil +} diff --git a/pkg/events/events_service_test.go b/pkg/events/events_service_test.go new file mode 100644 index 000000000..596ba484e --- /dev/null +++ b/pkg/events/events_service_test.go @@ -0,0 +1,29 @@ +//go:build unit +// +build unit + +package events + +import ( + "testing" + + "github.com/newrelic/newrelic-client-go/v2/pkg/config" + "github.com/stretchr/testify/assert" +) + +var testAPIkey = "efgh56789" + +func TestShouldCreateEventsService(t *testing.T) { + t.Parallel() + + service, err := NewEventsService(config.ConfigPersonalAPIKey(testAPIkey)) + assert.NoError(t, err) + assert.NotNil(t, service) +} + +func TestShouldErrorCreateEventsService(t *testing.T) { + t.Parallel() + + service, err := NewEventsService(config.ConfigPersonalAPIKey("")) + assert.Error(t, err) + assert.Nil(t, service) +}