diff --git a/.mockery.yaml b/.mockery.yaml index e3845b9..1bed7f8 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -1,10 +1,13 @@ -with-expecter: True -dir: internal/mocks/{{.PackageName}} -mockname: "{{.InterfaceName}}" -outpkg: "{{.PackageName}}" -filename: "{{.InterfaceName}}.go" -all: True +all: false +dir: 'internal/mocks/{{.SrcPackageName}}' +filename: '{{.InterfaceName}}.go' +force-file-write: true +formatter: goimports +structname: '{{.InterfaceName}}' +pkgname: '{{.SrcPackageName}}' +template: testify packages: github.com/lovelaze/nebula-sync: config: - recursive: True + all: true + recursive: true diff --git a/README.md b/README.md index de84b29..85503af 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,6 @@ The following environment variables can be specified: | `CLIENT_RETRY_DELAY_SECONDS` | 1 | 5 | Seconds to delay between connection attempts | | `CLIENT_TIMEOUT_SECONDS` | 20 | 60 | Http client timeout in seconds | - > **Note:** The following optional settings apply only if `FULL_SYNC=false`. They allow for granular control of synchronization if a full sync is not wanted. | Name | Default | Description | @@ -156,7 +155,7 @@ Additionally, you can skip TLS verification for all webhooks if necessary: |-------------------------------------------------|---------|-----------------|----------------------------------------------------| | `SYNC_WEBHOOK_CLIENT_SKIP_TLS_VERIFICATION` | false | true | Skips TLS certificate verification | -#### Examples +#### Integration examples: ##### healthcheck.io: diff --git a/go.mod b/go.mod index 24aac73..ebf5b2d 100644 --- a/go.mod +++ b/go.mod @@ -70,9 +70,9 @@ require ( go.opentelemetry.io/otel/metric v1.24.0 // indirect go.opentelemetry.io/otel/sdk v1.24.0 // indirect go.opentelemetry.io/otel/trace v1.24.0 // indirect - golang.org/x/crypto v0.35.0 // indirect - golang.org/x/net v0.36.0 // indirect - golang.org/x/sys v0.31.0 // indirect + golang.org/x/crypto v0.36.0 // indirect + golang.org/x/net v0.38.0 // indirect + golang.org/x/sys v0.32.0 // indirect golang.org/x/time v0.3.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index d864bf3..2a0f454 100644 --- a/go.sum +++ b/go.sum @@ -165,16 +165,16 @@ go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v8 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= -golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA= -golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -191,14 +191,14 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= -golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/internal/config/config.go b/internal/config/config.go index cf623e5..60855b3 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -232,7 +232,7 @@ func (cs *ConfigFilter) String() string { return fmt.Sprintf("%+v", *cs) } -func (wes *WebhookEventSetting) String() string { +func (wes *WebhookRequest) String() string { return fmt.Sprintf("%+v", *wes) } diff --git a/internal/config/webhook.go b/internal/config/webhook.go index 2477bb1..7ebdcec 100644 --- a/internal/config/webhook.go +++ b/internal/config/webhook.go @@ -7,39 +7,34 @@ import ( ) type WebhookSettings struct { - Failure WebhookEventSetting `ignored:"true"` - Success WebhookEventSetting `ignored:"true"` - Client WebhookClient `ignored:"true"` + Failure WebhookRequest `ignored:"true"` + Success WebhookRequest `ignored:"true"` + Client WebhookClient `ignored:"true"` } type WebhookClient struct { SkipTLSVerification bool `default:"false" envconfig:"SKIP_TLS_VERIFICATION"` } -type WebhookEventSetting struct { - Body string `default:"" envconfig:"BODY"` - Headers map[string]string `default:"" envconfig:"HEADERS"` +type WebhookRequest struct { + Body string `envconfig:"BODY"` + Headers map[string]string `envconfig:"HEADERS"` Method string `default:"POST" envconfig:"METHOD"` - Url string `default:"" envconfig:"URL"` + Url string `envconfig:"URL"` } -const envPrefix = "SYNC_WEBHOOK_" +const webhookEnvPrefix = "SYNC_WEBHOOK_" func (c *Config) loadWebhookSettings() error { - webhookSettings := WebhookSettings{ - Failure: WebhookEventSetting{}, - Success: WebhookEventSetting{}, - Client: WebhookClient{}, - } + webhookSettings := WebhookSettings{} - if err := envconfig.Process(envPrefix+"FAILURE", &webhookSettings.Failure); err != nil { + if err := envconfig.Process(webhookEnvPrefix+"FAILURE", &webhookSettings.Failure); err != nil { return fmt.Errorf("process webhook env vars for failure: %w", err) } - if err := envconfig.Process(envPrefix+"SUCCESS", &webhookSettings.Success); err != nil { + if err := envconfig.Process(webhookEnvPrefix+"SUCCESS", &webhookSettings.Success); err != nil { return fmt.Errorf("process webhook env vars for success: %w", err) } - - if err := envconfig.Process(envPrefix+"CLIENT", &webhookSettings.Client); err != nil { + if err := envconfig.Process(webhookEnvPrefix+"CLIENT", &webhookSettings.Client); err != nil { return fmt.Errorf("process webhook env vars for client: %w", err) } diff --git a/internal/mocks/pihole/Client.go b/internal/mocks/pihole/Client.go index 0a16ac7..bb6b673 100644 --- a/internal/mocks/pihole/Client.go +++ b/internal/mocks/pihole/Client.go @@ -1,12 +1,28 @@ -// Code generated by mockery v2.53.3. DO NOT EDIT. +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify package pihole import ( - model "github.com/lovelaze/nebula-sync/internal/pihole/model" + "github.com/lovelaze/nebula-sync/internal/pihole/model" mock "github.com/stretchr/testify/mock" ) +// NewClient creates a new instance of Client. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewClient(t interface { + mock.TestingT + Cleanup(func()) +}) *Client { + mock := &Client{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + // Client is an autogenerated mock type for the Client type type Client struct { mock.Mock @@ -20,21 +36,20 @@ func (_m *Client) EXPECT() *Client_Expecter { return &Client_Expecter{mock: &_m.Mock} } -// ApiPath provides a mock function with given fields: target -func (_m *Client) ApiPath(target string) string { - ret := _m.Called(target) +// ApiPath provides a mock function for the type Client +func (_mock *Client) ApiPath(target string) string { + ret := _mock.Called(target) if len(ret) == 0 { panic("no return value specified for ApiPath") } var r0 string - if rf, ok := ret.Get(0).(func(string) string); ok { - r0 = rf(target) + if returnFunc, ok := ret.Get(0).(func(string) string); ok { + r0 = returnFunc(target) } else { r0 = ret.Get(0).(string) } - return r0 } @@ -44,7 +59,7 @@ type Client_ApiPath_Call struct { } // ApiPath is a helper method to define mock.On call -// - target string +// - target func (_e *Client_Expecter) ApiPath(target interface{}) *Client_ApiPath_Call { return &Client_ApiPath_Call{Call: _e.mock.On("ApiPath", target)} } @@ -56,31 +71,30 @@ func (_c *Client_ApiPath_Call) Run(run func(target string)) *Client_ApiPath_Call return _c } -func (_c *Client_ApiPath_Call) Return(_a0 string) *Client_ApiPath_Call { - _c.Call.Return(_a0) +func (_c *Client_ApiPath_Call) Return(s string) *Client_ApiPath_Call { + _c.Call.Return(s) return _c } -func (_c *Client_ApiPath_Call) RunAndReturn(run func(string) string) *Client_ApiPath_Call { +func (_c *Client_ApiPath_Call) RunAndReturn(run func(target string) string) *Client_ApiPath_Call { _c.Call.Return(run) return _c } -// DeleteSession provides a mock function with no fields -func (_m *Client) DeleteSession() error { - ret := _m.Called() +// DeleteSession provides a mock function for the type Client +func (_mock *Client) DeleteSession() error { + ret := _mock.Called() if len(ret) == 0 { panic("no return value specified for DeleteSession") } var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() + if returnFunc, ok := ret.Get(0).(func() error); ok { + r0 = returnFunc() } else { r0 = ret.Error(0) } - return r0 } @@ -101,8 +115,8 @@ func (_c *Client_DeleteSession_Call) Run(run func()) *Client_DeleteSession_Call return _c } -func (_c *Client_DeleteSession_Call) Return(_a0 error) *Client_DeleteSession_Call { - _c.Call.Return(_a0) +func (_c *Client_DeleteSession_Call) Return(err error) *Client_DeleteSession_Call { + _c.Call.Return(err) return _c } @@ -111,9 +125,9 @@ func (_c *Client_DeleteSession_Call) RunAndReturn(run func() error) *Client_Dele return _c } -// GetConfig provides a mock function with no fields -func (_m *Client) GetConfig() (*model.ConfigResponse, error) { - ret := _m.Called() +// GetConfig provides a mock function for the type Client +func (_mock *Client) GetConfig() (*model.ConfigResponse, error) { + ret := _mock.Called() if len(ret) == 0 { panic("no return value specified for GetConfig") @@ -121,23 +135,21 @@ func (_m *Client) GetConfig() (*model.ConfigResponse, error) { var r0 *model.ConfigResponse var r1 error - if rf, ok := ret.Get(0).(func() (*model.ConfigResponse, error)); ok { - return rf() + if returnFunc, ok := ret.Get(0).(func() (*model.ConfigResponse, error)); ok { + return returnFunc() } - if rf, ok := ret.Get(0).(func() *model.ConfigResponse); ok { - r0 = rf() + if returnFunc, ok := ret.Get(0).(func() *model.ConfigResponse); ok { + r0 = returnFunc() } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*model.ConfigResponse) } } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() + if returnFunc, ok := ret.Get(1).(func() error); ok { + r1 = returnFunc() } else { r1 = ret.Error(1) } - return r0, r1 } @@ -168,9 +180,9 @@ func (_c *Client_GetConfig_Call) RunAndReturn(run func() (*model.ConfigResponse, return _c } -// GetTeleporter provides a mock function with no fields -func (_m *Client) GetTeleporter() ([]byte, error) { - ret := _m.Called() +// GetTeleporter provides a mock function for the type Client +func (_mock *Client) GetTeleporter() ([]byte, error) { + ret := _mock.Called() if len(ret) == 0 { panic("no return value specified for GetTeleporter") @@ -178,23 +190,21 @@ func (_m *Client) GetTeleporter() ([]byte, error) { var r0 []byte var r1 error - if rf, ok := ret.Get(0).(func() ([]byte, error)); ok { - return rf() + if returnFunc, ok := ret.Get(0).(func() ([]byte, error)); ok { + return returnFunc() } - if rf, ok := ret.Get(0).(func() []byte); ok { - r0 = rf() + if returnFunc, ok := ret.Get(0).(func() []byte); ok { + r0 = returnFunc() } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]byte) } } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() + if returnFunc, ok := ret.Get(1).(func() error); ok { + r1 = returnFunc() } else { r1 = ret.Error(1) } - return r0, r1 } @@ -215,8 +225,8 @@ func (_c *Client_GetTeleporter_Call) Run(run func()) *Client_GetTeleporter_Call return _c } -func (_c *Client_GetTeleporter_Call) Return(_a0 []byte, _a1 error) *Client_GetTeleporter_Call { - _c.Call.Return(_a0, _a1) +func (_c *Client_GetTeleporter_Call) Return(bytes []byte, err error) *Client_GetTeleporter_Call { + _c.Call.Return(bytes, err) return _c } @@ -225,9 +235,9 @@ func (_c *Client_GetTeleporter_Call) RunAndReturn(run func() ([]byte, error)) *C return _c } -// GetVersion provides a mock function with no fields -func (_m *Client) GetVersion() (*model.VersionResponse, error) { - ret := _m.Called() +// GetVersion provides a mock function for the type Client +func (_mock *Client) GetVersion() (*model.VersionResponse, error) { + ret := _mock.Called() if len(ret) == 0 { panic("no return value specified for GetVersion") @@ -235,23 +245,21 @@ func (_m *Client) GetVersion() (*model.VersionResponse, error) { var r0 *model.VersionResponse var r1 error - if rf, ok := ret.Get(0).(func() (*model.VersionResponse, error)); ok { - return rf() + if returnFunc, ok := ret.Get(0).(func() (*model.VersionResponse, error)); ok { + return returnFunc() } - if rf, ok := ret.Get(0).(func() *model.VersionResponse); ok { - r0 = rf() + if returnFunc, ok := ret.Get(0).(func() *model.VersionResponse); ok { + r0 = returnFunc() } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*model.VersionResponse) } } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() + if returnFunc, ok := ret.Get(1).(func() error); ok { + r1 = returnFunc() } else { r1 = ret.Error(1) } - return r0, r1 } @@ -272,8 +280,8 @@ func (_c *Client_GetVersion_Call) Run(run func()) *Client_GetVersion_Call { return _c } -func (_c *Client_GetVersion_Call) Return(_a0 *model.VersionResponse, _a1 error) *Client_GetVersion_Call { - _c.Call.Return(_a0, _a1) +func (_c *Client_GetVersion_Call) Return(versionResponse *model.VersionResponse, err error) *Client_GetVersion_Call { + _c.Call.Return(versionResponse, err) return _c } @@ -282,21 +290,20 @@ func (_c *Client_GetVersion_Call) RunAndReturn(run func() (*model.VersionRespons return _c } -// PatchConfig provides a mock function with given fields: patchRequest -func (_m *Client) PatchConfig(patchRequest *model.PatchConfigRequest) error { - ret := _m.Called(patchRequest) +// PatchConfig provides a mock function for the type Client +func (_mock *Client) PatchConfig(patchRequest *model.PatchConfigRequest) error { + ret := _mock.Called(patchRequest) if len(ret) == 0 { panic("no return value specified for PatchConfig") } var r0 error - if rf, ok := ret.Get(0).(func(*model.PatchConfigRequest) error); ok { - r0 = rf(patchRequest) + if returnFunc, ok := ret.Get(0).(func(*model.PatchConfigRequest) error); ok { + r0 = returnFunc(patchRequest) } else { r0 = ret.Error(0) } - return r0 } @@ -306,7 +313,7 @@ type Client_PatchConfig_Call struct { } // PatchConfig is a helper method to define mock.On call -// - patchRequest *model.PatchConfigRequest +// - patchRequest func (_e *Client_Expecter) PatchConfig(patchRequest interface{}) *Client_PatchConfig_Call { return &Client_PatchConfig_Call{Call: _e.mock.On("PatchConfig", patchRequest)} } @@ -318,31 +325,30 @@ func (_c *Client_PatchConfig_Call) Run(run func(patchRequest *model.PatchConfigR return _c } -func (_c *Client_PatchConfig_Call) Return(_a0 error) *Client_PatchConfig_Call { - _c.Call.Return(_a0) +func (_c *Client_PatchConfig_Call) Return(err error) *Client_PatchConfig_Call { + _c.Call.Return(err) return _c } -func (_c *Client_PatchConfig_Call) RunAndReturn(run func(*model.PatchConfigRequest) error) *Client_PatchConfig_Call { +func (_c *Client_PatchConfig_Call) RunAndReturn(run func(patchRequest *model.PatchConfigRequest) error) *Client_PatchConfig_Call { _c.Call.Return(run) return _c } -// PostAuth provides a mock function with no fields -func (_m *Client) PostAuth() error { - ret := _m.Called() +// PostAuth provides a mock function for the type Client +func (_mock *Client) PostAuth() error { + ret := _mock.Called() if len(ret) == 0 { panic("no return value specified for PostAuth") } var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() + if returnFunc, ok := ret.Get(0).(func() error); ok { + r0 = returnFunc() } else { r0 = ret.Error(0) } - return r0 } @@ -363,8 +369,8 @@ func (_c *Client_PostAuth_Call) Run(run func()) *Client_PostAuth_Call { return _c } -func (_c *Client_PostAuth_Call) Return(_a0 error) *Client_PostAuth_Call { - _c.Call.Return(_a0) +func (_c *Client_PostAuth_Call) Return(err error) *Client_PostAuth_Call { + _c.Call.Return(err) return _c } @@ -373,21 +379,20 @@ func (_c *Client_PostAuth_Call) RunAndReturn(run func() error) *Client_PostAuth_ return _c } -// PostRunGravity provides a mock function with no fields -func (_m *Client) PostRunGravity() error { - ret := _m.Called() +// PostRunGravity provides a mock function for the type Client +func (_mock *Client) PostRunGravity() error { + ret := _mock.Called() if len(ret) == 0 { panic("no return value specified for PostRunGravity") } var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() + if returnFunc, ok := ret.Get(0).(func() error); ok { + r0 = returnFunc() } else { r0 = ret.Error(0) } - return r0 } @@ -408,8 +413,8 @@ func (_c *Client_PostRunGravity_Call) Run(run func()) *Client_PostRunGravity_Cal return _c } -func (_c *Client_PostRunGravity_Call) Return(_a0 error) *Client_PostRunGravity_Call { - _c.Call.Return(_a0) +func (_c *Client_PostRunGravity_Call) Return(err error) *Client_PostRunGravity_Call { + _c.Call.Return(err) return _c } @@ -418,21 +423,20 @@ func (_c *Client_PostRunGravity_Call) RunAndReturn(run func() error) *Client_Pos return _c } -// PostTeleporter provides a mock function with given fields: payload, teleporterRequest -func (_m *Client) PostTeleporter(payload []byte, teleporterRequest *model.PostTeleporterRequest) error { - ret := _m.Called(payload, teleporterRequest) +// PostTeleporter provides a mock function for the type Client +func (_mock *Client) PostTeleporter(payload []byte, teleporterRequest *model.PostTeleporterRequest) error { + ret := _mock.Called(payload, teleporterRequest) if len(ret) == 0 { panic("no return value specified for PostTeleporter") } var r0 error - if rf, ok := ret.Get(0).(func([]byte, *model.PostTeleporterRequest) error); ok { - r0 = rf(payload, teleporterRequest) + if returnFunc, ok := ret.Get(0).(func([]byte, *model.PostTeleporterRequest) error); ok { + r0 = returnFunc(payload, teleporterRequest) } else { r0 = ret.Error(0) } - return r0 } @@ -442,8 +446,8 @@ type Client_PostTeleporter_Call struct { } // PostTeleporter is a helper method to define mock.On call -// - payload []byte -// - teleporterRequest *model.PostTeleporterRequest +// - payload +// - teleporterRequest func (_e *Client_Expecter) PostTeleporter(payload interface{}, teleporterRequest interface{}) *Client_PostTeleporter_Call { return &Client_PostTeleporter_Call{Call: _e.mock.On("PostTeleporter", payload, teleporterRequest)} } @@ -455,31 +459,30 @@ func (_c *Client_PostTeleporter_Call) Run(run func(payload []byte, teleporterReq return _c } -func (_c *Client_PostTeleporter_Call) Return(_a0 error) *Client_PostTeleporter_Call { - _c.Call.Return(_a0) +func (_c *Client_PostTeleporter_Call) Return(err error) *Client_PostTeleporter_Call { + _c.Call.Return(err) return _c } -func (_c *Client_PostTeleporter_Call) RunAndReturn(run func([]byte, *model.PostTeleporterRequest) error) *Client_PostTeleporter_Call { +func (_c *Client_PostTeleporter_Call) RunAndReturn(run func(payload []byte, teleporterRequest *model.PostTeleporterRequest) error) *Client_PostTeleporter_Call { _c.Call.Return(run) return _c } -// String provides a mock function with no fields -func (_m *Client) String() string { - ret := _m.Called() +// String provides a mock function for the type Client +func (_mock *Client) String() string { + ret := _mock.Called() if len(ret) == 0 { panic("no return value specified for String") } var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() + if returnFunc, ok := ret.Get(0).(func() string); ok { + r0 = returnFunc() } else { r0 = ret.Get(0).(string) } - return r0 } @@ -500,8 +503,8 @@ func (_c *Client_String_Call) Run(run func()) *Client_String_Call { return _c } -func (_c *Client_String_Call) Return(_a0 string) *Client_String_Call { - _c.Call.Return(_a0) +func (_c *Client_String_Call) Return(s string) *Client_String_Call { + _c.Call.Return(s) return _c } @@ -509,17 +512,3 @@ func (_c *Client_String_Call) RunAndReturn(run func() string) *Client_String_Cal _c.Call.Return(run) return _c } - -// NewClient creates a new instance of Client. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewClient(t interface { - mock.TestingT - Cleanup(func()) -}) *Client { - mock := &Client{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/internal/mocks/sync/Callback.go b/internal/mocks/sync/Callback.go new file mode 100644 index 0000000..62a15a7 --- /dev/null +++ b/internal/mocks/sync/Callback.go @@ -0,0 +1,103 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package sync + +import ( + mock "github.com/stretchr/testify/mock" +) + +// NewCallback creates a new instance of Callback. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewCallback(t interface { + mock.TestingT + Cleanup(func()) +}) *Callback { + mock := &Callback{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// Callback is an autogenerated mock type for the Callback type +type Callback struct { + mock.Mock +} + +type Callback_Expecter struct { + mock *mock.Mock +} + +func (_m *Callback) EXPECT() *Callback_Expecter { + return &Callback_Expecter{mock: &_m.Mock} +} + +// OnFailure provides a mock function for the type Callback +func (_mock *Callback) OnFailure(err error) { + _mock.Called(err) + return +} + +// Callback_OnFailure_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OnFailure' +type Callback_OnFailure_Call struct { + *mock.Call +} + +// OnFailure is a helper method to define mock.On call +// - err +func (_e *Callback_Expecter) OnFailure(err interface{}) *Callback_OnFailure_Call { + return &Callback_OnFailure_Call{Call: _e.mock.On("OnFailure", err)} +} + +func (_c *Callback_OnFailure_Call) Run(run func(err error)) *Callback_OnFailure_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(error)) + }) + return _c +} + +func (_c *Callback_OnFailure_Call) Return() *Callback_OnFailure_Call { + _c.Call.Return() + return _c +} + +func (_c *Callback_OnFailure_Call) RunAndReturn(run func(err error)) *Callback_OnFailure_Call { + _c.Run(run) + return _c +} + +// OnSuccess provides a mock function for the type Callback +func (_mock *Callback) OnSuccess() { + _mock.Called() + return +} + +// Callback_OnSuccess_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OnSuccess' +type Callback_OnSuccess_Call struct { + *mock.Call +} + +// OnSuccess is a helper method to define mock.On call +func (_e *Callback_Expecter) OnSuccess() *Callback_OnSuccess_Call { + return &Callback_OnSuccess_Call{Call: _e.mock.On("OnSuccess")} +} + +func (_c *Callback_OnSuccess_Call) Run(run func()) *Callback_OnSuccess_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Callback_OnSuccess_Call) Return() *Callback_OnSuccess_Call { + _c.Call.Return() + return _c +} + +func (_c *Callback_OnSuccess_Call) RunAndReturn(run func()) *Callback_OnSuccess_Call { + _c.Run(run) + return _c +} diff --git a/internal/mocks/sync/Target.go b/internal/mocks/sync/Target.go index a3ba3fe..49291f2 100644 --- a/internal/mocks/sync/Target.go +++ b/internal/mocks/sync/Target.go @@ -1,117 +1,14 @@ -// Code generated by mockery v2.53.3. DO NOT EDIT. +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify package sync import ( - config "github.com/lovelaze/nebula-sync/internal/config" + "github.com/lovelaze/nebula-sync/internal/config" mock "github.com/stretchr/testify/mock" ) -// Target is an autogenerated mock type for the Target type -type Target struct { - mock.Mock -} - -type Target_Expecter struct { - mock *mock.Mock -} - -func (_m *Target) EXPECT() *Target_Expecter { - return &Target_Expecter{mock: &_m.Mock} -} - -// FullSync provides a mock function with given fields: _a0 -func (_m *Target) FullSync(_a0 *config.Sync) error { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for FullSync") - } - - var r0 error - if rf, ok := ret.Get(0).(func(*config.Sync) error); ok { - r0 = rf(_a0) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Target_FullSync_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FullSync' -type Target_FullSync_Call struct { - *mock.Call -} - -// FullSync is a helper method to define mock.On call -// - _a0 *config.Sync -func (_e *Target_Expecter) FullSync(_a0 interface{}) *Target_FullSync_Call { - return &Target_FullSync_Call{Call: _e.mock.On("FullSync", _a0)} -} - -func (_c *Target_FullSync_Call) Run(run func(_a0 *config.Sync)) *Target_FullSync_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(*config.Sync)) - }) - return _c -} - -func (_c *Target_FullSync_Call) Return(_a0 error) *Target_FullSync_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *Target_FullSync_Call) RunAndReturn(run func(*config.Sync) error) *Target_FullSync_Call { - _c.Call.Return(run) - return _c -} - -// SelectiveSync provides a mock function with given fields: _a0 -func (_m *Target) SelectiveSync(_a0 *config.Sync) error { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for SelectiveSync") - } - - var r0 error - if rf, ok := ret.Get(0).(func(*config.Sync) error); ok { - r0 = rf(_a0) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Target_SelectiveSync_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SelectiveSync' -type Target_SelectiveSync_Call struct { - *mock.Call -} - -// SelectiveSync is a helper method to define mock.On call -// - _a0 *config.Sync -func (_e *Target_Expecter) SelectiveSync(_a0 interface{}) *Target_SelectiveSync_Call { - return &Target_SelectiveSync_Call{Call: _e.mock.On("SelectiveSync", _a0)} -} - -func (_c *Target_SelectiveSync_Call) Run(run func(_a0 *config.Sync)) *Target_SelectiveSync_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(*config.Sync)) - }) - return _c -} - -func (_c *Target_SelectiveSync_Call) Return(_a0 error) *Target_SelectiveSync_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *Target_SelectiveSync_Call) RunAndReturn(run func(*config.Sync) error) *Target_SelectiveSync_Call { - _c.Call.Return(run) - return _c -} - // NewTarget creates a new instance of Target. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewTarget(t interface { @@ -125,3 +22,106 @@ func NewTarget(t interface { return mock } + +// Target is an autogenerated mock type for the Target type +type Target struct { + mock.Mock +} + +type Target_Expecter struct { + mock *mock.Mock +} + +func (_m *Target) EXPECT() *Target_Expecter { + return &Target_Expecter{mock: &_m.Mock} +} + +// FullSync provides a mock function for the type Target +func (_mock *Target) FullSync(sync *config.Sync) error { + ret := _mock.Called(sync) + + if len(ret) == 0 { + panic("no return value specified for FullSync") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(*config.Sync) error); ok { + r0 = returnFunc(sync) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// Target_FullSync_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FullSync' +type Target_FullSync_Call struct { + *mock.Call +} + +// FullSync is a helper method to define mock.On call +// - sync +func (_e *Target_Expecter) FullSync(sync interface{}) *Target_FullSync_Call { + return &Target_FullSync_Call{Call: _e.mock.On("FullSync", sync)} +} + +func (_c *Target_FullSync_Call) Run(run func(sync *config.Sync)) *Target_FullSync_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*config.Sync)) + }) + return _c +} + +func (_c *Target_FullSync_Call) Return(err error) *Target_FullSync_Call { + _c.Call.Return(err) + return _c +} + +func (_c *Target_FullSync_Call) RunAndReturn(run func(sync *config.Sync) error) *Target_FullSync_Call { + _c.Call.Return(run) + return _c +} + +// SelectiveSync provides a mock function for the type Target +func (_mock *Target) SelectiveSync(sync *config.Sync) error { + ret := _mock.Called(sync) + + if len(ret) == 0 { + panic("no return value specified for SelectiveSync") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(*config.Sync) error); ok { + r0 = returnFunc(sync) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// Target_SelectiveSync_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SelectiveSync' +type Target_SelectiveSync_Call struct { + *mock.Call +} + +// SelectiveSync is a helper method to define mock.On call +// - sync +func (_e *Target_Expecter) SelectiveSync(sync interface{}) *Target_SelectiveSync_Call { + return &Target_SelectiveSync_Call{Call: _e.mock.On("SelectiveSync", sync)} +} + +func (_c *Target_SelectiveSync_Call) Run(run func(sync *config.Sync)) *Target_SelectiveSync_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*config.Sync)) + }) + return _c +} + +func (_c *Target_SelectiveSync_Call) Return(err error) *Target_SelectiveSync_Call { + _c.Call.Return(err) + return _c +} + +func (_c *Target_SelectiveSync_Call) RunAndReturn(run func(sync *config.Sync) error) *Target_SelectiveSync_Call { + _c.Call.Return(run) + return _c +} diff --git a/internal/mocks/webhook/WebhookClient.go b/internal/mocks/webhook/WebhookClient.go deleted file mode 100644 index 1a972b1..0000000 --- a/internal/mocks/webhook/WebhookClient.go +++ /dev/null @@ -1,122 +0,0 @@ -// Code generated by mockery v2.53.3. DO NOT EDIT. - -package webhook - -import mock "github.com/stretchr/testify/mock" - -// WebhookClient is an autogenerated mock type for the WebhookClient type -type WebhookClient struct { - mock.Mock -} - -type WebhookClient_Expecter struct { - mock *mock.Mock -} - -func (_m *WebhookClient) EXPECT() *WebhookClient_Expecter { - return &WebhookClient_Expecter{mock: &_m.Mock} -} - -// Failure provides a mock function with no fields -func (_m *WebhookClient) Failure() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Failure") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// WebhookClient_Failure_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Failure' -type WebhookClient_Failure_Call struct { - *mock.Call -} - -// Failure is a helper method to define mock.On call -func (_e *WebhookClient_Expecter) Failure() *WebhookClient_Failure_Call { - return &WebhookClient_Failure_Call{Call: _e.mock.On("Failure")} -} - -func (_c *WebhookClient_Failure_Call) Run(run func()) *WebhookClient_Failure_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *WebhookClient_Failure_Call) Return(_a0 error) *WebhookClient_Failure_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *WebhookClient_Failure_Call) RunAndReturn(run func() error) *WebhookClient_Failure_Call { - _c.Call.Return(run) - return _c -} - -// Success provides a mock function with no fields -func (_m *WebhookClient) Success() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Success") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// WebhookClient_Success_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Success' -type WebhookClient_Success_Call struct { - *mock.Call -} - -// Success is a helper method to define mock.On call -func (_e *WebhookClient_Expecter) Success() *WebhookClient_Success_Call { - return &WebhookClient_Success_Call{Call: _e.mock.On("Success")} -} - -func (_c *WebhookClient_Success_Call) Run(run func()) *WebhookClient_Success_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *WebhookClient_Success_Call) Return(_a0 error) *WebhookClient_Success_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *WebhookClient_Success_Call) RunAndReturn(run func() error) *WebhookClient_Success_Call { - _c.Call.Return(run) - return _c -} - -// NewWebhookClient creates a new instance of WebhookClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewWebhookClient(t interface { - mock.TestingT - Cleanup(func()) -}) *WebhookClient { - mock := &WebhookClient{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/internal/service/service.go b/internal/service/service.go index 37789e0..3c0ad9e 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -2,22 +2,29 @@ package service import ( "fmt" - "github.com/lovelaze/nebula-sync/internal/sync/retry" + "github.com/lovelaze/nebula-sync/internal/webhook" "github.com/lovelaze/nebula-sync/internal/config" "github.com/lovelaze/nebula-sync/internal/pihole" "github.com/lovelaze/nebula-sync/internal/sync" - "github.com/lovelaze/nebula-sync/internal/webhook" "github.com/lovelaze/nebula-sync/version" "github.com/robfig/cron/v3" "github.com/rs/zerolog/log" ) type Service struct { - target sync.Target - conf config.Config - webhook webhook.WebhookClient + target sync.Target + conf config.Config + callbacks []sync.Callback +} + +func NewService(target sync.Target, conf config.Config, callbacks ...sync.Callback) *Service { + return &Service{ + target: target, + conf: conf, + callbacks: callbacks, + } } func Init() (*Service, error) { @@ -35,11 +42,11 @@ func Init() (*Service, error) { replicas = append(replicas, pihole.NewClient(replica, httpClient)) } - return &Service{ - target: sync.NewTarget(primary, replicas), - conf: conf, - webhook: webhook.NewWebhookClient(conf.Sync.WebhookSettings), - }, nil + webhookClient := webhook.NewClient(conf.Sync.WebhookSettings) + + target := sync.NewTarget(primary, replicas) + service := NewService(target, conf, webhookClient) + return service, nil } func (service *Service) Run() error { @@ -69,15 +76,14 @@ func (service *Service) doSync(t sync.Target) (err error) { } if err != nil { - if err := service.webhook.Failure(); err != nil { - log.Error().Err(err).Msg("Failed to send failure webhook") + for _, callback := range service.callbacks { + callback.OnFailure(err) } } else { - log.Info().Msg("Sync completed") - - if err := service.webhook.Success(); err != nil { - log.Error().Err(err).Msg("Failed to send success webhook") + for _, callback := range service.callbacks { + callback.OnSuccess() } + log.Info().Msg("Sync completed") } return err diff --git a/internal/service/service_test.go b/internal/service/service_test.go index 4a5819d..8aaa219 100644 --- a/internal/service/service_test.go +++ b/internal/service/service_test.go @@ -6,7 +6,6 @@ import ( "github.com/lovelaze/nebula-sync/internal/config" syncmock "github.com/lovelaze/nebula-sync/internal/mocks/sync" - webhookmock "github.com/lovelaze/nebula-sync/internal/mocks/webhook" "github.com/lovelaze/nebula-sync/internal/pihole/model" "github.com/stretchr/testify/require" ) @@ -22,15 +21,11 @@ func TestRun_full(t *testing.T) { } target := syncmock.NewTarget(t) - webhook := webhookmock.NewWebhookClient(t) + callback := syncmock.NewCallback(t) target.On("FullSync", conf.Sync).Return(nil) - webhook.On("Success").Return(nil) + callback.On("OnSuccess").Return(nil) - service := Service{ - target: target, - conf: conf, - webhook: webhook, - } + service := NewService(target, conf, callback) err := service.Run() require.NoError(t, err) @@ -49,15 +44,11 @@ func TestRun_selective(t *testing.T) { } target := syncmock.NewTarget(t) - webhook := webhookmock.NewWebhookClient(t) + callback := syncmock.NewCallback(t) target.On("SelectiveSync", conf.Sync).Return(nil) - webhook.On("Success").Return(nil) + callback.On("OnSuccess").Return(nil) - service := Service{ - target: target, - conf: conf, - webhook: webhook, - } + service := NewService(target, conf, callback) err := service.Run() require.NoError(t, err) @@ -76,23 +67,19 @@ func TestRun_webhook_success(t *testing.T) { } target := syncmock.NewTarget(t) - webhook := webhookmock.NewWebhookClient(t) + callback := syncmock.NewCallback(t) target.On("SelectiveSync", conf.Sync).Return(nil) - webhook.On("Success").Return(nil) + callback.On("OnSuccess").Return(nil) - service := Service{ - target: target, - conf: conf, - webhook: webhook, - } + service := NewService(target, conf, callback) err := service.Run() require.NoError(t, err) target.AssertCalled(t, "SelectiveSync", conf.Sync) - webhook.AssertCalled(t, "Success") - webhook.AssertNotCalled(t, "Failure") + callback.AssertCalled(t, "OnSuccess") + callback.AssertNotCalled(t, "OnFailure") } func TestRun_webhook_failure(t *testing.T) { @@ -107,23 +94,19 @@ func TestRun_webhook_failure(t *testing.T) { syncErr := errors.New("sync failed") target := syncmock.NewTarget(t) - webhook := webhookmock.NewWebhookClient(t) + callback := syncmock.NewCallback(t) target.On("SelectiveSync", conf.Sync).Return(syncErr) - webhook.On("Failure").Return(nil) + callback.On("OnFailure", syncErr).Return(nil) - service := Service{ - target: target, - conf: conf, - webhook: webhook, - } + service := NewService(target, conf, callback) err := service.Run() require.ErrorIs(t, err, syncErr) target.AssertCalled(t, "SelectiveSync", conf.Sync) - webhook.AssertCalled(t, "Failure") - webhook.AssertNotCalled(t, "Success") + callback.AssertCalled(t, "OnFailure", syncErr) + callback.AssertNotCalled(t, "OnSuccess") } func TestRun_webhook_error_does_not_affect_result(t *testing.T) { @@ -137,20 +120,16 @@ func TestRun_webhook_error_does_not_affect_result(t *testing.T) { } target := syncmock.NewTarget(t) - webhook := webhookmock.NewWebhookClient(t) + callback := syncmock.NewCallback(t) target.On("FullSync", conf.Sync).Return(nil) - webhook.On("Success").Return(errors.New("webhook failed")) + callback.On("OnSuccess").Return(nil) - service := Service{ - target: target, - conf: conf, - webhook: webhook, - } + service := NewService(target, conf, callback) err := service.Run() require.NoError(t, err) target.AssertCalled(t, "FullSync", conf.Sync) - webhook.AssertCalled(t, "Success") + callback.AssertCalled(t, "OnSuccess") } diff --git a/internal/sync/callback.go b/internal/sync/callback.go new file mode 100644 index 0000000..f448a48 --- /dev/null +++ b/internal/sync/callback.go @@ -0,0 +1,6 @@ +package sync + +type Callback interface { + OnSuccess() + OnFailure(error) +} diff --git a/internal/webhook/webhook.go b/internal/webhook/webhook.go index adc7f90..6782e15 100644 --- a/internal/webhook/webhook.go +++ b/internal/webhook/webhook.go @@ -12,22 +12,17 @@ import ( "github.com/rs/zerolog/log" ) -type WebhookClient interface { - Success() error - Failure() error +type Client struct { + success config.WebhookRequest + failure config.WebhookRequest + httpClient *http.Client } -type webhookClient struct { - successConfig config.WebhookEventSetting - failureConfig config.WebhookEventSetting - client *http.Client -} - -func NewWebhookClient(c *config.WebhookSettings) WebhookClient { - return &webhookClient{ - successConfig: c.Success, - failureConfig: c.Failure, - client: &http.Client{ +func NewClient(c *config.WebhookSettings) *Client { + return &Client{ + success: c.Success, + failure: c.Failure, + httpClient: &http.Client{ Timeout: 10 * time.Second, Transport: &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: c.Client.SkipTLSVerification}, @@ -35,15 +30,27 @@ func NewWebhookClient(c *config.WebhookSettings) WebhookClient { } } -func (webhookClient *webhookClient) Success() error { - return invokeWebhook(webhookClient.client, webhookClient.successConfig) +func (c *Client) OnSuccess() { + if err := c.triggerSuccess(); err != nil { + log.Warn().Err(err).Msg("Webhook trigger failed") + } } -func (webhookClient *webhookClient) Failure() error { - return invokeWebhook(webhookClient.client, webhookClient.failureConfig) +func (c *Client) OnFailure(err error) { + if err := c.triggerFailure(); err != nil { + log.Warn().Err(err).Msg("Webhook trigger failed") + } } -func invokeWebhook(client *http.Client, settings config.WebhookEventSetting) error { +func (c *Client) triggerSuccess() error { + return invoke(c.httpClient, c.success) +} + +func (c *Client) triggerFailure() error { + return invoke(c.httpClient, c.failure) +} + +func invoke(client *http.Client, settings config.WebhookRequest) error { if settings.Url == "" { return nil } diff --git a/internal/webhook/webhook_test.go b/internal/webhook/webhook_test.go index f58c46d..76f2597 100644 --- a/internal/webhook/webhook_test.go +++ b/internal/webhook/webhook_test.go @@ -1,6 +1,7 @@ package webhook import ( + "io" "net/http" "net/http/httptest" "testing" @@ -11,23 +12,23 @@ import ( "github.com/stretchr/testify/require" ) -func TestWebhookClient(t *testing.T) { +func TestWebhook(t *testing.T) { t.Run("success webhook uses success configuration", func(t *testing.T) { // Setup test server to verify request var receivedHeaders http.Header var receivedBody string ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { receivedHeaders = r.Header - buf := make([]byte, 1024) - n, _ := r.Body.Read(buf) - receivedBody = string(buf[:n]) + buf, err := io.ReadAll(r.Body) + require.NoError(t, err) + receivedBody = string(buf) w.WriteHeader(http.StatusOK) })) defer ts.Close() // Create webhook settings settings := &config.WebhookSettings{ - Success: config.WebhookEventSetting{ + Success: config.WebhookRequest{ Url: ts.URL, Method: "POST", Body: "success-body", @@ -36,8 +37,8 @@ func TestWebhookClient(t *testing.T) { Client: config.WebhookClient{}, } - client := NewWebhookClient(settings) - err := client.Success() + client := NewClient(settings) + err := client.triggerSuccess() require.NoError(t, err) // Verify request @@ -51,15 +52,15 @@ func TestWebhookClient(t *testing.T) { var receivedBody string ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { receivedHeaders = r.Header - buf := make([]byte, 1024) - n, _ := r.Body.Read(buf) - receivedBody = string(buf[:n]) + buf, err := io.ReadAll(r.Body) + require.NoError(t, err) + receivedBody = string(buf) w.WriteHeader(http.StatusOK) })) defer ts.Close() settings := &config.WebhookSettings{ - Failure: config.WebhookEventSetting{ + Failure: config.WebhookRequest{ Url: ts.URL, Method: "PUT", Body: "failure-body", @@ -68,8 +69,8 @@ func TestWebhookClient(t *testing.T) { Client: config.WebhookClient{}, } - client := NewWebhookClient(settings) - err := client.Failure() + client := NewClient(settings) + err := client.triggerFailure() require.NoError(t, err) assert.Equal(t, "failure-body", receivedBody) @@ -78,13 +79,13 @@ func TestWebhookClient(t *testing.T) { t.Run("empty url skips webhook", func(t *testing.T) { settings := &config.WebhookSettings{ - Success: config.WebhookEventSetting{ + Success: config.WebhookRequest{ Url: "", }, } - client := NewWebhookClient(settings) - err := client.Success() + client := NewClient(settings) + err := client.triggerSuccess() require.NoError(t, err) }) @@ -95,13 +96,13 @@ func TestWebhookClient(t *testing.T) { defer ts.Close() settings := &config.WebhookSettings{ - Success: config.WebhookEventSetting{ + Success: config.WebhookRequest{ Url: ts.URL, }, } - client := NewWebhookClient(settings) - err := client.Success() + client := NewClient(settings) + err := client.triggerSuccess() require.Error(t, err) assert.Contains(t, err.Error(), "webhook returned status 400") })