-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtypes.go
131 lines (111 loc) · 2.87 KB
/
types.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
/*
(C) 2022 Robert Kisteleki & RIPE NCC
See LICENSE file for the license.
*/
package goat
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
"time"
)
// Geolocation type
type Geolocation struct {
Type string `json:"type"`
Coordinates []float32 `json:"coordinates"`
}
// Tag type
type Tag struct {
Name string `json:"name"`
Slug string `json:"slug"`
}
// ErrorResponse type
type MultiErrorResponse struct {
ErrorDetail
Error ErrorDetail `json:"error"`
Errors []ErrorMessage `json:"errors"`
}
type ErrorMessage struct {
Source ErrorSource `json:"source"`
Detail string `json:"detail"`
}
type ErrorSource struct {
Pointer string `json:"pointer"`
}
// ErrorDetails type
type ErrorDetail struct {
Detail string `json:"detail"`
Status int `json:"status"`
Title string `json:"title"`
Code int `json:"code"`
Errors []ErrorMessage `json:"errors"`
}
// valueOrNA turns various types into a string if they have values
// (i.e. pointer is not nil) or "N/A" otherwise
func valueOrNA[T any](prefix string, quote bool, val *T) string {
if val != nil {
if quote {
return fmt.Sprintf("\t\"%s%v\"", prefix, *val)
} else {
return fmt.Sprintf("\t%s%v", prefix, *val)
}
} else {
return "\tN/A"
}
}
// a datetime type that can be unmarshaled from UNIX epoch *or* ISO times
type uniTime time.Time
// output is ISO8601(Z) down to seconds
func (ut *uniTime) MarshalJSON() (b []byte, e error) {
layout := "2006-01-02T15:04:05Z"
return []byte("\"" + time.Time(*ut).UTC().Format(layout) + "\""), nil
}
func (ut *uniTime) UnmarshalJSON(data []byte) error {
// try parsing as UNIX epoch first
epoch, err := strconv.Atoi(string(data))
if err == nil {
*ut = uniTime(time.Unix(int64(epoch), 0))
return nil
}
// try parsing ISO8601(Z)
layout := "2006-01-02T15:04:05"
noquote := strings.ReplaceAll(string(data), "\"", "")
noz := strings.ReplaceAll(noquote, "Z", "")
unix, err := time.Parse(layout, noz)
if err != nil {
return err
}
*ut = uniTime(unix)
return nil
}
// default output format for uniTime type is ISO8601
func (ut uniTime) String() string {
return time.Time(ut).UTC().Format(time.RFC3339)
}
// something went wrong; see if the error page can be parsed
// it could be a single error or a bunch of them
func parseAPIError(resp *http.Response) error {
var err error
var decoded MultiErrorResponse
err = json.NewDecoder(resp.Body).Decode(&decoded)
if err != nil {
return err
}
r := make([]string, 0)
if decoded.Status != 0 {
r = append(r, decoded.Title)
for _, e := range decoded.Errors {
r = append(r, e.Detail)
}
}
if decoded.Error.Status != 0 {
r = append(r, decoded.Error.Title)
r = append(r, decoded.Error.Detail)
for _, e := range decoded.Error.Errors {
r = append(r, e.Detail)
}
}
return fmt.Errorf("%d %s %s", decoded.Status, decoded.Title, strings.Join(r, ", "))
}