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

UiPath / uipathcli / 14747911552

30 Apr 2025 06:07AM UTC coverage: 90.432% (+0.01%) from 90.419%
14747911552

Pull #187

github

thschmitt
Extend plugin paramters to support default value and allowed values

The core command executor already supports default values and a list
of allowed values for parameters. Extended the plugin parameters
to support these features as well to reduce custom handling in the
plugins themselves.

Changed the plugin command to use builder pattern for more complex
parameter configurations.

Example:

```
*plugin.NewCommand("myplugin").
  WithOperation("get", "Get Data", "Gets data from the API").
  WithParameter(plugin.NewParameter("filter", plugin.ParameterTypeString, "This is a filter").
    WithDefaultValue("all").
    WithAllowedValues([]interface{}{"all", "default", "none"}))
```
Pull Request #187: 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