-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhooks.go
More file actions
223 lines (188 loc) · 6.07 KB
/
hooks.go
File metadata and controls
223 lines (188 loc) · 6.07 KB
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
package posthook
import (
"context"
"errors"
"fmt"
"iter"
"net/http"
"net/url"
"time"
)
// HooksService handles communication with the hook-related endpoints of the
// Posthook API.
type HooksService service
// Schedule creates a new scheduled hook. Exactly one of PostAt, PostAtLocal
// (with Timezone), or PostIn must be set on params.
func (s *HooksService) Schedule(ctx context.Context, params *HookScheduleParams) (*Hook, *Response, error) {
modes := 0
if !params.PostAt.IsZero() {
modes++
}
if params.PostAtLocal != "" {
modes++
}
if params.PostIn != "" {
modes++
}
if modes == 0 {
return nil, nil, fmt.Errorf("posthook: exactly one of PostAt, PostAtLocal, or PostIn must be set")
}
if modes > 1 {
return nil, nil, fmt.Errorf("posthook: only one of PostAt, PostAtLocal, or PostIn may be set (got %d)", modes)
}
req, err := s.client.newRequest(http.MethodPost, "/v1/hooks", params)
if err != nil {
return nil, nil, err
}
hook := new(Hook)
resp, err := s.client.do(ctx, req, hook)
if err != nil {
return nil, resp, err
}
return hook, resp, nil
}
// Get retrieves a single hook by its ID.
func (s *HooksService) Get(ctx context.Context, id string) (*Hook, *Response, error) {
if id == "" {
return nil, nil, fmt.Errorf("posthook: hook id is required")
}
req, err := s.client.newRequest(http.MethodGet, fmt.Sprintf("/v1/hooks/%s", url.PathEscape(id)), nil)
if err != nil {
return nil, nil, err
}
hook := new(Hook)
resp, err := s.client.do(ctx, req, hook)
if err != nil {
return nil, resp, err
}
return hook, resp, nil
}
// List retrieves a paginated list of hooks. Use Limit and Offset on
// HookListParams for pagination. When the returned slice is shorter than
// the requested Limit, you have reached the last page.
func (s *HooksService) List(ctx context.Context, params *HookListParams) ([]*Hook, *Response, error) {
req, err := s.client.newRequest(http.MethodGet, "/v1/hooks", nil)
if err != nil {
return nil, nil, err
}
addQueryParams(req.URL, params)
var hooks []*Hook
resp, err := s.client.do(ctx, req, &hooks)
if err != nil {
return nil, resp, err
}
return hooks, resp, nil
}
// ListAll returns an iterator that yields every matching hook across all pages.
// It uses cursor-based pagination via postAt ordering for consistency.
//
// Usage:
//
// for hook, err := range client.Hooks.ListAll(ctx, &posthook.HookListAllParams{Status: posthook.StatusFailed}) {
// if err != nil {
// log.Fatal(err)
// }
// fmt.Println(hook.ID)
// }
func (s *HooksService) ListAll(ctx context.Context, params *HookListAllParams) iter.Seq2[*Hook, error] {
return func(yield func(*Hook, error) bool) {
pageSize := 100
if params != nil && params.PageSize > 0 {
pageSize = params.PageSize
}
var status string
var cursor time.Time
if params != nil {
status = params.Status
cursor = params.PostAtAfter
}
for {
listParams := &HookListParams{
Status: status,
Limit: pageSize,
SortBy: SortByPostAt,
SortOrder: SortOrderAsc,
PostAtAfter: cursor,
}
hooks, _, err := s.List(ctx, listParams)
if err != nil {
yield(nil, err)
return
}
for _, hook := range hooks {
if !yield(hook, nil) {
return
}
}
if len(hooks) < pageSize {
return
}
cursor = hooks[len(hooks)-1].PostAt
}
}
}
// Delete removes a hook. To cancel a pending hook, delete it before
// delivery. Returns nil error on both 200 (deleted) and 404 (already deleted).
func (s *HooksService) Delete(ctx context.Context, id string) (*Response, error) {
if id == "" {
return nil, fmt.Errorf("posthook: hook id is required")
}
req, err := s.client.newRequest(http.MethodDelete, fmt.Sprintf("/v1/hooks/%s", url.PathEscape(id)), nil)
if err != nil {
return nil, err
}
resp, err := s.client.do(ctx, req, nil)
if err != nil {
// Swallow 404 — the hook was already deleted.
var notFound *NotFoundError
if errors.As(err, ¬Found) {
return resp, nil
}
return resp, err
}
return resp, nil
}
// Bulk returns a BulkActions handle for performing bulk operations on hooks.
func (s *HooksService) Bulk() *BulkActions {
return &BulkActions{client: s.client}
}
// BulkActions provides methods for performing bulk operations on hooks.
type BulkActions struct {
client *Client
}
// Retry re-attempts delivery for failed hooks specified by their IDs.
func (b *BulkActions) Retry(ctx context.Context, params *BulkActionByIDs) (*BulkActionResult, *Response, error) {
return b.doBulk(ctx, "/v1/hooks/bulk/retry", params)
}
// RetryByFilter re-attempts delivery for failed hooks matching a time range filter.
func (b *BulkActions) RetryByFilter(ctx context.Context, params *BulkActionByFilter) (*BulkActionResult, *Response, error) {
return b.doBulk(ctx, "/v1/hooks/bulk/retry", params)
}
// Replay re-delivers completed hooks specified by their IDs.
func (b *BulkActions) Replay(ctx context.Context, params *BulkActionByIDs) (*BulkActionResult, *Response, error) {
return b.doBulk(ctx, "/v1/hooks/bulk/replay", params)
}
// ReplayByFilter re-delivers completed hooks matching a time range filter.
func (b *BulkActions) ReplayByFilter(ctx context.Context, params *BulkActionByFilter) (*BulkActionResult, *Response, error) {
return b.doBulk(ctx, "/v1/hooks/bulk/replay", params)
}
// Cancel cancels pending hooks specified by their IDs.
func (b *BulkActions) Cancel(ctx context.Context, params *BulkActionByIDs) (*BulkActionResult, *Response, error) {
return b.doBulk(ctx, "/v1/hooks/bulk/cancel", params)
}
// CancelByFilter cancels pending hooks matching a time range filter.
func (b *BulkActions) CancelByFilter(ctx context.Context, params *BulkActionByFilter) (*BulkActionResult, *Response, error) {
return b.doBulk(ctx, "/v1/hooks/bulk/cancel", params)
}
func (b *BulkActions) doBulk(ctx context.Context, path string, body any) (*BulkActionResult, *Response, error) {
req, err := b.client.newRequest(http.MethodPost, path, body)
if err != nil {
return nil, nil, err
}
result := new(BulkActionResult)
resp, err := b.client.do(ctx, req, result)
if err != nil {
return nil, resp, err
}
return result, resp, nil
}