-
Notifications
You must be signed in to change notification settings - Fork 87
/
Copy pathresources.go
179 lines (162 loc) · 6.55 KB
/
resources.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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
package linodego
import (
"bytes"
"context"
"fmt"
"text/template"
"github.com/go-resty/resty/v2"
)
const (
accountName = "account"
accountSettingsName = "accountsettings"
domainRecordsName = "records"
domainsName = "domains"
eventsName = "events"
imagesName = "images"
instanceConfigsName = "configs"
instanceDisksName = "disks"
instanceIPsName = "ips"
instanceSnapshotsName = "snapshots"
instanceStatsName = "instancestats"
instanceVolumesName = "instancevolumes"
instancesName = "instances"
invoiceItemsName = "invoiceitems"
invoicesName = "invoices"
ipaddressesName = "ipaddresses"
ipv6poolsName = "ipv6pools"
ipv6rangesName = "ipv6ranges"
kernelsName = "kernels"
longviewName = "longview"
longviewclientsName = "longviewclients"
longviewsubscriptionsName = "longviewsubscriptions"
managedName = "managed"
nodebalancerconfigsName = "nodebalancerconfigs"
nodebalancernodesName = "nodebalancernodes"
nodebalancersName = "nodebalancers"
notificationsName = "notifications"
oauthClientsName = "oauthClients"
objectStorageBucketsName = "objectstoragebuckets"
objectStorageClustersName = "objectstorageclusters"
objectStorageKeysName = "objectstoragekeys"
paymentsName = "payments"
profileName = "profile"
regionsName = "regions"
sshkeysName = "sshkeys"
stackscriptsName = "stackscripts"
tagsName = "tags"
ticketsName = "tickets"
tokensName = "tokens"
typesName = "types"
usersName = "users"
volumesName = "volumes"
accountEndpoint = "account"
accountSettingsEndpoint = "account/settings"
domainRecordsEndpoint = "domains/{{ .ID }}/records"
domainsEndpoint = "domains"
eventsEndpoint = "account/events"
imagesEndpoint = "images"
instanceConfigsEndpoint = "linode/instances/{{ .ID }}/configs"
instanceDisksEndpoint = "linode/instances/{{ .ID }}/disks"
instanceIPsEndpoint = "linode/instances/{{ .ID }}/ips"
instanceSnapshotsEndpoint = "linode/instances/{{ .ID }}/backups"
instanceStatsEndpoint = "linode/instances/{{ .ID }}/stats"
instanceVolumesEndpoint = "linode/instances/{{ .ID }}/volumes"
instancesEndpoint = "linode/instances"
invoiceItemsEndpoint = "account/invoices/{{ .ID }}/items"
invoicesEndpoint = "account/invoices"
ipaddressesEndpoint = "networking/ips"
ipv6poolsEndpoint = "networking/ipv6/pools"
ipv6rangesEndpoint = "networking/ipv6/ranges"
kernelsEndpoint = "linode/kernels"
longviewEndpoint = "longview"
longviewclientsEndpoint = "longview/clients"
longviewsubscriptionsEndpoint = "longview/subscriptions"
managedEndpoint = "managed"
// @TODO we can't use these nodebalancer endpoints unless we include these templated fields
// The API seems inconsistent about including parent IDs in objects, (compare instance configs to nb configs)
// Parent IDs would be immutable for updates and are ignored in create requests ..
// Should we include these fields in CreateOpts and UpdateOpts?
nodebalancerconfigsEndpoint = "nodebalancers/{{ .ID }}/configs"
nodebalancernodesEndpoint = "nodebalancers/{{ .ID }}/configs/{{ .SecondID }}/nodes"
nodebalancersEndpoint = "nodebalancers"
notificationsEndpoint = "account/notifications"
oauthClientsEndpoint = "account/oauth-clients"
objectStorageBucketsEndpoint = "object-storage/buckets"
objectStorageClustersEndpoint = "object-storage/clusters"
objectStorageKeysEndpoint = "object-storage/keys"
paymentsEndpoint = "account/payments"
profileEndpoint = "profile"
regionsEndpoint = "regions"
sshkeysEndpoint = "profile/sshkeys"
stackscriptsEndpoint = "linode/stackscripts"
tagsEndpoint = "tags"
ticketsEndpoint = "support/tickets"
tokensEndpoint = "profile/tokens"
typesEndpoint = "linode/types"
usersEndpoint = "account/users"
volumesEndpoint = "volumes"
)
// Resource represents a linode API resource
type Resource struct {
name string
endpoint string
isTemplate bool
endpointTemplate *template.Template
R func(ctx context.Context) *resty.Request
PR func(ctx context.Context) *resty.Request
}
// NewResource is the factory to create a new Resource struct. If it has a template string the useTemplate bool must be set.
func NewResource(client *Client, name string, endpoint string, useTemplate bool, singleType interface{}, pagedType interface{}) *Resource {
var tmpl *template.Template
if useTemplate {
tmpl = template.Must(template.New(name).Parse(endpoint))
}
r := func(ctx context.Context) *resty.Request {
return client.R(ctx).SetResult(singleType)
}
pr := func(ctx context.Context) *resty.Request {
return client.R(ctx).SetResult(pagedType)
}
return &Resource{name, endpoint, useTemplate, tmpl, r, pr}
}
func (r Resource) render(data ...interface{}) (string, error) {
if data == nil {
return "", NewError("Cannot template endpoint with <nil> data")
}
out := ""
buf := bytes.NewBufferString(out)
var substitutions interface{}
switch len(data) {
case 1:
substitutions = struct{ ID interface{} }{data[0]}
case 2:
substitutions = struct {
ID interface{}
SecondID interface{}
}{data[0], data[1]}
default:
return "", NewError("Too many arguments to render template (expected 1 or 2)")
}
if err := r.endpointTemplate.Execute(buf, substitutions); err != nil {
return "", NewError(err)
}
return buf.String(), nil
}
// endpointWithID will return the rendered endpoint string for the resource with provided id
func (r Resource) endpointWithID(id ...int) (string, error) {
if !r.isTemplate {
return r.endpoint, nil
}
data := make([]interface{}, len(id))
for i, v := range id {
data[i] = v
}
return r.render(data...)
}
// Endpoint will return the non-templated endpoint string for resource
func (r Resource) Endpoint() (string, error) {
if r.isTemplate {
return "", NewError(fmt.Sprintf("Tried to get endpoint for %s without providing data for template", r.name))
}
return r.endpoint, nil
}