From f76c5fbfd21371e801e2447efdd66bf9cd1bcc5a Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Fri, 9 Dec 2022 11:00:46 -0500 Subject: [PATCH 1/3] archive mode --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 6f2ca9b..82b0411 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,11 @@ schema [![CircleCI](https://circleci.com/gh/gorilla/mux.svg?style=svg)](https://circleci.com/gh/gorilla/schema) [![Sourcegraph](https://sourcegraph.com/github.com/gorilla/schema/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/schema?badge) +--- + +**The Gorilla project has been archived, and is no longer under active maintainenance. You can read more here: https://github.com/gorilla#gorilla-toolkit** + +--- Package gorilla/schema converts structs to and from form values. From 533474fd7fc8b86fd9884e146a9583f257d5d815 Mon Sep 17 00:00:00 2001 From: Corey Daley Date: Sat, 15 Jul 2023 10:53:51 -0400 Subject: [PATCH 2/3] Update README.md Signed-off-by: Corey Daley --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index 82b0411..6f2ca9b 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,6 @@ schema [![CircleCI](https://circleci.com/gh/gorilla/mux.svg?style=svg)](https://circleci.com/gh/gorilla/schema) [![Sourcegraph](https://sourcegraph.com/github.com/gorilla/schema/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/schema?badge) ---- - -**The Gorilla project has been archived, and is no longer under active maintainenance. You can read more here: https://github.com/gorilla#gorilla-toolkit** - ---- Package gorilla/schema converts structs to and from form values. From 212775c4b75831ca3d86f0b51e8d5867a2329f41 Mon Sep 17 00:00:00 2001 From: Apoorva Jagtap <35304110+apoorvajagtap@users.noreply.github.com> Date: Wed, 26 Jul 2023 12:53:51 +0530 Subject: [PATCH 3/3] [GPT-98] Update go version & add verification/testing tools (#200) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What type of PR is this? (check all applicable) - [ ] Refactor - [ ] Feature - [ ] Bug Fix - [ ] Optimization - [ ] Documentation Update ## Description ## Related Tickets & Documents - Related Issue # - Closes # ## Added/updated tests? - [ ] Yes - [ ] No, and this is why: _please replace this line with details on why tests have not been included_ - [ ] I need help with writing tests ## Run verifications and test - [ ] `make verify` is passing - [ ] `make test` is passing --------- Signed-off-by: Apoorva Jagtap <35304110+apoorvajagtap@users.noreply.github.com> Co-authored-by: Corey Daley --- .circleci/config.yml | 70 ----------------------------------- .editorconfig | 20 ++++++++++ .github/release-drafter.yml | 8 ---- .github/stale.yml | 12 ------ .github/workflows/issues.yml | 20 ++++++++++ .github/workflows/test.yml | 55 ++++++++++++++++++++++++++++ .gitignore | 1 + LICENSE | 2 +- Makefile | 34 +++++++++++++++++ README.md | 13 ++++--- cache.go | 12 +++--- decoder.go | 2 +- decoder_test.go | 71 ++++++++++++++++++++++++++---------- encoder.go | 11 +++++- encoder_test.go | 24 +++++++++--- go.mod | 2 +- 16 files changed, 225 insertions(+), 132 deletions(-) delete mode 100644 .circleci/config.yml create mode 100644 .editorconfig delete mode 100644 .github/release-drafter.yml delete mode 100644 .github/stale.yml create mode 100644 .github/workflows/issues.yml create mode 100644 .github/workflows/test.yml create mode 100644 .gitignore create mode 100644 Makefile diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index d3dab1a..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,70 +0,0 @@ -version: 2.1 - -jobs: - "test": - parameters: - version: - type: string - default: "latest" - golint: - type: boolean - default: true - modules: - type: boolean - default: true - goproxy: - type: string - default: "" - docker: - - image: "circleci/golang:<< parameters.version >>" - working_directory: /go/src/github.com/gorilla/schema - environment: - GO111MODULE: "on" - GOPROXY: "<< parameters.goproxy >>" - steps: - - checkout - - run: - name: "Print the Go version" - command: > - go version - - run: - name: "Fetch dependencies" - command: > - if [[ << parameters.modules >> = true ]]; then - go mod download - export GO111MODULE=on - else - go get -v ./... - fi - # Only run gofmt, vet & lint against the latest Go version - - run: - name: "Run golint" - command: > - if [ << parameters.version >> = "latest" ] && [ << parameters.golint >> = true ]; then - go get -u golang.org/x/lint/golint - golint ./... - fi - - run: - name: "Run gofmt" - command: > - if [[ << parameters.version >> = "latest" ]]; then - diff -u <(echo -n) <(gofmt -d -e .) - fi - - run: - name: "Run go vet" - command: > - if [[ << parameters.version >> = "latest" ]]; then - go vet -v ./... - fi - - run: - name: "Run go test (+ race detector)" - command: > - go test -v -race ./... - -workflows: - tests: - jobs: - - test: - matrix: - parameters: - version: ["latest", "1.15", "1.14", "1.13", "1.12", "1.11"] diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c6b74c3 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +; https://editorconfig.org/ + +root = true + +[*] +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +[{Makefile,go.mod,go.sum,*.go,.gitmodules}] +indent_style = tab +indent_size = 4 + +[*.md] +indent_size = 4 +trim_trailing_whitespace = false + +eclint_indent_style = unset \ No newline at end of file diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml deleted file mode 100644 index 2db2e13..0000000 --- a/.github/release-drafter.yml +++ /dev/null @@ -1,8 +0,0 @@ -# Config for https://github.com/apps/release-drafter -template: | - - - - ## CHANGELOG - - $CHANGES diff --git a/.github/stale.yml b/.github/stale.yml deleted file mode 100644 index de8a678..0000000 --- a/.github/stale.yml +++ /dev/null @@ -1,12 +0,0 @@ -daysUntilStale: 60 -daysUntilClose: 7 -# Issues with these labels will never be considered stale -exemptLabels: - - v2 - - needs-review - - work-required -staleLabel: stale -markComment: > - This issue has been automatically marked as stale because it hasn't seen - a recent update. It'll be automatically closed in a few days. -closeComment: false diff --git a/.github/workflows/issues.yml b/.github/workflows/issues.yml new file mode 100644 index 0000000..055ca82 --- /dev/null +++ b/.github/workflows/issues.yml @@ -0,0 +1,20 @@ +# Add issues or pull-requests created to the project. +name: Add issue or pull request to Project + +on: + issues: + types: + - opened + pull_request: + types: + - opened + +jobs: + add-to-project: + runs-on: ubuntu-latest + steps: + - name: Add issue to project + uses: actions/add-to-project@v0.5.0 + with: + project-url: https://github.com/orgs/gorilla/projects/4 + github-token: ${{ secrets.ADD_TO_PROJECT_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..af48d22 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,55 @@ +name: CI +on: + push: + branches: + - main + pull_request: + branches: + - main + +permissions: + contents: read + +jobs: + verify-and-test: + strategy: + matrix: + go: ['1.19','1.20'] + os: [ubuntu-latest, macos-latest, windows-latest] + fail-fast: true + runs-on: ${{ matrix.os }} + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + - name: Setup Go ${{ matrix.go }} + uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.go }} + cache: false + + - name: Run GolangCI-Lint + uses: golangci/golangci-lint-action@v3 + with: + version: v1.53 + args: --timeout=5m + + - name: Run GoSec + if: matrix.os == 'ubuntu-latest' + uses: securego/gosec@master + with: + args: ./... + + - name: Run GoVulnCheck + uses: golang/govulncheck-action@v1 + with: + go-version-input: ${{ matrix.go }} + go-package: ./... + + - name: Run Tests + run: go test -race -cover -coverprofile=coverage -covermode=atomic -v ./... + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + files: ./coverage \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..84039fe --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +coverage.coverprofile diff --git a/LICENSE b/LICENSE index 0e5fb87..bb9d80b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2012 Rodrigo Moraes. All rights reserved. +Copyright (c) 2023 The Gorilla Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..98f5ab7 --- /dev/null +++ b/Makefile @@ -0,0 +1,34 @@ +GO_LINT=$(shell which golangci-lint 2> /dev/null || echo '') +GO_LINT_URI=github.com/golangci/golangci-lint/cmd/golangci-lint@latest + +GO_SEC=$(shell which gosec 2> /dev/null || echo '') +GO_SEC_URI=github.com/securego/gosec/v2/cmd/gosec@latest + +GO_VULNCHECK=$(shell which govulncheck 2> /dev/null || echo '') +GO_VULNCHECK_URI=golang.org/x/vuln/cmd/govulncheck@latest + +.PHONY: golangci-lint +golangci-lint: + $(if $(GO_LINT), ,go install $(GO_LINT_URI)) + @echo "##### Running golangci-lint" + golangci-lint run -v + +.PHONY: gosec +gosec: + $(if $(GO_SEC), ,go install $(GO_SEC_URI)) + @echo "##### Running gosec" + gosec ./... + +.PHONY: govulncheck +govulncheck: + $(if $(GO_VULNCHECK), ,go install $(GO_VULNCHECK_URI)) + @echo "##### Running govulncheck" + govulncheck ./... + +.PHONY: verify +verify: golangci-lint gosec govulncheck + +.PHONY: test +test: + @echo "##### Running tests" + go test -race -cover -coverprofile=coverage.coverprofile -covermode=atomic -v ./... \ No newline at end of file diff --git a/README.md b/README.md index 6f2ca9b..dbeff3d 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,12 @@ -schema -====== -[![GoDoc](https://godoc.org/github.com/gorilla/schema?status.svg)](https://godoc.org/github.com/gorilla/schema) -[![CircleCI](https://circleci.com/gh/gorilla/mux.svg?style=svg)](https://circleci.com/gh/gorilla/schema) -[![Sourcegraph](https://sourcegraph.com/github.com/gorilla/schema/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/schema?badge) +# gorilla/schema +![testing](https://github.com/gorilla/schema/actions/workflows/test.yml/badge.svg) +[![codecov](https://codecov.io/github/gorilla/schema/branch/main/graph/badge.svg)](https://codecov.io/github/gorilla/schema) +[![godoc](https://godoc.org/github.com/gorilla/schema?status.svg)](https://godoc.org/github.com/gorilla/schema) +[![sourcegraph](https://sourcegraph.com/github.com/gorilla/schema/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/schema?badge) + + +![Gorilla Logo](https://github.com/gorilla/.github/assets/53367916/d92caabf-98e0-473e-bfbf-ab554ba435e5) Package gorilla/schema converts structs to and from form values. diff --git a/cache.go b/cache.go index 0746c12..bf21697 100644 --- a/cache.go +++ b/cache.go @@ -12,7 +12,7 @@ import ( "sync" ) -var invalidPath = errors.New("schema: invalid path") +var errInvalidPath = errors.New("schema: invalid path") // newCache returns a new cache. func newCache() *cache { @@ -53,13 +53,13 @@ func (c *cache) parsePath(p string, t reflect.Type) ([]pathPart, error) { keys := strings.Split(p, ".") for i := 0; i < len(keys); i++ { if t.Kind() != reflect.Struct { - return nil, invalidPath + return nil, errInvalidPath } if struc = c.get(t); struc == nil { - return nil, invalidPath + return nil, errInvalidPath } if field = struc.get(keys[i]); field == nil { - return nil, invalidPath + return nil, errInvalidPath } // Valid field. Append index. path = append(path, field.name) @@ -72,10 +72,10 @@ func (c *cache) parsePath(p string, t reflect.Type) ([]pathPart, error) { // So checking i+2 is not necessary anymore. i++ if i+1 > len(keys) { - return nil, invalidPath + return nil, errInvalidPath } if index64, err = strconv.ParseInt(keys[i], 10, 0); err != nil { - return nil, invalidPath + return nil, errInvalidPath } parts = append(parts, pathPart{ path: path, diff --git a/decoder.go b/decoder.go index 025e438..28b560b 100644 --- a/decoder.go +++ b/decoder.go @@ -193,7 +193,7 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values if v.Type().Kind() == reflect.Struct { for i := 0; i < v.NumField(); i++ { field := v.Field(i) - if field.Type().Kind() == reflect.Ptr && field.IsNil() && v.Type().Field(i).Anonymous == true { + if field.Type().Kind() == reflect.Ptr && field.IsNil() && v.Type().Field(i).Anonymous { field.Set(reflect.New(field.Type().Elem())) } } diff --git a/decoder_test.go b/decoder_test.go index 863891f..f89a4c3 100644 --- a/decoder_test.go +++ b/decoder_test.go @@ -69,9 +69,7 @@ func (id *S19) UnmarshalText(text []byte) error { if len(buf) > len(*id) { return errors.New("out of range") } - for i := range buf { - (*id)[i] = buf[i] - } + copy((*id)[:], buf) return nil } @@ -673,7 +671,10 @@ func TestEmptyValue(t *testing.T) { "F01": {"", "foo"}, } s := &S5{} - NewDecoder().Decode(s, data) + err := NewDecoder().Decode(s, data) + if err != nil { + t.Fatalf("Failed to decode: %v", err) + } if len(s.F01) != 1 { t.Errorf("Expected 1 values in F01") } @@ -706,7 +707,10 @@ func TestUnexportedField(t *testing.T) { "id": {"identifier"}, } s := &S6{} - NewDecoder().Decode(s, data) + err := NewDecoder().Decode(s, data) + if err != nil { + t.Fatalf("Failed to decode: %v", err) + } if s.id != "" { t.Errorf("Unexported field expected to be ignored") } @@ -724,7 +728,10 @@ func TestMultipleValues(t *testing.T) { } s := S7{} - NewDecoder().Decode(&s, data) + err := NewDecoder().Decode(&s, data) + if err != nil { + t.Fatalf("Failed to decode: %v", err) + } if s.ID != "1" { t.Errorf("Last defined value must be used when multiple values for same field are provided") } @@ -742,7 +749,10 @@ func TestSetAliasTag(t *testing.T) { s := S8{} dec := NewDecoder() dec.SetAliasTag("json") - dec.Decode(&s, data) + err := dec.Decode(&s, data) + if err != nil { + t.Fatalf("Failed to decode: %v", err) + } if s.ID != "foo" { t.Fatalf("Bad value: got %q, want %q", s.ID, "foo") } @@ -813,7 +823,10 @@ func TestEmbeddedField(t *testing.T) { "Id": {"identifier"}, } s := &S10{} - NewDecoder().Decode(s, data) + err := NewDecoder().Decode(s, data) + if err != nil { + t.Fatalf("Failed to decode: %v", err) + } if s.Id != "identifier" { t.Errorf("Missing support for embedded fields") } @@ -1148,7 +1161,10 @@ func TestCSVSlice(t *testing.T) { } s := S12A{} - NewDecoder().Decode(&s, data) + err := NewDecoder().Decode(&s, data) + if err != nil { + t.Fatalf("Failed to decode: %v", err) + } if len(s.ID) != 2 { t.Errorf("Expected two values in the result list, got %+v", s.ID) } @@ -1161,14 +1177,17 @@ type S12B struct { ID []string } -//Decode should not split on , into a slice for string only +// Decode should not split on , into a slice for string only func TestCSVStringSlice(t *testing.T) { data := map[string][]string{ "ID": {"0,1"}, } s := S12B{} - NewDecoder().Decode(&s, data) + err := NewDecoder().Decode(&s, data) + if err != nil { + t.Fatalf("Failed to decode: %v", err) + } if len(s.ID) != 1 { t.Errorf("Expected one value in the result list, got %+v", s.ID) } @@ -1177,7 +1196,7 @@ func TestCSVStringSlice(t *testing.T) { } } -//Invalid data provided by client should not panic (github issue 33) +// Invalid data provided by client should not panic (github issue 33) func TestInvalidDataProvidedByClient(t *testing.T) { defer func() { if r := recover(); r != nil { @@ -1186,7 +1205,7 @@ func TestInvalidDataProvidedByClient(t *testing.T) { }() type S struct { - f string + f string // nolint:unused } data := map[string][]string{ @@ -1202,7 +1221,7 @@ func TestInvalidDataProvidedByClient(t *testing.T) { // underlying cause of error in issue 33 func TestInvalidPathInCacheParsePath(t *testing.T) { type S struct { - f string + f string // nolint:unused } typ := reflect.ValueOf(new(S)).Elem().Type() @@ -1218,7 +1237,10 @@ func TestDecodeToTypedField(t *testing.T) { type Aa bool s1 := &struct{ Aa }{} v1 := map[string][]string{"Aa": {"true"}} - NewDecoder().Decode(s1, v1) + err := NewDecoder().Decode(s1, v1) + if err != nil { + t.Fatalf("Failed to decode: %v", err) + } if s1.Aa != Aa(true) { t.Errorf("s1: expected %v, got %v", true, s1.Aa) } @@ -1238,7 +1260,10 @@ func TestRegisterConverter(t *testing.T) { decoder.RegisterConverter(s1.Bb, func(s string) reflect.Value { return reflect.ValueOf(2) }) v1 := map[string][]string{"Aa": {"4"}, "Bb": {"5"}} - decoder.Decode(s1, v1) + err := decoder.Decode(s1, v1) + if err != nil { + t.Fatalf("Failed to decode: %v", err) + } if s1.Aa != Aa(1) { t.Errorf("s1.Aa: expected %v, got %v", 1, s1.Aa) @@ -1260,9 +1285,12 @@ func TestRegisterConverterSlice(t *testing.T) { }{} expected := []string{"one", "two", "three"} - decoder.Decode(&result, map[string][]string{ + err := decoder.Decode(&result, map[string][]string{ "multiple": []string{"one,two,three"}, }) + if err != nil { + t.Fatalf("Failed to decode: %v", err) + } for i := range expected { if got, want := expected[i], result.Multiple[i]; got != want { t.Errorf("%d: got %s, want %s", i, got, want) @@ -1868,8 +1896,11 @@ func TestRegisterConverterOverridesTextUnmarshaler(t *testing.T) { ts := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC) decoder.RegisterConverter(s1.MyTime, func(s string) reflect.Value { return reflect.ValueOf(ts) }) - v1 := map[string][]string{"MyTime": {"4"}, "Bb": {"5"}} - decoder.Decode(s1, v1) + v1 := map[string][]string{"MyTime": {"4"}} + err := decoder.Decode(s1, v1) + if err != nil { + t.Fatalf("Failed to decode: %v", err) + } if s1.MyTime != MyTime(ts) { t.Errorf("s1.Aa: expected %v, got %v", ts, s1.MyTime) @@ -1951,7 +1982,7 @@ func TestTextUnmarshalerTypeSliceOfStructs(t *testing.T) { sb := struct { Value S21B }{} - if err := decoder.Decode(&sb, data); err == invalidPath { + if err := decoder.Decode(&sb, data); err == errInvalidPath { t.Fatal("Expecting invalid path error", err) } } diff --git a/encoder.go b/encoder.go index 8f9b9e0..51f0a78 100644 --- a/encoder.go +++ b/encoder.go @@ -3,6 +3,7 @@ package schema import ( "errors" "fmt" + "log" "reflect" "strconv" ) @@ -94,7 +95,10 @@ func (e *Encoder) encode(v reflect.Value, dst map[string][]string) error { // Encode struct pointer types if the field is a valid pointer and a struct. if isValidStructPointer(v.Field(i)) && !e.hasCustomEncoder(v.Field(i).Type()) { - e.encode(v.Field(i).Elem(), dst) + err := e.encode(v.Field(i).Elem(), dst) + if err != nil { + log.Fatal(err) + } continue } @@ -112,7 +116,10 @@ func (e *Encoder) encode(v reflect.Value, dst map[string][]string) error { } if v.Field(i).Type().Kind() == reflect.Struct { - e.encode(v.Field(i), dst) + err := e.encode(v.Field(i), dst) + if err != nil { + log.Fatal(err) + } continue } diff --git a/encoder_test.go b/encoder_test.go index 49f7fa4..07f3776 100644 --- a/encoder_test.go +++ b/encoder_test.go @@ -205,7 +205,7 @@ func TestCompatSlices(t *testing.T) { t.Errorf("Dncoder has non-nil error: %v", err) } - if len(src.Ints) != len(dst.Ints) || len(src.Ones) != len(src.Ones) { + if len(src.Ints) != len(dst.Ints) || len(src.Ones) != len(dst.Ones) { t.Fatalf("Expected %v, got %v", src, dst) } @@ -344,7 +344,10 @@ func TestEncoderSetAliasTag(t *testing.T) { } encoder := NewEncoder() encoder.SetAliasTag("json") - encoder.Encode(&s, data) + err := encoder.Encode(&s, data) + if err != nil { + t.Fatalf("Failed to encode: %v", err) + } valExists(t, "id", "foo", data) } @@ -376,7 +379,10 @@ func TestEncoderWithOmitempty(t *testing.T) { } encoder := NewEncoder() - encoder.Encode(&s, vals) + err := encoder.Encode(&s, vals) + if err != nil { + t.Fatalf("Failed to encode: %v", err) + } valNotExists(t, "f01", vals) valExists(t, "f02", "test", vals) @@ -402,7 +408,10 @@ func TestStructPointer(t *testing.T) { } encoder := NewEncoder() - encoder.Encode(&s, vals) + err := encoder.Encode(&s, vals) + if err != nil { + t.Fatalf("Failed to encode: %v", err) + } valExists(t, "F12", "2", vals) valExists(t, "F02", "null", vals) valNotExists(t, "F03", vals) @@ -428,7 +437,10 @@ func TestRegisterEncoderCustomArrayType(t *testing.T) { return fmt.Sprint(value.Interface()) }) - encoder.Encode(s, vals) + err := encoder.Encode(ss[s], vals) + if err != nil { + t.Fatalf("Failed to encode: %v", err) + } } } @@ -466,7 +478,7 @@ func TestRegisterEncoderStructIsZero(t *testing.T) { t.Error("expected tim1 to be present") } - if "2020-08-04T13:30:01Z" != ta[0] { + if ta[0] != "2020-08-04T13:30:01Z" { t.Error("expected correct tim1 time") } diff --git a/go.mod b/go.mod index 200e4fa..817d386 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module github.com/gorilla/schema -go 1.14 +go 1.19