-
Notifications
You must be signed in to change notification settings - Fork 0
/
url.go
142 lines (118 loc) · 3.45 KB
/
url.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
137
138
139
140
141
142
package twigots
import (
"errors"
"fmt"
"log"
"net/url"
"strconv"
"strings"
"time"
)
const (
TwicketsURL = "https://www.twickets.live"
countryQueryKey = "countryCode"
regionQueryKey = "regionCode"
)
var twicketsUrl *url.URL
func init() {
var err error
twicketsUrl, err = url.Parse(TwicketsURL)
if err != nil {
log.Fatal("failed to parse twickets url")
}
}
// ListingURL gets the url of a listing given its id and the
// number of tickets in the listing.
//
// Format is:
// https://www.twickets.live/app/block/<ticketId>,<numTickets>
func ListingURL(listingId string, numTickets int) string {
ticketUrl := cloneURL(twicketsUrl)
ticketUrl = ticketUrl.JoinPath("app", "block", fmt.Sprintf("%s,%d", listingId, numTickets))
return ticketUrl.String()
}
type FeedUrlInput struct {
// Required fields
APIKey string
Country Country
// Optional fields
Regions []Region // Defaults to all country regions
NumListings int // Defaults to 10 ticket listings
BeforeTime time.Time // Defaults to current time
}
// Validate the input struct used to get the feed url.
// This is used internally to check the input, but can also be used externally.
func (f FeedUrlInput) Validate() error {
if f.APIKey == "" {
return errors.New("api key must be set")
}
if f.Country.Value == "" {
return errors.New("country must be set")
}
if !Countries.Contains(f.Country) {
return fmt.Errorf("country '%s' is not valid", f.Country)
}
return nil
}
// FeedUrl gets the url of a feed of ticket listings
//
// Format is: https://www.twickets.live/services/catalogue?q=countryCode=GB&count=100&api_key=<api_key>
func FeedUrl(input FeedUrlInput) (string, error) {
err := input.Validate()
if err != nil {
return "", fmt.Errorf("invalid input parameters: %w", err)
}
feedUrl := cloneURL(twicketsUrl)
feedUrl = feedUrl.JoinPath("services", "catalogue")
// Set query params
queryParams := feedUrl.Query()
locationQuery := apiLocationQuery(input.Country, input.Regions...)
if locationQuery != "" {
queryParams.Set("q", locationQuery)
}
if !input.BeforeTime.IsZero() {
maxTime := input.BeforeTime.UnixMilli()
queryParams.Set("maxTime", strconv.Itoa(int(maxTime)))
}
if input.NumListings > 0 {
count := strconv.Itoa(input.NumListings)
queryParams.Set("count", count)
}
queryParams.Set("api_key", input.APIKey)
// Set query
encodedQuery := queryParams.Encode()
encodedQuery = strings.ReplaceAll(encodedQuery, "%3D", "=")
encodedQuery = strings.ReplaceAll(encodedQuery, "%2C", ",")
feedUrl.RawQuery = encodedQuery
return feedUrl.String(), nil
}
// apiLocationQuery converts a country and selection of regions to an api query string
func apiLocationQuery(country Country, regions ...Region) string {
if !Countries.Contains(country) {
return ""
}
queryParts := make([]string, 0, len(regions)+1)
countryQuery := fmt.Sprintf("%s=%s", countryQueryKey, country.Value)
queryParts = append(queryParts, countryQuery)
for _, region := range regions {
if Regions.Contains(region) {
regionQuery := fmt.Sprintf("%s=%s", regionQueryKey, region.Value)
queryParts = append(queryParts, regionQuery)
}
}
return strings.Join(queryParts, ",")
}
// cloneUrl clones a url. Copied directly from net/http internals
// See: https://github.com/golang/go/blob/go1.19/src/net/http/clone.go#L22
func cloneURL(u *url.URL) *url.URL {
if u == nil {
return nil
}
u2 := new(url.URL)
*u2 = *u
if u.User != nil {
u2.User = new(url.Userinfo)
*u2.User = *u.User
}
return u2
}