From 9087e7e327e1bc3df94997aea96b6f442bc91cf8 Mon Sep 17 00:00:00 2001 From: Marcelo Petrucio Date: Thu, 9 May 2024 13:09:13 -0300 Subject: [PATCH 1/4] General Improvements --- cmd/main.go | 26 ++++++--- go.mod | 8 --- internal/service/apicep/apicep_impl.go | 14 ++++- internal/service/brasilapi/brasilapi_impl.go | 11 +++- internal/service/correios/correios_impl.go | 5 +- internal/service/viacep/viacep_impl.go | 11 +++- .../widenet/{widenet.go => widenet_impl.go} | 15 ++++-- lagoinha.go | 54 ++++++++++++++++--- pkg/errors/cep.go | 10 ++++ pkg/errors/http_error.go | 12 +++++ 10 files changed, 139 insertions(+), 27 deletions(-) rename internal/service/widenet/{widenet.go => widenet_impl.go} (72%) create mode 100644 pkg/errors/http_error.go diff --git a/cmd/main.go b/cmd/main.go index e425140..3d30c53 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -2,20 +2,34 @@ package main import ( "fmt" + "log" "github.com/igorhalfeld/lagoinha" ) func main() { fmt.Println("Total amount of cep providers:", lagoinha.GetTotalAmountOfCepProviders()) - chResp, chErr := lagoinha.GetAddress("04568000", &lagoinha.GetAddressOptions{ + address, err := lagoinha.GetAddressWithoutChannel("15809240", &lagoinha.GetAddressOptions{ PreferenceForAPI: "Apicep", }) - select { - case address := <-chResp: - fmt.Printf("Response: %+v\n", address) - case err := <-chErr: - fmt.Printf("Error: %+v\n", err) + if err != nil { + log.Fatalf("Error: %+v\n", err) } + + fmt.Printf("Response: %+v\n", address) } + +// func main() { +// fmt.Println("Total amount of cep providers:", lagoinha.GetTotalAmountOfCepProviders()) +// chResp, chErr := lagoinha.GetAddress("04568000", &lagoinha.GetAddressOptions{ +// PreferenceForAPI: "Apicep", +// }) + +// select { +// case address := <-chResp: +// fmt.Printf("Response: %+v\n", address) +// case err := <-chErr: +// fmt.Printf("Error: %+v\n", err) +// } +// } diff --git a/go.mod b/go.mod index 998d895..5866360 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,3 @@ module github.com/igorhalfeld/lagoinha go 1.12 - -require ( - github.com/fatih/color v1.10.0 // indirect - github.com/fogleman/gg v1.3.0 // indirect - github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect - github.com/jpoles1/gopherbadger v2.4.0+incompatible // indirect - golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb // indirect -) diff --git a/internal/service/apicep/apicep_impl.go b/internal/service/apicep/apicep_impl.go index d68ec93..4fb25ed 100644 --- a/internal/service/apicep/apicep_impl.go +++ b/internal/service/apicep/apicep_impl.go @@ -3,6 +3,7 @@ package apicep import ( "encoding/json" "net/http" + "time" "github.com/igorhalfeld/lagoinha/internal/entity" "github.com/igorhalfeld/lagoinha/pkg/errors" @@ -18,18 +19,29 @@ func New() *ApicepService { func (wn *ApicepService) Request(cep string) (*entity.Cep, error) { result := apicepResponse{} - res, err := http.Get("https://ws.apicep.com/cep/" + cep + ".json") + client := &http.Client{ + Timeout: time.Second * 2, + } + + res, err := client.Get("https://ws.apicep.com/cep/" + cep + ".json") if err != nil { return nil, err } defer res.Body.Close() + if res.StatusCode != 200 { + return nil, errors.HttpError(res.StatusCode) + } + err = json.NewDecoder(res.Body).Decode(&result) if err != nil { return nil, err } + if result.Status != 200 { + return nil, errors.HttpError(result.Status) + } return wn.formater(&result) } diff --git a/internal/service/brasilapi/brasilapi_impl.go b/internal/service/brasilapi/brasilapi_impl.go index 111cd63..61027e2 100644 --- a/internal/service/brasilapi/brasilapi_impl.go +++ b/internal/service/brasilapi/brasilapi_impl.go @@ -3,6 +3,7 @@ package brasilapi import ( "encoding/json" "net/http" + "time" "github.com/igorhalfeld/lagoinha/internal/entity" "github.com/igorhalfeld/lagoinha/pkg/errors" @@ -18,13 +19,21 @@ func New() *BrasilAPIService { func (ba *BrasilAPIService) Request(cep string) (*entity.Cep, error) { result := brasilAPIResponse{} - res, err := http.Get("https://brasilapi.com.br/api/cep/v1/" + cep) + client := &http.Client{ + Timeout: time.Second * 2, + } + + res, err := client.Get("https://brasilapi.com.br/api/cep/v1/" + cep) if err != nil { return nil, err } defer res.Body.Close() + if res.StatusCode != 200 { + return nil, errors.HttpError(res.StatusCode) + } + err = json.NewDecoder(res.Body).Decode(&result) if err != nil { return nil, err diff --git a/internal/service/correios/correios_impl.go b/internal/service/correios/correios_impl.go index 583bd29..1a8ad99 100644 --- a/internal/service/correios/correios_impl.go +++ b/internal/service/correios/correios_impl.go @@ -5,6 +5,7 @@ import ( "encoding/xml" "errors" "net/http" + "time" "github.com/igorhalfeld/lagoinha/internal/entity" ) @@ -18,7 +19,9 @@ func New() *CorreiosService { // Request - fetch data from correios api func (cs *CorreiosService) Request(cep string) (*entity.Cep, error) { const proxyURL = "https://proxier.now.sh/" - client := &http.Client{} + client := &http.Client{ + Timeout: time.Second * 2, + } result := correiosResponse{} diff --git a/internal/service/viacep/viacep_impl.go b/internal/service/viacep/viacep_impl.go index 6a0b647..d957864 100644 --- a/internal/service/viacep/viacep_impl.go +++ b/internal/service/viacep/viacep_impl.go @@ -3,6 +3,7 @@ package viacep import ( "encoding/json" "net/http" + "time" "github.com/igorhalfeld/lagoinha/internal/entity" "github.com/igorhalfeld/lagoinha/pkg/errors" @@ -20,13 +21,21 @@ func New() *ViaCepService { func (vc *ViaCepService) Request(cep string) (*entity.Cep, error) { result := viaCepResponse{} - res, err := http.Get("https://viacep.com.br/ws/" + cep + "/json/") + client := &http.Client{ + Timeout: time.Second * 2, + } + + res, err := client.Get("https://viacep.com.br/ws/" + cep + "/json/") if err != nil { return nil, err } defer res.Body.Close() + if res.StatusCode != 200 { + return nil, errors.HttpError(res.StatusCode) + } + err = json.NewDecoder(res.Body).Decode(&result) if err != nil { return nil, err diff --git a/internal/service/widenet/widenet.go b/internal/service/widenet/widenet_impl.go similarity index 72% rename from internal/service/widenet/widenet.go rename to internal/service/widenet/widenet_impl.go index e42d83f..7a18b7d 100644 --- a/internal/service/widenet/widenet.go +++ b/internal/service/widenet/widenet_impl.go @@ -2,10 +2,11 @@ package widenet import ( "encoding/json" - "errors" "net/http" + "time" "github.com/igorhalfeld/lagoinha/internal/entity" + "github.com/igorhalfeld/lagoinha/pkg/errors" ) type WidenetService struct{} @@ -18,13 +19,21 @@ func New() *WidenetService { func (wn *WidenetService) Request(cep string) (*entity.Cep, error) { result := widenetResponse{} - res, err := http.Get("http://apps.widenet.com.br/busca-cep/api/cep/" + cep + ".json") + client := &http.Client{ + Timeout: time.Second * 2, + } + + res, err := client.Get("http://apps.widenet.com.br/busca-cep/api/cep/" + cep + ".json") if err != nil { return nil, err } defer res.Body.Close() + if res.StatusCode != 200 { + return nil, errors.HttpError(res.StatusCode) + } + err = json.NewDecoder(res.Body).Decode(&result) if err != nil { return nil, err @@ -35,7 +44,7 @@ func (wn *WidenetService) Request(cep string) (*entity.Cep, error) { func (wn *WidenetService) formater(r *widenetResponse) (*entity.Cep, error) { if r == nil { - return nil, errors.New("Cep not found") + return nil, errors.CepNotFoundError } cep := &entity.Cep{ diff --git a/lagoinha.go b/lagoinha.go index 6276176..7d465ce 100644 --- a/lagoinha.go +++ b/lagoinha.go @@ -1,11 +1,15 @@ package lagoinha import ( + "context" + "time" + "github.com/igorhalfeld/lagoinha/internal/entity" "github.com/igorhalfeld/lagoinha/internal/service" "github.com/igorhalfeld/lagoinha/internal/service/apicep" "github.com/igorhalfeld/lagoinha/internal/service/brasilapi" "github.com/igorhalfeld/lagoinha/internal/service/viacep" + "github.com/igorhalfeld/lagoinha/internal/service/widenet" "github.com/igorhalfeld/lagoinha/pkg/errors" ) @@ -13,6 +17,7 @@ var providers = map[string]service.Provider{ "BrasilAPI": brasilapi.New(), "ViaCEP": viacep.New(), "Apicep": apicep.New(), + "WidNet": widenet.New(), } // GetTotalAmountOfCepProviders returns amount of current enabled cep provivers @@ -33,20 +38,36 @@ func getAddress(cepRaw string, opts *GetAddressOptions, chResponse chan *entity. // TODO: enhance this way of handling options if opts != nil { if opts.PreferenceForAPI != "" { + chResponseInternal := make(chan *entity.Cep, GetTotalAmountOfCepProviders()) + chErrorInternal := make(chan error) + provider, ok := providers[opts.PreferenceForAPI] if !ok { chError <- errors.PreferenceProviderNotFound return } - c, err := provider.Request(cep.Cep) - if err != nil { + ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) + defer cancel() + + go func() { + c, err := provider.Request(cep.Cep) + if err != nil { + chErrorInternal <- err + return + } + + chResponseInternal <- c + }() + + select { + case address := <-chResponseInternal: + chResponse <- address + case err := <-chErrorInternal: chError <- err - return + case <-ctx.Done(): + cancel() } - - chResponse <- c - return } } @@ -77,3 +98,24 @@ func GetAddress(cepRaw string, opts *GetAddressOptions) (chan *entity.Cep, chan return chResponse, chError } + +func GetAddressWithoutChannel(cepRaw string, opts *GetAddressOptions) (*entity.Cep, error) { + totalAmountOfCepProvider := GetTotalAmountOfCepProviders() + chResponse := make(chan *entity.Cep, totalAmountOfCepProvider) + chError := make(chan error) + + go getAddress(cepRaw, opts, chResponse, chError) + + var err error + i := 0 + for i < totalAmountOfCepProvider { + select { + case address := <-chResponse: + return address, nil + case err = <-chError: + i++ + } + } + + return nil, err +} diff --git a/pkg/errors/cep.go b/pkg/errors/cep.go index f7d3dcc..06e342b 100644 --- a/pkg/errors/cep.go +++ b/pkg/errors/cep.go @@ -5,6 +5,16 @@ var CepNotFoundError error = &LagoinhaError{ Message: "cep not found", } +var TooManyRequestsError error = &LagoinhaError{ + Type: ApplicationError, + Message: "too many requests error", +} + +var InternalServerError error = &LagoinhaError{ + Type: ApplicationError, + Message: "internal server error", +} + var CepNotValidError error = &LagoinhaError{ Type: ValidationError, Message: "cep not valid", diff --git a/pkg/errors/http_error.go b/pkg/errors/http_error.go new file mode 100644 index 0000000..0a73c67 --- /dev/null +++ b/pkg/errors/http_error.go @@ -0,0 +1,12 @@ +package errors + +func HttpError(statusCode int) error { + switch statusCode { + case 429: + return TooManyRequestsError + case 500: + return InternalServerError + default: + return CepNotFoundError + } +} From 6390c436862094b1be5e516a3c2e4c62a71d4d40 Mon Sep 17 00:00:00 2001 From: Marcelo Petrucio Date: Tue, 21 May 2024 00:49:15 -0300 Subject: [PATCH 2/4] Melhorias propostas pelo IgorHalfeld --- cmd/example_sync/main.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 cmd/example_sync/main.go diff --git a/cmd/example_sync/main.go b/cmd/example_sync/main.go new file mode 100644 index 0000000..8c48463 --- /dev/null +++ b/cmd/example_sync/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "fmt" + "log" + + "github.com/igorhalfeld/lagoinha" +) + +func main() { + fmt.Println("Total amount of cep providers:", lagoinha.GetTotalAmountOfCepProviders()) + address, err := lagoinha.GetAddressSync("15809240", &lagoinha.GetAddressOptions{ + PreferenceForAPI: "Apicep", + }) + + if err != nil { + log.Fatalf("Error: %+v\n", err) + } + + fmt.Printf("Response: %+v\n", address) +} From 102fbe0ad342cbf202baa1b2624499a86f91d8ea Mon Sep 17 00:00:00 2001 From: Marcelo Petrucio Date: Tue, 21 May 2024 00:58:11 -0300 Subject: [PATCH 3/4] Melhorias no retorno de erro do GetAddressSync --- Makefile | 7 +- cmd/example_async/main.go | 21 +++ cmd/main.go | 35 ----- internal/service/apicep/apicep_impl.go | 19 ++- internal/service/brasilapi/brasilapi_impl.go | 9 +- internal/service/correios/correios_impl.go | 16 +- internal/service/viacep/viacep_impl.go | 9 +- internal/service/widenet/widenet_impl.go | 9 +- lagoinha copy.txt | 155 +++++++++++++++++++ lagoinha.go | 29 +++- pkg/errors/app.go | 5 + pkg/errors/http_error.go | 12 -- 12 files changed, 263 insertions(+), 63 deletions(-) create mode 100644 cmd/example_async/main.go delete mode 100644 cmd/main.go create mode 100644 lagoinha copy.txt delete mode 100644 pkg/errors/http_error.go diff --git a/Makefile b/Makefile index 0b9a9b4..b591620 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,8 @@ -run-example: - go run cmd/main.go +run-example-async: + go run cmd/example_async/main.go + +run-example-sync: + go run cmd/example_sync/main.go test: APP_ENV=testing CONFIG_DIR=${PWD} go test -v -cover -count 1 -failfast ./... diff --git a/cmd/example_async/main.go b/cmd/example_async/main.go new file mode 100644 index 0000000..e425140 --- /dev/null +++ b/cmd/example_async/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "fmt" + + "github.com/igorhalfeld/lagoinha" +) + +func main() { + fmt.Println("Total amount of cep providers:", lagoinha.GetTotalAmountOfCepProviders()) + chResp, chErr := lagoinha.GetAddress("04568000", &lagoinha.GetAddressOptions{ + PreferenceForAPI: "Apicep", + }) + + select { + case address := <-chResp: + fmt.Printf("Response: %+v\n", address) + case err := <-chErr: + fmt.Printf("Error: %+v\n", err) + } +} diff --git a/cmd/main.go b/cmd/main.go deleted file mode 100644 index 3d30c53..0000000 --- a/cmd/main.go +++ /dev/null @@ -1,35 +0,0 @@ -package main - -import ( - "fmt" - "log" - - "github.com/igorhalfeld/lagoinha" -) - -func main() { - fmt.Println("Total amount of cep providers:", lagoinha.GetTotalAmountOfCepProviders()) - address, err := lagoinha.GetAddressWithoutChannel("15809240", &lagoinha.GetAddressOptions{ - PreferenceForAPI: "Apicep", - }) - - if err != nil { - log.Fatalf("Error: %+v\n", err) - } - - fmt.Printf("Response: %+v\n", address) -} - -// func main() { -// fmt.Println("Total amount of cep providers:", lagoinha.GetTotalAmountOfCepProviders()) -// chResp, chErr := lagoinha.GetAddress("04568000", &lagoinha.GetAddressOptions{ -// PreferenceForAPI: "Apicep", -// }) - -// select { -// case address := <-chResp: -// fmt.Printf("Response: %+v\n", address) -// case err := <-chErr: -// fmt.Printf("Error: %+v\n", err) -// } -// } diff --git a/internal/service/apicep/apicep_impl.go b/internal/service/apicep/apicep_impl.go index 4fb25ed..b0860f5 100644 --- a/internal/service/apicep/apicep_impl.go +++ b/internal/service/apicep/apicep_impl.go @@ -31,7 +31,14 @@ func (wn *ApicepService) Request(cep string) (*entity.Cep, error) { defer res.Body.Close() if res.StatusCode != 200 { - return nil, errors.HttpError(res.StatusCode) + switch res.StatusCode { + case http.StatusTooManyRequests: + return nil, errors.TooManyRequestsError + case http.StatusInternalServerError: + return nil, errors.InternalServerError + default: + return nil, errors.CepNotFoundError + } } err = json.NewDecoder(res.Body).Decode(&result) @@ -40,8 +47,16 @@ func (wn *ApicepService) Request(cep string) (*entity.Cep, error) { } if result.Status != 200 { - return nil, errors.HttpError(result.Status) + switch result.Status { + case http.StatusTooManyRequests: + return nil, errors.TooManyRequestsError + case http.StatusInternalServerError: + return nil, errors.InternalServerError + default: + return nil, errors.CepNotFoundError + } } + return wn.formater(&result) } diff --git a/internal/service/brasilapi/brasilapi_impl.go b/internal/service/brasilapi/brasilapi_impl.go index 61027e2..2823035 100644 --- a/internal/service/brasilapi/brasilapi_impl.go +++ b/internal/service/brasilapi/brasilapi_impl.go @@ -31,7 +31,14 @@ func (ba *BrasilAPIService) Request(cep string) (*entity.Cep, error) { defer res.Body.Close() if res.StatusCode != 200 { - return nil, errors.HttpError(res.StatusCode) + switch res.StatusCode { + case http.StatusTooManyRequests: + return nil, errors.TooManyRequestsError + case http.StatusInternalServerError: + return nil, errors.InternalServerError + default: + return nil, errors.CepNotFoundError + } } err = json.NewDecoder(res.Body).Decode(&result) diff --git a/internal/service/correios/correios_impl.go b/internal/service/correios/correios_impl.go index 1a8ad99..127c1d9 100644 --- a/internal/service/correios/correios_impl.go +++ b/internal/service/correios/correios_impl.go @@ -3,11 +3,12 @@ package correios import ( "bytes" "encoding/xml" - "errors" + "net/http" "time" "github.com/igorhalfeld/lagoinha/internal/entity" + "github.com/igorhalfeld/lagoinha/pkg/errors" ) type CorreiosService struct{} @@ -51,6 +52,17 @@ func (cs *CorreiosService) Request(cep string) (*entity.Cep, error) { defer res.Body.Close() + if res.StatusCode != 200 { + switch res.StatusCode { + case http.StatusTooManyRequests: + return nil, errors.TooManyRequestsError + case http.StatusInternalServerError: + return nil, errors.InternalServerError + default: + return nil, errors.CepNotFoundError + } + } + err = xml.NewDecoder(res.Body).Decode(&result) if err != nil { return nil, err @@ -61,7 +73,7 @@ func (cs *CorreiosService) Request(cep string) (*entity.Cep, error) { func (cs *CorreiosService) formater(r *correiosResponse) (*entity.Cep, error) { if r == nil { - return nil, errors.New("Cep not found") + return nil, errors.CepNotFoundError } cep := &entity.Cep{ diff --git a/internal/service/viacep/viacep_impl.go b/internal/service/viacep/viacep_impl.go index d957864..2024488 100644 --- a/internal/service/viacep/viacep_impl.go +++ b/internal/service/viacep/viacep_impl.go @@ -33,7 +33,14 @@ func (vc *ViaCepService) Request(cep string) (*entity.Cep, error) { defer res.Body.Close() if res.StatusCode != 200 { - return nil, errors.HttpError(res.StatusCode) + switch res.StatusCode { + case http.StatusTooManyRequests: + return nil, errors.TooManyRequestsError + case http.StatusInternalServerError: + return nil, errors.InternalServerError + default: + return nil, errors.CepNotFoundError + } } err = json.NewDecoder(res.Body).Decode(&result) diff --git a/internal/service/widenet/widenet_impl.go b/internal/service/widenet/widenet_impl.go index 7a18b7d..2923efe 100644 --- a/internal/service/widenet/widenet_impl.go +++ b/internal/service/widenet/widenet_impl.go @@ -31,7 +31,14 @@ func (wn *WidenetService) Request(cep string) (*entity.Cep, error) { defer res.Body.Close() if res.StatusCode != 200 { - return nil, errors.HttpError(res.StatusCode) + switch res.StatusCode { + case http.StatusTooManyRequests: + return nil, errors.TooManyRequestsError + case http.StatusInternalServerError: + return nil, errors.InternalServerError + default: + return nil, errors.CepNotFoundError + } } err = json.NewDecoder(res.Body).Decode(&result) diff --git a/lagoinha copy.txt b/lagoinha copy.txt new file mode 100644 index 0000000..c91bf9b --- /dev/null +++ b/lagoinha copy.txt @@ -0,0 +1,155 @@ +package lagoinha + +import ( + "context" + "time" + + "github.com/igorhalfeld/lagoinha/internal/entity" + "github.com/igorhalfeld/lagoinha/internal/service" + "github.com/igorhalfeld/lagoinha/internal/service/apicep" + "github.com/igorhalfeld/lagoinha/internal/service/brasilapi" + "github.com/igorhalfeld/lagoinha/internal/service/viacep" + "github.com/igorhalfeld/lagoinha/internal/service/widenet" + "github.com/igorhalfeld/lagoinha/pkg/errors" +) + +var providers = map[string]service.Provider{ + "BrasilAPI": brasilapi.New(), + "ViaCEP": viacep.New(), + "Apicep": apicep.New(), + "WidNet": widenet.New(), +} + +// GetTotalAmountOfCepProviders returns amount of current enabled cep provivers +func GetTotalAmountOfCepProviders() int { + return len(providers) +} + +func getAddress(cepRaw string, opts *GetAddressOptions, chResponse chan *entity.Cep, chError chan error) { + // var wg sync.WaitGroup + + cep := entity.Cep{ + Cep: cepRaw, + } + + if !cep.IsValid() { + chError <- errors.CepNotValidError + return + } + + // TODO: enhance this way of handling options + if opts != nil { + if opts.PreferenceForAPI != "" { + chResponseInternal := make(chan *entity.Cep, GetTotalAmountOfCepProviders()) + chErrorInternal := make(chan error) + + provider, ok := providers[opts.PreferenceForAPI] + if !ok { + chError <- errors.PreferenceProviderNotFound + return + } + + ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) + defer cancel() + + // wg.Add(1) + go func() { + // defer wg.Done() + c, err := provider.Request(cep.Cep) + if err != nil { + chErrorInternal <- err + return + } + + chResponseInternal <- c + }() + + select { + case address := <-chResponseInternal: + chResponse <- address + case err := <-chErrorInternal: + chError <- err + case <-ctx.Done(): + cancel() + } + } + } + + // TODO: add context.WithCancel for slower requests + for providerName := range providers { + // wg.Add(1) + go func(p, cv string) { + // defer wg.Done() + c, err := providers[p].Request(cv) + if err != nil { + chError <- err + return + } + + chResponse <- c + }(providerName, cep.Cep) + } + + // wg.Wait() + // close(chResponse) + // close(chError) +} + +type GetAddressOptions struct { + PreferenceForAPI string +} + +// GetAddress - get address +func GetAddress(cepRaw string, opts *GetAddressOptions) (chan *entity.Cep, chan error) { + chResponse := make(chan *entity.Cep, GetTotalAmountOfCepProviders()) + chError := make(chan error) + + go getAddress(cepRaw, opts, chResponse, chError) + + return chResponse, chError +} + +func GetAddressSync(cepRaw string, opts *GetAddressOptions) (*entity.Cep, error) { + totalAmountOfCepProvider := GetTotalAmountOfCepProviders() + chResponse := make(chan *entity.Cep, totalAmountOfCepProvider) + chError := make(chan error) + + go getAddress(cepRaw, opts, chResponse, chError) + + var err error + i := 0 + for i < totalAmountOfCepProvider { + select { + case address := <-chResponse: + return address, nil + case err = <-chError: + i++ + } + } + + return nil, err +} + +// func GetAddressSync(cepRaw string, opts *GetAddressOptions) (*entity.Cep, error) { +// totalAmountOfCepProvider := GetTotalAmountOfCepProviders() +// chResponse := make(chan *entity.Cep, totalAmountOfCepProvider) +// chError := make(chan error) + +// go getAddress(cepRaw, opts, chResponse, chError) + +// var lastError error +// for i := 0; i < totalAmountOfCepProvider; i++ { +// select { +// case address := <-chResponse: +// return address, nil +// case err := <-chError: +// lastError = err +// } +// } + +// if lastError != nil { +// return nil, lastError +// } + +// return nil, fmt.Errorf("address not found for cep %s", cepRaw) +// } diff --git a/lagoinha.go b/lagoinha.go index 7d465ce..a9c1b68 100644 --- a/lagoinha.go +++ b/lagoinha.go @@ -2,6 +2,8 @@ package lagoinha import ( "context" + "fmt" + "sync" "time" "github.com/igorhalfeld/lagoinha/internal/entity" @@ -26,6 +28,8 @@ func GetTotalAmountOfCepProviders() int { } func getAddress(cepRaw string, opts *GetAddressOptions, chResponse chan *entity.Cep, chError chan error) { + var wg sync.WaitGroup + cep := entity.Cep{ Cep: cepRaw, } @@ -50,7 +54,9 @@ func getAddress(cepRaw string, opts *GetAddressOptions, chResponse chan *entity. ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) defer cancel() + wg.Add(1) go func() { + defer wg.Done() c, err := provider.Request(cep.Cep) if err != nil { chErrorInternal <- err @@ -73,7 +79,9 @@ func getAddress(cepRaw string, opts *GetAddressOptions, chResponse chan *entity. // TODO: add context.WithCancel for slower requests for providerName := range providers { + wg.Add(1) go func(p, cv string) { + defer wg.Done() c, err := providers[p].Request(cv) if err != nil { chError <- err @@ -83,6 +91,10 @@ func getAddress(cepRaw string, opts *GetAddressOptions, chResponse chan *entity. chResponse <- c }(providerName, cep.Cep) } + + wg.Wait() + close(chResponse) + close(chError) } type GetAddressOptions struct { @@ -99,23 +111,26 @@ func GetAddress(cepRaw string, opts *GetAddressOptions) (chan *entity.Cep, chan return chResponse, chError } -func GetAddressWithoutChannel(cepRaw string, opts *GetAddressOptions) (*entity.Cep, error) { +func GetAddressSync(cepRaw string, opts *GetAddressOptions) (*entity.Cep, error) { totalAmountOfCepProvider := GetTotalAmountOfCepProviders() chResponse := make(chan *entity.Cep, totalAmountOfCepProvider) chError := make(chan error) go getAddress(cepRaw, opts, chResponse, chError) - var err error - i := 0 - for i < totalAmountOfCepProvider { + var lastError error + for i := 0; i <= totalAmountOfCepProvider; i++ { select { case address := <-chResponse: return address, nil - case err = <-chError: - i++ + case err := <-chError: + lastError = err } } - return nil, err + if lastError != nil { + return nil, lastError + } + + return nil, fmt.Errorf("address not found for cep %s", cepRaw) } diff --git a/pkg/errors/app.go b/pkg/errors/app.go index 134b1ee..d92f0c9 100644 --- a/pkg/errors/app.go +++ b/pkg/errors/app.go @@ -4,3 +4,8 @@ var PreferenceProviderNotFound error = &LagoinhaError{ Type: ApplicationError, Message: "preference provider not found", } + +var RequestTimeout error = &LagoinhaError{ + Type: ApplicationError, + Message: "request timed out", +} diff --git a/pkg/errors/http_error.go b/pkg/errors/http_error.go deleted file mode 100644 index 0a73c67..0000000 --- a/pkg/errors/http_error.go +++ /dev/null @@ -1,12 +0,0 @@ -package errors - -func HttpError(statusCode int) error { - switch statusCode { - case 429: - return TooManyRequestsError - case 500: - return InternalServerError - default: - return CepNotFoundError - } -} From 2e4b126799e21f01faf7d064d25d751f380300ce Mon Sep 17 00:00:00 2001 From: Marcelo Petrucio Date: Tue, 21 May 2024 00:59:57 -0300 Subject: [PATCH 4/4] remove lagoinha copy.txt --- lagoinha copy.txt | 155 ---------------------------------------------- 1 file changed, 155 deletions(-) delete mode 100644 lagoinha copy.txt diff --git a/lagoinha copy.txt b/lagoinha copy.txt deleted file mode 100644 index c91bf9b..0000000 --- a/lagoinha copy.txt +++ /dev/null @@ -1,155 +0,0 @@ -package lagoinha - -import ( - "context" - "time" - - "github.com/igorhalfeld/lagoinha/internal/entity" - "github.com/igorhalfeld/lagoinha/internal/service" - "github.com/igorhalfeld/lagoinha/internal/service/apicep" - "github.com/igorhalfeld/lagoinha/internal/service/brasilapi" - "github.com/igorhalfeld/lagoinha/internal/service/viacep" - "github.com/igorhalfeld/lagoinha/internal/service/widenet" - "github.com/igorhalfeld/lagoinha/pkg/errors" -) - -var providers = map[string]service.Provider{ - "BrasilAPI": brasilapi.New(), - "ViaCEP": viacep.New(), - "Apicep": apicep.New(), - "WidNet": widenet.New(), -} - -// GetTotalAmountOfCepProviders returns amount of current enabled cep provivers -func GetTotalAmountOfCepProviders() int { - return len(providers) -} - -func getAddress(cepRaw string, opts *GetAddressOptions, chResponse chan *entity.Cep, chError chan error) { - // var wg sync.WaitGroup - - cep := entity.Cep{ - Cep: cepRaw, - } - - if !cep.IsValid() { - chError <- errors.CepNotValidError - return - } - - // TODO: enhance this way of handling options - if opts != nil { - if opts.PreferenceForAPI != "" { - chResponseInternal := make(chan *entity.Cep, GetTotalAmountOfCepProviders()) - chErrorInternal := make(chan error) - - provider, ok := providers[opts.PreferenceForAPI] - if !ok { - chError <- errors.PreferenceProviderNotFound - return - } - - ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) - defer cancel() - - // wg.Add(1) - go func() { - // defer wg.Done() - c, err := provider.Request(cep.Cep) - if err != nil { - chErrorInternal <- err - return - } - - chResponseInternal <- c - }() - - select { - case address := <-chResponseInternal: - chResponse <- address - case err := <-chErrorInternal: - chError <- err - case <-ctx.Done(): - cancel() - } - } - } - - // TODO: add context.WithCancel for slower requests - for providerName := range providers { - // wg.Add(1) - go func(p, cv string) { - // defer wg.Done() - c, err := providers[p].Request(cv) - if err != nil { - chError <- err - return - } - - chResponse <- c - }(providerName, cep.Cep) - } - - // wg.Wait() - // close(chResponse) - // close(chError) -} - -type GetAddressOptions struct { - PreferenceForAPI string -} - -// GetAddress - get address -func GetAddress(cepRaw string, opts *GetAddressOptions) (chan *entity.Cep, chan error) { - chResponse := make(chan *entity.Cep, GetTotalAmountOfCepProviders()) - chError := make(chan error) - - go getAddress(cepRaw, opts, chResponse, chError) - - return chResponse, chError -} - -func GetAddressSync(cepRaw string, opts *GetAddressOptions) (*entity.Cep, error) { - totalAmountOfCepProvider := GetTotalAmountOfCepProviders() - chResponse := make(chan *entity.Cep, totalAmountOfCepProvider) - chError := make(chan error) - - go getAddress(cepRaw, opts, chResponse, chError) - - var err error - i := 0 - for i < totalAmountOfCepProvider { - select { - case address := <-chResponse: - return address, nil - case err = <-chError: - i++ - } - } - - return nil, err -} - -// func GetAddressSync(cepRaw string, opts *GetAddressOptions) (*entity.Cep, error) { -// totalAmountOfCepProvider := GetTotalAmountOfCepProviders() -// chResponse := make(chan *entity.Cep, totalAmountOfCepProvider) -// chError := make(chan error) - -// go getAddress(cepRaw, opts, chResponse, chError) - -// var lastError error -// for i := 0; i < totalAmountOfCepProvider; i++ { -// select { -// case address := <-chResponse: -// return address, nil -// case err := <-chError: -// lastError = err -// } -// } - -// if lastError != nil { -// return nil, lastError -// } - -// return nil, fmt.Errorf("address not found for cep %s", cepRaw) -// }