forked from cvbarros/go-teamcity
/
build_type.go
351 lines (276 loc) · 9.17 KB
/
build_type.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
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
package teamcity
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"github.com/dghubble/sling"
)
type buildTypeJSON struct {
// description
Description string `json:"description,omitempty" xml:"description"`
// href
Href string `json:"href,omitempty" xml:"href"`
// id
ID string `json:"id,omitempty" xml:"id"`
// inherited
// Inherited *bool `json:"inherited,omitempty" xml:"inherited"`
// internal Id
InternalID string `json:"internalId,omitempty" xml:"internalId"`
// locator
Locator string `json:"locator,omitempty" xml:"locator"`
// name
Name string `json:"name,omitempty" xml:"name"`
// Parameters for the build configuration. Read-only, only useful when retrieving project details
Parameters *Parameters `json:"parameters,omitempty"`
// paused
Paused *bool `json:"paused,omitempty" xml:"paused"`
// project
Project *Project `json:"project,omitempty"`
// project Id
ProjectID string `json:"projectId,omitempty" xml:"projectId"`
// project internal Id
ProjectInternalID string `json:"projectInternalId,omitempty" xml:"projectInternalId"`
// project name
ProjectName string `json:"projectName,omitempty" xml:"projectName"`
// snapshot dependencies
SnapshotDependencies *SnapshotDependencies `json:"snapshot-dependencies,omitempty"`
// template flag
TemplateFlag *bool `json:"templateFlag,omitempty" xml:"templateFlag"`
// type
Type string `json:"type,omitempty" xml:"type"`
// uuid
UUID string `json:"uuid,omitempty" xml:"uuid"`
// settings
Settings *Properties `json:"settings,omitempty"`
// vcs root entries
VcsRootEntries *VcsRootEntries `json:"vcs-root-entries,omitempty"`
// web Url
WebURL string `json:"webUrl,omitempty" xml:"webUrl"`
}
// BuildType represents a build configuration or a build configuration template
type BuildType struct {
ProjectID string
ID string
Name string
Description string
Options *BuildTypeOptions
Disabled bool
VcsRootEntries []*VcsRootEntry
Parameters *Parameters
buildTypeJSON *buildTypeJSON
}
//NewBuildType returns a build configuration with default options
func NewBuildType(projectID string, name string) (*BuildType, error) {
if projectID == "" || name == "" {
return nil, fmt.Errorf("projectID and name are required")
}
opt := NewBuildTypeOptionsWithDefaults()
return &BuildType{
ProjectID: projectID,
Name: name,
Options: opt,
Parameters: NewParametersEmpty(),
buildTypeJSON: &buildTypeJSON{
ProjectID: projectID,
Settings: opt.properties(),
},
}, nil
}
//MarshalJSON implements JSON serialization for BuildType
func (b *BuildType) MarshalJSON() ([]byte, error) {
optProps := b.Options.properties()
out := &buildTypeJSON{
ID: b.ID,
ProjectID: b.ProjectID,
Description: b.Description,
Name: b.Name,
Settings: optProps,
Parameters: b.Parameters,
}
return json.Marshal(out)
}
//UnmarshalJSON implements JSON deserialization for TriggerSchedule
func (b *BuildType) UnmarshalJSON(data []byte) error {
var aux buildTypeJSON
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
if err := b.read(&aux); err != nil {
return err
}
return nil
}
func (b *BuildType) read(dt *buildTypeJSON) error {
opts := dt.Settings.buildTypeOptions()
b.ID = dt.ID
b.Name = dt.Name
b.Description = dt.Description
b.Options = opts
b.ProjectID = dt.ProjectID
b.VcsRootEntries = dt.VcsRootEntries.Items
b.Parameters = dt.Parameters
return nil
}
// BuildTypeReference represents a subset detail of a Build Type
type BuildTypeReference struct {
// id
ID string `json:"id,omitempty" xml:"id"`
// name
Name string `json:"name,omitempty" xml:"name"`
// project Id
ProjectID string `json:"projectId,omitempty" xml:"projectId"`
}
// Reference converts a BuildType entity to a BuildType reference
func (b *BuildType) Reference() *BuildTypeReference {
return &BuildTypeReference{
ID: b.ID,
Name: b.Name,
ProjectID: b.ProjectID,
}
}
// BuildTypeService has operations for handling build configurations and templates
type BuildTypeService struct {
sling *sling.Sling
httpClient *http.Client
restHelper *restHelper
}
func newBuildTypeService(base *sling.Sling, httpClient *http.Client) *BuildTypeService {
sling := base.Path("buildTypes/")
return &BuildTypeService{
httpClient: httpClient,
sling: sling,
restHelper: newRestHelperWithSling(httpClient, sling),
}
}
// Create Creates a new build type under a project
func (s *BuildTypeService) Create(projectID string, buildType *BuildType) (*BuildTypeReference, error) {
var created BuildTypeReference
_, err := s.sling.New().Post("").BodyJSON(buildType).ReceiveSuccess(&created)
if err != nil {
return nil, err
}
return &created, nil
}
// GetByID Retrieves a build type resource by ID
func (s *BuildTypeService) GetByID(id string) (*BuildType, error) {
var out BuildType
resp, err := s.sling.New().Get(id).ReceiveSuccess(&out)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("Error when retrieving BuildType id = '%s', status: %d", id, resp.StatusCode)
}
return &out, err
}
//Update changes the resource in-place for this build configuration.
//TeamCity API does not support "PUT" on the whole Build Configuration resource, so the only updateable field is "Description". Other field updates will be ignored.
//This method also updates Settings and Parameters, but this is not an atomic operation. If an error occurs, it will be returned to caller what was updated or not.
func (s *BuildTypeService) Update(buildType *BuildType) (*BuildType, error) {
_, err := s.restHelper.putTextPlain(buildType.ID+"/description", buildType.Description, "build type description")
if err != nil {
return nil, err
}
//Update settings
var settings BuildTypeOptions
err = s.restHelper.put(buildType.ID+"/settings", buildType.Options.properties(), &settings, "build type settings")
if err != nil {
return nil, err
}
//Update Parameters
var parameters *Properties
err = s.restHelper.put(buildType.ID+"/parameters", buildType.Parameters, ¶meters, "build type parameters")
if err != nil {
return nil, err
}
out, err := s.GetByID(buildType.ID) //Refresh after update
if err != nil {
return nil, err
}
return out, nil
}
//Delete a build type resource
func (s *BuildTypeService) Delete(id string) error {
request, _ := s.sling.New().Delete(id).Request()
response, err := s.httpClient.Do(request)
if err != nil {
return err
}
defer response.Body.Close()
if response.StatusCode == 204 {
return nil
}
if response.StatusCode != 200 && response.StatusCode != 204 {
respData, err := ioutil.ReadAll(response.Body)
if err != nil {
return err
}
return fmt.Errorf("Error '%d' when deleting build type: %s", response.StatusCode, string(respData))
}
return nil
}
// AttachVcsRoot adds the VcsRoot reference to this build type
func (s *BuildTypeService) AttachVcsRoot(id string, vcsRoot *VcsRootReference) error {
var vcsEntry = NewVcsRootEntry(vcsRoot)
return s.AttachVcsRootEntry(id, vcsEntry)
}
// AttachVcsRootEntry adds the VcsRootEntry to this build type
func (s *BuildTypeService) AttachVcsRootEntry(id string, entry *VcsRootEntry) error {
var created VcsRootEntry
_, err := s.sling.New().Post(fmt.Sprintf("%s/vcs-root-entries/", LocatorID(id))).BodyJSON(entry).ReceiveSuccess(&created)
if err != nil {
return err
}
return nil
}
// AddStep creates a new build step for the build configuration with given id.
func (s *BuildTypeService) AddStep(id string, step Step) (Step, error) {
var created Step
path := fmt.Sprintf("%s/steps/", LocatorID(id))
err := s.restHelper.postCustom(path, step, &created, "build step", stepReadingFunc)
if err != nil {
return nil, err
}
return created, nil
}
//GetSteps return the list of steps for a Build configuration with given id.
func (s *BuildTypeService) GetSteps(id string) ([]Step, error) {
var aux stepsJSON
path := fmt.Sprintf("%s/steps/", LocatorID(id))
err := s.restHelper.get(path, &aux, "build steps")
if err != nil {
return nil, err
}
steps := make([]Step, aux.Count)
for i := range aux.Items {
dt, err := json.Marshal(aux.Items[i])
if err != nil {
return nil, err
}
stepReadingFunc(dt, &steps[i])
}
return steps, nil
}
// UpdateSettings will do a remote call for each setting being updated. Operation is not atomic, and the list of settings is processed in the order sent.
// Will return the error of the first failure and not process the rest
func (s *BuildTypeService) UpdateSettings(id string, settings *Properties) error {
for _, item := range settings.Items {
bodyProvider := textPlainBodyProvider{payload: item.Value}
req, err := s.sling.New().Put(fmt.Sprintf("%s/settings/%s", LocatorID(id), item.Name)).BodyProvider(bodyProvider).Add("Accept", "text/plain").Request()
response, err := s.httpClient.Do(req)
response.Body.Close()
if err != nil {
return fmt.Errorf("error updating buildType id: '%s' setting '%s': %s", id, item.Name, err)
}
}
return nil
}
//DeleteStep removes a build step from this build type by its id
func (s *BuildTypeService) DeleteStep(id string, stepID string) error {
_, err := s.sling.New().Delete(fmt.Sprintf("%s/steps/%s", LocatorID(id), stepID)).ReceiveSuccess(nil)
if err != nil {
return err
}
return nil
}