diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..3912072 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,74 @@ +name: Test + +on: [push] + +env: + SIL_COMMS_BASE_URL: ${{ secrets.SIL_COMMS_BASE_URL }} + SIL_COMMS_EMAIL: ${{ secrets.SIL_COMMS_EMAIL }} + SIL_COMMS_PASSWORD: ${{ secrets.SIL_COMMS_PASSWORD }} + SIL_COMMS_SENDER_ID: ${{ secrets.SIL_COMMS_SENDER_ID }} + +jobs: + lint_and_test: + strategy: + fail-fast: true + matrix: + go-version: [1.17.x] + os: [ubuntu-latest] + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v3 + - name: Install Golang + uses: actions/setup-go@v3 + with: + go-version: ${{ matrix.go-version }} + + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + version: v1.29 + + - name: Install Go dependencies + run: | + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.37.1 + go get -u github.com/kisielk/errcheck + go get -u golang.org/x/lint/golint + go get -u honnef.co/go/tools/cmd/staticcheck + go get -u github.com/axw/gocov/gocov + go get -u github.com/securego/gosec/cmd/gosec + go get -u github.com/ory/go-acc + go get -u github.com/client9/misspell/cmd/misspell + go get -u github.com/gordonklaus/ineffassign + go get github.com/fzipp/gocyclo + go get github.com/stretchr/testify/assert@v1.7.0 + go get github.com/ory/go-acc + go get google.golang.org/grpc@v1.38.0 + go get google.golang.org/grpc/balancer/grpclb@v1.38.0 + go get google.golang.org/api/support/bundler@v0.48.0 + + - name: Run lint and test + run: | + staticcheck ./... + go fmt $(go list ./... | grep -v /vendor/) + go vet $(go list ./... | grep -v /vendor/) + golint -set_exit_status $(go list ./... | grep -v /vendor/) + errcheck -ignore 'os:.*,' $(go list ./... | grep -v /vendor/) + misspell -error . + gosec ./... + go-acc -o coverage.txt --ignore generated,cmd ./... -- -timeout 60m + grep -v "generated.go" coverage.txt > coverage.out + go tool cover -html=coverage.out -o coverage.html + gocov convert coverage.out > coverage.json + gocov report coverage.json > coverage_report.txt + tail coverage_report.txt + + - name: Install goveralls + env: + GO111MODULE: off + run: go get github.com/mattn/goveralls + + - name: Send coverage + env: + COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: goveralls -coverprofile=coverage.out -service=github \ No newline at end of file diff --git a/client.go b/client.go index da2d8ac..70db9c0 100644 --- a/client.go +++ b/client.go @@ -15,24 +15,25 @@ import ( ) var ( - // COMMS_BASE_URL - COMMS_BASE_URL = serverutils.MustGetEnvVar("SIL_COMMS_BASE_URL") + // commsBaseURL represents the SIL-Comms base URL + commsBaseURL = serverutils.MustGetEnvVar("SIL_COMMS_BASE_URL") - // COMMS_EMAIL - COMMS_EMAIL = serverutils.MustGetEnvVar("SIL_COMMS_EMAIL") + // commsEmail is used for authentication against the SIL comms API + commsEmail = serverutils.MustGetEnvVar("SIL_COMMS_EMAIL") - // COMMS_PASSWORD - COMMS_PASSWORD = serverutils.MustGetEnvVar("SIL_COMMS_PASSWORD") + // commsPassword is used for authentication against the SIL comms API + commsPassword = serverutils.MustGetEnvVar("SIL_COMMS_PASSWORD") - // ACCESS_TOKEN_TIMEOUT - ACCESS_TOKEN_TIMEOUT = 30 * time.Minute + // accessTokenTimeout shows the access token expiry time. + // After the access token expires, one is required to obtain a new one + accessTokenTimeout = 30 * time.Minute - // REFRESH_TOKEN_TIMEOUT - REFRESH_TOKEN_TIMEOUT = 24 * time.Hour + // refreshTokenTimeout shows the refresh token expiry time + refreshTokenTimeout = 24 * time.Hour ) -// SILCommsClient is the client used to make API request to sil communications API -type SILCommsClient struct { +// CommsClient is the client used to make API request to sil communications API +type CommsClient struct { client http.Client refreshToken string @@ -43,8 +44,8 @@ type SILCommsClient struct { } // NewSILCommsClient initializes a new SIL comms client instance -func NewSILCommsClient() *SILCommsClient { - s := &SILCommsClient{ +func NewSILCommsClient() *CommsClient { + s := &CommsClient{ client: http.Client{}, accessToken: "", refreshToken: "", @@ -56,7 +57,7 @@ func NewSILCommsClient() *SILCommsClient { } // executed as a go routine to update the api tokens when they timeout -func (s *SILCommsClient) background() { +func (s *CommsClient) background() { for { select { case t := <-s.refreshTokenTicker.C: @@ -71,34 +72,34 @@ func (s *SILCommsClient) background() { } } -func (s *SILCommsClient) setAccessToken(token string) { +func (s *CommsClient) setAccessToken(token string) { s.accessToken = token if s.accessTokenTicker != nil { - s.accessTokenTicker.Reset(ACCESS_TOKEN_TIMEOUT) + s.accessTokenTicker.Reset(accessTokenTimeout) } else { - s.accessTokenTicker = time.NewTicker(ACCESS_TOKEN_TIMEOUT) + s.accessTokenTicker = time.NewTicker(accessTokenTimeout) } } -func (s *SILCommsClient) setRefreshToken(token string) { +func (s *CommsClient) setRefreshToken(token string) { s.refreshToken = token if s.refreshTokenTicker != nil { - s.refreshTokenTicker.Reset(REFRESH_TOKEN_TIMEOUT) + s.refreshTokenTicker.Reset(refreshTokenTimeout) } else { - s.refreshTokenTicker = time.NewTicker(REFRESH_TOKEN_TIMEOUT) + s.refreshTokenTicker = time.NewTicker(refreshTokenTimeout) } } // login uses the provided credentials to login to the SIL communications backend // It obtains the necessary tokens required to make authenticated requests -func (s *SILCommsClient) login() { +func (s *CommsClient) login() { path := "/auth/token/" payload := struct { Email string `json:"email"` Password string `json:"password"` }{ - Email: COMMS_EMAIL, - Password: COMMS_PASSWORD, + Email: commsEmail, + Password: commsPassword, } response, err := s.MakeRequest(context.Background(), http.MethodPost, path, nil, payload, false) @@ -131,7 +132,7 @@ func (s *SILCommsClient) login() { } -func (s *SILCommsClient) refreshAccessToken() { +func (s *CommsClient) refreshAccessToken() { path := "/auth/token/refresh/" payload := struct { Refresh string `json:"refresh"` @@ -169,8 +170,8 @@ func (s *SILCommsClient) refreshAccessToken() { } // MakeRequest performs a HTTP request to the provided path -func (s *SILCommsClient) MakeRequest(ctx context.Context, method, path string, queryParams map[string]string, body interface{}, authorised bool) (*http.Response, error) { - urlPath := fmt.Sprintf("%s%s", COMMS_BASE_URL, path) +func (s *CommsClient) MakeRequest(ctx context.Context, method, path string, queryParams map[string]string, body interface{}, authorised bool) (*http.Response, error) { + urlPath := fmt.Sprintf("%s%s", commsBaseURL, path) var request *http.Request switch method { diff --git a/client_test.go b/client_test.go index 0410382..90f9bfe 100644 --- a/client_test.go +++ b/client_test.go @@ -42,7 +42,7 @@ func TestSILComms_Login(t *testing.T) { }) } - s := &SILCommsClient{ + s := &CommsClient{ client: tt.fields.client, } s.login() @@ -83,7 +83,7 @@ func TestSILCommsClient_refreshAccessToken(t *testing.T) { }) } - s := &SILCommsClient{ + s := &CommsClient{ client: tt.fields.client, refreshToken: tt.fields.refreshToken, } @@ -175,7 +175,7 @@ func TestSILCommsClient_MakeRequest(t *testing.T) { }) } - s := &SILCommsClient{ + s := &CommsClient{ client: tt.fields.client, accessToken: tt.fields.accessToken, } diff --git a/enums.go b/enums.go index 1253177..da616cd 100644 --- a/enums.go +++ b/enums.go @@ -4,9 +4,12 @@ package silcomms type Status string const ( + // StatusSuccess returns a `success` response StatusSuccess Status = "success" + // StatusFailure returns a `failure` response StatusFailure Status = "failure" - StatusError Status = "error" + // StatusError returns an `error` response + StatusError Status = "error" ) // IsValid returns true if a status is valid diff --git a/sms.go b/sms.go index 52b89ab..d118408 100644 --- a/sms.go +++ b/sms.go @@ -11,8 +11,8 @@ import ( ) var ( - // COMMS_SENDER_ID - COMMS_SENDER_ID = serverutils.MustGetEnvVar("SIL_COMMS_SENDER_ID") + // commsSenderID is the ID used to send the SMS + commsSenderID = serverutils.MustGetEnvVar("SIL_COMMS_SENDER_ID") ) // ICommsClient is the interface for the client to make API request to sil communications @@ -20,14 +20,14 @@ type ICommsClient interface { MakeRequest(ctx context.Context, method, path string, queryParams map[string]string, body interface{}, authorised bool) (*http.Response, error) } -// SILCommsLib is the SDK implementation for interacting with the sil communications API -type SILCommsLib struct { +// CommsLib is the SDK implementation for interacting with the sil communications API +type CommsLib struct { Client ICommsClient } // NewSILCommsLib initializes a new implementation of the SIL Comms SDK -func NewSILCommsLib(client ICommsClient) *SILCommsLib { - l := &SILCommsLib{ +func NewSILCommsLib(client ICommsClient) *CommsLib { + l := &CommsLib{ Client: client, } @@ -37,14 +37,14 @@ func NewSILCommsLib(client ICommsClient) *SILCommsLib { // SendBulkSMS returns a 202 Accepted synchronous response while the API attempts to send the SMS in the background. // An asynchronous call is made to the app's sms_callback URL with a notification that shows the Bulk SMS status. // An asynchronous call is made to the app's sms_callback individually for each of the recipients with the SMS status. -func (l SILCommsLib) SendBulkSMS(ctx context.Context, message string, recipients []string) (*BulkSMSResponse, error) { +func (l CommsLib) SendBulkSMS(ctx context.Context, message string, recipients []string) (*BulkSMSResponse, error) { path := "/v1/sms/bulk/" payload := struct { Sender string `json:"sender"` Message string `json:"message"` Recipients []string `json:"recipients"` }{ - Sender: COMMS_SENDER_ID, + Sender: commsSenderID, Message: message, Recipients: recipients, }