• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

UiPath / uipathcli / 14753230831

30 Apr 2025 11:13AM UTC coverage: 90.432% (+0.01%) from 90.419%
14753230831

push

github

web-flow
Merge pull request #187 from UiPath/feature/extend-plugin-parameters

Extend plugin paramters to support default value and allowed values

99 of 99 new or added lines in 15 files covered. (100.0%)

2 existing lines in 1 file now uncovered.

6323 of 6992 relevant lines covered (90.43%)

1.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.51
/utils/api/orchestrator_client.go
1
package api
2

3
import (
4
        "bytes"
5
        "context"
6
        "encoding/json"
7
        "errors"
8
        "fmt"
9
        "io"
10
        "mime/multipart"
11
        "net/http"
12
        "net/textproto"
13
        "strconv"
14
        "strings"
15
        "time"
16

17
        "github.com/UiPath/uipathcli/auth"
18
        "github.com/UiPath/uipathcli/log"
19
        "github.com/UiPath/uipathcli/plugin"
20
        "github.com/UiPath/uipathcli/utils/network"
21
        "github.com/UiPath/uipathcli/utils/stream"
22
        "github.com/UiPath/uipathcli/utils/visualization"
23
)
24

25
var ErrPackageAlreadyExists = errors.New("Package already exists")
26

27
type OrchestratorClient struct {
28
        baseUri  string
29
        token    *auth.AuthToken
30
        debug    bool
31
        settings plugin.ExecutionSettings
32
        logger   log.Logger
33
}
34

35
func (c OrchestratorClient) GetSharedFolderId() (int, error) {
1✔
36
        request := c.createGetFolderRequest()
1✔
37
        client := network.NewHttpClient(c.logger, c.httpClientSettings())
1✔
38
        response, err := client.Send(request)
1✔
39
        if err != nil {
2✔
40
                return -1, err
1✔
41
        }
1✔
42
        defer func() { _ = response.Body.Close() }()
2✔
43
        body, err := io.ReadAll(response.Body)
1✔
44
        if err != nil {
1✔
45
                return -1, fmt.Errorf("Error reading response: %w", err)
×
46
        }
×
47
        if response.StatusCode != http.StatusOK {
2✔
48
                return -1, fmt.Errorf("Orchestrator returned status code '%v' and body '%v'", response.StatusCode, string(body))
1✔
49
        }
1✔
50

51
        var result getFoldersResponseJson
1✔
52
        err = json.Unmarshal(body, &result)
1✔
53
        if err != nil {
2✔
54
                return -1, fmt.Errorf("Orchestrator returned invalid response body '%v'", string(body))
1✔
55
        }
1✔
56
        folderId := c.findFolderId(result)
1✔
57
        if folderId == nil {
2✔
58
                return -1, errors.New("Could not find 'Shared' orchestrator folder.")
1✔
59
        }
1✔
60
        return *folderId, nil
1✔
61
}
62

63
func (c OrchestratorClient) createGetFolderRequest() *network.HttpRequest {
1✔
64
        uri := c.baseUri + "/odata/Folders"
1✔
65
        header := http.Header{
1✔
66
                "Content-Type": {"application/json"},
1✔
67
        }
1✔
68
        return network.NewHttpGetRequest(uri, c.toAuthorization(c.token), header)
1✔
69
}
1✔
70

71
func (c OrchestratorClient) findFolderId(result getFoldersResponseJson) *int {
1✔
72
        for _, value := range result.Value {
2✔
73
                if value.Name == "Shared" {
2✔
74
                        return &value.Id
1✔
75
                }
1✔
76
        }
77
        if len(result.Value) > 0 {
1✔
78
                return &result.Value[0].Id
×
79
        }
×
80
        return nil
1✔
81
}
82

83
func (c OrchestratorClient) Upload(file stream.Stream, feedId string, uploadBar *visualization.ProgressBar) error {
1✔
84
        context, cancel := context.WithCancelCause(context.Background())
1✔
85
        request := c.createUploadRequest(file, feedId, uploadBar, cancel)
1✔
86
        client := network.NewHttpClient(c.logger, c.httpClientSettings())
1✔
87
        response, err := client.SendWithContext(request, context)
1✔
88
        if err != nil {
2✔
89
                return err
1✔
90
        }
1✔
91
        defer func() { _ = response.Body.Close() }()
2✔
92
        body, err := io.ReadAll(response.Body)
1✔
93
        if err != nil {
1✔
94
                return fmt.Errorf("Error reading response: %w", err)
×
95
        }
×
96
        if response.StatusCode == http.StatusConflict {
2✔
97
                return ErrPackageAlreadyExists
1✔
98
        }
1✔
99
        if response.StatusCode != http.StatusOK {
2✔
100
                return fmt.Errorf("Orchestrator returned status code '%v' and body '%v'", response.StatusCode, string(body))
1✔
101
        }
1✔
102
        return nil
1✔
103
}
104

105
func (c OrchestratorClient) createUploadRequest(file stream.Stream, feedId string, uploadBar *visualization.ProgressBar, cancel context.CancelCauseFunc) *network.HttpRequest {
1✔
106
        bodyReader, bodyWriter := io.Pipe()
1✔
107
        streamSize, _ := file.Size()
1✔
108
        contentType := c.writeMultipartBody(bodyWriter, file, "application/octet-stream", cancel)
1✔
109
        uploadReader := c.progressReader("uploading...", "completing  ", bodyReader, streamSize, uploadBar)
1✔
110

1✔
111
        uri := c.baseUri + "/odata/Processes/UiPath.Server.Configuration.OData.UploadPackage"
1✔
112
        if feedId != "" {
2✔
113
                uri = uri + "?feedId=" + feedId
1✔
114
        }
1✔
115
        header := http.Header{
1✔
116
                "Content-Type": {contentType},
1✔
117
        }
1✔
118
        return network.NewHttpPostRequest(uri, c.toAuthorization(c.token), header, uploadReader, -1)
1✔
119
}
120

121
func (c OrchestratorClient) writeMultipartBody(bodyWriter *io.PipeWriter, stream stream.Stream, contentType string, cancel context.CancelCauseFunc) string {
1✔
122
        formWriter := multipart.NewWriter(bodyWriter)
1✔
123
        go func() {
2✔
124
                defer func() { _ = bodyWriter.Close() }()
2✔
125
                defer func() { _ = formWriter.Close() }()
2✔
126
                err := c.writeMultipartForm(formWriter, stream, contentType)
1✔
127
                if err != nil {
1✔
128
                        cancel(err)
×
129
                        return
×
130
                }
×
131
        }()
132
        return formWriter.FormDataContentType()
1✔
133
}
134

135
func (c OrchestratorClient) writeMultipartForm(writer *multipart.Writer, stream stream.Stream, contentType string) error {
1✔
136
        filePart := textproto.MIMEHeader{}
1✔
137
        filePart.Set("Content-Disposition", fmt.Sprintf(`form-data; name="file"; filename="%s"`, stream.Name()))
1✔
138
        filePart.Set("Content-Type", contentType)
1✔
139
        w, err := writer.CreatePart(filePart)
1✔
140
        if err != nil {
1✔
141
                return fmt.Errorf("Error creating form field 'file': %w", err)
×
142
        }
×
143
        data, err := stream.Data()
1✔
144
        if err != nil {
1✔
145
                return err
×
146
        }
×
147
        defer func() { _ = data.Close() }()
2✔
148
        _, err = io.Copy(w, data)
1✔
149
        if err != nil {
1✔
150
                return fmt.Errorf("Error writing form field 'file': %w", err)
×
151
        }
×
152
        return nil
1✔
153
}
154

155
func (c OrchestratorClient) GetReleases(folderId int, processKey string) ([]Release, error) {
1✔
156
        request := c.createGetReleasesRequest(folderId, processKey)
1✔
157
        client := network.NewHttpClient(c.logger, c.httpClientSettings())
1✔
158
        response, err := client.Send(request)
1✔
159
        if err != nil {
1✔
UNCOV
160
                return []Release{}, err
×
UNCOV
161
        }
×
162
        defer func() { _ = response.Body.Close() }()
2✔
163
        body, err := io.ReadAll(response.Body)
1✔
164
        if err != nil {
1✔
165
                return []Release{}, fmt.Errorf("Error reading response: %w", err)
×
166
        }
×
167
        if response.StatusCode != http.StatusOK {
2✔
168
                return []Release{}, fmt.Errorf("Orchestrator returned status code '%v' and body '%v'", response.StatusCode, string(body))
1✔
169
        }
1✔
170

171
        var result getReleasesResponseJson
1✔
172
        err = json.Unmarshal(body, &result)
1✔
173
        if err != nil {
2✔
174
                return []Release{}, fmt.Errorf("Orchestrator returned invalid response body '%v'", string(body))
1✔
175
        }
1✔
176
        return c.convertToReleases(result), nil
1✔
177
}
178

179
func (c OrchestratorClient) convertToReleases(json getReleasesResponseJson) []Release {
1✔
180
        releases := []Release{}
1✔
181
        for _, v := range json.Value {
2✔
182
                releases = append(releases, *NewRelease(v.Id, v.Name))
1✔
183
        }
1✔
184
        return releases
1✔
185
}
186

187
func (c OrchestratorClient) createGetReleasesRequest(folderId int, processKey string) *network.HttpRequest {
1✔
188
        uri := c.baseUri + "/odata/Releases?$filter=ProcessKey%20eq%20'" + processKey + "'"
1✔
189
        header := http.Header{
1✔
190
                "Content-Type":                {"application/json"},
1✔
191
                "X-Uipath-Organizationunitid": {strconv.Itoa(folderId)},
1✔
192
        }
1✔
193
        return network.NewHttpGetRequest(uri, c.toAuthorization(c.token), header)
1✔
194
}
1✔
195

196
func (c OrchestratorClient) CreateOrUpdateRelease(folderId int, processKey string, processVersion string) (int, error) {
1✔
197
        releases, err := c.GetReleases(folderId, processKey)
1✔
198
        if err != nil {
2✔
199
                return -1, err
1✔
200
        }
1✔
201
        if len(releases) > 0 {
2✔
202
                return c.UpdateRelease(folderId, releases[0].Id, processKey, processVersion)
1✔
203
        }
1✔
204
        return c.CreateRelease(folderId, processKey, processVersion)
1✔
205
}
206

207
func (c OrchestratorClient) CreateRelease(folderId int, processKey string, processVersion string) (int, error) {
1✔
208
        request, err := c.createNewReleaseRequest(folderId, processKey, processVersion)
1✔
209
        if err != nil {
1✔
210
                return -1, err
×
211
        }
×
212
        client := network.NewHttpClient(c.logger, c.httpClientSettings())
1✔
213
        response, err := client.Send(request)
1✔
214
        if err != nil {
1✔
215
                return -1, err
×
216
        }
×
217
        defer func() { _ = response.Body.Close() }()
2✔
218
        body, err := io.ReadAll(response.Body)
1✔
219
        if err != nil {
1✔
220
                return -1, fmt.Errorf("Error reading response: %w", err)
×
221
        }
×
222
        if response.StatusCode != http.StatusCreated {
2✔
223
                return -1, fmt.Errorf("Orchestrator returned status code '%v' and body '%v'", response.StatusCode, string(body))
1✔
224
        }
1✔
225

226
        var result createReleaseResponseJson
1✔
227
        err = json.Unmarshal(body, &result)
1✔
228
        if err != nil {
2✔
229
                return -1, fmt.Errorf("Orchestrator returned invalid response body '%v'", string(body))
1✔
230
        }
1✔
231
        return result.Id, nil
1✔
232
}
233

234
func (c OrchestratorClient) createNewReleaseRequest(folderId int, processKey string, processVersion string) (*network.HttpRequest, error) {
1✔
235
        json, err := json.Marshal(createReleaseRequestJson{
1✔
236
                Name:           processKey,
1✔
237
                ProcessKey:     processKey,
1✔
238
                ProcessVersion: processVersion,
1✔
239
        })
1✔
240
        if err != nil {
1✔
241
                return nil, err
×
242
        }
×
243
        uri := c.baseUri + "/odata/Releases"
1✔
244
        header := http.Header{
1✔
245
                "Content-Type":                {"application/json"},
1✔
246
                "X-Uipath-Organizationunitid": {strconv.Itoa(folderId)},
1✔
247
        }
1✔
248
        return network.NewHttpPostRequest(uri, c.toAuthorization(c.token), header, bytes.NewBuffer(json), -1), nil
1✔
249
}
250

251
func (c OrchestratorClient) UpdateRelease(folderId int, releaseId int, processKey string, processVersion string) (int, error) {
1✔
252
        request, err := c.createUpdateReleaseRequest(folderId, releaseId, processKey, processVersion)
1✔
253
        if err != nil {
1✔
254
                return -1, err
×
255
        }
×
256
        client := network.NewHttpClient(c.logger, c.httpClientSettings())
1✔
257
        response, err := client.Send(request)
1✔
258
        if err != nil {
1✔
259
                return -1, err
×
260
        }
×
261
        defer func() { _ = response.Body.Close() }()
2✔
262
        body, err := io.ReadAll(response.Body)
1✔
263
        if err != nil {
1✔
264
                return -1, fmt.Errorf("Error reading response: %w", err)
×
265
        }
×
266
        if response.StatusCode != http.StatusOK {
1✔
267
                return -1, fmt.Errorf("Orchestrator returned status code '%v' and body '%v'", response.StatusCode, string(body))
×
268
        }
×
269
        return releaseId, nil
1✔
270
}
271

272
func (c OrchestratorClient) createUpdateReleaseRequest(folderId int, releaseId int, processKey string, processVersion string) (*network.HttpRequest, error) {
1✔
273
        json, err := json.Marshal(createReleaseRequestJson{
1✔
274
                Name:           processKey,
1✔
275
                ProcessKey:     processKey,
1✔
276
                ProcessVersion: processVersion,
1✔
277
        })
1✔
278
        if err != nil {
1✔
279
                return nil, err
×
280
        }
×
281
        uri := c.baseUri + fmt.Sprintf("/odata/Releases(%d)", releaseId)
1✔
282
        header := http.Header{
1✔
283
                "Content-Type":                {"application/json"},
1✔
284
                "X-Uipath-Organizationunitid": {strconv.Itoa(folderId)},
1✔
285
        }
1✔
286
        return network.NewHttpPatchRequest(uri, c.toAuthorization(c.token), header, bytes.NewBuffer(json), -1), nil
1✔
287
}
288

289
func (c OrchestratorClient) CreateTestSet(folderId int, releaseId int, processVersion string) (int, error) {
1✔
290
        request, err := c.createTestSetRequest(folderId, releaseId, processVersion)
1✔
291
        if err != nil {
1✔
292
                return -1, err
×
293
        }
×
294
        client := network.NewHttpClient(c.logger, c.httpClientSettings())
1✔
295
        response, err := client.Send(request)
1✔
296
        if err != nil {
2✔
297
                return -1, err
1✔
298
        }
1✔
299
        defer func() { _ = response.Body.Close() }()
2✔
300
        body, err := io.ReadAll(response.Body)
1✔
301
        if err != nil {
1✔
302
                return -1, fmt.Errorf("Error reading response: %w", err)
×
303
        }
×
304
        if response.StatusCode != http.StatusCreated {
1✔
305
                return -1, fmt.Errorf("Orchestrator returned status code '%v' and body '%v'", response.StatusCode, string(body))
×
306
        }
×
307
        return strconv.Atoi(string(body))
1✔
308
}
309

310
func (c OrchestratorClient) createTestSetRequest(folderId int, releaseId int, processVersion string) (*network.HttpRequest, error) {
1✔
311
        json, err := json.Marshal(createTestSetRequestJson{
1✔
312
                ReleaseId:     releaseId,
1✔
313
                VersionNumber: processVersion,
1✔
314
        })
1✔
315
        if err != nil {
1✔
316
                return nil, err
×
317
        }
×
318
        uri := c.baseUri + "/api/TestAutomation/CreateTestSetForReleaseVersion"
1✔
319
        header := http.Header{
1✔
320
                "Content-Type":                {"application/json"},
1✔
321
                "X-Uipath-Organizationunitid": {strconv.Itoa(folderId)},
1✔
322
        }
1✔
323
        return network.NewHttpPostRequest(uri, c.toAuthorization(c.token), header, bytes.NewBuffer(json), -1), nil
1✔
324
}
325

326
func (c OrchestratorClient) ExecuteTestSet(folderId int, testSetId int) (int, error) {
1✔
327
        request := c.createExecuteTestSetRequest(folderId, testSetId)
1✔
328
        client := network.NewHttpClient(c.logger, c.httpClientSettings())
1✔
329
        response, err := client.Send(request)
1✔
330
        if err != nil {
2✔
331
                return -1, err
1✔
332
        }
1✔
333
        defer func() { _ = response.Body.Close() }()
2✔
334
        body, err := io.ReadAll(response.Body)
1✔
335
        if err != nil {
1✔
336
                return -1, fmt.Errorf("Error reading response: %w", err)
×
337
        }
×
338
        if response.StatusCode != http.StatusOK {
1✔
339
                return -1, fmt.Errorf("Orchestrator returned status code '%v' and body '%v'", response.StatusCode, string(body))
×
340
        }
×
341
        return strconv.Atoi(string(body))
1✔
342
}
343

344
func (c OrchestratorClient) createExecuteTestSetRequest(folderId int, testSetId int) *network.HttpRequest {
1✔
345
        uri := c.baseUri + fmt.Sprintf("/api/TestAutomation/StartTestSetExecution?testSetId=%d&triggerType=ExternalTool", testSetId)
1✔
346
        header := http.Header{
1✔
347
                "Content-Type":                {"application/json"},
1✔
348
                "X-Uipath-Organizationunitid": {strconv.Itoa(folderId)},
1✔
349
        }
1✔
350
        return network.NewHttpPostRequest(uri, c.toAuthorization(c.token), header, bytes.NewReader([]byte{}), -1)
1✔
351
}
1✔
352

353
func (c OrchestratorClient) WaitForTestExecutionToFinish(folderId int, executionId int, timeout time.Duration, statusFunc func(TestExecution)) (*TestExecution, error) {
1✔
354
        startTime := time.Now()
1✔
355
        for {
2✔
356
                execution, err := c.GetTestExecution(folderId, executionId)
1✔
357
                if err != nil {
1✔
358
                        return nil, err
×
359
                }
×
360
                statusFunc(*execution)
1✔
361
                if execution.IsCompleted() {
2✔
362
                        return execution, nil
1✔
363
                }
1✔
364
                if time.Since(startTime) >= timeout {
2✔
365
                        return nil, fmt.Errorf("Timeout waiting for test execution '%d' to finish.", executionId)
1✔
366
                }
1✔
367
                time.Sleep(1 * time.Second)
1✔
368
        }
369
}
370

371
func (c OrchestratorClient) GetTestSet(folderId int, testSetId int) (*TestSet, error) {
1✔
372
        request := c.createGetTestSetRequest(folderId, testSetId)
1✔
373
        client := network.NewHttpClient(c.logger, c.httpClientSettings())
1✔
374
        response, err := client.Send(request)
1✔
375
        if err != nil {
1✔
376
                return nil, err
×
377
        }
×
378
        defer func() { _ = response.Body.Close() }()
2✔
379
        body, err := io.ReadAll(response.Body)
1✔
380
        if err != nil {
1✔
381
                return nil, fmt.Errorf("Error reading response: %w", err)
×
382
        }
×
383
        if response.StatusCode != http.StatusOK {
1✔
384
                return nil, fmt.Errorf("Orchestrator returned status code '%v' and body '%v'", response.StatusCode, string(body))
×
385
        }
×
386

387
        var result getTestSetResponseJson
1✔
388
        err = json.Unmarshal(body, &result)
1✔
389
        if err != nil {
1✔
390
                return nil, fmt.Errorf("Orchestrator returned invalid response body '%v'", string(body))
×
391
        }
×
392
        return c.convertToTestSet(result), nil
1✔
393
}
394

395
func (c OrchestratorClient) createGetTestSetRequest(folderId int, testSetId int) *network.HttpRequest {
1✔
396
        uri := c.baseUri + fmt.Sprintf("/odata/TestSets(%d)?$expand=TestCases($expand=Definition;$select=Id,Definition,DefinitionId,ReleaseId,VersionNumber),Packages&$select=TestCases,Name", testSetId)
1✔
397
        header := http.Header{
1✔
398
                "Content-Type":                {"application/json"},
1✔
399
                "X-Uipath-Organizationunitid": {strconv.Itoa(folderId)},
1✔
400
        }
1✔
401
        return network.NewHttpGetRequest(uri, c.toAuthorization(c.token), header)
1✔
402
}
1✔
403

404
func (c OrchestratorClient) GetTestExecution(folderId int, executionId int) (*TestExecution, error) {
1✔
405
        request := c.createGetTestExecutionRequest(folderId, executionId)
1✔
406
        client := network.NewHttpClient(c.logger, c.httpClientSettings())
1✔
407
        response, err := client.Send(request)
1✔
408
        if err != nil {
1✔
409
                return nil, err
×
410
        }
×
411
        defer func() { _ = response.Body.Close() }()
2✔
412
        body, err := io.ReadAll(response.Body)
1✔
413
        if err != nil {
1✔
414
                return nil, fmt.Errorf("Error reading response: %w", err)
×
415
        }
×
416
        if response.StatusCode != http.StatusOK {
1✔
417
                return nil, fmt.Errorf("Orchestrator returned status code '%v' and body '%v'", response.StatusCode, string(body))
×
418
        }
×
419

420
        var result getTestExecutionResponseJson
1✔
421
        err = json.Unmarshal(body, &result)
1✔
422
        if err != nil {
1✔
423
                return nil, fmt.Errorf("Orchestrator returned invalid response body '%v'", string(body))
×
424
        }
×
425
        return c.convertToTestExecution(result), nil
1✔
426
}
427

428
func (c OrchestratorClient) createGetTestExecutionRequest(folderId int, executionId int) *network.HttpRequest {
1✔
429
        uri := c.baseUri + fmt.Sprintf("/odata/TestSetExecutions(%d)?$expand=TestCaseExecutions($expand=TestCaseAssertions)", executionId)
1✔
430
        header := http.Header{
1✔
431
                "Content-Type":                {"application/json"},
1✔
432
                "X-Uipath-Organizationunitid": {strconv.Itoa(folderId)},
1✔
433
        }
1✔
434
        return network.NewHttpGetRequest(uri, c.toAuthorization(c.token), header)
1✔
435
}
1✔
436

437
func (c OrchestratorClient) GetRobotLogs(folderId int, jobKey string) ([]RobotLog, error) {
1✔
438
        request := c.createGetRobotLogsRequest(folderId, jobKey)
1✔
439
        client := network.NewHttpClient(c.logger, c.httpClientSettings())
1✔
440
        response, err := client.Send(request)
1✔
441
        if err != nil {
1✔
442
                return []RobotLog{}, err
×
443
        }
×
444
        defer func() { _ = response.Body.Close() }()
2✔
445
        body, err := io.ReadAll(response.Body)
1✔
446
        if err != nil {
1✔
447
                return []RobotLog{}, fmt.Errorf("Error reading response: %w", err)
×
448
        }
×
449
        if response.StatusCode != http.StatusOK {
1✔
450
                return []RobotLog{}, fmt.Errorf("Orchestrator returned status code '%v' and body '%v'", response.StatusCode, string(body))
×
451
        }
×
452

453
        var result getRobotLogsResponseJson
1✔
454
        err = json.Unmarshal(body, &result)
1✔
455
        if err != nil {
1✔
456
                return []RobotLog{}, fmt.Errorf("Orchestrator returned invalid response body '%v'", string(body))
×
457
        }
×
458
        return c.convertToRobotLogs(result), nil
1✔
459
}
460

461
func (c OrchestratorClient) createGetRobotLogsRequest(folderId int, jobKey string) *network.HttpRequest {
1✔
462
        uri := c.baseUri + "/odata/RobotLogs?$filter=JobKey%20eq%20" + jobKey
1✔
463
        header := http.Header{
1✔
464
                "X-Uipath-Organizationunitid": {strconv.Itoa(folderId)},
1✔
465
        }
1✔
466
        return network.NewHttpGetRequest(uri, c.toAuthorization(c.token), header)
1✔
467
}
1✔
468

469
func (c OrchestratorClient) GetFolderFeed(folderId int) (string, error) {
1✔
470
        request := c.createGetFolderFeedRequest(folderId)
1✔
471
        client := network.NewHttpClient(c.logger, c.httpClientSettings())
1✔
472
        response, err := client.Send(request)
1✔
473
        if err != nil {
1✔
474
                return "", err
×
475
        }
×
476
        defer func() { _ = response.Body.Close() }()
2✔
477
        body, err := io.ReadAll(response.Body)
1✔
478
        if err != nil {
1✔
479
                return "", fmt.Errorf("Error reading response: %w", err)
×
480
        }
×
481
        if response.StatusCode != http.StatusOK {
2✔
482
                return "", fmt.Errorf("Orchestrator returned status code '%v' and body '%v'", response.StatusCode, string(body))
1✔
483
        }
1✔
484
        feedId := string(body)
1✔
485
        if feedId == "null" {
2✔
486
                return "", nil
1✔
487
        }
1✔
488
        return strings.Trim(feedId, "\""), nil
1✔
489
}
490

491
func (c OrchestratorClient) createGetFolderFeedRequest(folderId int) *network.HttpRequest {
1✔
492
        uri := c.baseUri + fmt.Sprintf("/api/PackageFeeds/GetFolderFeed?folderId=%d", folderId)
1✔
493
        header := http.Header{
1✔
494
                "Content-Type": {"application/json"},
1✔
495
        }
1✔
496
        return network.NewHttpGetRequest(uri, c.toAuthorization(c.token), header)
1✔
497
}
1✔
498

499
func (c OrchestratorClient) GetReadUrl(folderId int, bucketId int, path string) (string, error) {
1✔
500
        request := c.createReadUrlRequest(folderId, bucketId, path)
1✔
501
        client := network.NewHttpClient(c.logger, c.httpClientSettings())
1✔
502
        response, err := client.Send(request)
1✔
503
        if err != nil {
1✔
504
                return "", fmt.Errorf("Error sending request: %w", err)
×
505
        }
×
506
        defer func() { _ = response.Body.Close() }()
2✔
507
        body, err := io.ReadAll(response.Body)
1✔
508
        if err != nil {
1✔
509
                return "", fmt.Errorf("Error reading response: %w", err)
×
510
        }
×
511
        if response.StatusCode != http.StatusOK {
2✔
512
                return "", fmt.Errorf("Orchestrator returned status code '%v' and body '%v'", response.StatusCode, string(body))
1✔
513
        }
1✔
514
        var result urlResponse
1✔
515
        err = json.Unmarshal(body, &result)
1✔
516
        if err != nil {
1✔
517
                return "", fmt.Errorf("Error parsing json response: %w", err)
×
518
        }
×
519
        return result.Uri, nil
1✔
520
}
521

522
func (c OrchestratorClient) createReadUrlRequest(folderId int, bucketId int, path string) *network.HttpRequest {
1✔
523
        uri := c.baseUri + fmt.Sprintf("/odata/Buckets(%d)/UiPath.Server.Configuration.OData.GetReadUri?path=%s", bucketId, path)
1✔
524
        header := http.Header{
1✔
525
                "X-UiPath-OrganizationUnitId": {strconv.Itoa(folderId)},
1✔
526
        }
1✔
527
        return network.NewHttpGetRequest(uri, c.toAuthorization(c.token), header)
1✔
528
}
1✔
529

530
func (c OrchestratorClient) GetWriteUrl(folderId int, bucketId int, path string) (string, error) {
1✔
531
        request := c.createWriteUrlRequest(folderId, bucketId, path)
1✔
532
        client := network.NewHttpClient(c.logger, c.httpClientSettings())
1✔
533
        response, err := client.Send(request)
1✔
534
        if err != nil {
1✔
535
                return "", err
×
536
        }
×
537
        defer func() { _ = response.Body.Close() }()
2✔
538
        body, err := io.ReadAll(response.Body)
1✔
539
        if err != nil {
1✔
540
                return "", fmt.Errorf("Error reading response: %w", err)
×
541
        }
×
542
        if response.StatusCode != http.StatusOK {
2✔
543
                return "", fmt.Errorf("Orchestrator returned status code '%v' and body '%v'", response.StatusCode, string(body))
1✔
544
        }
1✔
545
        var result urlResponse
1✔
546
        err = json.Unmarshal(body, &result)
1✔
547
        if err != nil {
1✔
548
                return "", fmt.Errorf("Error parsing json response: %w", err)
×
549
        }
×
550
        return result.Uri, nil
1✔
551
}
552

553
func (c OrchestratorClient) createWriteUrlRequest(folderId int, bucketId int, path string) *network.HttpRequest {
1✔
554
        uri := c.baseUri + fmt.Sprintf("/odata/Buckets(%d)/UiPath.Server.Configuration.OData.GetWriteUri?path=%s", bucketId, path)
1✔
555
        header := http.Header{
1✔
556
                "X-UiPath-OrganizationUnitId": {strconv.Itoa(folderId)},
1✔
557
        }
1✔
558
        return network.NewHttpGetRequest(uri, c.toAuthorization(c.token), header)
1✔
559
}
1✔
560

561
func (c OrchestratorClient) convertToRobotLogs(json getRobotLogsResponseJson) []RobotLog {
1✔
562
        logs := []RobotLog{}
1✔
563
        for _, l := range json.Value {
2✔
564
                logs = append(logs, c.convertToRobotLog(l))
1✔
565
        }
1✔
566
        return logs
1✔
567
}
568

569
func (c OrchestratorClient) convertToRobotLog(json getRobotLogJson) RobotLog {
1✔
570
        return *NewRobotLog(
1✔
571
                json.Id,
1✔
572
                json.Level,
1✔
573
                json.WindowsIdentity,
1✔
574
                json.ProcessName,
1✔
575
                json.TimeStamp,
1✔
576
                json.Message,
1✔
577
                json.RobotName,
1✔
578
                json.HostMachineName,
1✔
579
                json.MachineId,
1✔
580
                json.MachineKey,
1✔
581
                json.RuntimeType,
1✔
582
        )
1✔
583
}
1✔
584

585
func (c OrchestratorClient) convertToTestSet(json getTestSetResponseJson) *TestSet {
1✔
586
        testCases := []TestCase{}
1✔
587
        for _, c := range json.TestCases {
2✔
588
                testCase := NewTestCase(
1✔
589
                        c.Id,
1✔
590
                        c.Definition.Name,
1✔
591
                        c.Definition.PackageIdentifier,
1✔
592
                )
1✔
593
                testCases = append(testCases, *testCase)
1✔
594
        }
1✔
595
        testPackages := []TestPackage{}
1✔
596
        for _, p := range json.Packages {
2✔
597
                testPackage := NewTestPackage(
1✔
598
                        p.PackageIdentifier,
1✔
599
                        p.VersionMask,
1✔
600
                )
1✔
601
                testPackages = append(testPackages, *testPackage)
1✔
602
        }
1✔
603
        return NewTestSet(json.Id, testCases, testPackages)
1✔
604
}
605

606
func (c OrchestratorClient) convertToTestExecution(json getTestExecutionResponseJson) *TestExecution {
1✔
607
        return NewTestExecution(
1✔
608
                json.Id,
1✔
609
                json.Status,
1✔
610
                json.TestSetId,
1✔
611
                json.Name,
1✔
612
                json.StartTime,
1✔
613
                json.EndTime,
1✔
614
                c.convertToTestCaseExecutions(json.TestCaseExecutions),
1✔
615
        )
1✔
616
}
1✔
617

618
func (c OrchestratorClient) convertToTestCaseExecutions(json []testCaseExecutionJson) []TestCaseExecution {
1✔
619
        executions := []TestCaseExecution{}
1✔
620
        for _, v := range json {
2✔
621
                execution := NewTestCaseExecution(
1✔
622
                        v.Id,
1✔
623
                        v.Status,
1✔
624
                        v.TestCaseId,
1✔
625
                        v.VersionNumber,
1✔
626
                        v.EntryPointPath,
1✔
627
                        v.Info,
1✔
628
                        v.StartTime,
1✔
629
                        v.EndTime,
1✔
630
                        v.JobId,
1✔
631
                        v.JobKey,
1✔
632
                        v.InputArguments,
1✔
633
                        v.OutputArguments,
1✔
634
                        v.DataVariationIdentifier,
1✔
635
                        c.convertToTestCaseAssertions(v.TestCaseAssertions),
1✔
636
                        nil,
1✔
637
                )
1✔
638
                executions = append(executions, *execution)
1✔
639
        }
1✔
640
        return executions
1✔
641
}
642

643
func (c OrchestratorClient) convertToTestCaseAssertions(json []testCaseExecutionAssertionJson) []TestCaseAssertion {
1✔
644
        assertions := []TestCaseAssertion{}
1✔
645
        for _, v := range json {
1✔
646
                assertion := NewTestCaseAssertion(v.Message, v.Succeeded)
×
647
                assertions = append(assertions, *assertion)
×
648
        }
×
649
        return assertions
1✔
650
}
651

652
func (c OrchestratorClient) progressReader(text string, completedText string, reader io.Reader, length int64, progressBar *visualization.ProgressBar) io.Reader {
1✔
653
        if progressBar == nil || length < 10*1024*1024 {
2✔
654
                return reader
1✔
655
        }
1✔
656
        return visualization.NewProgressReader(reader, func(progress visualization.Progress) {
2✔
657
                displayText := text
1✔
658
                if progress.Completed {
2✔
659
                        displayText = completedText
1✔
660
                }
1✔
661
                progressBar.UpdateProgress(displayText, progress.BytesRead, length, progress.BytesPerSecond)
1✔
662
        })
663
}
664

665
func (c OrchestratorClient) httpClientSettings() network.HttpClientSettings {
1✔
666
        return *network.NewHttpClientSettings(
1✔
667
                c.debug,
1✔
668
                c.settings.OperationId,
1✔
669
                c.settings.Header,
1✔
670
                c.settings.Timeout,
1✔
671
                c.settings.MaxAttempts,
1✔
672
                c.settings.Insecure)
1✔
673
}
1✔
674

675
func (c OrchestratorClient) toAuthorization(token *auth.AuthToken) *network.Authorization {
1✔
676
        if token == nil {
2✔
677
                return nil
1✔
678
        }
1✔
679
        return network.NewAuthorization(token.Type, token.Value)
1✔
680
}
681

682
type createReleaseRequestJson struct {
683
        Name           string `json:"name"`
684
        ProcessKey     string `json:"processKey"`
685
        ProcessVersion string `json:"processVersion"`
686
}
687

688
type createTestSetRequestJson struct {
689
        ReleaseId     int    `json:"releaseId"`
690
        VersionNumber string `json:"versionNumber"`
691
}
692

693
type getFoldersResponseJson struct {
694
        Value []getFoldersResponseValueJson `json:"value"`
695
}
696

697
type getFoldersResponseValueJson struct {
698
        Id   int    `json:"Id"`
699
        Name string `json:"FullyQualifiedName"`
700
}
701

702
type getReleasesResponseJson struct {
703
        Value []getReleasesResponseValueJson `json:"value"`
704
}
705

706
type getReleasesResponseValueJson struct {
707
        Id   int    `json:"Id"`
708
        Name string `json:"Name"`
709
}
710

711
type createReleaseResponseJson struct {
712
        Id int `json:"id"`
713
}
714

715
type getTestSetResponseJson struct {
716
        Id        int                      `json:"Id"`
717
        TestCases []getTestSetTestCaseJson `json:"TestCases"`
718
        Packages  []getTestSetPackagesJson `json:"Packages"`
719
}
720

721
type getTestSetTestCaseJson struct {
722
        Id         int                              `json:"Id"`
723
        Definition getTestSetTestCaseDefinitionJson `json:"Definition"`
724
}
725

726
type getTestSetPackagesJson struct {
727
        PackageIdentifier string `json:"PackageIdentifier"`
728
        VersionMask       string `json:"VersionMask"`
729
}
730

731
type getTestSetTestCaseDefinitionJson struct {
732
        Name              string `json:"Name"`
733
        PackageIdentifier string `json:"PackageIdentifier"`
734
}
735

736
type getTestExecutionResponseJson struct {
737
        Id                 int                     `json:"Id"`
738
        Status             string                  `json:"Status"`
739
        TestSetId          int                     `json:"TestSetId"`
740
        Name               string                  `json:"Name"`
741
        StartTime          time.Time               `json:"StartTime"`
742
        EndTime            time.Time               `json:"EndTime"`
743
        TestCaseExecutions []testCaseExecutionJson `json:"TestCaseExecutions"`
744
}
745

746
type testCaseExecutionJson struct {
747
        Id                      int                              `json:"Id"`
748
        Status                  string                           `json:"Status"`
749
        TestCaseId              int                              `json:"TestCaseId"`
750
        VersionNumber           string                           `json:"VersionNumber"`
751
        EntryPointPath          string                           `json:"EntryPointPath"`
752
        Info                    string                           `json:"Info"`
753
        StartTime               time.Time                        `json:"StartTime"`
754
        EndTime                 time.Time                        `json:"EndTime"`
755
        JobId                   int                              `json:"JobId"`
756
        JobKey                  string                           `json:"JobKey"`
757
        InputArguments          string                           `json:"InputArguments"`
758
        OutputArguments         string                           `json:"OutputArguments"`
759
        DataVariationIdentifier string                           `json:"DataVariationIdentifier"`
760
        TestCaseAssertions      []testCaseExecutionAssertionJson `json:"TestCaseAssertions"`
761
}
762

763
type testCaseExecutionAssertionJson struct {
764
        Message   string `json:"Message"`
765
        Succeeded bool   `json:"Succeeded"`
766
}
767

768
type getRobotLogsResponseJson struct {
769
        Value []getRobotLogJson `json:"value"`
770
}
771

772
type getRobotLogJson struct {
773
        Id              int       `json:"Id"`
774
        Level           string    `json:"Level"`
775
        WindowsIdentity string    `json:"WindowsIdentity"`
776
        ProcessName     string    `json:"ProcessName"`
777
        TimeStamp       time.Time `json:"TimeStamp"`
778
        Message         string    `json:"Message"`
779
        RobotName       string    `json:"RobotName"`
780
        HostMachineName string    `json:"HostMachineName"`
781
        MachineId       int       `json:"MachineId"`
782
        MachineKey      string    `json:"MachineKey"`
783
        RuntimeType     string    `json:"RuntimeType"`
784
}
785

786
type urlResponse struct {
787
        Uri string `json:"Uri"`
788
}
789

790
func NewOrchestratorClient(baseUri string, token *auth.AuthToken, debug bool, settings plugin.ExecutionSettings, logger log.Logger) *OrchestratorClient {
1✔
791
        return &OrchestratorClient{baseUri, token, debug, settings, logger}
1✔
792
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc