@pippit-dev/cli 0.0.11 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -5
- package/checksums.txt +6 -6
- package/cmd/short_drama/short_drama.go +0 -3
- package/cmd/short_drama_test.go +97 -10
- package/internal/common/client.go +95 -0
- package/internal/common/upload_file.go +79 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ npx @pippit-dev/cli@latest install
|
|
|
12
12
|
export XYQ_ACCESS_KEY="<access-key>"
|
|
13
13
|
pippit-cli --version
|
|
14
14
|
pippit-cli short-drama +submit-run --message "写一个赛博朋克短剧开头"
|
|
15
|
-
pippit-cli short-drama +upload-file --path ./
|
|
15
|
+
pippit-cli short-drama +upload-file --path ./reference.doc
|
|
16
16
|
pippit-cli short-drama +get-thread --thread-id thread_123 --run-id run_456 --after-seq 0
|
|
17
17
|
pippit-cli short-drama +download-result --output-path ./thread_123/results/result.mp4 --url URL
|
|
18
18
|
```
|
|
@@ -26,7 +26,7 @@ Submit a Run task for the short drama scene:
|
|
|
26
26
|
export XYQ_ACCESS_KEY="<access-key>"
|
|
27
27
|
go run . --version
|
|
28
28
|
go run . short-drama +submit-run --message "写一个赛博朋克短剧开头"
|
|
29
|
-
go run . short-drama +upload-file --path ./
|
|
29
|
+
go run . short-drama +upload-file --path ./reference.doc
|
|
30
30
|
go run . short-drama +get-thread --thread-id thread_123 --run-id run_456 --after-seq 0
|
|
31
31
|
go run . short-drama +download-result --output-path ./thread_123/results/result.mp4 --url URL
|
|
32
32
|
```
|
|
@@ -35,9 +35,11 @@ go run . short-drama +download-result --output-path ./thread_123/results/result.
|
|
|
35
35
|
`run_id`, and `web_thread_link`; `--message` is required.
|
|
36
36
|
`+get-thread` calls
|
|
37
37
|
`/api/biz/v1/skill/get_thread` and prints extracted `messages`.
|
|
38
|
+
`+upload-file` calls `/api/biz/v1/skill/upload_file` with
|
|
39
|
+
`multipart/form-data` and prints the returned `asset_id`.
|
|
40
|
+
Only `.doc` and `.txt` file extensions are supported.
|
|
38
41
|
`+download-result` downloads the result URL to
|
|
39
|
-
the `--output-path` file path.
|
|
40
|
-
its real service contract is wired.
|
|
42
|
+
the `--output-path` file path.
|
|
41
43
|
|
|
42
44
|
## HTTP Client
|
|
43
45
|
|
|
@@ -47,6 +49,6 @@ settings such as base URL, HTTP timeout, and API paths are loaded by
|
|
|
47
49
|
|
|
48
50
|
## Auth
|
|
49
51
|
|
|
50
|
-
`short-drama +submit-run` and `short-drama +
|
|
52
|
+
`short-drama +submit-run`, `short-drama +get-thread`, and `short-drama +upload-file` authenticate with
|
|
51
53
|
`Authorization: Bearer <XYQ_ACCESS_KEY>`. OAuth command code remains available,
|
|
52
54
|
but runtime short drama requests do not use it.
|
package/checksums.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
5062d1d4950aa9e0d8b1770e62476d9bb2076f4492cd24a541ad93b05a254f49 pippit-cli-0.0.12-darwin-amd64.tar.gz
|
|
2
|
+
5a0c8d044ebcb717d18954147433ac5f54f2dc89285e2e50f6be6755431ee99c pippit-cli-0.0.12-darwin-arm64.tar.gz
|
|
3
|
+
456420a71cd9909e152907faa96f35abb93e62079786021d1bee28cfac61e8ff pippit-cli-0.0.12-linux-amd64.tar.gz
|
|
4
|
+
1c6681fa8f8edaa19dac835dd9bdf4bc17c06936700316f7d74b68d55d572460 pippit-cli-0.0.12-linux-arm64.tar.gz
|
|
5
|
+
e7c156bb35fb373fa89d05c729252b20951a41c70f3bbd4e8794e4c5abe65e06 pippit-cli-0.0.12-windows-amd64.zip
|
|
6
|
+
2832786f9c13069f3580407e24037e95cc9452534e9996366dc6751f841fcd26 pippit-cli-0.0.12-windows-arm64.zip
|
|
@@ -3,7 +3,6 @@ package short_drama
|
|
|
3
3
|
import (
|
|
4
4
|
"fmt"
|
|
5
5
|
"io"
|
|
6
|
-
"path/filepath"
|
|
7
6
|
"strings"
|
|
8
7
|
|
|
9
8
|
"github.com/Pippit-dev/pippit-cli/internal/common"
|
|
@@ -72,8 +71,6 @@ func newShortDramaUploadFileCommand(stdout, stderr io.Writer, runner *common.Run
|
|
|
72
71
|
if opts.Path == "" {
|
|
73
72
|
return fmt.Errorf("--path is required")
|
|
74
73
|
}
|
|
75
|
-
opts.FileName = filepath.Base(opts.Path)
|
|
76
|
-
opts.Mock = true
|
|
77
74
|
|
|
78
75
|
result, err := common.UploadFile(cmd.Context(), opts, runner)
|
|
79
76
|
if err != nil {
|
package/cmd/short_drama_test.go
CHANGED
|
@@ -176,23 +176,65 @@ func TestShortDramaSubmitRunRequiresAccessKey(t *testing.T) {
|
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
func TestShortDramaUploadFile(t *testing.T) {
|
|
179
|
+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
180
|
+
if r.Method != http.MethodPost {
|
|
181
|
+
t.Fatalf("method = %s, want POST", r.Method)
|
|
182
|
+
}
|
|
183
|
+
if r.URL.Path != "/api/biz/v1/skill/upload_file" {
|
|
184
|
+
t.Fatalf("path = %s, want upload_file path", r.URL.Path)
|
|
185
|
+
}
|
|
186
|
+
if r.Header.Get("Authorization") != "Bearer test-token" {
|
|
187
|
+
t.Fatalf("Authorization = %q, want test bearer token", r.Header.Get("Authorization"))
|
|
188
|
+
}
|
|
189
|
+
if err := r.ParseMultipartForm(1 << 20); err != nil {
|
|
190
|
+
t.Fatalf("ParseMultipartForm(): %v", err)
|
|
191
|
+
}
|
|
192
|
+
if got := r.FormValue("accessKey"); got != "" {
|
|
193
|
+
t.Fatalf("accessKey = %q, want empty (auth via header only)", got)
|
|
194
|
+
}
|
|
195
|
+
files := r.MultipartForm.File["file"]
|
|
196
|
+
if len(files) != 1 {
|
|
197
|
+
t.Fatalf("file parts = %d, want 1", len(files))
|
|
198
|
+
}
|
|
199
|
+
if files[0].Filename != "story.txt" {
|
|
200
|
+
t.Fatalf("filename = %q, want story.txt", files[0].Filename)
|
|
201
|
+
}
|
|
202
|
+
if got := files[0].Header.Get("Content-Type"); got != "text/plain; charset=utf-8" {
|
|
203
|
+
t.Fatalf("Content-Type = %q, want text/plain; charset=utf-8", got)
|
|
204
|
+
}
|
|
205
|
+
file, err := files[0].Open()
|
|
206
|
+
if err != nil {
|
|
207
|
+
t.Fatalf("Open multipart file: %v", err)
|
|
208
|
+
}
|
|
209
|
+
defer file.Close()
|
|
210
|
+
data, err := io.ReadAll(file)
|
|
211
|
+
if err != nil {
|
|
212
|
+
t.Fatalf("ReadAll multipart file: %v", err)
|
|
213
|
+
}
|
|
214
|
+
if string(data) != "txt-data" {
|
|
215
|
+
t.Fatalf("file content = %q, want txt-data", string(data))
|
|
216
|
+
}
|
|
217
|
+
_, _ = w.Write([]byte(`{"ret":"0","errmsg":"","data":{"pippit_asset_id":"asset_123"}}`))
|
|
218
|
+
}))
|
|
219
|
+
defer server.Close()
|
|
220
|
+
|
|
221
|
+
cwd := chdirTemp(t)
|
|
222
|
+
path := filepath.Join(cwd, "story.txt")
|
|
223
|
+
if err := os.WriteFile(path, []byte("txt-data"), 0o644); err != nil {
|
|
224
|
+
t.Fatalf("WriteFile(): %v", err)
|
|
225
|
+
}
|
|
226
|
+
|
|
179
227
|
var stdout, stderr bytes.Buffer
|
|
180
|
-
root :=
|
|
181
|
-
root.SetArgs([]string{"short-drama", "+upload-file", "--path",
|
|
228
|
+
root := newTestRootCommand(t, &stdout, &stderr, server.URL)
|
|
229
|
+
root.SetArgs([]string{"short-drama", "+upload-file", "--path", path})
|
|
182
230
|
|
|
183
231
|
if err := root.Execute(); err != nil {
|
|
184
232
|
t.Fatalf("Execute() error = %v, stderr = %s", err, stderr.String())
|
|
185
233
|
}
|
|
186
234
|
|
|
187
235
|
got := decodeJSON(t, stdout.Bytes())
|
|
188
|
-
if got["
|
|
189
|
-
t.Fatalf("
|
|
190
|
-
}
|
|
191
|
-
if got["status"] != "uploaded" {
|
|
192
|
-
t.Fatalf("status = %v, want uploaded", got["status"])
|
|
193
|
-
}
|
|
194
|
-
if !strings.HasPrefix(got["file_id"].(string), "file_mock_") {
|
|
195
|
-
t.Fatalf("file_id = %v, want mock file id", got["file_id"])
|
|
236
|
+
if got["asset_id"] != "asset_123" {
|
|
237
|
+
t.Fatalf("asset_id = %v, want asset_123", got["asset_id"])
|
|
196
238
|
}
|
|
197
239
|
}
|
|
198
240
|
|
|
@@ -210,6 +252,51 @@ func TestShortDramaUploadFileRequiresPath(t *testing.T) {
|
|
|
210
252
|
}
|
|
211
253
|
}
|
|
212
254
|
|
|
255
|
+
func TestShortDramaUploadFileRequiresAccessKey(t *testing.T) {
|
|
256
|
+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
257
|
+
t.Fatal("server should not receive request without access key")
|
|
258
|
+
}))
|
|
259
|
+
defer server.Close()
|
|
260
|
+
|
|
261
|
+
cwd := chdirTemp(t)
|
|
262
|
+
path := filepath.Join(cwd, "story.txt")
|
|
263
|
+
if err := os.WriteFile(path, []byte("txt-data"), 0o644); err != nil {
|
|
264
|
+
t.Fatalf("WriteFile(): %v", err)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
var stdout, stderr bytes.Buffer
|
|
268
|
+
root := newTestRootCommandWithAccessKey(t, &stdout, &stderr, server.URL, "")
|
|
269
|
+
root.SetArgs([]string{"short-drama", "+upload-file", "--path", path})
|
|
270
|
+
|
|
271
|
+
err := root.Execute()
|
|
272
|
+
if err == nil {
|
|
273
|
+
t.Fatal("Execute() error = nil, want access key error")
|
|
274
|
+
}
|
|
275
|
+
if !strings.Contains(err.Error(), "XYQ_ACCESS_KEY is required") {
|
|
276
|
+
t.Fatalf("error = %q, want access key guidance", err)
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
func TestShortDramaUploadFileRejectsUnsupportedFileType(t *testing.T) {
|
|
281
|
+
cwd := chdirTemp(t)
|
|
282
|
+
path := filepath.Join(cwd, "story.png")
|
|
283
|
+
if err := os.WriteFile(path, []byte("png-data"), 0o644); err != nil {
|
|
284
|
+
t.Fatalf("WriteFile(): %v", err)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
var stdout, stderr bytes.Buffer
|
|
288
|
+
root := NewRootCommand(&stdout, &stderr)
|
|
289
|
+
root.SetArgs([]string{"short-drama", "+upload-file", "--path", path})
|
|
290
|
+
|
|
291
|
+
err := root.Execute()
|
|
292
|
+
if err == nil {
|
|
293
|
+
t.Fatal("Execute() error = nil, want file type validation error")
|
|
294
|
+
}
|
|
295
|
+
if !strings.Contains(err.Error(), "only .doc and .txt uploads are supported") {
|
|
296
|
+
t.Fatalf("error = %q, want unsupported type validation", err)
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
213
300
|
func TestShortDramaDownloadResult(t *testing.T) {
|
|
214
301
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
215
302
|
if r.Header.Get("User-Agent") != "Pippit-CLI/1.0" {
|
|
@@ -5,8 +5,12 @@ import (
|
|
|
5
5
|
"context"
|
|
6
6
|
"fmt"
|
|
7
7
|
"io"
|
|
8
|
+
"mime/multipart"
|
|
8
9
|
"net/http"
|
|
10
|
+
"net/textproto"
|
|
9
11
|
"net/url"
|
|
12
|
+
"os"
|
|
13
|
+
"path/filepath"
|
|
10
14
|
"reflect"
|
|
11
15
|
"strings"
|
|
12
16
|
"time"
|
|
@@ -16,12 +20,20 @@ import (
|
|
|
16
20
|
|
|
17
21
|
type Client interface {
|
|
18
22
|
SendRequest(ctx context.Context, path string, body any, out any) error
|
|
23
|
+
SendMultipartRequest(ctx context.Context, path string, fields map[string]string, file MultipartFile, out any) error
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
type RequestAuthorizer interface {
|
|
22
27
|
Inject(ctx context.Context, req *http.Request) error
|
|
23
28
|
}
|
|
24
29
|
|
|
30
|
+
type MultipartFile struct {
|
|
31
|
+
FieldName string
|
|
32
|
+
Path string
|
|
33
|
+
FileName string
|
|
34
|
+
ContentType string
|
|
35
|
+
}
|
|
36
|
+
|
|
25
37
|
type httpClient struct {
|
|
26
38
|
baseURL string
|
|
27
39
|
httpClient *http.Client
|
|
@@ -100,6 +112,89 @@ func (c *httpClient) SendRequest(ctx context.Context, path string, body any, out
|
|
|
100
112
|
return c.do(req, out)
|
|
101
113
|
}
|
|
102
114
|
|
|
115
|
+
func (c *httpClient) SendMultipartRequest(ctx context.Context, path string, fields map[string]string, file MultipartFile, out any) error {
|
|
116
|
+
reqURL, err := c.resolveURL(path, nil)
|
|
117
|
+
if err != nil {
|
|
118
|
+
return err
|
|
119
|
+
}
|
|
120
|
+
if file.FieldName == "" {
|
|
121
|
+
return fmt.Errorf("multipart file field name is required")
|
|
122
|
+
}
|
|
123
|
+
if file.FileName == "" {
|
|
124
|
+
file.FileName = filepath.Base(file.Path)
|
|
125
|
+
}
|
|
126
|
+
if file.ContentType == "" {
|
|
127
|
+
file.ContentType = "application/octet-stream"
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
pr, pw := io.Pipe()
|
|
131
|
+
writer := multipart.NewWriter(pw)
|
|
132
|
+
req, err := http.NewRequestWithContext(ctx, http.MethodPost, reqURL, pr)
|
|
133
|
+
if err != nil {
|
|
134
|
+
_ = pr.Close()
|
|
135
|
+
_ = pw.Close()
|
|
136
|
+
return fmt.Errorf("build POST request: %w", err)
|
|
137
|
+
}
|
|
138
|
+
req.Header.Set("Content-Type", writer.FormDataContentType())
|
|
139
|
+
req.Header.Set("Accept", "application/json")
|
|
140
|
+
|
|
141
|
+
if c.authorizer == nil {
|
|
142
|
+
_ = pr.Close()
|
|
143
|
+
_ = pw.Close()
|
|
144
|
+
return fmt.Errorf("authorized request requires authorizer")
|
|
145
|
+
}
|
|
146
|
+
if err := c.authorizer.Inject(ctx, req); err != nil {
|
|
147
|
+
_ = pr.Close()
|
|
148
|
+
_ = pw.Close()
|
|
149
|
+
return fmt.Errorf("inject auth headers: %w", err)
|
|
150
|
+
}
|
|
151
|
+
c.injectHeaders(req)
|
|
152
|
+
|
|
153
|
+
go func() {
|
|
154
|
+
err := writeMultipartBody(writer, fields, file)
|
|
155
|
+
closeErr := writer.Close()
|
|
156
|
+
if err != nil {
|
|
157
|
+
_ = pw.CloseWithError(err)
|
|
158
|
+
return
|
|
159
|
+
}
|
|
160
|
+
_ = pw.CloseWithError(closeErr)
|
|
161
|
+
}()
|
|
162
|
+
|
|
163
|
+
return c.do(req, out)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
func writeMultipartBody(writer *multipart.Writer, fields map[string]string, file MultipartFile) error {
|
|
167
|
+
for key, value := range fields {
|
|
168
|
+
if err := writer.WriteField(key, value); err != nil {
|
|
169
|
+
return err
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
f, err := os.Open(file.Path)
|
|
174
|
+
if err != nil {
|
|
175
|
+
return fmt.Errorf("open upload file: %w", err)
|
|
176
|
+
}
|
|
177
|
+
defer f.Close()
|
|
178
|
+
|
|
179
|
+
header := make(textproto.MIMEHeader)
|
|
180
|
+
header.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"; filename="%s"`, escapeQuotes(file.FieldName), escapeQuotes(file.FileName)))
|
|
181
|
+
header.Set("Content-Type", file.ContentType)
|
|
182
|
+
part, err := writer.CreatePart(header)
|
|
183
|
+
if err != nil {
|
|
184
|
+
return err
|
|
185
|
+
}
|
|
186
|
+
if _, err := io.Copy(part, f); err != nil {
|
|
187
|
+
return fmt.Errorf("write upload file: %w", err)
|
|
188
|
+
}
|
|
189
|
+
return nil
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
var quotesReplacer = strings.NewReplacer("\\", "\\\\", `"`, `\"`)
|
|
193
|
+
|
|
194
|
+
func escapeQuotes(s string) string {
|
|
195
|
+
return quotesReplacer.Replace(s)
|
|
196
|
+
}
|
|
197
|
+
|
|
103
198
|
func (c *httpClient) injectHeaders(req *http.Request) {
|
|
104
199
|
for k, values := range c.headers {
|
|
105
200
|
for _, v := range values {
|
|
@@ -2,48 +2,103 @@ package common
|
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
4
|
"context"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
5
|
+
"fmt"
|
|
6
|
+
"mime"
|
|
7
|
+
"os"
|
|
8
|
+
"path/filepath"
|
|
9
|
+
"strings"
|
|
10
|
+
|
|
11
|
+
"github.com/Pippit-dev/pippit-cli/internal/config"
|
|
8
12
|
)
|
|
9
13
|
|
|
10
14
|
// UploadFileOptions is the stable command-facing request shape for file upload.
|
|
11
15
|
type UploadFileOptions struct {
|
|
12
16
|
Path string `json:"path"`
|
|
13
17
|
FileName string `json:"file_name"`
|
|
14
|
-
Purpose string `json:"purpose"`
|
|
15
|
-
Mock bool `json:"mock"`
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
// UploadFileResult is the JSON envelope printed by `pippit-cli short-drama +upload-file`.
|
|
19
21
|
type UploadFileResult struct {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
AssetID string `json:"asset_id"`
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type uploadFileResponse struct {
|
|
26
|
+
Ret string `json:"ret"`
|
|
27
|
+
Errmsg string `json:"errmsg"`
|
|
28
|
+
SvrTime int64 `json:"svr_time"`
|
|
29
|
+
LogID string `json:"log_id"`
|
|
30
|
+
Data struct {
|
|
31
|
+
PippitAssetID string `json:"pippit_asset_id"`
|
|
32
|
+
AssetID string `json:"asset_id"`
|
|
33
|
+
} `json:"data"`
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const uploadFileFieldName = "file"
|
|
37
|
+
|
|
38
|
+
var allowedUploadExtensions = map[string]bool{
|
|
39
|
+
".doc": true,
|
|
40
|
+
".txt": true,
|
|
25
41
|
}
|
|
26
42
|
|
|
27
|
-
func UploadFile(ctx context.Context, opts UploadFileOptions,
|
|
43
|
+
func UploadFile(ctx context.Context, opts UploadFileOptions, runner *Runner) (*UploadFileResult, error) {
|
|
28
44
|
if err := ctx.Err(); err != nil {
|
|
29
45
|
return nil, err
|
|
30
46
|
}
|
|
31
|
-
|
|
32
|
-
|
|
47
|
+
if runner == nil || runner.Client == nil {
|
|
48
|
+
return nil, fmt.Errorf("upload_file runner client is required")
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
path := strings.TrimSpace(opts.Path)
|
|
52
|
+
if path == "" {
|
|
53
|
+
return nil, fmt.Errorf("upload file path is required")
|
|
54
|
+
}
|
|
55
|
+
info, err := os.Stat(path)
|
|
56
|
+
if err != nil {
|
|
57
|
+
return nil, fmt.Errorf("stat upload file: %w", err)
|
|
58
|
+
}
|
|
59
|
+
if info.IsDir() {
|
|
60
|
+
return nil, fmt.Errorf("upload path %q is a directory", path)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
ext := strings.ToLower(filepath.Ext(path))
|
|
64
|
+
if !allowedUploadExtensions[ext] {
|
|
65
|
+
return nil, fmt.Errorf("unsupported file extension %q; only .doc and .txt uploads are supported", ext)
|
|
66
|
+
}
|
|
67
|
+
fileName := filepath.Base(path)
|
|
68
|
+
contentType := mime.TypeByExtension(ext)
|
|
69
|
+
|
|
70
|
+
var resp uploadFileResponse
|
|
71
|
+
if err := runner.Client.SendMultipartRequest(ctx, uploadFilePath(runner), nil, MultipartFile{
|
|
72
|
+
FieldName: uploadFileFieldName,
|
|
73
|
+
Path: path,
|
|
74
|
+
FileName: fileName,
|
|
75
|
+
ContentType: contentType,
|
|
76
|
+
}, &resp); err != nil {
|
|
77
|
+
return nil, fmt.Errorf("upload_file request failed: %w", err)
|
|
78
|
+
}
|
|
79
|
+
if resp.Ret != "0" {
|
|
80
|
+
if resp.Errmsg == "" {
|
|
81
|
+
resp.Errmsg = "unknown error"
|
|
82
|
+
}
|
|
83
|
+
return nil, fmt.Errorf("upload_file failed: ret=%s errmsg=%s", resp.Ret, resp.Errmsg)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
assetID := strings.TrimSpace(resp.Data.PippitAssetID)
|
|
87
|
+
if assetID == "" {
|
|
88
|
+
assetID = strings.TrimSpace(resp.Data.AssetID)
|
|
89
|
+
}
|
|
90
|
+
if assetID == "" {
|
|
91
|
+
return nil, fmt.Errorf("upload_file response missing pippit_asset_id")
|
|
92
|
+
}
|
|
93
|
+
|
|
33
94
|
return &UploadFileResult{
|
|
34
|
-
|
|
35
|
-
FileID: "file_mock_" + id,
|
|
36
|
-
Status: "uploaded",
|
|
37
|
-
Uploaded: time.Now().UTC().Format(time.RFC3339),
|
|
38
|
-
Request: opts,
|
|
95
|
+
AssetID: assetID,
|
|
39
96
|
}, nil
|
|
40
97
|
}
|
|
41
98
|
|
|
42
|
-
func
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if n > len(id) {
|
|
46
|
-
return id
|
|
99
|
+
func uploadFilePath(runner *Runner) string {
|
|
100
|
+
if runner != nil && runner.Config != nil && runner.Config.Paths != nil && runner.Config.Paths.UploadFile != "" {
|
|
101
|
+
return runner.Config.Paths.UploadFile
|
|
47
102
|
}
|
|
48
|
-
return
|
|
103
|
+
return config.UploadFilePath
|
|
49
104
|
}
|