Skip to content

Commit

Permalink
cover all critical parts with tests
Browse files Browse the repository at this point in the history
- enhance error reporting

Signed-off-by: Silvio Ginter <[email protected]>
  • Loading branch information
sGy1980de committed Dec 19, 2024
1 parent ca08691 commit 9056bce
Show file tree
Hide file tree
Showing 8 changed files with 1,339 additions and 57 deletions.
5 changes: 3 additions & 2 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func MakeConfig(svc *sheets.Service, spreadsheetID string, opts ...ConfigOption)
type ConfigOption func(*config)

// WithContext sets the given context for the Config.
// This context will be used for all API calls, and cancellation will be respected during iteration.
func WithContext(ctx context.Context) ConfigOption {
return func(c *config) {
c.ctx = ctx
Expand All @@ -64,7 +65,7 @@ func WithSpreadsheetID(id string) ConfigOption {
}
}

// WithSheetName sets the sheet-name for the Config.
// WithSheetName sets the sheet-name for the Config.
func WithSheetName(name string) ConfigOption {
return func(c *config) {
c.sheetName = name
Expand All @@ -80,7 +81,7 @@ func WithTagName(name string) ConfigOption {
}
}

// WithDatetimeFormats allows to define additional date-time formats to be recognized.
// WithDatetimeFormats allows to define additional date-time formats to be recognized during the parsing.
func WithDatetimeFormats(formats ...string) ConfigOption {
return func(c *config) {
c.datetimeFormats = formats
Expand Down
50 changes: 47 additions & 3 deletions errors.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package gsheets

import "errors"
import (
"errors"
"fmt"
"reflect"
"strings"
)

var (
// ErrNoService is returned when no Google API service is registered to the Config.
Expand All @@ -15,6 +20,45 @@ var (
ErrFieldNotFoundInSheet = errors.New("gsheets: field not found in sheet")
// ErrFieldNotFoundInStruct is returned when a field/column is not found in the struct.
ErrFieldNotFoundInStruct = errors.New("gsheets: field not found in struct")
// ErrInvalidDateTimeFormat is returned when a datetime format is invalid, or not configured.
ErrInvalidDateTimeFormat = errors.New("gsheets: invalid datetime format")
)

// InvalidDateTimeFormatError is returned when an invalid datetime format is encountered.
type InvalidDateTimeFormatError struct {
CV string
Formats []string
}

func (e *InvalidDateTimeFormatError) Error() string {
return fmt.Sprintf("gsheets: invalid datetime format in value %q, recognized formats are: [\"%v\"]", e.CV, strings.Join(e.Formats, `", "`))
}

// ConvertError is returned when a conversion error occurs.
type ConvertError struct {
Typ reflect.Kind
CV string
err error
}

func (e *ConvertError) Error() string {
return fmt.Sprintf("gsheets: conversion error, could not convert value %q into Go type %q", e.CV, e.Typ.String())
}

func (e *ConvertError) Unwrap() error {
return e.err
}

// MappingError is returned when an error is encountered during the mapping.
type MappingError struct {
Sheet string
Cell string
Field string
err error
}

func (e *MappingError) Error() string {
return fmt.Sprintf("%s\n\tsheet: %q\n\tcell: %q\n\tfield: %q", e.err, e.Sheet, e.Cell, e.Field)
}

func (e *MappingError) Unwrap() error {
return e.err
}
56 changes: 56 additions & 0 deletions errors_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package gsheets

import (
"errors"
"reflect"
"testing"

"github.com/stretchr/testify/assert"
)

func TestConvertError_Error(t *testing.T) {
innerErr := errors.New("inner error")
err := &ConvertError{
CV: "test",
Typ: reflect.Bool,
err: innerErr,
}
assert.Equal(t, `gsheets: conversion error, could not convert value "test" into Go type "bool"`, err.Error())
}

func TestConvertError_Unwrap(t *testing.T) {
expectedErr := errors.New("inner error")
err := &ConvertError{err: expectedErr}
assert.Equal(t, expectedErr, err.Unwrap())
}

func TestInvalidDateTimeFormatError_Error(t *testing.T) {
err := &InvalidDateTimeFormatError{
CV: "2024-12-31",
Formats: []string{"2.1.2006", "1/2/2006"},
}
assert.Equal(t, `gsheets: invalid datetime format in value "2024-12-31", recognized formats are: ["2.1.2006", "1/2/2006"]`, err.Error())
}

func TestMappingError_Error(t *testing.T) {
innerErr := errors.New("inner error")
err := &MappingError{
Sheet: "test",
Cell: "A1",
Field: "Type.Field",
err: innerErr,
}

const msg = `inner error
sheet: "test"
cell: "A1"
field: "Type.Field"`

assert.Equal(t, msg, err.Error())
}

func TestMappingError_Unwrap(t *testing.T) {
expectedErr := errors.New("inner error")
err := &MappingError{err: expectedErr}
assert.Equal(t, expectedErr, err.Unwrap())
}
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.23

require (
github.com/gertd/go-pluralize v0.2.1
github.com/stretchr/testify v1.10.0
golang.org/x/oauth2 v0.24.0
google.golang.org/api v0.213.0
)
Expand All @@ -12,13 +13,15 @@ require (
cloud.google.com/go/auth v0.13.0 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect
cloud.google.com/go/compute/metadata v0.6.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/s2a-go v0.1.8 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
github.com/googleapis/gax-go/v2 v2.14.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
go.opentelemetry.io/otel v1.33.0 // indirect
Expand All @@ -32,4 +35,5 @@ require (
google.golang.org/genproto/googleapis/rpc v0.0.0-20241216192217-9240e9c98484 // indirect
google.golang.org/grpc v1.69.2 // indirect
google.golang.org/protobuf v1.36.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,14 @@ github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gT
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
github.com/googleapis/gax-go/v2 v2.14.0 h1:f+jMrjBPl+DL9nI4IQzLUxMq7XrAqFYB7hBPqMNIe8o=
github.com/googleapis/gax-go/v2 v2.14.0/go.mod h1:lhBCnjdLrWRaPvLWhmc8IS24m9mr07qSYnHncrgo+zk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
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/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
Expand Down Expand Up @@ -67,5 +73,8 @@ google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU=
google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ=
google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
12 changes: 8 additions & 4 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,8 @@ func parseSheet[T any](cfg Config, opts []ConfigOption) (iter.Seq2[int, Result[T

fillEmptyValues(resp)

ctx := cfg.Context()
return func(yield func(int, Result[T]) bool) {
ctx := cfg.Context()

rows:
for i, row := range resp.Values[1:] {
select {
Expand All @@ -78,7 +77,12 @@ func parseSheet[T any](cfg Config, opts []ConfigOption) (iter.Seq2[int, Result[T
for _, mapping := range mappings {
val, nonEmpty, err := mapping.convert(row[mapping.colIndex].(string), cfg.datetimeFormats)
if err != nil {
err = fmt.Errorf("%s: %s%d: %w", cfg.sheetName, columnName(mapping.colIndex), rowIdx, err)
err = &MappingError{
Sheet: cfg.sheetName,
Cell: fmt.Sprintf("%s%d", columnName(mapping.colIndex), rowIdx),
Field: mapping.typeName + "." + mapping.field.Name,
err: err,
}
if !yield(rowIdx, Result[T]{Err: err}) {
return
}
Expand All @@ -102,7 +106,7 @@ func parseSheet[T any](cfg Config, opts []ConfigOption) (iter.Seq2[int, Result[T
}
}
}
}, len(resp.Values[1:]), nil
}, len(resp.Values[1:]), ctx.Err()
}

func fillEmptyValues(data *sheets.ValueRange) {
Expand Down
Loading

0 comments on commit 9056bce

Please sign in to comment.