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

UiPath / uipathcli / 13944852368

19 Mar 2025 10:56AM UTC coverage: 90.44% (+0.3%) from 90.112%
13944852368

push

github

thschmitt
Add new api package with orchestrator and du client

- Moved the orchestrator client to new api package.

- Extracted client-related logic from the DigitizeCommand and
  Upload/DownloadCommand into the new api package.

- Changed auth module to only return the auth token and no more
  additional headers to simplify the auth module.

260 of 291 new or added lines in 21 files covered. (89.35%)

1 existing line in 1 file now uncovered.

5894 of 6517 relevant lines covered (90.44%)

1.01 hits per line

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

85.47
/utils/api/du_client.go
1
package api
2

3
import (
4
        "context"
5
        "encoding/json"
6
        "fmt"
7
        "io"
8
        "mime/multipart"
9
        "net/http"
10
        "net/textproto"
11

12
        "github.com/UiPath/uipathcli/auth"
13
        "github.com/UiPath/uipathcli/log"
14
        "github.com/UiPath/uipathcli/plugin"
15
        "github.com/UiPath/uipathcli/utils/network"
16
        "github.com/UiPath/uipathcli/utils/stream"
17
        "github.com/UiPath/uipathcli/utils/visualization"
18
)
19

20
type DuClient struct {
21
        baseUri  string
22
        token    *auth.AuthToken
23
        debug    bool
24
        settings plugin.ExecutionSettings
25
        logger   log.Logger
26
}
27

28
func (c DuClient) StartDigitization(projectId string, file stream.Stream, contentType string, uploadBar *visualization.ProgressBar) (string, error) {
1✔
29
        context, cancel := context.WithCancelCause(context.Background())
1✔
30
        request := c.createStartDigitizationRequest(projectId, file, contentType, uploadBar, cancel)
1✔
31
        client := network.NewHttpClient(c.logger, c.httpClientSettings())
1✔
32
        response, err := client.SendWithContext(request, context)
1✔
33
        if err != nil {
2✔
34
                return "", err
1✔
35
        }
1✔
36
        defer response.Body.Close()
1✔
37
        body, err := io.ReadAll(response.Body)
1✔
38
        if err != nil {
1✔
NEW
39
                return "", fmt.Errorf("Error reading response: %w", err)
×
NEW
40
        }
×
41
        if response.StatusCode != http.StatusAccepted {
2✔
42
                return "", fmt.Errorf("Digitizer returned status code '%v' and body '%v'", response.StatusCode, string(body))
1✔
43
        }
1✔
44
        var result digitizeResponse
1✔
45
        err = json.Unmarshal(body, &result)
1✔
46
        if err != nil {
1✔
NEW
47
                return "", fmt.Errorf("Error parsing json response: %w", err)
×
NEW
48
        }
×
49
        return result.DocumentId, nil
1✔
50
}
51

52
func (c DuClient) createStartDigitizationRequest(projectId string, file stream.Stream, contentType string, uploadBar *visualization.ProgressBar, cancel context.CancelCauseFunc) *network.HttpRequest {
1✔
53
        streamSize, _ := file.Size()
1✔
54
        bodyReader, bodyWriter := io.Pipe()
1✔
55
        formDataContentType := c.writeMultipartBody(bodyWriter, file, contentType, cancel)
1✔
56
        uploadReader := c.progressReader("uploading...", "completing  ", bodyReader, streamSize, uploadBar)
1✔
57

1✔
58
        uri := c.baseUri + fmt.Sprintf("/api/framework/projects/%s/digitization/start?api-version=1", projectId)
1✔
59
        header := http.Header{
1✔
60
                "Content-Type": {formDataContentType},
1✔
61
        }
1✔
62
        return network.NewHttpPostRequest(uri, c.toAuthorization(c.token), header, uploadReader, -1)
1✔
63
}
1✔
64

65
func (c DuClient) GetDigitizationResult(projectId string, documentId string) (string, error) {
1✔
66
        request := c.createDigitizeStatusRequest(projectId, documentId)
1✔
67
        client := network.NewHttpClient(c.logger, c.httpClientSettings())
1✔
68
        response, err := client.Send(request)
1✔
69
        if err != nil {
1✔
NEW
70
                return "", err
×
NEW
71
        }
×
72
        defer response.Body.Close()
1✔
73
        body, err := io.ReadAll(response.Body)
1✔
74
        if err != nil {
1✔
NEW
75
                return "", fmt.Errorf("Error reading response: %w", err)
×
NEW
76
        }
×
77
        if response.StatusCode != http.StatusOK {
2✔
78
                return "", fmt.Errorf("Digitizer returned status code '%v' and body '%v'", response.StatusCode, string(body))
1✔
79
        }
1✔
80
        var result digitizeResultResponse
1✔
81
        err = json.Unmarshal(body, &result)
1✔
82
        if err != nil {
1✔
NEW
83
                return "", fmt.Errorf("Error parsing json response: %w", err)
×
NEW
84
        }
×
85
        if result.Status == "NotStarted" || result.Status == "Running" {
1✔
NEW
86
                return "", nil
×
NEW
87
        }
×
88
        return string(body), err
1✔
89
}
90

91
func (c DuClient) createDigitizeStatusRequest(projectId string, documentId string) *network.HttpRequest {
1✔
92
        uri := c.baseUri + fmt.Sprintf("/api/framework/projects/%s/digitization/result/%s?api-version=1", projectId, documentId)
1✔
93
        return network.NewHttpGetRequest(uri, c.toAuthorization(c.token), http.Header{})
1✔
94
}
1✔
95

96
func (c DuClient) writeMultipartBody(bodyWriter *io.PipeWriter, stream stream.Stream, contentType string, cancel context.CancelCauseFunc) string {
1✔
97
        formWriter := multipart.NewWriter(bodyWriter)
1✔
98
        go func() {
2✔
99
                defer bodyWriter.Close()
1✔
100
                defer formWriter.Close()
1✔
101
                err := c.writeMultipartForm(formWriter, stream, contentType)
1✔
102
                if err != nil {
2✔
103
                        cancel(err)
1✔
104
                        return
1✔
105
                }
1✔
106
        }()
107
        return formWriter.FormDataContentType()
1✔
108
}
109

110
func (c DuClient) writeMultipartForm(writer *multipart.Writer, stream stream.Stream, contentType string) error {
1✔
111
        filePart := textproto.MIMEHeader{}
1✔
112
        filePart.Set("Content-Disposition", fmt.Sprintf(`form-data; name="file"; filename="%s"`, stream.Name()))
1✔
113
        filePart.Set("Content-Type", contentType)
1✔
114
        w, err := writer.CreatePart(filePart)
1✔
115
        if err != nil {
1✔
NEW
116
                return fmt.Errorf("Error creating form field 'file': %w", err)
×
NEW
117
        }
×
118
        data, err := stream.Data()
1✔
119
        if err != nil {
2✔
120
                return err
1✔
121
        }
1✔
122
        defer data.Close()
1✔
123
        _, err = io.Copy(w, data)
1✔
124
        if err != nil {
1✔
NEW
125
                return fmt.Errorf("Error writing form field 'file': %w", err)
×
NEW
126
        }
×
127
        return nil
1✔
128
}
129

130
func (c DuClient) progressReader(text string, completedText string, reader io.Reader, length int64, progressBar *visualization.ProgressBar) io.Reader {
1✔
131
        if length < 10*1024*1024 {
2✔
132
                return reader
1✔
133
        }
1✔
134
        return visualization.NewProgressReader(reader, func(progress visualization.Progress) {
2✔
135
                displayText := text
1✔
136
                if progress.Completed {
2✔
137
                        displayText = completedText
1✔
138
                }
1✔
139
                progressBar.UpdateProgress(displayText, progress.BytesRead, length, progress.BytesPerSecond)
1✔
140
        })
141
}
142

143
func (c DuClient) httpClientSettings() network.HttpClientSettings {
1✔
144
        return *network.NewHttpClientSettings(
1✔
145
                c.debug,
1✔
146
                c.settings.OperationId,
1✔
147
                c.settings.Timeout,
1✔
148
                c.settings.MaxAttempts,
1✔
149
                c.settings.Insecure)
1✔
150
}
1✔
151

152
func (c DuClient) toAuthorization(token *auth.AuthToken) *network.Authorization {
1✔
153
        if token == nil {
2✔
154
                return nil
1✔
155
        }
1✔
NEW
156
        return network.NewAuthorization(token.Type, token.Value)
×
157
}
158

159
func NewDuClient(baseUri string, token *auth.AuthToken, debug bool, settings plugin.ExecutionSettings, logger log.Logger) *DuClient {
1✔
160
        return &DuClient{baseUri, token, debug, settings, logger}
1✔
161
}
1✔
162

163
type digitizeResponse struct {
164
        DocumentId string `json:"documentId"`
165
}
166

167
type digitizeResultResponse struct {
168
        Status string `json:"status"`
169
}
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