-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathlagoinha.go
136 lines (111 loc) · 3.05 KB
/
lagoinha.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package lagoinha
import (
"context"
"fmt"
"sync"
"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 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)
}