-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhttp_client.go
135 lines (109 loc) · 2.76 KB
/
http_client.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
package gbfs
import (
"encoding/json"
"fmt"
"net/http"
)
type (
// HTTPClient use HTTP protocol to fetch feeds
HTTPClient struct {
client http.Client
baseURL string
urls map[string]string
language string
}
)
// NewHTTPClient return a gbfs.Client will use http to fetch feeds
func NewHTTPClient(opts ...HTTPOption) (Client, error) {
c := &HTTPClient{urls: make(map[string]string)}
for _, opt := range opts {
opt(c)
}
if c.baseURL == "" {
return nil, ErrBaseURLMissing
}
return c, nil
}
// ForceURLs set the full URL for each feed. Where map key is the feed name and value is URL
// Set replace to true when you want to clean previous build URL
// Useful for provider that doesn't respect the standard for the URL
func (c *HTTPClient) ForceURLs(urls map[string]string, replace bool) {
if replace {
c.urls = urls
return
}
for k, u := range urls {
c.urls[k] = u
}
}
// Get one feed and try to decode the response in 'out' structure
func (c *HTTPClient) Get(key string, out Feed) error {
if out.FeedKey() != key {
return ErrInvalidFeed
}
req, err := http.NewRequest(http.MethodGet, c.url(key), nil)
if err != nil {
return fmt.Errorf("http.NewRequest: %w", err)
}
res, err := c.client.Do(req)
if err != nil {
return fmt.Errorf("http.Do: %w", err)
}
defer func() { _ = res.Body.Close() }()
if res.StatusCode != http.StatusOK {
switch res.StatusCode {
case http.StatusNotFound:
return ErrFeedNotExist
default:
return fmt.Errorf("invalid status code (%d)", res.StatusCode)
}
}
return json.NewDecoder(res.Body).Decode(out)
}
// Refresh the feed when is expired or forced (via 'forceRefresh')
func (c *HTTPClient) Refresh(f Feed, forceRefresh bool) error {
// not forced and feed not expired
if !forceRefresh && !f.IsExpired() {
return nil
}
return c.Get(f.FeedKey(), f)
}
func (c *HTTPClient) url(key string) string {
if u, ok := c.urls[key]; ok {
return u
}
l := c.language
if l != "" {
l = fmt.Sprintf("%s/", l)
}
c.urls[key] = fmt.Sprintf("%s/%s%s.json", c.baseURL, l, key)
return c.urls[key]
}
// ==========
// OPTIONS
// ==========
// HTTPOptionClient specify and http.Client for gbfs.HTTPClient
func HTTPOptionClient(h http.Client) HTTPOption {
return func(c *HTTPClient) {
c.client = h
}
}
// HTTPOptionBaseURL specify the base URL for the feed
func HTTPOptionBaseURL(url string) HTTPOption {
return func(c *HTTPClient) {
c.baseURL = url
}
}
// HTTPOptionLanguage specify the language of the feed
// Used to determined the path of the feed
func HTTPOptionLanguage(lang string) HTTPOption {
return func(c *HTTPClient) {
c.language = lang
}
}
// HTTPOptionForceURL specify an URL to use for the feed
func HTTPOptionForceURL(key, url string) HTTPOption {
return func(c *HTTPClient) {
c.urls[key] = url
}
}