jpsclient 1.5.0 → 1.7.0
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.
- checksums.yaml +4 -4
- data/lib/jpsclient/api/commit_log.rb +116 -29
- data/lib/jpsclient/api/file.rb +32 -7
- data/lib/jpsclient/api/project_package.rb +7 -4
- data/lib/jpsclient/{api → base}/client.rb +10 -1
- data/lib/jpsclient/http/http_client.rb +40 -7
- data/lib/jpsclient/upload/upload_config.rb +23 -8
- data/lib/jpsclient/upload/upload_media_client.rb +374 -0
- data/lib/jpsclient/version.rb +1 -1
- data/lib/jpsclient.rb +2 -3
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fc2afd23d09740af2160dbbfc2c6c1f0cffb937b5da3ab905c29582d0738eeb1
|
|
4
|
+
data.tar.gz: 6dacc82e06e921343226aca9a2277ea7a5a6f2c43f949fc46f4cbdc5136ba1ad
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b9b0002b6139c3b5d83dc4afcb9d4c185e4009205845db11fecbc06429cf4b588d7557bf71f1a648465f20532e2915f3d3a3939b4fec6209b6b57f862f4715b6
|
|
7
|
+
data.tar.gz: 0ab8ae8afd9e9db0a5a508a8ce2c4a6f2ba5552906506f05168ac6d053140ffda61f62c15d7de8599438d9b62c03d63018a8aa98940ede6e7921201ffc0aadb0
|
|
@@ -4,31 +4,67 @@ module JPSClient
|
|
|
4
4
|
# 处理 /api/commit_log/* 路径的所有接口
|
|
5
5
|
module CommitLog
|
|
6
6
|
|
|
7
|
+
# 更新提交记录
|
|
8
|
+
#
|
|
9
|
+
# @param id [Integer] 提交记录ID(必需)
|
|
10
|
+
# @param params [Hash] 更新参数
|
|
11
|
+
# @option params [String] :description 描述
|
|
12
|
+
# @option params [Array<String>] :fileUrls 文件URL数组
|
|
13
|
+
# @return [Hash] API响应
|
|
14
|
+
def update_commit_log(id:, params: {})
|
|
15
|
+
config = @request_config && @request_config["commit_log_update"]
|
|
16
|
+
raise JPSClient::ExceptionError, "Missing config for commit_log_update" unless config && config["url"]
|
|
17
|
+
path = config["url"]
|
|
18
|
+
|
|
19
|
+
body_params = {
|
|
20
|
+
id: id
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
# 添加可选参数
|
|
24
|
+
body_params[:description] = params[:description] if params.key?(:description)
|
|
25
|
+
body_params[:fileUrls] = params[:fileUrls] if params[:fileUrls]
|
|
26
|
+
|
|
27
|
+
response = @http_client.post(path, body: body_params)
|
|
28
|
+
result = JPSClient::Response.new(response)
|
|
29
|
+
|
|
30
|
+
if result.need_login?
|
|
31
|
+
do_login(force_login: true)
|
|
32
|
+
response = @http_client.post(path, body: body_params)
|
|
33
|
+
result = JPSClient::Response.new(response)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
return result.to_h
|
|
37
|
+
end
|
|
38
|
+
|
|
7
39
|
# 发送提交记录消息通知
|
|
8
40
|
#
|
|
9
41
|
# @param projectId [String] 项目ID(必需)
|
|
10
|
-
# @param
|
|
42
|
+
# @param workflowId [Integer] 工作流ID(必需)
|
|
11
43
|
# @param params [Hash] 其他参数
|
|
12
44
|
# @option params [Boolean] :single 是否单个提交(默认 true)
|
|
13
|
-
# @option params [String] :
|
|
45
|
+
# @option params [Array<String>] :branches 分支名称数组
|
|
46
|
+
# @option params [Array<String>] :commitIds 提交ID数组
|
|
47
|
+
# @option params [Integer] :startTimestamp 开始时间戳
|
|
48
|
+
# @option params [Integer] :endTimestamp 结束时间戳
|
|
14
49
|
# @option params [Integer] :indexNo 索引号
|
|
15
|
-
# @option params [Integer] :workflowId 工作流ID
|
|
16
50
|
# @return [Hash] API响应
|
|
17
|
-
def send_commit_log_message(projectId:,
|
|
51
|
+
def send_commit_log_message(projectId:, workflowId:, params: {})
|
|
18
52
|
config = @request_config && @request_config["commit_log_send_message"]
|
|
19
53
|
raise JPSClient::ExceptionError, "Missing config for commit_log_send_message" unless config && config["url"]
|
|
20
54
|
path = config["url"]
|
|
21
55
|
|
|
22
56
|
body_params = {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
57
|
+
projectId: projectId,
|
|
58
|
+
workflowId: workflowId,
|
|
59
|
+
single: params.fetch(:single, true)
|
|
26
60
|
}
|
|
27
61
|
|
|
28
62
|
# 添加可选参数
|
|
29
|
-
body_params[:
|
|
63
|
+
body_params[:branches] = params[:branches] if params[:branches]
|
|
64
|
+
body_params[:commitIds] = params[:commitIds] if params[:commitIds]
|
|
65
|
+
body_params[:startTimestamp] = params[:startTimestamp] if params[:startTimestamp]
|
|
66
|
+
body_params[:endTimestamp] = params[:endTimestamp] if params[:endTimestamp]
|
|
30
67
|
body_params[:indexNo] = params[:indexNo] if params[:indexNo]
|
|
31
|
-
body_params[:workflowId] = params[:workflowId] if params[:workflowId]
|
|
32
68
|
|
|
33
69
|
response = @http_client.post(path, body: body_params)
|
|
34
70
|
result = JPSClient::Response.new(response)
|
|
@@ -36,7 +72,6 @@ module JPSClient
|
|
|
36
72
|
# 处理 401 错误,自动重新登录
|
|
37
73
|
if result.need_login?
|
|
38
74
|
do_login(force_login: true)
|
|
39
|
-
# 重试请求
|
|
40
75
|
response = @http_client.post(path, body: body_params)
|
|
41
76
|
result = JPSClient::Response.new(response)
|
|
42
77
|
end
|
|
@@ -44,28 +79,53 @@ module JPSClient
|
|
|
44
79
|
return result.to_h
|
|
45
80
|
end
|
|
46
81
|
|
|
47
|
-
#
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
82
|
+
# 获取提交记录简要信息
|
|
83
|
+
#
|
|
84
|
+
# @param commitId [String] 提交ID(必需)
|
|
85
|
+
# @return [Hash] API响应
|
|
86
|
+
def get_commit_log_simple(commitId:)
|
|
87
|
+
config = @request_config && @request_config["commit_log_simple"]
|
|
88
|
+
raise JPSClient::ExceptionError, "Missing config for commit_log_simple" unless config && config["url"]
|
|
51
89
|
path = config["url"]
|
|
52
90
|
|
|
53
91
|
get_params = {
|
|
54
|
-
|
|
55
|
-
pageNo: params[:pageNo] || 1,
|
|
56
|
-
pageSize: params[:pageSize] || 20
|
|
92
|
+
commitId: commitId
|
|
57
93
|
}
|
|
58
94
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
95
|
+
response = @http_client.get(path, params: get_params)
|
|
96
|
+
result = JPSClient::Response.new(response)
|
|
97
|
+
|
|
98
|
+
if result.need_login?
|
|
99
|
+
do_login(force_login: true)
|
|
100
|
+
response = @http_client.get(path, params: get_params)
|
|
101
|
+
result = JPSClient::Response.new(response)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
return result.to_h
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# 预览提交记录
|
|
108
|
+
#
|
|
109
|
+
# @param workflowId [Integer] 工作流ID(必需)
|
|
110
|
+
# @param commitIds [Array<String>] 提交ID数组(必需)
|
|
111
|
+
# @param params [Hash] 其他参数
|
|
112
|
+
# @option params [Boolean] :onlyCliff 是否只显示cliff格式
|
|
113
|
+
# @return [Hash] API响应
|
|
114
|
+
def get_commit_log_preview(workflowId:, commitIds:, params: {})
|
|
115
|
+
config = @request_config && @request_config["commit_log_preview"]
|
|
116
|
+
raise JPSClient::ExceptionError, "Missing config for commit_log_preview" unless config && config["url"]
|
|
117
|
+
path = config["url"]
|
|
118
|
+
|
|
119
|
+
get_params = {
|
|
120
|
+
workflowId: workflowId,
|
|
121
|
+
commitIds: commitIds.is_a?(Array) ? commitIds.join(',') : commitIds
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
get_params[:onlyCliff] = params[:onlyCliff] if params.key?(:onlyCliff)
|
|
64
125
|
|
|
65
126
|
response = @http_client.get(path, params: get_params)
|
|
66
127
|
result = JPSClient::Response.new(response)
|
|
67
128
|
|
|
68
|
-
# 处理 401 错误
|
|
69
129
|
if result.need_login?
|
|
70
130
|
do_login(force_login: true)
|
|
71
131
|
response = @http_client.get(path, params: get_params)
|
|
@@ -75,17 +135,44 @@ module JPSClient
|
|
|
75
135
|
return result.to_h
|
|
76
136
|
end
|
|
77
137
|
|
|
78
|
-
#
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
138
|
+
# 获取提交记录列表
|
|
139
|
+
#
|
|
140
|
+
# @param params [Hash] 查询参数
|
|
141
|
+
# @option params [Array<Integer>] :ids 提交记录ID数组
|
|
142
|
+
# @option params [Array<Integer>] :workflowIds 工作流ID数组
|
|
143
|
+
# @option params [Array<String>] :committers 提交者数组
|
|
144
|
+
# @option params [String] :repoPath 仓库路径
|
|
145
|
+
# @option params [String] :keyword 关键字
|
|
146
|
+
# @option params [String] :remark 备注
|
|
147
|
+
# @option params [Boolean] :onlyCliff 是否只显示cliff格式
|
|
148
|
+
# @option params [Boolean] :personalCenter 是否个人中心
|
|
149
|
+
# @option params [Integer] :pageNo 页码,默认1
|
|
150
|
+
# @option params [Integer] :pageSize 每页数量,默认20
|
|
151
|
+
# @option params [Integer] :startTimestamp 开始时间戳
|
|
152
|
+
# @option params [Integer] :endTimestamp 结束时间戳
|
|
153
|
+
# @return [Hash] API响应
|
|
154
|
+
def get_commit_log_list(params: {})
|
|
155
|
+
config = @request_config && @request_config["commit_log_list"]
|
|
156
|
+
raise JPSClient::ExceptionError, "Missing config for commit_log_list" unless config && config["url"]
|
|
82
157
|
path = config["url"]
|
|
83
158
|
|
|
84
159
|
get_params = {
|
|
85
|
-
|
|
86
|
-
|
|
160
|
+
pageNo: params[:pageNo] || 1,
|
|
161
|
+
pageSize: params[:pageSize] || 20
|
|
87
162
|
}
|
|
88
163
|
|
|
164
|
+
# 添加可选的筛选参数
|
|
165
|
+
get_params[:ids] = params[:ids].join(',') if params[:ids]
|
|
166
|
+
get_params[:workflowIds] = params[:workflowIds].join(',') if params[:workflowIds]
|
|
167
|
+
get_params[:committers] = params[:committers].join(',') if params[:committers]
|
|
168
|
+
get_params[:repoPath] = params[:repoPath] if params[:repoPath]
|
|
169
|
+
get_params[:keyword] = params[:keyword] if params[:keyword]
|
|
170
|
+
get_params[:remark] = params[:remark] if params[:remark]
|
|
171
|
+
get_params[:onlyCliff] = params[:onlyCliff] if params.key?(:onlyCliff)
|
|
172
|
+
get_params[:personalCenter] = params[:personalCenter] if params.key?(:personalCenter)
|
|
173
|
+
get_params[:startTimestamp] = params[:startTimestamp] if params[:startTimestamp]
|
|
174
|
+
get_params[:endTimestamp] = params[:endTimestamp] if params[:endTimestamp]
|
|
175
|
+
|
|
89
176
|
response = @http_client.get(path, params: get_params)
|
|
90
177
|
result = JPSClient::Response.new(response)
|
|
91
178
|
|
data/lib/jpsclient/api/file.rb
CHANGED
|
@@ -16,13 +16,39 @@ module JPSClient
|
|
|
16
16
|
region: upload_config.region
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
response = @http_client.post(path, body: body_params
|
|
19
|
+
response = @http_client.post(path, body: body_params)
|
|
20
20
|
result = JPSClient::Response.new(response)
|
|
21
21
|
|
|
22
22
|
# 处理 401 错误,自动重新登录
|
|
23
23
|
if result.need_login?
|
|
24
24
|
do_login(force_login: true)
|
|
25
|
-
response = @http_client.post(path, body: body_params
|
|
25
|
+
response = @http_client.post(path, body: body_params)
|
|
26
|
+
result = JPSClient::Response.new(response)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
return result.to_h
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# 获取简单上传预签名URL(用于小文件直接上传,使用 media 配置)
|
|
33
|
+
# @param s3_key [String] S3 文件路径
|
|
34
|
+
# @param upload_config [UploadConfig] 上传配置
|
|
35
|
+
# @return [Hash] 包含预签名URL的响应
|
|
36
|
+
def get_simple_sign_url(s3_key:, upload_config:)
|
|
37
|
+
path = @request_config["file_sign_url"]["url"]
|
|
38
|
+
|
|
39
|
+
body_params = {
|
|
40
|
+
s3Key: s3_key,
|
|
41
|
+
uploadType: upload_config.upload_type,
|
|
42
|
+
bucketName: upload_config.media_bucket_name,
|
|
43
|
+
region: upload_config.media_region
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
response = @http_client.post(path, body: body_params)
|
|
47
|
+
result = JPSClient::Response.new(response)
|
|
48
|
+
|
|
49
|
+
if result.need_login?
|
|
50
|
+
do_login(force_login: true)
|
|
51
|
+
response = @http_client.post(path, body: body_params)
|
|
26
52
|
result = JPSClient::Response.new(response)
|
|
27
53
|
end
|
|
28
54
|
|
|
@@ -42,12 +68,12 @@ module JPSClient
|
|
|
42
68
|
region: upload_config.region
|
|
43
69
|
}
|
|
44
70
|
|
|
45
|
-
response = @http_client.post(path, body: body_params
|
|
71
|
+
response = @http_client.post(path, body: body_params)
|
|
46
72
|
result = JPSClient::Response.new(response)
|
|
47
73
|
|
|
48
74
|
if result.need_login?
|
|
49
75
|
do_login(force_login: true)
|
|
50
|
-
response = @http_client.post(path, body: body_params
|
|
76
|
+
response = @http_client.post(path, body: body_params)
|
|
51
77
|
result = JPSClient::Response.new(response)
|
|
52
78
|
end
|
|
53
79
|
|
|
@@ -66,13 +92,12 @@ module JPSClient
|
|
|
66
92
|
region: upload_config.region
|
|
67
93
|
}
|
|
68
94
|
|
|
69
|
-
|
|
70
|
-
response = @http_client.post(path, body: body_params, timeout: 120)
|
|
95
|
+
response = @http_client.post(path, body: body_params)
|
|
71
96
|
result = JPSClient::Response.new(response)
|
|
72
97
|
|
|
73
98
|
if result.need_login?
|
|
74
99
|
do_login(force_login: true)
|
|
75
|
-
response = @http_client.post(path, body: body_params
|
|
100
|
+
response = @http_client.post(path, body: body_params)
|
|
76
101
|
result = JPSClient::Response.new(response)
|
|
77
102
|
end
|
|
78
103
|
|
|
@@ -16,7 +16,10 @@ module JPSClient
|
|
|
16
16
|
module ProjectPackage
|
|
17
17
|
|
|
18
18
|
# 上传项目包
|
|
19
|
-
|
|
19
|
+
# @param projectId [String] 项目ID(必需)
|
|
20
|
+
# @param params [Hash] 上传参数
|
|
21
|
+
# @param timeout [Integer, nil] 请求超时时间(秒),nil 时使用 common_http_config.timeout_seconds
|
|
22
|
+
def upload_project_package(projectId:nil, params:nil, timeout:nil)
|
|
20
23
|
config = @request_config && @request_config["project_package_upload"]
|
|
21
24
|
raise JPSClient::ExceptionError, "Missing config for project_package_upload" unless config && config["url"]
|
|
22
25
|
path = config["url"]
|
|
@@ -42,14 +45,14 @@ module JPSClient
|
|
|
42
45
|
# 移除值为nil的键
|
|
43
46
|
body_params.compact!
|
|
44
47
|
|
|
45
|
-
response = @http_client.post(path, body: body_params)
|
|
48
|
+
response = @http_client.post(path, body: body_params, timeout: timeout)
|
|
46
49
|
result = JPSClient::Response.new(response)
|
|
47
50
|
|
|
48
51
|
# 处理 401 错误,自动重新登录
|
|
49
52
|
if result.need_login?
|
|
50
53
|
do_login(force_login: true)
|
|
51
54
|
# 重试请求
|
|
52
|
-
response = @http_client.post(path, body: body_params)
|
|
55
|
+
response = @http_client.post(path, body: body_params, timeout: timeout)
|
|
53
56
|
result = JPSClient::Response.new(response)
|
|
54
57
|
end
|
|
55
58
|
|
|
@@ -268,7 +271,7 @@ module JPSClient
|
|
|
268
271
|
packId: params[:packId],
|
|
269
272
|
packageType: params[:packageType],
|
|
270
273
|
packageVersion: params[:packageVersion],
|
|
271
|
-
|
|
274
|
+
projectId: params[:projectId]
|
|
272
275
|
}.compact # 移除nil值
|
|
273
276
|
|
|
274
277
|
response = @http_client.get(path, params: get_params)
|
|
@@ -222,8 +222,17 @@ module JPSClient
|
|
|
222
222
|
@token = @token_manager.to_h
|
|
223
223
|
end
|
|
224
224
|
|
|
225
|
+
# 从 common_http_config 获取 HTTP 配置
|
|
226
|
+
common_http_config = @config_json["common_http_config"] || {}
|
|
227
|
+
default_timeout = common_http_config["timeout_seconds"]
|
|
228
|
+
max_retry_times = common_http_config["max_retry_times"] || 3
|
|
229
|
+
|
|
225
230
|
# 初始化共享的 HTTP 客户端
|
|
226
|
-
@http_client = HttpClient.new(
|
|
231
|
+
@http_client = HttpClient.new(
|
|
232
|
+
base_url: @baseurl,
|
|
233
|
+
default_timeout: default_timeout,
|
|
234
|
+
max_retry_times: max_retry_times
|
|
235
|
+
)
|
|
227
236
|
@http_client.update_token(@token["token"]) if @token && @token["token"]
|
|
228
237
|
|
|
229
238
|
rescue JSON::ParserError => error
|
|
@@ -7,19 +7,29 @@ module JPSClient
|
|
|
7
7
|
class HttpClient
|
|
8
8
|
attr_accessor :base_url
|
|
9
9
|
attr_accessor :token
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
attr_accessor :default_timeout
|
|
11
|
+
attr_accessor :max_retry_times
|
|
12
|
+
|
|
13
|
+
# @param base_url [String] API 基础 URL
|
|
14
|
+
# @param token [String] 认证 token
|
|
15
|
+
# @param default_timeout [Integer] 默认超时时间(秒),默认 60 秒
|
|
16
|
+
# @param max_retry_times [Integer] 最大重试次数,默认 3
|
|
17
|
+
def initialize(base_url: nil, token: nil, default_timeout: nil, max_retry_times: 3)
|
|
12
18
|
@base_url = base_url
|
|
13
19
|
@token = token
|
|
20
|
+
@default_timeout = default_timeout || 60
|
|
21
|
+
@max_retry_times = max_retry_times || 3
|
|
14
22
|
@connection = nil
|
|
15
23
|
end
|
|
16
24
|
|
|
17
25
|
# GET 请求
|
|
18
|
-
|
|
19
|
-
|
|
26
|
+
# @param timeout [Integer, nil] 超时时间,nil 时使用 default_timeout
|
|
27
|
+
def get(path, params: nil, timeout: nil)
|
|
28
|
+
request(:get, path, params: params, timeout: timeout)
|
|
20
29
|
end
|
|
21
30
|
|
|
22
31
|
# POST 请求
|
|
32
|
+
# @param timeout [Integer, nil] 超时时间,nil 时使用 default_timeout
|
|
23
33
|
def post(path, body: nil, timeout: nil)
|
|
24
34
|
request(:post, path, body: body, timeout: timeout)
|
|
25
35
|
end
|
|
@@ -35,6 +45,16 @@ module JPSClient
|
|
|
35
45
|
def request(method, path, params: nil, body: nil, timeout: nil)
|
|
36
46
|
url = @base_url + path
|
|
37
47
|
|
|
48
|
+
# 如果没有指定 timeout,使用默认超时
|
|
49
|
+
actual_timeout = timeout || @default_timeout
|
|
50
|
+
|
|
51
|
+
# 调试输出:请求信息
|
|
52
|
+
if ENV['JPS_CLIENT_DEBUG']
|
|
53
|
+
puts "[JPS_CLIENT_DEBUG] HTTP #{method.upcase} #{url}"
|
|
54
|
+
puts "[JPS_CLIENT_DEBUG] Params: #{params.inspect}" if params
|
|
55
|
+
puts "[JPS_CLIENT_DEBUG] Timeout: #{actual_timeout}s" if actual_timeout
|
|
56
|
+
end
|
|
57
|
+
|
|
38
58
|
begin
|
|
39
59
|
response = connection.send(method) do |req|
|
|
40
60
|
req.url url
|
|
@@ -42,11 +62,24 @@ module JPSClient
|
|
|
42
62
|
req.headers['token'] = @token if @token
|
|
43
63
|
req.params = params if params
|
|
44
64
|
req.body = body.to_json if body && method == :post
|
|
45
|
-
req.options.timeout =
|
|
65
|
+
req.options.timeout = actual_timeout if actual_timeout
|
|
46
66
|
end
|
|
47
67
|
|
|
48
|
-
parse_response(response)
|
|
68
|
+
result = parse_response(response)
|
|
69
|
+
|
|
70
|
+
# 调试输出:响应信息
|
|
71
|
+
if ENV['JPS_CLIENT_DEBUG']
|
|
72
|
+
puts "[JPS_CLIENT_DEBUG] HTTP Response: code=#{result[:code]}, success=#{result[:success]}"
|
|
73
|
+
puts "[JPS_CLIENT_DEBUG] Response body: #{result[:body].inspect}" if result[:body]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
result
|
|
49
77
|
rescue Faraday::Error => e
|
|
78
|
+
# 调试输出:错误信息
|
|
79
|
+
if ENV['JPS_CLIENT_DEBUG']
|
|
80
|
+
puts "[JPS_CLIENT_DEBUG] HTTP Error: #{e.class} - #{e.message}"
|
|
81
|
+
end
|
|
82
|
+
|
|
50
83
|
# 处理网络错误
|
|
51
84
|
{
|
|
52
85
|
code: 0,
|
|
@@ -62,7 +95,7 @@ module JPSClient
|
|
|
62
95
|
@connection ||= Faraday.new do |config|
|
|
63
96
|
# 重试配置
|
|
64
97
|
config.request :retry, {
|
|
65
|
-
max:
|
|
98
|
+
max: @max_retry_times,
|
|
66
99
|
interval: 0.5,
|
|
67
100
|
backoff_factor: 2,
|
|
68
101
|
interval_randomness: 0.5,
|
|
@@ -9,11 +9,14 @@ module JPSClient
|
|
|
9
9
|
attr_accessor :access_key_secret
|
|
10
10
|
attr_accessor :default_url
|
|
11
11
|
attr_accessor :attach_url
|
|
12
|
-
attr_accessor :upload_type #
|
|
13
|
-
attr_accessor :concurrent_workers #
|
|
14
|
-
attr_accessor :chunk_size_mb #
|
|
15
|
-
attr_accessor :max_retry_times #
|
|
16
|
-
attr_accessor :timeout_seconds #
|
|
12
|
+
attr_accessor :upload_type # 上传类型
|
|
13
|
+
attr_accessor :concurrent_workers # 并发上传工作线程数
|
|
14
|
+
attr_accessor :chunk_size_mb # 分片大小(MB)
|
|
15
|
+
attr_accessor :max_retry_times # 每个分片的最大重试次数
|
|
16
|
+
attr_accessor :timeout_seconds # 单个分片的超时时间(秒)
|
|
17
|
+
attr_accessor :media_region # 媒体文件上传区域
|
|
18
|
+
attr_accessor :media_bucket_name # 媒体文件存储桶
|
|
19
|
+
attr_accessor :media_default_url # 媒体文件默认路径
|
|
17
20
|
|
|
18
21
|
def initialize(
|
|
19
22
|
region: nil,
|
|
@@ -26,7 +29,10 @@ module JPSClient
|
|
|
26
29
|
concurrent_workers: nil,
|
|
27
30
|
chunk_size_mb: nil,
|
|
28
31
|
max_retry_times: nil,
|
|
29
|
-
timeout_seconds: nil
|
|
32
|
+
timeout_seconds: nil,
|
|
33
|
+
media_region: nil,
|
|
34
|
+
media_bucket_name: nil,
|
|
35
|
+
media_default_url: nil
|
|
30
36
|
)
|
|
31
37
|
@region = region
|
|
32
38
|
@bucket_name = bucket_name
|
|
@@ -39,6 +45,9 @@ module JPSClient
|
|
|
39
45
|
@chunk_size_mb = chunk_size_mb
|
|
40
46
|
@max_retry_times = max_retry_times
|
|
41
47
|
@timeout_seconds = timeout_seconds
|
|
48
|
+
@media_region = media_region
|
|
49
|
+
@media_bucket_name = media_bucket_name
|
|
50
|
+
@media_default_url = media_default_url
|
|
42
51
|
end
|
|
43
52
|
|
|
44
53
|
# 从 JSON 配置创建实例
|
|
@@ -59,7 +68,10 @@ module JPSClient
|
|
|
59
68
|
concurrent_workers: concurrent_workers,
|
|
60
69
|
chunk_size_mb: json_config['chunk_size_mb'] || 5,
|
|
61
70
|
max_retry_times: json_config['max_retry_times'] || 6, # 每个分片重试6次
|
|
62
|
-
timeout_seconds: json_config['timeout_seconds'] || 600 # 单个分片超时10分钟
|
|
71
|
+
timeout_seconds: json_config['timeout_seconds'] || 600, # 单个分片超时10分钟
|
|
72
|
+
media_region: json_config['media_region'] || "ap-east-1",
|
|
73
|
+
media_bucket_name: json_config['media_bucket_name'] || "jps-resource",
|
|
74
|
+
media_default_url: json_config['media_default_url'] || "resource/"
|
|
63
75
|
)
|
|
64
76
|
end
|
|
65
77
|
|
|
@@ -126,7 +138,10 @@ module JPSClient
|
|
|
126
138
|
'concurrent_workers' => @concurrent_workers,
|
|
127
139
|
'chunk_size_mb' => @chunk_size_mb,
|
|
128
140
|
'max_retry_times' => @max_retry_times,
|
|
129
|
-
'timeout_seconds' => @timeout_seconds
|
|
141
|
+
'timeout_seconds' => @timeout_seconds,
|
|
142
|
+
'media_region' => @media_region,
|
|
143
|
+
'media_bucket_name' => @media_bucket_name,
|
|
144
|
+
'media_default_url' => @media_default_url
|
|
130
145
|
}
|
|
131
146
|
end
|
|
132
147
|
end
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
require 'securerandom'
|
|
2
|
+
require 'typhoeus'
|
|
3
|
+
require 'thread'
|
|
4
|
+
require 'jpsclient/base/exception'
|
|
5
|
+
require 'jpsclient/upload/upload_config'
|
|
6
|
+
require 'jpsclient/utils/logger'
|
|
7
|
+
|
|
8
|
+
module JPSClient
|
|
9
|
+
# Media 文件上传客户端
|
|
10
|
+
# 用于并发上传多个小文件(图片、视频等)到 S3
|
|
11
|
+
# 与 UploadClient 的区别:
|
|
12
|
+
# - UploadClient: 单个大文件 → 分片 → 并发上传分片
|
|
13
|
+
# - UploadMediaClient: 多个小文件 → 并发上传文件
|
|
14
|
+
class UploadMediaClient
|
|
15
|
+
attr_reader :jps_client
|
|
16
|
+
|
|
17
|
+
def initialize(jps_client)
|
|
18
|
+
raise ExceptionError, "必须提供 Client 实例" unless jps_client
|
|
19
|
+
|
|
20
|
+
@jps_client = jps_client
|
|
21
|
+
|
|
22
|
+
# 从 Client 获取配置
|
|
23
|
+
config_json = @jps_client.config_json
|
|
24
|
+
|
|
25
|
+
# 加载上传配置
|
|
26
|
+
@upload_config = UploadConfig.from_json(config_json["upload_config"])
|
|
27
|
+
unless @upload_config
|
|
28
|
+
raise ExceptionError, "上传配置无效或不完整"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# 线程安全的互斥锁
|
|
32
|
+
@results_mutex = Mutex.new
|
|
33
|
+
@tasks_queue_mutex = Mutex.new
|
|
34
|
+
@active_tasks_mutex = Mutex.new
|
|
35
|
+
@upload_failed_mutex = Mutex.new
|
|
36
|
+
@upload_failed = false
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# 并发上传多个 media 文件
|
|
40
|
+
#
|
|
41
|
+
# @param file_paths [Array<String>] 要上传的文件路径列表
|
|
42
|
+
# @return [Hash] 上传结果
|
|
43
|
+
# - results: 每个文件的上传结果数组
|
|
44
|
+
# - success_urls: 成功上传的 URL 列表
|
|
45
|
+
# - failed_files: 上传失败的文件路径列表
|
|
46
|
+
# - total: 总文件数
|
|
47
|
+
# - success_count: 成功数
|
|
48
|
+
# - failed_count: 失败数
|
|
49
|
+
def upload_files(file_paths:)
|
|
50
|
+
result = {
|
|
51
|
+
"results" => [],
|
|
52
|
+
"success_urls" => [],
|
|
53
|
+
"failed_files" => [],
|
|
54
|
+
"total" => 0,
|
|
55
|
+
"success_count" => 0,
|
|
56
|
+
"failed_count" => 0
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# 验证参数
|
|
60
|
+
if file_paths.nil? || file_paths.empty?
|
|
61
|
+
Logger.instance.fancyinfo_error("未提供要上传的文件")
|
|
62
|
+
return result
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# 过滤有效文件
|
|
66
|
+
valid_files = file_paths.select { |f| File.exist?(f) }
|
|
67
|
+
invalid_files = file_paths - valid_files
|
|
68
|
+
|
|
69
|
+
# 记录无效文件
|
|
70
|
+
invalid_files.each do |f|
|
|
71
|
+
result["results"] << {
|
|
72
|
+
"file_path" => f,
|
|
73
|
+
"url" => nil,
|
|
74
|
+
"success" => false,
|
|
75
|
+
"error" => "文件不存在"
|
|
76
|
+
}
|
|
77
|
+
result["failed_files"] << f
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
if valid_files.empty?
|
|
81
|
+
result["total"] = file_paths.size
|
|
82
|
+
result["failed_count"] = invalid_files.size
|
|
83
|
+
Logger.instance.fancyinfo_error("没有有效的文件可上传")
|
|
84
|
+
return result
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
result["total"] = file_paths.size
|
|
88
|
+
|
|
89
|
+
# 重置状态
|
|
90
|
+
@upload_failed = false
|
|
91
|
+
@upload_results = []
|
|
92
|
+
|
|
93
|
+
# 准备任务队列
|
|
94
|
+
@tasks_queue = Queue.new
|
|
95
|
+
@worker_threads = []
|
|
96
|
+
@expected_files = valid_files.size
|
|
97
|
+
@active_tasks = 0
|
|
98
|
+
|
|
99
|
+
# 获取并发和重试配置
|
|
100
|
+
concurrent_workers = [@upload_config.concurrent_workers, valid_files.size].min
|
|
101
|
+
retry_count = @upload_config.max_retry_times
|
|
102
|
+
|
|
103
|
+
puts "准备上传 #{valid_files.size} 个文件"
|
|
104
|
+
puts "并发上传线程数: #{concurrent_workers}"
|
|
105
|
+
puts "失败重试次数: #{retry_count}"
|
|
106
|
+
puts
|
|
107
|
+
|
|
108
|
+
# 创建文件上传任务
|
|
109
|
+
valid_files.each do |file_path|
|
|
110
|
+
task_item = {
|
|
111
|
+
"file_path" => file_path,
|
|
112
|
+
"retry_count" => retry_count
|
|
113
|
+
}
|
|
114
|
+
@tasks_queue.push(task_item)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# 开始并发上传
|
|
118
|
+
Logger.instance.fancyinfo_start("开始上传...")
|
|
119
|
+
|
|
120
|
+
begin
|
|
121
|
+
concurrent_upload(concurrency: concurrent_workers)
|
|
122
|
+
ensure
|
|
123
|
+
cleanup_worker_threads
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# 收集结果
|
|
127
|
+
@upload_results.each do |item|
|
|
128
|
+
result["results"] << item
|
|
129
|
+
if item["success"]
|
|
130
|
+
result["success_urls"] << item["url"]
|
|
131
|
+
result["success_count"] += 1
|
|
132
|
+
else
|
|
133
|
+
result["failed_files"] << item["file_path"]
|
|
134
|
+
result["failed_count"] += 1
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# 加上之前的无效文件
|
|
139
|
+
result["failed_count"] += invalid_files.size
|
|
140
|
+
|
|
141
|
+
# 输出统计
|
|
142
|
+
if result["success_count"] > 0
|
|
143
|
+
Logger.instance.fancyinfo_success("上传完成! 成功: #{result["success_count"]}, 失败: #{result["failed_count"]}")
|
|
144
|
+
else
|
|
145
|
+
Logger.instance.fancyinfo_error("上传失败! 成功: #{result["success_count"]}, 失败: #{result["failed_count"]}")
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
return result
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# 上传单个文件(便捷方法)
|
|
152
|
+
#
|
|
153
|
+
# @param file_path [String] 文件路径
|
|
154
|
+
# @return [String, nil] 成功返回 URL,失败返回 nil
|
|
155
|
+
def upload_file(file_path:)
|
|
156
|
+
result = upload_files(file_paths: [file_path])
|
|
157
|
+
result["success_urls"].first
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
private
|
|
161
|
+
|
|
162
|
+
# 并发上传控制
|
|
163
|
+
def concurrent_upload(concurrency: 1)
|
|
164
|
+
@worker_threads = []
|
|
165
|
+
@active_tasks = 0
|
|
166
|
+
@stop_workers = false
|
|
167
|
+
@task_complete_cv = ConditionVariable.new
|
|
168
|
+
|
|
169
|
+
# 创建工作线程
|
|
170
|
+
concurrency.times do
|
|
171
|
+
@worker_threads << Thread.new { worker_loop }
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# 等待所有任务完成
|
|
175
|
+
@tasks_queue_mutex.synchronize do
|
|
176
|
+
while (@active_tasks > 0 || !@tasks_queue.empty?) && !upload_failed?
|
|
177
|
+
@task_complete_cv.wait(@tasks_queue_mutex, 30)
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# 停止工作线程
|
|
182
|
+
@stop_workers = true
|
|
183
|
+
@tasks_queue_mutex.synchronize { @task_complete_cv.broadcast }
|
|
184
|
+
|
|
185
|
+
# 等待线程结束
|
|
186
|
+
@worker_threads.each do |t|
|
|
187
|
+
t.join(5)
|
|
188
|
+
t.kill if t.alive?
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# 工作线程循环
|
|
193
|
+
def worker_loop
|
|
194
|
+
loop do
|
|
195
|
+
task_item = nil
|
|
196
|
+
|
|
197
|
+
# 从队列获取任务
|
|
198
|
+
@tasks_queue_mutex.synchronize do
|
|
199
|
+
return if @stop_workers || (upload_failed? && @tasks_queue.empty?)
|
|
200
|
+
|
|
201
|
+
if @tasks_queue.empty?
|
|
202
|
+
@task_complete_cv.wait(@tasks_queue_mutex, 1)
|
|
203
|
+
next
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
task_item = @tasks_queue.pop
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# 增加活跃任务计数
|
|
210
|
+
@active_tasks_mutex.synchronize { @active_tasks += 1 } if task_item
|
|
211
|
+
|
|
212
|
+
# 处理任务
|
|
213
|
+
if task_item
|
|
214
|
+
begin
|
|
215
|
+
process_upload_task(task_item)
|
|
216
|
+
rescue => e
|
|
217
|
+
handle_task_error(task_item, e.message)
|
|
218
|
+
ensure
|
|
219
|
+
@active_tasks_mutex.synchronize { @active_tasks -= 1 }
|
|
220
|
+
@tasks_queue_mutex.synchronize { @task_complete_cv.broadcast }
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
break if upload_failed?
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# 处理单个文件上传任务
|
|
229
|
+
def process_upload_task(task_item)
|
|
230
|
+
file_path = task_item["file_path"]
|
|
231
|
+
file_name = File.basename(file_path)
|
|
232
|
+
|
|
233
|
+
# 生成 S3 Key
|
|
234
|
+
file_uuid = SecureRandom.uuid
|
|
235
|
+
extension = File.extname(file_path)
|
|
236
|
+
s3_key = "#{@upload_config.media_default_url}#{file_uuid}#{extension}"
|
|
237
|
+
|
|
238
|
+
# 获取预签名 URL
|
|
239
|
+
sign_result = @jps_client.get_simple_sign_url(
|
|
240
|
+
s3_key: s3_key,
|
|
241
|
+
upload_config: @upload_config
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
unless sign_result && sign_result.dig("data", "url")
|
|
245
|
+
handle_retry(task_item, "获取预签名 URL 失败")
|
|
246
|
+
return
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
upload_url = sign_result["data"]["url"]
|
|
250
|
+
|
|
251
|
+
# 读取文件
|
|
252
|
+
begin
|
|
253
|
+
file_content = File.binread(file_path)
|
|
254
|
+
file_size = file_content.bytesize
|
|
255
|
+
rescue => e
|
|
256
|
+
handle_retry(task_item, "读取文件失败: #{e.message}")
|
|
257
|
+
return
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# 计算超时时间(基于文件大小,最小 30 秒,最大 300 秒)
|
|
261
|
+
timeout = calculate_timeout(file_size)
|
|
262
|
+
|
|
263
|
+
# 上传文件
|
|
264
|
+
request = Typhoeus::Request.new(
|
|
265
|
+
upload_url,
|
|
266
|
+
method: :put,
|
|
267
|
+
body: file_content,
|
|
268
|
+
headers: {
|
|
269
|
+
'Content-Type' => 'application/octet-stream',
|
|
270
|
+
'Content-Length' => file_size.to_s
|
|
271
|
+
},
|
|
272
|
+
timeout: timeout,
|
|
273
|
+
connecttimeout: 30
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
response = request.run
|
|
277
|
+
|
|
278
|
+
if response.success?
|
|
279
|
+
# 构建文件访问 URL
|
|
280
|
+
file_url = "https://#{@upload_config.media_bucket_name}.s3.#{@upload_config.media_region}.amazonaws.com/#{s3_key}"
|
|
281
|
+
|
|
282
|
+
# 记录成功结果
|
|
283
|
+
@results_mutex.synchronize do
|
|
284
|
+
@upload_results << {
|
|
285
|
+
"file_path" => file_path,
|
|
286
|
+
"url" => file_url,
|
|
287
|
+
"success" => true,
|
|
288
|
+
"error" => nil
|
|
289
|
+
}
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
puts " ✅ #{file_name}"
|
|
293
|
+
elsif response.timed_out?
|
|
294
|
+
handle_retry(task_item, "上传超时")
|
|
295
|
+
elsif response.code == 0
|
|
296
|
+
error_msg = response.return_message || "网络错误"
|
|
297
|
+
handle_retry(task_item, error_msg)
|
|
298
|
+
else
|
|
299
|
+
handle_retry(task_item, "HTTP #{response.code}")
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# 处理重试
|
|
304
|
+
def handle_retry(task_item, error_reason)
|
|
305
|
+
file_path = task_item["file_path"]
|
|
306
|
+
file_name = File.basename(file_path)
|
|
307
|
+
|
|
308
|
+
task_item["retry_count"] -= 1
|
|
309
|
+
|
|
310
|
+
if task_item["retry_count"] > 0
|
|
311
|
+
# 放回队列重试
|
|
312
|
+
puts " ⚠️ #{file_name} 失败: #{error_reason},准备重试..."
|
|
313
|
+
@tasks_queue_mutex.synchronize { @tasks_queue.push(task_item) }
|
|
314
|
+
else
|
|
315
|
+
# 达到最大重试次数,记录失败
|
|
316
|
+
puts " ❌ #{file_name} 失败: #{error_reason}"
|
|
317
|
+
@results_mutex.synchronize do
|
|
318
|
+
@upload_results << {
|
|
319
|
+
"file_path" => file_path,
|
|
320
|
+
"url" => nil,
|
|
321
|
+
"success" => false,
|
|
322
|
+
"error" => error_reason
|
|
323
|
+
}
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
# 处理任务异常
|
|
329
|
+
def handle_task_error(task_item, error_message)
|
|
330
|
+
file_path = task_item["file_path"]
|
|
331
|
+
@results_mutex.synchronize do
|
|
332
|
+
@upload_results << {
|
|
333
|
+
"file_path" => file_path,
|
|
334
|
+
"url" => nil,
|
|
335
|
+
"success" => false,
|
|
336
|
+
"error" => error_message
|
|
337
|
+
}
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# 计算上传超时时间
|
|
342
|
+
def calculate_timeout(file_size)
|
|
343
|
+
# 假设最低速度 100KB/s
|
|
344
|
+
min_speed = 100 * 1024
|
|
345
|
+
calculated = (file_size.to_f / min_speed).ceil + 10
|
|
346
|
+
|
|
347
|
+
# 限制在 30-300 秒之间
|
|
348
|
+
[[calculated, 30].max, 300].min
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
# 检查是否上传失败
|
|
352
|
+
def upload_failed?
|
|
353
|
+
@upload_failed_mutex.synchronize { @upload_failed }
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
# 设置上传失败状态
|
|
357
|
+
def set_upload_failed(error_msg = nil)
|
|
358
|
+
@upload_failed_mutex.synchronize do
|
|
359
|
+
@upload_failed = true
|
|
360
|
+
Logger.instance.fancyinfo_error("上传失败: #{error_msg}") if error_msg
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
# 清理工作线程
|
|
365
|
+
def cleanup_worker_threads
|
|
366
|
+
return unless @worker_threads
|
|
367
|
+
|
|
368
|
+
@worker_threads.each do |thread|
|
|
369
|
+
thread.exit if thread.alive?
|
|
370
|
+
end
|
|
371
|
+
@worker_threads.clear
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
end
|
data/lib/jpsclient/version.rb
CHANGED
data/lib/jpsclient.rb
CHANGED
|
@@ -4,9 +4,7 @@ require 'jpsclient/version'
|
|
|
4
4
|
# 基础模块
|
|
5
5
|
require 'jpsclient/base/exception'
|
|
6
6
|
require 'jpsclient/base/api_config'
|
|
7
|
-
|
|
8
|
-
# API 客户端
|
|
9
|
-
require 'jpsclient/api/client'
|
|
7
|
+
require 'jpsclient/base/client'
|
|
10
8
|
|
|
11
9
|
# 工具模块
|
|
12
10
|
require 'jpsclient/utils/logger'
|
|
@@ -23,6 +21,7 @@ require 'jpsclient/auth/auth'
|
|
|
23
21
|
require 'jpsclient/upload/upload_config'
|
|
24
22
|
require 'jpsclient/upload/upload_progress'
|
|
25
23
|
require 'jpsclient/upload/upload_client'
|
|
24
|
+
require 'jpsclient/upload/upload_media_client'
|
|
26
25
|
|
|
27
26
|
module JPSClient
|
|
28
27
|
class << self
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jpsclient
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Your Name
|
|
@@ -211,7 +211,6 @@ files:
|
|
|
211
211
|
- lib/jpsclient/api/bug.rb
|
|
212
212
|
- lib/jpsclient/api/category.rb
|
|
213
213
|
- lib/jpsclient/api/cert.rb
|
|
214
|
-
- lib/jpsclient/api/client.rb
|
|
215
214
|
- lib/jpsclient/api/collect.rb
|
|
216
215
|
- lib/jpsclient/api/collection.rb
|
|
217
216
|
- lib/jpsclient/api/commit_log.rb
|
|
@@ -273,10 +272,12 @@ files:
|
|
|
273
272
|
- lib/jpsclient/auth/auth.rb
|
|
274
273
|
- lib/jpsclient/auth/token.rb
|
|
275
274
|
- lib/jpsclient/base/api_config.rb
|
|
275
|
+
- lib/jpsclient/base/client.rb
|
|
276
276
|
- lib/jpsclient/base/exception.rb
|
|
277
277
|
- lib/jpsclient/http/http_client.rb
|
|
278
278
|
- lib/jpsclient/upload/upload_client.rb
|
|
279
279
|
- lib/jpsclient/upload/upload_config.rb
|
|
280
|
+
- lib/jpsclient/upload/upload_media_client.rb
|
|
280
281
|
- lib/jpsclient/upload/upload_progress.rb
|
|
281
282
|
- lib/jpsclient/utils/aes.rb
|
|
282
283
|
- lib/jpsclient/utils/logger.rb
|