diff --git a/.github/workflows/quality-and-tests.yml b/.github/workflows/quality-and-tests.yml index 504b5b9..f2695ea 100644 --- a/.github/workflows/quality-and-tests.yml +++ b/.github/workflows/quality-and-tests.yml @@ -12,11 +12,11 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.20.1 + go-version: 1.21.1 - name: Install gotestsum run: make install-gotestsum - name: Install revive - run: go get -u github.com/mgechev/revive + run: go install github.com/mgechev/revive@latest - name: go mod tidy run: go mod tidy - name: go mod vendor diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7c99ccc..66fc9c5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,7 +17,7 @@ repos: - repo: https://github.com/tekwizely/pre-commit-golang rev: v0.8.3 hooks: - - id: go-imports +# - id: go-imports - id: go-vet-mod - id: go-revive args: ['-config', '.revive.toml', '--'] diff --git a/.tool-versions b/.tool-versions index 6582967..e4ab54c 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -golang 1.20.1 +golang 1.21.1 diff --git a/README.md b/README.md index ed13038..54bd387 100644 --- a/README.md +++ b/README.md @@ -40,9 +40,11 @@ as examples, we would create an entry in the configuration file like this: ## Usage -Clone this repo and build the plugin. +Clone this repo, install dependencies, and build the plugin. ```shell +git clone git@github.com:sprak3000/xbar-whats-up.git +make setup-osx-env make build ``` diff --git a/docs/contributing.md b/docs/contributing.md index 4248834..bf31b5d 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -13,21 +13,20 @@ When you write your tests, please make sure they're in line with the existing te ## Bug Reports -When the inevitable happens and you discover a bug in the documentation or the code, please follow the process below to -help us out. +If you discover a bug in the documentation or the code, please follow the process below to help us out. - Search the existing issues to see if the issue has already been filed - Make sure the issue is a bug and not simply a preference - If you have found a new issue, please file it -From that point, if you are interest in contributing some code, ask in the issue if we are willing to accept a failing -test case, and/or a fix. If we are, then follow the steps for contributing and we can go from there! +From that point, if you are interested in contributing some code, ask in the issue if we are willing to accept a failing +test case and/or a fix. If we are, then follow the steps for contributing, and we can go from there! ## Feature Requests Every new feature request will be scrutinized to make sure we are not going to experience feature bloat. Every new -feature should fit the Vision for the project. If you have got an idea for a new feature and you feel it fits the -vision, file an issue and we can discuss it. +feature should fit the Vision for the project. If you have an idea for a new feature and feel it fits the vision, file +an issue, and we can discuss it. Make sure any feature request you make fits the [INVEST](http://en.wikipedia.org/wiki/INVEST_(mnemonic)) mnemonic. diff --git a/go.mod b/go.mod index 2415760..deba5c5 100644 --- a/go.mod +++ b/go.mod @@ -1,16 +1,20 @@ module github.com/sprak3000/xbar-whats-up -go 1.17 +go 1.21 -require github.com/sprak3000/go-client v1.0.1 +toolchain go1.21.1 + +require github.com/sprak3000/go-client v1.1.0 require ( - github.com/sprak3000/go-glitch v1.0.1 - github.com/stretchr/testify v1.7.1 + github.com/sprak3000/go-glitch v1.1.0 + github.com/sprak3000/go-whatsup-client v1.1.0 + github.com/stretchr/testify v1.8.4 ) require ( - github.com/davecgh/go-spew v1.1.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/golang/mock v1.6.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 23214b5..84241de 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,17 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/lib/pq v1.10.5/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sprak3000/go-client v1.0.1 h1:zHTtanfkNrVFae6y+zqsY2B+LrPkxWCZlK5F3uOnkc4= -github.com/sprak3000/go-client v1.0.1/go.mod h1:ch8ZmYJ0gEvm6cn5pXQb/2FE/jkzEgkBpWj6QePBPi0= -github.com/sprak3000/go-glitch v1.0.1 h1:fI5HjMmib3jq/9p/ccL1d+CtdP20Xc7QUTlNUpty5GI= -github.com/sprak3000/go-glitch v1.0.1/go.mod h1:gAghf5TnJBpx+SwVPviNhUD7qeeqBy8hB1W7V+z/wrU= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/sprak3000/go-client v1.1.0 h1:RxcuubHsRAJuby8JHOiR1Uzg7QjJLeFF19X+nb6D1Bk= +github.com/sprak3000/go-client v1.1.0/go.mod h1:g2/5bTIZIkQa44cGKRRSq87NIDFpysfJG/9YvM4Tc1g= +github.com/sprak3000/go-glitch v1.1.0 h1:6haWWwI/X12nT/1metfmuIJ55glM8n8Y9MAjOBgwGyo= +github.com/sprak3000/go-glitch v1.1.0/go.mod h1:YiS3Au3XFzjkfGFzEWCR/j7WZq5+fhgZotchT4yYblY= +github.com/sprak3000/go-whatsup-client v1.1.0 h1:FsjLJcF4IPyCld6PvRiAVxoCSZgvDsuJo1AbobOzBnQ= +github.com/sprak3000/go-whatsup-client v1.1.0/go.mod h1:r8h5Herq50rVll/Tn8kLioCd3eu/4VpgkvLS8AXUo1g= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 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= @@ -36,5 +37,5 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/service/client.go b/service/client.go deleted file mode 100644 index fa2e858..0000000 --- a/service/client.go +++ /dev/null @@ -1,47 +0,0 @@ -// Package service handles communicating with sites to obtain their current status details -package service - -import ( - "errors" - "net/url" - - "github.com/sprak3000/go-client/client" - "github.com/sprak3000/go-glitch/glitch" - "github.com/sprak3000/xbar-whats-up/slack" - "github.com/sprak3000/xbar-whats-up/status" - "github.com/sprak3000/xbar-whats-up/statuspageio" -) - -// Reader provides the requirements for anyone implementing reading a service's status -type Reader interface { - ReadStatus(serviceFinder client.ServiceFinder, serviceName, slug string) (status.Details, glitch.DataError) -} - -// ReaderServiceFinder is an alias for the closure that finds a Reader based on the given service type -type ReaderServiceFinder func(serviceType string) (Reader, error) - -// NewReaderServiceFinder returns a service.ReaderServiceFinder to provider a Reader based on the given service type -func NewReaderServiceFinder() ReaderServiceFinder { - return func(serviceType string) (Reader, error) { - switch serviceType { - case statuspageio.ServiceType: - return statuspageio.ClientReader{}, nil - case slack.ServiceType: - return slack.ClientReader{}, nil - default: - return nil, errors.New("reader not implemented for type " + serviceType) - } - } -} - -// NewClientServiceFinder returns a client.ServiceFinder suitable for use with go-client -func NewClientServiceFinder(sites Sites) client.ServiceFinder { - return func(serviceName string, useTLS bool) (url.URL, error) { - u, ok := sites[serviceName] - if !ok { - return url.URL{}, errors.New("unable to find " + serviceName + " in the service list") - } - - return u.URL, nil - } -} diff --git a/service/client_test.go b/service/client_test.go deleted file mode 100644 index 54d18ac..0000000 --- a/service/client_test.go +++ /dev/null @@ -1,113 +0,0 @@ -package service - -import ( - "errors" - "net/url" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/sprak3000/xbar-whats-up/slack" - "github.com/sprak3000/xbar-whats-up/statuspageio" -) - -func TestUnit_NewClientServiceFinder(t *testing.T) { - codeClimateURL, err := url.Parse("https://status.codeclimate.com/api/v2/status.json") - require.NoError(t, err) - - circleciURL, err := url.Parse("https://status.circleci.com/api/v2/status.json") - require.NoError(t, err) - - tests := map[string]struct { - sites Sites - serviceName string - expectedURL url.URL - expectedErr error - validate func(t *testing.T, expectedURL, actualURL url.URL, expectedErr, actualErr error) - }{ - "base path": { - sites: Sites{ - "CodeClimate": { - URL: *codeClimateURL, - Type: statuspageio.ServiceType, - }, - "CircleCI": { - URL: *circleciURL, - Type: statuspageio.ServiceType, - }, - }, - serviceName: "CodeClimate", - expectedURL: *codeClimateURL, - validate: func(t *testing.T, expectedURL, actualURL url.URL, expectedErr, actualErr error) { - require.NoError(t, actualErr) - require.Equal(t, expectedURL, actualURL) - }, - }, - "exceptional path- unable to find service": { - sites: Sites{ - "CodeClimate": { - URL: *codeClimateURL, - Type: statuspageio.ServiceType, - }, - "CircleCI": { - URL: *circleciURL, - Type: statuspageio.ServiceType, - }, - }, - serviceName: "GitHub", - expectedErr: errors.New("unable to find GitHub in the service list"), - validate: func(t *testing.T, expectedURL, actualURL url.URL, expectedErr, actualErr error) { - require.Error(t, actualErr) - require.Equal(t, expectedErr, actualErr) - }, - }, - } - for name, tc := range tests { - t.Run(name, func(t *testing.T) { - f := NewClientServiceFinder(tc.sites) - u, err := f(tc.serviceName, true) - tc.validate(t, tc.expectedURL, u, tc.expectedErr, err) - }) - } -} - -func TestUnit_NewReaderServiceFinder(t *testing.T) { - tests := map[string]struct { - serviceType string - expectedReader Reader - expectedErr error - validate func(t *testing.T, expectedReader, actualReader Reader, expectedErr, actualErr error) - }{ - "base path- statuspage.io": { - serviceType: statuspageio.ServiceType, - expectedReader: statuspageio.ClientReader{}, - validate: func(t *testing.T, expectedReader, actualReader Reader, expectedErr, actualErr error) { - require.NoError(t, actualErr) - require.Equal(t, expectedReader, actualReader) - }, - }, - "base path- slack": { - serviceType: slack.ServiceType, - expectedReader: slack.ClientReader{}, - validate: func(t *testing.T, expectedReader, actualReader Reader, expectedErr, actualErr error) { - require.NoError(t, actualErr) - require.Equal(t, expectedReader, actualReader) - }, - }, - "exceptional path- unsupported service type": { - serviceType: "not-a-finger", - expectedErr: errors.New("reader not implemented for type not-a-finger"), - validate: func(t *testing.T, expectedReader, actualReader Reader, expectedErr, actualErr error) { - require.Error(t, actualErr) - require.Equal(t, expectedErr, actualErr) - }, - }, - } - for name, tc := range tests { - t.Run(name, func(t *testing.T) { - f := NewReaderServiceFinder() - r, fErr := f(tc.serviceType) - tc.validate(t, tc.expectedReader, r, tc.expectedErr, fErr) - }) - } -} diff --git a/service/site.go b/service/site.go index 42be841..bffb5f5 100644 --- a/service/site.go +++ b/service/site.go @@ -5,10 +5,13 @@ import ( "encoding/json" "net/url" - "github.com/sprak3000/go-client/client" "github.com/sprak3000/go-glitch/glitch" + "github.com/sprak3000/go-whatsup-client/whatsup" + "github.com/sprak3000/xbar-whats-up/configuration" + "github.com/sprak3000/xbar-whats-up/slack" "github.com/sprak3000/xbar-whats-up/status" + "github.com/sprak3000/xbar-whats-up/statuspageio" ) // Error codes @@ -17,6 +20,11 @@ const ( ErrorUnableToParseConfiguration = "UNABLE_TO_PARSE_CONFIGURATION" ) +// Reader provides the requirements for anyone implementing reading a service's status +type Reader interface { + ReadStatus(client whatsup.StatusPageClient) (status.Details, glitch.DataError) +} + // Site holds the data for service status pages type Site struct { URL url.URL `json:"url,string"` @@ -53,7 +61,7 @@ func (s *Site) UnmarshalJSON(data []byte) error { type Sites map[string]Site // GetOverview returns the details about the services monitored -func (sites Sites) GetOverview(serviceFinder client.ServiceFinder, readerFinder ReaderServiceFinder) status.Overview { +func (sites Sites) GetOverview(client whatsup.StatusPageClient) status.Overview { overview := status.Overview{ OverallStatus: "none", List: map[string][]status.Details{}, @@ -61,14 +69,23 @@ func (sites Sites) GetOverview(serviceFinder client.ServiceFinder, readerFinder } for k, v := range sites { - reader, rfErr := readerFinder(v.Type) - if rfErr != nil { + var reader Reader + + switch v.Type { + case statuspageio.ServiceType: + reader = statuspageio.ClientReader{ + ServiceName: k, + PageURL: v.URL.String(), + } + case slack.ServiceType: + reader = slack.ClientReader{} + default: overview.Errors = append(overview.Errors, k+" uses an unsupported service type "+v.Type) // Unsupported at this time continue } - resp, rErr := reader.ReadStatus(serviceFinder, k, v.URL.Path) + resp, rErr := reader.ReadStatus(client) if rErr != nil { overview.Errors = append(overview.Errors, rErr.Error()) continue diff --git a/service/site_test.go b/service/site_test.go index 5a11fad..11effe0 100644 --- a/service/site_test.go +++ b/service/site_test.go @@ -7,10 +7,13 @@ import ( "net/url" "testing" + "github.com/golang/mock/gomock" + "github.com/sprak3000/go-glitch/glitch" + whatsupstatus "github.com/sprak3000/go-whatsup-client/status" + "github.com/sprak3000/go-whatsup-client/whatsup" + "github.com/sprak3000/go-whatsup-client/whatsup/clientmock" "github.com/stretchr/testify/require" - "github.com/sprak3000/go-client/client" - "github.com/sprak3000/go-glitch/glitch" "github.com/sprak3000/xbar-whats-up/configuration" "github.com/sprak3000/xbar-whats-up/status" "github.com/sprak3000/xbar-whats-up/statuspageio" @@ -56,18 +59,69 @@ func TestUnit_Site_UnmarshalJSON(t *testing.T) { } func TestUnit_GetOverview(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + codeClimateURL, err := url.Parse("https://status.codeclimate.com/api/v2/status.json") require.NoError(t, err) + codeClimateMinorOutageResp := statuspageio.Response{ + Page: statuspageio.Page{ + ID: "code-climate", + Name: "CodeClimate", + URL: "https://status.codeclimate.com/api/v2/status.json", + }, + Status: statuspageio.Status{ + Indicator: "minor", + Description: "minor outage", + }, + } + + codeClimateNoOutageResp := statuspageio.Response{ + Page: statuspageio.Page{ + ID: "code-climate", + Name: "CodeClimate", + URL: "https://status.codeclimate.com/api/v2/status.json", + }, + Status: statuspageio.Status{ + Indicator: "none", + Description: "none", + }, + } + circleciURL, err := url.Parse("https://status.circleci.com/api/v2/status.json") require.NoError(t, err) + circleciMajorOutageResp := statuspageio.Response{ + Page: statuspageio.Page{ + ID: "circle-ci", + Name: "CircleCI", + URL: "https://status.circleci.com/api/v2/status.json", + }, + Status: statuspageio.Status{ + Indicator: "major", + Description: "major outage", + }, + } + + circleciMinorOutageResp := statuspageio.Response{ + Page: statuspageio.Page{ + ID: "circle-ci", + Name: "CircleCI", + URL: "https://status.circleci.com/api/v2/status.json", + }, + Status: statuspageio.Status{ + Indicator: "minor", + Description: "minor outage", + }, + } + tests := map[string]struct { - sites Sites - serviceFinder client.ServiceFinder - readerFinder ReaderServiceFinder - expectedOverview status.Overview - validate func(t *testing.T, expectedOverview, actualOverview status.Overview) + sites Sites + setupStatusPageClient func(t *testing.T, expectedErr glitch.DataError) whatsup.StatusPageClient + expectedOverview status.Overview + expectedClientErr glitch.DataError + validate func(t *testing.T, expectedOverview, actualOverview status.Overview) }{ "base path- highest severity is major": { sites: Sites{ @@ -80,38 +134,21 @@ func TestUnit_GetOverview(t *testing.T) { Type: statuspageio.ServiceType, }, }, - readerFinder: func(serviceName string) (Reader, error) { - return clientReaderSeverityMajor{}, nil + setupStatusPageClient: func(t *testing.T, expectedErr glitch.DataError) whatsup.StatusPageClient { + c := clientmock.NewMockStatusPageClient(ctrl) + c.EXPECT().StatuspageIoService("CodeClimate", codeClimateURL.String()).Times(1).Return(codeClimateMinorOutageResp, nil) + c.EXPECT().StatuspageIoService("CircleCI", circleciURL.String()).Times(1).Return(circleciMajorOutageResp, nil) + return c }, expectedOverview: status.Overview{ OverallStatus: "major", LargestStringSize: 11, List: map[string][]status.Details{ "major": { - statuspageio.Response{ - Page: statuspageio.Page{ - ID: "circle-ci", - Name: "CircleCI", - URL: "https://status.circleci.com/api/v2/status.json", - }, - Status: statuspageio.Status{ - Indicator: "major", - Description: "major outage", - }, - }, + circleciMajorOutageResp, }, "minor": { - statuspageio.Response{ - Page: statuspageio.Page{ - ID: "code-climate", - Name: "CodeClimate", - URL: "https://status.codeclimate.com/api/v2/status.json", - }, - Status: statuspageio.Status{ - Indicator: "minor", - Description: "minor outage", - }, - }, + codeClimateMinorOutageResp, }, }, Errors: []string{}, @@ -131,38 +168,21 @@ func TestUnit_GetOverview(t *testing.T) { Type: statuspageio.ServiceType, }, }, - readerFinder: func(serviceName string) (Reader, error) { - return clientReaderSeverityMinor{}, nil + setupStatusPageClient: func(t *testing.T, expectedErr glitch.DataError) whatsup.StatusPageClient { + c := clientmock.NewMockStatusPageClient(ctrl) + c.EXPECT().StatuspageIoService("CodeClimate", codeClimateURL.String()).Times(1).Return(codeClimateNoOutageResp, nil) + c.EXPECT().StatuspageIoService("CircleCI", circleciURL.String()).Times(1).Return(circleciMinorOutageResp, nil) + return c }, expectedOverview: status.Overview{ OverallStatus: "minor", LargestStringSize: 11, List: map[string][]status.Details{ "minor": { - statuspageio.Response{ - Page: statuspageio.Page{ - ID: "circle-ci", - Name: "CircleCI", - URL: "https://status.circleci.com/api/v2/status.json", - }, - Status: statuspageio.Status{ - Indicator: "minor", - Description: "minor outage", - }, - }, + circleciMinorOutageResp, }, "none": { - statuspageio.Response{ - Page: statuspageio.Page{ - ID: "code-climate", - Name: "CodeClimate", - URL: "https://status.codeclimate.com/api/v2/status.json", - }, - Status: statuspageio.Status{ - Indicator: "none", - Description: "none", - }, - }, + codeClimateNoOutageResp, }, }, Errors: []string{}, @@ -182,8 +202,11 @@ func TestUnit_GetOverview(t *testing.T) { Type: statuspageio.ServiceType, }, }, - readerFinder: func(serviceName string) (Reader, error) { - return clientReaderHasError{}, nil + setupStatusPageClient: func(t *testing.T, expectedErr glitch.DataError) whatsup.StatusPageClient { + c := clientmock.NewMockStatusPageClient(ctrl) + c.EXPECT().StatuspageIoService("CodeClimate", codeClimateURL.String()).AnyTimes().Return(codeClimateNoOutageResp, nil) + c.EXPECT().StatuspageIoService("CircleCI", circleciURL.String()).AnyTimes().Return(nil, glitch.NewDataError(nil, whatsupstatus.ErrorUnableToMakeClientRequest, "test err")) + return c }, expectedOverview: status.Overview{ OverallStatus: "none", @@ -215,17 +238,18 @@ func TestUnit_GetOverview(t *testing.T) { sites: Sites{ "CodeClimate": { URL: *codeClimateURL, - Type: statuspageio.ServiceType, + Type: "not-a-finger", }, }, - readerFinder: func(serviceName string) (Reader, error) { - return nil, errors.New("not supported") + setupStatusPageClient: func(t *testing.T, expectedErr glitch.DataError) whatsup.StatusPageClient { + c := clientmock.NewMockStatusPageClient(ctrl) + return c }, expectedOverview: status.Overview{ OverallStatus: "none", List: map[string][]status.Details{}, Errors: []string{ - "CodeClimate uses an unsupported service type statuspage.io", + "CodeClimate uses an unsupported service type not-a-finger", }, }, validate: func(t *testing.T, expectedOverview, actualOverview status.Overview) { @@ -235,95 +259,12 @@ func TestUnit_GetOverview(t *testing.T) { } for name, tc := range tests { t.Run(name, func(t *testing.T) { - o := tc.sites.GetOverview(tc.serviceFinder, tc.readerFinder) + o := tc.sites.GetOverview(tc.setupStatusPageClient(t, tc.expectedClientErr)) tc.validate(t, tc.expectedOverview, o) }) } } -type clientReaderSeverityMajor struct { -} - -func (crsMajor clientReaderSeverityMajor) ReadStatus(_ client.ServiceFinder, serviceName, _ string) (status.Details, glitch.DataError) { - if serviceName == "CircleCI" { - return statuspageio.Response{ - Page: statuspageio.Page{ - ID: "circle-ci", - Name: "CircleCI", - URL: "https://status.circleci.com/api/v2/status.json", - }, - Status: statuspageio.Status{ - Indicator: "major", - Description: "major outage", - }, - }, nil - } - - return statuspageio.Response{ - Page: statuspageio.Page{ - ID: "code-climate", - Name: "CodeClimate", - URL: "https://status.codeclimate.com/api/v2/status.json", - }, - Status: statuspageio.Status{ - Indicator: "minor", - Description: "minor outage", - }, - }, nil -} - -type clientReaderSeverityMinor struct { -} - -func (crsMinor clientReaderSeverityMinor) ReadStatus(_ client.ServiceFinder, serviceName, _ string) (status.Details, glitch.DataError) { - if serviceName == "CircleCI" { - return statuspageio.Response{ - Page: statuspageio.Page{ - ID: "circle-ci", - Name: "CircleCI", - URL: "https://status.circleci.com/api/v2/status.json", - }, - Status: statuspageio.Status{ - Indicator: "minor", - Description: "minor outage", - }, - }, nil - } - - return statuspageio.Response{ - Page: statuspageio.Page{ - ID: "code-climate", - Name: "CodeClimate", - URL: "https://status.codeclimate.com/api/v2/status.json", - }, - Status: statuspageio.Status{ - Indicator: "none", - Description: "none", - }, - }, nil -} - -type clientReaderHasError struct { -} - -func (crhe clientReaderHasError) ReadStatus(_ client.ServiceFinder, serviceName, _ string) (status.Details, glitch.DataError) { - if serviceName == "CircleCI" { - return statuspageio.Response{}, glitch.NewDataError(nil, status.ErrorUnableToMakeClientRequest, "test err") - } - - return statuspageio.Response{ - Page: statuspageio.Page{ - ID: "code-climate", - Name: "CodeClimate", - URL: "https://status.codeclimate.com/api/v2/status.json", - }, - Status: statuspageio.Status{ - Indicator: "none", - Description: "none", - }, - }, nil -} - func TestUnit_LoadSites(t *testing.T) { codeClimateURL, err := url.Parse("https://status.codeclimate.com/api/v2/status.json") require.NoError(t, err) diff --git a/slack/response.go b/slack/response.go index 19f7c8f..07e228b 100644 --- a/slack/response.go +++ b/slack/response.go @@ -2,12 +2,11 @@ package slack import ( - "encoding/json" - "fmt" "time" - "github.com/sprak3000/go-client/client" "github.com/sprak3000/go-glitch/glitch" + "github.com/sprak3000/go-whatsup-client/whatsup" + "github.com/sprak3000/xbar-whats-up/status" ) @@ -38,20 +37,8 @@ type ClientReader struct { } // ReadStatus handles communicating with the service to get its status details -func (cr ClientReader) ReadStatus(serviceFinder client.ServiceFinder, serviceName, slug string) (status.Details, glitch.DataError) { - resp := Response{} - - respBytes, err := status.Get(serviceFinder, serviceName, slug) - if err != nil { - return resp, glitch.NewDataError(err, status.ErrorUnableToMakeClientRequest, fmt.Sprintf("unable to make client request for %s: %v", serviceName, err)) - } - - uErr := json.Unmarshal(respBytes, &resp) - if uErr != nil { - return resp, glitch.NewDataError(uErr, status.ErrorUnableToParseClientResponse, fmt.Sprintf("unable to parse client response for %s: %v", serviceName, uErr)) - } - - return resp, nil +func (cr ClientReader) ReadStatus(client whatsup.StatusPageClient) (status.Details, glitch.DataError) { + return client.Slack() } // Response is the structure returned by Slack powered service status pages diff --git a/status/details.go b/status/details.go index f547346..e2270ab 100644 --- a/status/details.go +++ b/status/details.go @@ -2,18 +2,7 @@ package status import ( - "context" - "net/http" "time" - - "github.com/sprak3000/go-client/client" - "github.com/sprak3000/go-glitch/glitch" -) - -// Error codes -const ( - ErrorUnableToMakeClientRequest = "UNABLE_TO_MAKE_CLIENT_REQUEST" - ErrorUnableToParseClientResponse = "UNABLE_TO_PARSE_CLIENT_RESPONSE" ) // Details provides an interface for extracting information from a service's status response @@ -23,10 +12,3 @@ type Details interface { UpdatedAt() time.Time URL() string } - -// Get makes the network request to get a status page -func Get(serviceFinder client.ServiceFinder, serviceName, slug string) ([]byte, glitch.DataError) { - c := client.NewBaseClient(serviceFinder, serviceName, true, 10*time.Second, nil) - _, respBytes, err := c.MakeRequest(context.Background(), http.MethodGet, slug, nil, nil, nil) - return respBytes, err -} diff --git a/statuspageio/response.go b/statuspageio/response.go index d3890df..e1c4f90 100644 --- a/statuspageio/response.go +++ b/statuspageio/response.go @@ -2,12 +2,11 @@ package statuspageio import ( - "encoding/json" - "fmt" "time" - "github.com/sprak3000/go-client/client" "github.com/sprak3000/go-glitch/glitch" + "github.com/sprak3000/go-whatsup-client/whatsup" + "github.com/sprak3000/xbar-whats-up/status" ) @@ -31,23 +30,13 @@ type Status struct { // ClientReader implements the Reader interface for go-client based reading of a service's status type ClientReader struct { + ServiceName string + PageURL string } // ReadStatus handles communicating with the service to get its status details -func (cr ClientReader) ReadStatus(serviceFinder client.ServiceFinder, serviceName, slug string) (status.Details, glitch.DataError) { - resp := Response{} - - respBytes, err := status.Get(serviceFinder, serviceName, slug) - if err != nil { - return resp, glitch.NewDataError(err, status.ErrorUnableToMakeClientRequest, fmt.Sprintf("unable to make client request for %s: %v", serviceName, err)) - } - - uErr := json.Unmarshal(respBytes, &resp) - if uErr != nil { - return resp, glitch.NewDataError(uErr, status.ErrorUnableToParseClientResponse, fmt.Sprintf("unable to parse client response for %s: %v", serviceName, uErr)) - } - - return resp, nil +func (cr ClientReader) ReadStatus(client whatsup.StatusPageClient) (status.Details, glitch.DataError) { + return client.StatuspageIoService(cr.ServiceName, cr.PageURL) } // Response is the structure returned by statuspage.io powered service status pages diff --git a/whats-up.1h.go b/whats-up.1h.go index 864aba9..0d15ba6 100644 --- a/whats-up.1h.go +++ b/whats-up.1h.go @@ -12,6 +12,8 @@ import ( "fmt" "os" + "github.com/sprak3000/go-whatsup-client/whatsup" + "github.com/sprak3000/xbar-whats-up/configuration" "github.com/sprak3000/xbar-whats-up/service" ) @@ -25,5 +27,7 @@ func main() { os.Exit(1) } - sites.GetOverview(service.NewClientServiceFinder(sites), service.NewReaderServiceFinder()).Display(os.Stdout) + c := whatsup.NewStatusPageClient() + + sites.GetOverview(c).Display(os.Stdout) }