generated from keboola/template
/
storage_branch.go
242 lines (213 loc) · 7.77 KB
/
storage_branch.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
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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
package keboola
import (
"context"
"encoding/json"
"fmt"
"strconv"
"time"
"github.com/relvacode/iso8601"
"github.com/keboola/go-client/pkg/request"
)
// BranchID is an ID of a development branch in Storage API.
type BranchID int
func (id BranchID) String() string {
return strconv.Itoa(int(id))
}
// BranchKey is a unique identifier of a branch.
type BranchKey struct {
ID BranchID `json:"id" writeoptional:"true"`
}
func (k BranchKey) ObjectID() any {
return k.ID
}
// Branch https://keboola.docs.apiary.io/#reference/development-branches/branches/list-branches
type Branch struct {
BranchKey
Name string `json:"name"`
Description string `json:"description"`
Created iso8601.Time `json:"created" readonly:"true"`
IsDefault bool `json:"isDefault" readonly:"true"`
}
// ListBranchesRequest https://keboola.docs.apiary.io/#reference/development-branches/branches/list-branches
func (a *AuthorizedAPI) ListBranchesRequest() request.APIRequest[*[]*Branch] {
result := make([]*Branch, 0)
req := a.
newRequest(StorageAPI).
WithResult(&result).
WithGet("dev-branches")
return request.NewAPIRequest(&result, req)
}
// GetDefaultBranchRequest lists all branches and returns the default branch.
func (a *AuthorizedAPI) GetDefaultBranchRequest() request.APIRequest[*Branch] {
defaultBranch := &Branch{}
req := a.
ListBranchesRequest().
WithOnSuccess(func(ctx context.Context, result *[]*Branch) error {
for _, branch := range *result {
if branch.IsDefault {
*defaultBranch = *branch
return nil
}
}
return fmt.Errorf("no default branch found")
})
return request.NewAPIRequest(defaultBranch, req)
}
// GetBranchRequest https://keboola.docs.apiary.io/#reference/development-branches/branch-manipulation/branch-detail
func (a *AuthorizedAPI) GetBranchRequest(key BranchKey) request.APIRequest[*Branch] {
result := &Branch{}
req := a.
newRequest(StorageAPI).
WithResult(result).
WithGet("dev-branches/{branchId}").
AndPathParam("branchId", key.ID.String())
return request.NewAPIRequest(result, req)
}
// CreateBranchRequest https://keboola.docs.apiary.io/#reference/development-branches/branches/create-branch
func (a *AuthorizedAPI) CreateBranchRequest(branch *Branch) request.APIRequest[*Branch] {
req := a.
CreateBranchAsyncRequest(branch).
WithOnSuccess(func(ctx context.Context, job *StorageJob) error {
// Wait for storage job
waitCtx, waitCancelFn := context.WithTimeout(ctx, time.Minute*1)
defer waitCancelFn()
if err := a.WaitForStorageJob(waitCtx, job); err != nil {
return err
}
// Map job results to branch
resultsBytes, err := json.Marshal(job.Results)
if err != nil {
return fmt.Errorf("cannot convert job.results to JSON: %w", err)
}
if err := json.Unmarshal(resultsBytes, branch); err != nil {
return fmt.Errorf("cannot map job.results to branch: %w", err)
}
return nil
})
// Result is branch, not job.
return request.NewAPIRequest(branch, req)
}
// CreateBranchAsyncRequest https://keboola.docs.apiary.io/#reference/development-branches/branches/create-branch
func (a *AuthorizedAPI) CreateBranchAsyncRequest(branch *Branch) request.APIRequest[*StorageJob] {
// ID is autogenerated
if branch.ID != 0 {
panic(fmt.Errorf("branch id is set but it should be auto-generated"))
}
// Default branch cannot be created
if branch.IsDefault {
panic(fmt.Errorf("default branch cannot be created"))
}
result := &StorageJob{}
req := a.
newRequest(StorageAPI).
WithResult(result).
WithPost("dev-branches").
WithJSONBody(request.StructToMap(branch, nil))
return request.NewAPIRequest(result, req)
}
// UpdateBranchRequest https://keboola.docs.apiary.io/#reference/development-branches/branches/update-branch
func (a *AuthorizedAPI) UpdateBranchRequest(branch *Branch, changedFields []string) request.APIRequest[*Branch] {
// ID is required
if branch.ID == 0 {
panic("branch id must be set")
}
if branch.IsDefault {
for _, field := range changedFields {
if field == "name" {
panic(fmt.Errorf("the name of the main branch cannot be changed"))
}
}
}
// Create request
req := a.
newRequest(StorageAPI).
WithResult(branch).
WithPut("dev-branches/{branchId}").
AndPathParam("branchId", branch.ID.String()).
WithJSONBody(request.StructToMap(branch, changedFields))
return request.NewAPIRequest(branch, req)
}
// DeleteBranchRequest https://keboola.docs.apiary.io/#reference/development-branches/branch-manipulation/delete-branch
func (a *AuthorizedAPI) DeleteBranchRequest(key BranchKey) request.APIRequest[request.NoResult] {
req := a.
DeleteBranchAsyncRequest(key).
WithOnSuccess(func(ctx context.Context, job *StorageJob) error {
// Wait for storage job
waitCtx, waitCancelFn := context.WithTimeout(ctx, time.Minute*1)
defer waitCancelFn()
return a.WaitForStorageJob(waitCtx, job)
})
return request.NewAPIRequest(request.NoResult{}, req)
}
// DeleteBranchAsyncRequest https://keboola.docs.apiary.io/#reference/development-branches/branch-manipulation/delete-branch
func (a *AuthorizedAPI) DeleteBranchAsyncRequest(key BranchKey) request.APIRequest[*StorageJob] {
result := &StorageJob{}
req := a.
newRequest(StorageAPI).
WithResult(result).
WithDelete("dev-branches/{branchId}").
AndPathParam("branchId", key.ID.String())
return request.NewAPIRequest(result, req)
}
// ListBranchMetadataRequest https://keboola.docs.apiary.io/#reference/metadata/development-branch-metadata/list
func (a *AuthorizedAPI) ListBranchMetadataRequest(key BranchKey) request.APIRequest[*MetadataDetails] {
result := make(MetadataDetails, 0)
req := a.
newRequest(StorageAPI).
WithResult(&result).
WithGet("branch/{branchId}/metadata").
AndPathParam("branchId", key.ID.String())
return request.NewAPIRequest(&result, req)
}
// AppendBranchMetadataRequest https://keboola.docs.apiary.io/#reference/metadata/development-branch-metadata/create-or-update https://keboola.docs.apiary.io/#reference/metadata/development-branch-metadata/delete
func (a *AuthorizedAPI) AppendBranchMetadataRequest(key BranchKey, metadata Metadata) request.APIRequest[request.NoResult] {
// Empty, we have nothing to append
if len(metadata) == 0 {
return request.NewNoOperationAPIRequest(request.NoResult{})
}
// Metadata with empty values must be collected and deleted separately
toDelete := map[string]bool{}
var payload MetadataPayload
i := 0
for k, v := range metadata {
if v == "" {
toDelete[k] = true
} else {
payload.Metadata = append(payload.Metadata, MetadataKV{Key: k, Value: v})
i++
}
}
requestAppend := a.
newRequest(StorageAPI).
WithPost("branch/{branchId}/metadata").
AndPathParam("branchId", key.ID.String()).
WithJSONBody(payload)
// Delete metadata with empty values
if len(toDelete) > 0 {
requestListDelete := a.
ListBranchMetadataRequest(key).
WithOnSuccess(func(ctx context.Context, details *MetadataDetails) error {
wg := request.NewWaitGroup(ctx)
for _, detail := range *details {
if found := toDelete[detail.Key]; found {
wg.Send(a.DeleteBranchMetadataRequest(key, detail.ID))
}
}
return wg.Wait()
})
if len(payload.Metadata) > 0 {
return request.NewAPIRequest(request.NoResult{}, requestAppend, requestListDelete)
}
return request.NewAPIRequest(request.NoResult{}, requestListDelete)
}
return request.NewAPIRequest(request.NoResult{}, requestAppend)
}
// DeleteBranchMetadataRequest https://keboola.docs.apiary.io/#reference/metadata/development-branch-metadata/delete
func (a *AuthorizedAPI) DeleteBranchMetadataRequest(branch BranchKey, metaID string) request.APIRequest[request.NoResult] {
req := a.
newRequest(StorageAPI).
WithDelete("branch/{branchId}/metadata/{metadataId}").
AndPathParam("branchId", branch.ID.String()).
AndPathParam("metadataId", metaID)
return request.NewAPIRequest(request.NoResult{}, req)
}