From 89e55e9cd1240c7ab2c3c415965367c07cadfa4f Mon Sep 17 00:00:00 2001 From: Simone Vellei Date: Fri, 15 Sep 2023 08:08:11 +0200 Subject: [PATCH] Add linter (#9) * chore: add linter * fix --- .github/workflows/test.yml | 13 ++- .golangci.yml | 208 +++++++++++++++++++++++++++++++++++++ restclientgo.go | 27 +++-- restclientgo_test.go | 69 +++++++++--- 4 files changed, 291 insertions(+), 26 deletions(-) create mode 100644 .golangci.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a2f4f96..4236aea 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,4 +16,15 @@ jobs: - name: Get dependencies run: go get -v -t -d ./... - name: Run tests - run: go test ./... \ No newline at end of file + run: go test ./... + golanci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/setup-go@v4 + with: + go-version: '1.19' + cache: false + - uses: actions/checkout@v3 + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..abd0e28 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,208 @@ +# Copyright 2013-2023 The Cobra Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +run: + deadline: 5m + + +# This file contains only configs which differ from defaults. +# All possible options can be found here https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml +linters-settings: + cyclop: + # The maximal code complexity to report. + # Default: 10 + max-complexity: 30 + # The maximal average package complexity. + # If it's higher than 0.0 (float) the check is enabled + # Default: 0.0 + package-average: 10.0 + + errcheck: + # Report about not checking of errors in type assertions: `a := b.(MyStruct)`. + # Such cases aren't reported by default. + # Default: false + check-type-assertions: true + + funlen: + # Checks the number of lines in a function. + # If lower than 0, disable the check. + # Default: 60 + lines: 100 + # Checks the number of statements in a function. + # If lower than 0, disable the check. + # Default: 40 + statements: 50 + + gocognit: + # Minimal code complexity to report + # Default: 30 (but we recommend 10-20) + min-complexity: 20 + + gocritic: + # Settings passed to gocritic. + # The settings key is the name of a supported gocritic checker. + # The list of supported checkers can be find in https://go-critic.github.io/overview. + settings: + captLocal: + # Whether to restrict checker to params only. + # Default: true + paramsOnly: false + underef: + # Whether to skip (*x).method() calls where x is a pointer receiver. + # Default: true + skipRecvDeref: false + + gomnd: + # List of function patterns to exclude from analysis. + # Values always ignored: `time.Date` + # Default: [] + ignored-functions: + - os.Chmod + - os.Mkdir + - os.MkdirAll + - os.OpenFile + - os.WriteFile + - prometheus.ExponentialBuckets + - prometheus.ExponentialBucketsRange + - prometheus.LinearBuckets + - strconv.FormatFloat + - strconv.FormatInt + - strconv.FormatUint + - strconv.ParseFloat + - strconv.ParseInt + - strconv.ParseUint + + govet: + # Enable all analyzers. + # Default: false + enable-all: true + # Disable analyzers by name. + # Run `go tool vet help` to see all analyzers. + # Default: [] + disable: + - fieldalignment # too strict + # Settings per analyzer. + settings: + shadow: + # Whether to be strict about shadowing; can be noisy. + # Default: false + strict: true + + nolintlint: + # Exclude following linters from requiring an explanation. + # Default: [] + allow-no-explanation: [ funlen, gocognit, lll ] + # Enable to require an explanation of nonzero length after each nolint directive. + # Default: false + require-explanation: true + # Enable to require nolint directives to mention the specific linter being suppressed. + # Default: false + require-specific: true + + tenv: + # The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures. + # Otherwise, only methods that take `*testing.T`, `*testing.B`, and `testing.TB` as arguments are checked. + # Default: false + all: true + + varcheck: + # Check usage of exported fields and variables. + # Default: false + exported-fields: false # default false # TODO: enable after fixing false positives + +linters: + disable-all: true + enable: + # enabled by default + - errcheck + - gosimple + - govet + - ineffassign + - staticcheck + - typecheck + - unused + # disabled by default + - asciicheck + - bidichk + - bodyclose + - contextcheck + - cyclop + - dupl + - durationcheck + - errname + - errorlint + - execinquery + - exhaustive + - exportloopref + # - forbidigo + - funlen + # - gochecknoinits + - gocognit + - goconst + # - gocritic this can be enabled + - gocyclo + # - godot + - goimports + # - gomnd this can be enabled + - gomoddirectives + - gomodguard + - goprintffuncname + - gosec + - lll + - makezero + - nestif + - nilerr + - nilnil + # - nolintlint this can be enabled + - nosprintfhostport + - predeclared + - promlinter + - revive + - rowserrcheck + - sqlclosecheck + - stylecheck + - tenv + # - testpackage + - tparallel + - unconvert + - unparam + - wastedassign + - whitespace + fast: false + + +issues: + # Maximum count of issues with the same text. + # Set to 0 to disable. + # Default: 3 + max-same-issues: 50 + + exclude-rules: + - source: "^//\\s*go:generate\\s" + linters: [ lll ] + - source: "(noinspection|TODO)" + linters: [ godot ] + - source: "//noinspection" + linters: [ gocritic ] + - source: "^\\s+if _, ok := err\\.\\([^.]+\\.InternalError\\); ok {" + linters: [ errorlint ] + - path: "_test\\.go" + linters: + - bodyclose + - dupl + - funlen + - goconst + - gosec + - noctx + - wrapcheck \ No newline at end of file diff --git a/restclientgo.go b/restclientgo.go index 3430f37..faac770 100644 --- a/restclientgo.go +++ b/restclientgo.go @@ -116,18 +116,18 @@ func (r *RestClient) Patch(ctx context.Context, request Request, response Respon func (r *RestClient) do(ctx context.Context, method httpMethod, request Request, response Response) error { requestPath, err := request.Path() if err != nil { - return fmt.Errorf("%w: %s", ErrRequestPath, err) + return fmt.Errorf("%w: %w", ErrRequestPath, err) } requestEncodedBody, err := request.Encode() if err != nil { - return fmt.Errorf("%w: %s", ErrRequestEncode, err) + return fmt.Errorf("%w: %w", ErrRequestEncode, err) } requestURL := r.endpoint + requestPath httpRequest, err := http.NewRequest(string(method), requestURL, requestEncodedBody) if err != nil { - return fmt.Errorf("%w: %s", ErrHTTPRequest, err) + return fmt.Errorf("%w: %w", ErrHTTPRequest, err) } if request.ContentType() != "" { @@ -142,7 +142,7 @@ func (r *RestClient) do(ctx context.Context, method httpMethod, request Request, httpResponse, err := r.httpClient.Do(httpRequest) if err != nil { - return fmt.Errorf("%w: %s", ErrHTTPRequest, err) + return fmt.Errorf("%w: %w", ErrHTTPRequest, err) } defer httpResponse.Body.Close() @@ -156,14 +156,24 @@ func (r *RestClient) do(ctx context.Context, method httpMethod, request Request, return err } - response.SetStatusCode(httpResponse.StatusCode) + err = response.SetStatusCode(httpResponse.StatusCode) + if err != nil { + return err + } + if httpResponse.StatusCode >= 400 { - response.SetBody(httpResponse.Body) + err = response.SetBody(httpResponse.Body) + if err != nil { + return err + } return nil } if response.AcceptContentType() == "" { - response.SetBody(httpResponse.Body) + err = response.SetBody(httpResponse.Body) + if err != nil { + return err + } return nil } @@ -174,14 +184,13 @@ func (r *RestClient) do(ctx context.Context, method httpMethod, request Request, err = response.Decode(httpResponse.Body) if err != nil { - return fmt.Errorf("%w: %s", ErrResponseDecode, err) + return fmt.Errorf("%w: %w", ErrResponseDecode, err) } return nil } func (r *RestClient) matchContentType(httpResponse *http.Response, response Response) error { - contentTypeToMatch := response.AcceptContentType() contentType := httpResponse.Header.Get("Content-Type") diff --git a/restclientgo_test.go b/restclientgo_test.go index 93fbeea..b96d5bc 100644 --- a/restclientgo_test.go +++ b/restclientgo_test.go @@ -25,10 +25,10 @@ func (r *todoRequest) Path() (string, error) { return "/todos/" + r.ID, nil func (r *todoRequest) Encode() (io.Reader, error) { return nil, nil } func (r *todoRequest) ContentType() string { return "" } func (r *TodoResponse) Decode(body io.Reader) error { - return json.NewDecoder(body).Decode(r) } func (r *TodoResponse) SetBody(body io.Reader) error { + _ = body return nil } func (r *TodoResponse) AcceptContentType() string { return "application/json" } @@ -36,7 +36,10 @@ func (r *TodoResponse) SetStatusCode(code int) error { r.HTTPStatusCode = code return nil } -func (r *TodoResponse) SetHeaders(headers Headers) error { return nil } +func (r *TodoResponse) SetHeaders(headers Headers) error { + _ = headers + return nil +} //--------------------------------------------- @@ -54,10 +57,11 @@ func (r *deletePostRequest) Path() (string, error) { return "/posts/" + fmt.Spri func (r *deletePostRequest) Encode() (io.Reader, error) { return nil, nil } func (r *deletePostRequest) ContentType() string { return "" } func (r *DeletePostResponse) Decode(body io.Reader) error { - + _ = body return nil } func (r *DeletePostResponse) SetBody(body io.Reader) error { + _ = body return nil } func (r *DeletePostResponse) AcceptContentType() string { return "" } @@ -65,7 +69,10 @@ func (r *DeletePostResponse) SetStatusCode(code int) error { r.HTTPStatusCode = code return nil } -func (r *DeletePostResponse) SetHeaders(headers Headers) error { return nil } +func (r *DeletePostResponse) SetHeaders(headers Headers) error { + _ = headers + return nil +} // --------------------------------------------- @@ -95,10 +102,10 @@ func (r *updatePostRequest) Encode() (io.Reader, error) { } func (r *updatePostRequest) ContentType() string { return "application/json; charset=UTF-8" } func (r *UpdatePostResponse) Decode(body io.Reader) error { - return json.NewDecoder(body).Decode(r) } func (r *UpdatePostResponse) SetBody(body io.Reader) error { + _ = body return nil } func (r *UpdatePostResponse) AcceptContentType() string { return "application/json" } @@ -106,7 +113,10 @@ func (r *UpdatePostResponse) SetStatusCode(code int) error { r.HTTPStatusCode = code return nil } -func (r *UpdatePostResponse) SetHeaders(headers Headers) error { return nil } +func (r *UpdatePostResponse) SetHeaders(headers Headers) error { + _ = headers + return nil +} // --------------------------------------------- @@ -137,10 +147,10 @@ func (r *createPostRequest) Encode() (io.Reader, error) { } func (r *createPostRequest) ContentType() string { return "application/json" } func (r *CreatePostResponse) Decode(body io.Reader) error { - return json.NewDecoder(body).Decode(r) } func (r *CreatePostResponse) SetBody(body io.Reader) error { + _ = body return nil } func (r *CreatePostResponse) AcceptContentType() string { return "application/json" } @@ -148,7 +158,10 @@ func (r *CreatePostResponse) SetStatusCode(code int) error { r.HTTPStatusCode = code return nil } -func (r *CreatePostResponse) SetHeaders(headers Headers) error { return nil } +func (r *CreatePostResponse) SetHeaders(headers Headers) error { + _ = headers + return nil +} // --------------------------------------------- @@ -232,8 +245,14 @@ func TestRestClient_Get(t *testing.T) { t.Errorf("RestClient.Get() error = %v, wantErr %v", err, tt.wantErr) } - response := tt.args.response.(*TodoResponse) - wantResponse := tt.wantResponse.(*TodoResponse) + response, ok := tt.args.response.(*TodoResponse) + if !ok { + t.Errorf("Cannot cast response to TodoResponse") + } + wantResponse, ok := tt.wantResponse.(*TodoResponse) + if !ok { + t.Errorf("Cannot cast response to TodoResponse") + } jsonReponse, err := json.Marshal(*response) if err != nil { @@ -355,8 +374,14 @@ func TestRestClient_Patch(t *testing.T) { t.Errorf("RestClient.Patch() error = %v, wantErr %v", err, tt.wantErr) } - response := tt.args.response.(*UpdatePostResponse) - wantResponse := tt.wantResponse.(*UpdatePostResponse) + response, ok := tt.args.response.(*UpdatePostResponse) + if !ok { + t.Errorf("Cannot cast response to UpdatePostResponse") + } + wantResponse, ok := tt.wantResponse.(*UpdatePostResponse) + if !ok { + t.Errorf("Cannot cast wantResponse to UpdatePostResponse") + } jsonReponse, err := json.Marshal(*response) if err != nil { @@ -432,8 +457,14 @@ func TestRestClient_Post(t *testing.T) { t.Errorf("RestClient.Post() error = %v, wantErr %v", err, tt.wantErr) } - response := tt.args.response.(*CreatePostResponse) - wantResponse := tt.wantResponse.(*CreatePostResponse) + response, ok := tt.args.response.(*CreatePostResponse) + if !ok { + t.Errorf("Cannot cast response to CreatePostResponse") + } + wantResponse, ok := tt.wantResponse.(*CreatePostResponse) + if !ok { + t.Errorf("Cannot cast wantResponse to CreatePostResponse") + } jsonReponse, err := json.Marshal(*response) if err != nil { @@ -510,8 +541,14 @@ func TestRestClient_Put(t *testing.T) { t.Errorf("RestClient.Put() error = %v, wantErr %v", err, tt.wantErr) } - response := tt.args.response.(*UpdatePostResponse) - wantResponse := tt.wantResponse.(*UpdatePostResponse) + response, ok := tt.args.response.(*UpdatePostResponse) + if !ok { + t.Errorf("Cannot cast response to UpdatePostResponse") + } + wantResponse, ok := tt.wantResponse.(*UpdatePostResponse) + if !ok { + t.Errorf("Cannot cast wantResponse to UpdatePostResponse") + } jsonReponse, err := json.Marshal(*response) if err != nil {