k3cloud_webapi_sdk 8.2.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a27712a9e76c2eda70bb656656b3954970d77675f3de42da92051b45a740f89e
4
+ data.tar.gz: 43933aac6f718c17beecb34fbb5a283125a73127bdcc2657dd62e70f710022f5
5
+ SHA512:
6
+ metadata.gz: 8c8e8b13851a2db1e4d358c27e8283c388443ec8e6b9b72cf646a0532af7cc7e1a074e30a157abce3bfc429a09451d480fb2b01c3ca8cd1f19ab9b09094092bf
7
+ data.tar.gz: bf4ec0fd3ef417210f99f8d5bab096204434a52c35ebc78e7fa54022cf5c6a5cc2a0867b110fd63997e73628a9e7c739055bb9331d8edfd634a39b834e62dd11
@@ -0,0 +1,25 @@
1
+ module K3CloudWebapiSdk
2
+ module Const
3
+ # InvokeMethod constants
4
+ module InvokeMethod
5
+ SYNC = 1
6
+ ASYNC = 2
7
+ QUERY = 3
8
+ end
9
+
10
+ # QueryState constants
11
+ module QueryState
12
+ Pending = 0
13
+ Running = 1
14
+ Complete = 2
15
+ end
16
+
17
+ # QueryMode constants
18
+ module QueryMode
19
+ BeginMethod_Header = 'beginmethod'
20
+ BeginMethod_Method = 'BeginQueryImpl'
21
+ QueryMethod_Header = 'querymethod'
22
+ QueryMethod_Method = 'QueryAsyncResult'
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,17 @@
1
+ module K3CloudWebapiSdk
2
+ module Const
3
+ module HeaderParam
4
+ X_Api_ClientID = 'X-Api-ClientID'
5
+ X_Api_Auth_Version = 'X-Api-Auth-Version'
6
+ X_Api_Timestamp = 'x-api-timestamp'
7
+ X_Api_Nonce = 'x-api-nonce'
8
+ X_Api_SignHeaders = 'x-api-signheaders'
9
+ X_Api_Signature = 'X-Api-Signature'
10
+ X_KD_AppKey = 'X-Kd-Appkey'
11
+ X_KD_AppData = 'X-Kd-Appdata'
12
+ X_KD_Signature = 'X-Kd-Signature'
13
+ KDService_SessionId = 'kdservice-sessionid'
14
+ Cookie_Set = 'Set-Cookie'
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,255 @@
1
+ require 'json'
2
+ require 'rest-client'
3
+ require 'uri'
4
+ require 'time'
5
+ require_relative '../const/const_define'
6
+ require_relative '../const/header_param'
7
+ require_relative '../model/cookie'
8
+ require_relative '../model/cookie_store'
9
+ require_relative '../model/identity'
10
+ require_relative '../model/query_param'
11
+ require_relative '../util/encode_util'
12
+ require_relative '../util/hmac_util'
13
+ require_relative '../util/base64_util'
14
+
15
+ module K3CloudWebapiSdk
16
+ module Core
17
+ # Result validation function
18
+ def self.valid_result(response_content)
19
+ if response_content.start_with?('response_error:')
20
+ res_error = response_content.sub('response_error:', '').strip
21
+ if !res_error.empty?
22
+ raise RuntimeError, res_error
23
+ else
24
+ raise RuntimeError, 'Empty exception message'
25
+ end
26
+ else
27
+ response_content
28
+ end
29
+ end
30
+
31
+ # Web API Client class
32
+ class WebApiClient
33
+ attr_accessor :initialize, :config, :identify, :cookies_store, :connect_timeout, :request_timeout, :proxy
34
+
35
+ def initialize
36
+ @initialize = false
37
+ @config = nil
38
+ @identify = nil
39
+ @cookies_store = Model::CookieStore.new
40
+ @connect_timeout = 120
41
+ @request_timeout = 120
42
+ @proxy = ''
43
+ end
44
+
45
+ # Initialize the client
46
+ def init(server_url, timeout, sdk_initialize)
47
+ @initialize = sdk_initialize
48
+
49
+ if @config.nil?
50
+ raise 'Configuration file unload, you need initial config firstly!'
51
+ end
52
+
53
+ if server_url.empty?
54
+ # Cancel default old gateway, require url input by Ann 2025-01-15
55
+ raise 'ServerUrl is required'
56
+ end
57
+
58
+ @identify = Model::Identity.new(
59
+ server_url,
60
+ @config.dcid,
61
+ @config.user_name,
62
+ @config.app_id,
63
+ @config.app_secret,
64
+ @config.org_num,
65
+ @config.lcid,
66
+ ''
67
+ )
68
+
69
+ @connect_timeout = @config.connect_timeout if @config.connect_timeout > 0
70
+ @request_timeout = @config.request_timeout if @config.request_timeout > 0
71
+ @request_timeout = timeout if timeout > 0
72
+ @proxy = @config.proxy if !@config.proxy.empty?
73
+
74
+ self
75
+ end
76
+
77
+ # Build request headers
78
+ def build_header(service_url)
79
+ path_url = service_url
80
+ if service_url.start_with?('http')
81
+ uri = URI.parse(service_url)
82
+ path_url = uri.path
83
+ end
84
+
85
+ path_url = ERB::Util.url_encode(path_url).gsub('/', '%2F')
86
+ time_stamp = Time.now.to_i.to_s
87
+ nonce = Time.now.to_i.to_s
88
+ client_id = ''
89
+ client_sec = ''
90
+
91
+ arr = @identify.app_id.split('_')
92
+ if arr.length == 2
93
+ client_id = arr[0]
94
+ client_sec = Util::EncodeUtil.decode_app_secret(arr[1])
95
+ end
96
+
97
+ api_sign = 'POST\n' + path_url + '\n\nx-api-nonce:' + nonce + '\nx-api-timestamp:' + time_stamp + '\n'
98
+ app_data = @identify.dcid + ',' + @identify.user_name + ',' + @identify.lcid.to_s + ',' + @identify.org_num.to_s
99
+
100
+ dic_header = {
101
+ Const::HeaderParam::X_Api_ClientID => client_id,
102
+ Const::HeaderParam::X_Api_Auth_Version => '2.0',
103
+ Const::HeaderParam::X_Api_Timestamp => time_stamp,
104
+ Const::HeaderParam::X_Api_Nonce => nonce,
105
+ Const::HeaderParam::X_Api_SignHeaders => 'x-api-timestamp,x-api-nonce',
106
+ Const::HeaderParam::X_Api_Signature => Util::HmacUtil.hmac_sha256(api_sign, client_sec),
107
+ Const::HeaderParam::X_KD_AppKey => @identify.app_id,
108
+ Const::HeaderParam::X_KD_AppData => Util::Base64Util.encode(app_data.bytes),
109
+ Const::HeaderParam::X_KD_Signature => Util::HmacUtil.hmac_sha256(@identify.app_id + app_data, @identify.app_secret)
110
+ }
111
+
112
+ if !@cookies_store.sid.empty?
113
+ dic_header[Const::HeaderParam::KDService_SessionId] = @cookies_store.sid
114
+ end
115
+
116
+ if @cookies_store.cookies.length > 0
117
+ cookie_str = 'Theme=standard'
118
+ @cookies_store.cookies.each_value do |cookie|
119
+ cookie_str += ';' + cookie.to_s
120
+ end
121
+ dic_header['Cookie'] = cookie_str
122
+ end
123
+
124
+ dic_header['Accept-Charset'] = 'utf-8'
125
+ dic_header['User-Agent'] = 'Kingdee/Ruby WebApi SDK 8.2.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)'
126
+ dic_header['Content-Type'] = 'application/json'
127
+
128
+ dic_header
129
+ end
130
+
131
+ # Post JSON data
132
+ def post_json(service_name, json_data = {}, invoke_type = Const::InvokeMethod::SYNC)
133
+ json_data ||= {}
134
+
135
+ req_url = if @identify.server_url.end_with?('/')
136
+ @identify.server_url + service_name + '.common.kdsvc'
137
+ else
138
+ @identify.server_url + '/' + service_name + '.common.kdsvc'
139
+ end
140
+
141
+ proxies = nil
142
+ if !@proxy.empty?
143
+ proxies = { URI.parse(@proxy).scheme => @proxy }
144
+ end
145
+
146
+ if invoke_type == Const::InvokeMethod::QUERY
147
+ json_data[Const::QueryMode::BeginMethod_Header] = Const::QueryMode::BeginMethod_Method
148
+ json_data[Const::QueryMode::QueryMethod_Header] = Const::QueryMode::QueryMethod_Method
149
+ end
150
+
151
+ headers = build_header(req_url)
152
+
153
+ begin
154
+ response = RestClient.post(
155
+ req_url,
156
+ json_data.to_json,
157
+ headers: headers,
158
+ timeout: [@connect_timeout, @request_timeout],
159
+ verify_ssl: false,
160
+ proxy: @proxy.empty? ? nil : @proxy
161
+ )
162
+
163
+ if response.code == 200 || response.code == 206
164
+ fill_cookie_and_header(response.cookies, response.headers)
165
+ return Core.valid_result(response.body)
166
+ else
167
+ raise "HTTP Error #{response.code}: #{response.body}"
168
+ end
169
+ rescue RestClient::Exception => e
170
+ raise "Request failed: #{e.message}"
171
+ end
172
+ end
173
+
174
+ # Fill cookies and headers
175
+ def fill_cookie_and_header(cookies, headers)
176
+ @cookies_store.set_sid(cookies[Const::HeaderParam::KDService_SessionId])
177
+
178
+ if headers[Const::HeaderParam::Cookie_Set]
179
+ @cookies_store.cookies.clear
180
+ headers[Const::HeaderParam::Cookie_Set].split(',').each do |item|
181
+ ck = Model::Cookie.parse(item)
182
+ if ck
183
+ @cookies_store.cookies[ck.name] = ck
184
+ end
185
+ end
186
+ end
187
+ end
188
+
189
+ # Execute API call
190
+ def execute(service_name, json_data = nil, invoke_type = Const::InvokeMethod::SYNC)
191
+ if !@initialize
192
+ raise '拒绝请求,请先正确初始化!'
193
+ end
194
+
195
+ if @config.nil?
196
+ raise '请先初始化SDK配置信息!'
197
+ end
198
+
199
+ case invoke_type
200
+ when Const::InvokeMethod::SYNC
201
+ post_json(service_name, json_data, Const::InvokeMethod::SYNC)
202
+ when Const::InvokeMethod::QUERY
203
+ execute_by_query(service_name, json_data)
204
+ else
205
+ raise 'Not support for InvokeMode:' + invoke_type.to_s
206
+ end
207
+ end
208
+
209
+ # Execute by query method
210
+ def execute_by_query(service_name, json_data = nil)
211
+ response_content = post_json(service_name, json_data, Const::InvokeMethod::QUERY)
212
+ json_result = JSON.parse(response_content)
213
+
214
+ if json_result['Status'] == Const::QueryState::Complete
215
+ return json_result['Result'].to_json
216
+ else
217
+ return query_task_result(
218
+ service_name,
219
+ { 'TaskId' => json_result['TaskId'], 'Cancelled' => false },
220
+ 5
221
+ )
222
+ end
223
+ end
224
+
225
+ # Query task result
226
+ def query_task_result(service_name, param, retry_count)
227
+ sleep(1)
228
+
229
+ dot_index = service_name.rindex('.')
230
+ query_service = if dot_index
231
+ service_name[0, dot_index] + '.' + Const::QueryMode::QueryMethod_Method
232
+ else
233
+ service_name + '.' + Const::QueryMode::QueryMethod_Method
234
+ end
235
+
236
+ begin
237
+ response_content = post_json(query_service, { 'queryInfo' => param.to_json }, Const::InvokeMethod::SYNC)
238
+ json_result = JSON.parse(response_content)
239
+
240
+ if json_result['Status'] == Const::QueryState::Complete
241
+ json_result['Result'].to_json
242
+ else
243
+ query_task_result(service_name, param, 5)
244
+ end
245
+ rescue => err
246
+ if retry_count > 0
247
+ query_task_result(service_name, param, retry_count - 1)
248
+ else
249
+ raise err
250
+ end
251
+ end
252
+ end
253
+ end
254
+ end
255
+ end
@@ -0,0 +1,213 @@
1
+ require_relative 'core/webapi_client'
2
+ require_relative 'const/const_define'
3
+ require_relative 'util/config_util'
4
+
5
+ module K3CloudWebapiSdk
6
+ # Main K3Cloud API SDK class
7
+ class K3CloudApiSdk < Core::WebApiClient
8
+ attr_accessor :server_url, :timeout
9
+
10
+ def initialize(server_url, timeout = 120)
11
+ super()
12
+ @server_url = server_url
13
+ @timeout = timeout
14
+ end
15
+
16
+ # Initialize configuration with parameters
17
+ def init_config(acct_id, user_name, app_id, app_secret, server_url, lcid = 2052, org_num = 0,
18
+ connect_timeout = 120, request_timeout = 120, proxy = '')
19
+
20
+ @config = Util::ConfigUtil.init_config_by_params(
21
+ acct_id, user_name, app_id, app_secret, server_url, lcid, org_num,
22
+ connect_timeout, request_timeout, proxy
23
+ )
24
+
25
+ init(@server_url, @timeout, is_valid)
26
+ end
27
+
28
+ # Initialize configuration from file
29
+ def init(config_path = 'conf.ini', config_node = 'config')
30
+ @config = Util::ConfigUtil.init_config(config_path, config_node)
31
+ init(@server_url, @timeout, is_valid)
32
+ end
33
+
34
+ # Check if configuration is valid
35
+ def is_valid
36
+ messages = []
37
+
38
+ messages << '账套ID' if @config.dcid.empty?
39
+ messages << '用户' if @config.user_name.empty?
40
+ messages << '应用ID' if @config.app_id.empty?
41
+ messages << '应用密钥' if @config.app_secret.empty?
42
+
43
+ if !messages.empty?
44
+ puts 'SDK初始化失败,缺少必填授权项:' + messages.join(',')
45
+ return false
46
+ else
47
+ return true
48
+ end
49
+ end
50
+
51
+ # Get data centers
52
+ def get_data_centers
53
+ execute('Kingdee.BOS.ServiceFacade.ServicesStub.Account.AccountService.GetDataCenterList')
54
+ end
55
+
56
+ # Execute operation
57
+ def execute_operation(formid, op_number, data)
58
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.ExcuteOperation',
59
+ { 'formid' => formid, 'opNumber' => op_number, 'data' => data })
60
+ end
61
+
62
+ # Save data
63
+ def save(formid, data)
64
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Save',
65
+ { 'formid' => formid, 'data' => data })
66
+ end
67
+
68
+ # Batch save
69
+ def batch_save(formid, data)
70
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.BatchSave',
71
+ { 'formid' => formid, 'data' => data })
72
+ end
73
+
74
+ # Batch save with query
75
+ def batch_save_query(formid, data)
76
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.BatchSave',
77
+ { 'formid' => formid, 'data' => data },
78
+ Const::InvokeMethod::QUERY)
79
+ end
80
+
81
+ # Audit
82
+ def audit(formid, data)
83
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Audit',
84
+ { 'formid' => formid, 'data' => data })
85
+ end
86
+
87
+ # Delete
88
+ def delete(formid, data)
89
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Delete',
90
+ { 'formid' => formid, 'data' => data })
91
+ end
92
+
93
+ # Undo audit
94
+ def un_audit(formid, data)
95
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.UnAudit',
96
+ { 'formid' => formid, 'data' => data })
97
+ end
98
+
99
+ # Submit
100
+ def submit(formid, data)
101
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Submit',
102
+ { 'formid' => formid, 'data' => data })
103
+ end
104
+
105
+ # View
106
+ def view(formid, data)
107
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.View',
108
+ { 'formid' => formid, 'data' => data })
109
+ end
110
+
111
+ # Execute bill query
112
+ def execute_bill_query(data)
113
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.ExecuteBillQuery',
114
+ { 'data' => data })
115
+ end
116
+
117
+ # Bill query
118
+ def bill_query(data)
119
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.BillQuery',
120
+ { 'data' => data })
121
+ end
122
+
123
+ # Draft
124
+ def draft(formid, data)
125
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Draft',
126
+ { 'formid' => formid, 'data' => data })
127
+ end
128
+
129
+ # Allocate
130
+ def allocate(formid, data)
131
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Allocate',
132
+ { 'formid' => formid, 'data' => data })
133
+ end
134
+
135
+ # Flex save
136
+ def flex_save(formid, data)
137
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.FlexSave',
138
+ { 'formid' => formid, 'data' => data })
139
+ end
140
+
141
+ # Send message
142
+ def send_msg(data)
143
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.SendMsg',
144
+ { 'data' => data })
145
+ end
146
+
147
+ # Push
148
+ def push(formid, data)
149
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Push',
150
+ { 'formid' => formid, 'data' => data })
151
+ end
152
+
153
+ # Group save
154
+ def group_save(formid, data)
155
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.GroupSave',
156
+ { 'formid' => formid, 'data' => data })
157
+ end
158
+
159
+ # Disassembly
160
+ def disassembly(formid, data)
161
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Disassembly',
162
+ { 'formid' => formid, 'data' => data })
163
+ end
164
+
165
+ # Query business info
166
+ def query_business_info(data)
167
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.QueryBusinessInfo',
168
+ { 'data' => data })
169
+ end
170
+
171
+ # Query group info
172
+ def query_group_info(data)
173
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.QueryGroupInfo',
174
+ { 'data' => data })
175
+ end
176
+
177
+ # Workflow audit
178
+ def workflow_audit(data)
179
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.WorkflowAudit',
180
+ { 'data' => data })
181
+ end
182
+
183
+ # Group delete
184
+ def group_delete(data)
185
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.GroupDelete',
186
+ { 'data' => data })
187
+ end
188
+
189
+ # Switch organization
190
+ def switch_org(data)
191
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.SwitchOrg',
192
+ { 'data' => data })
193
+ end
194
+
195
+ # Get system report data
196
+ def get_sys_report_data(formid, data)
197
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.GetSysReportData',
198
+ { 'formid' => formid, 'data' => data })
199
+ end
200
+
201
+ # Upload attachment
202
+ def attachment_upload(data)
203
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.AttachmentUpload',
204
+ { 'data' => data })
205
+ end
206
+
207
+ # Download attachment
208
+ def attachment_download(data)
209
+ execute('Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.AttachmentDownLoad',
210
+ { 'data' => data })
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,33 @@
1
+ module K3CloudWebapiSdk
2
+ module Model
3
+ class ApiConfig
4
+ # Class variable for XOR code (equivalent to Python's class variable)
5
+ @@xor_code = ''
6
+
7
+ attr_accessor :server_url, :dcid, :user_name, :app_id, :app_secret,
8
+ :lcid, :org_num, :connect_timeout, :request_timeout, :proxy
9
+
10
+ def initialize
11
+ # Cancel default old gateway, require url input by Ann 2025-01-15
12
+ @server_url = ''
13
+ @dcid = ''
14
+ @user_name = ''
15
+ @app_id = ''
16
+ @app_secret = ''
17
+ @lcid = 2052
18
+ @org_num = 0
19
+ @connect_timeout = 120
20
+ @request_timeout = 120
21
+ @proxy = ''
22
+ end
23
+
24
+ def self.xor_code
25
+ @@xor_code
26
+ end
27
+
28
+ def self.xor_code=(value)
29
+ @@xor_code = value
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ module K3CloudWebapiSdk
2
+ module Model
3
+ class Cookie
4
+ attr_accessor :name, :value, :domain, :path, :expires, :http_only, :secure
5
+
6
+ def initialize(name: '', value: '', domain: '', path: '/', expires: nil, http_only: false, secure: false)
7
+ @name = name
8
+ @value = value
9
+ @domain = domain
10
+ @path = path
11
+ @expires = expires
12
+ @http_only = http_only
13
+ @secure = secure
14
+ end
15
+
16
+ def to_s
17
+ "#{@name}=#{@value}"
18
+ end
19
+
20
+ def to_h
21
+ {
22
+ name: @name,
23
+ value: @value,
24
+ domain: @domain,
25
+ path: @path,
26
+ expires: @expires,
27
+ http_only: @http_only,
28
+ secure: @secure
29
+ }
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,37 @@
1
+ module K3cloudWebapiSdk
2
+ module Model
3
+ class CookieStore
4
+ attr_accessor :sid, :cookies
5
+
6
+ def initialize(sid = '', cookies = nil)
7
+ @sid = sid
8
+ @cookies = cookies || {}
9
+ end
10
+
11
+ def set_sid(sid)
12
+ @sid = sid if !sid.nil? && !sid.empty?
13
+ end
14
+
15
+ def set_cookie(name, value, domain = '', path = '/', expires = nil)
16
+ @cookies[name] = {
17
+ value: value,
18
+ domain: domain,
19
+ path: path,
20
+ expires: expires
21
+ }
22
+ end
23
+
24
+ def get_cookie(name)
25
+ @cookies[name]
26
+ end
27
+
28
+ def delete_cookie(name)
29
+ @cookies.delete(name)
30
+ end
31
+
32
+ def clear_cookies
33
+ @cookies.clear
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,31 @@
1
+ module K3CloudWebapiSdk
2
+ module Model
3
+ class Identity
4
+ attr_accessor :server_url, :dcid, :lcid, :user_name, :pwd, :app_id, :app_secret, :org_num
5
+
6
+ def initialize(server_url, dcid, user_name, app_id, app_secret, org_num, lcid = 2052, pwd = '')
7
+ @server_url = server_url
8
+ @dcid = dcid
9
+ @lcid = lcid
10
+ @user_name = user_name
11
+ @pwd = pwd
12
+ @app_id = app_id
13
+ @app_secret = app_secret
14
+ @org_num = org_num
15
+ end
16
+
17
+ def to_h
18
+ {
19
+ server_url: @server_url,
20
+ dcid: @dcid,
21
+ lcid: @lcid,
22
+ user_name: @user_name,
23
+ pwd: @pwd,
24
+ app_id: @app_id,
25
+ app_secret: @app_secret,
26
+ org_num: @org_num
27
+ }
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,19 @@
1
+ module K3CloudWebapiSdk
2
+ module Model
3
+ class QueryTaskParam
4
+ attr_accessor :task_id, :cancelled
5
+
6
+ def initialize(task_id = '', is_cancelled = false)
7
+ @task_id = task_id
8
+ @cancelled = is_cancelled
9
+ end
10
+
11
+ def to_h
12
+ {
13
+ task_id: @task_id,
14
+ cancelled: @cancelled
15
+ }
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,84 @@
1
+ module K3CloudWebapiSdk
2
+ module Util
3
+ module Base64Util
4
+ CHARSET = ('A'..'Z').to_a.join + ('a'..'z').to_a.join + ('0'..'9').to_a.join + '+/'
5
+
6
+ # Custom base64 encode (Ruby equivalent of Python's base64_util.py)
7
+ def self.encode(origin_bytes)
8
+ return '' if origin_bytes.nil? || origin_bytes.empty?
9
+
10
+ # Convert to binary string
11
+ base64_bytes = origin_bytes.bytes.map { |b| b.to_s(2).rjust(8, '0') }
12
+
13
+ resp = ''
14
+ nums = base64_bytes.length / 3
15
+ remain = base64_bytes.length % 3
16
+
17
+ # Process complete 3-byte chunks
18
+ (0...nums).each do |i|
19
+ tmp_unit = base64_bytes[i*3..i*3+2].join('')
20
+ [0, 6, 12, 18].each do |offset|
21
+ value = tmp_unit[offset, 6].to_i(2)
22
+ resp << CHARSET[value]
23
+ end
24
+ end
25
+
26
+ # Handle remaining bytes
27
+ if remain > 0
28
+ remain_part = base64_bytes[nums*3..-1].join('').ljust((3-remain)*8, '0')
29
+ [0, 6, 12, 18].each_with_index do |offset, idx|
30
+ break if idx >= remain + 1
31
+ value = remain_part[offset, 6].to_i(2)
32
+ resp << CHARSET[value]
33
+ end
34
+ resp << '=' * (3 - remain)
35
+ end
36
+
37
+ resp
38
+ end
39
+
40
+ # Custom base64 decode
41
+ def self.decode(base64_str)
42
+ return [] if !valid_base64_str(base64_str)
43
+
44
+ # Remove padding for processing
45
+ chars_to_process = base64_str.gsub('=', '')
46
+
47
+ # Convert to 6-bit chunks
48
+ base64_bytes = chars_to_process.chars.map do |c|
49
+ CHARSET.index(c).to_s(2).rjust(6, '0')
50
+ end
51
+
52
+ resp = []
53
+ nums = base64_bytes.length / 4
54
+ remain = base64_bytes.length % 4
55
+
56
+ # Process complete 4-chunk units
57
+ (0...nums).each do |i|
58
+ tmp_unit = base64_bytes[i*4..i*4+3].join('')
59
+ [0, 8, 16].each do |offset|
60
+ value = tmp_unit[offset, 8].to_i(2)
61
+ resp << value
62
+ end
63
+ end
64
+
65
+ # Handle remaining chunks
66
+ if remain > 0
67
+ remain_part = base64_bytes[nums*4..-1].join('')
68
+ (0...remain-1).each do |i|
69
+ value = remain_part[i*8, 8].to_i(2)
70
+ resp << value
71
+ end
72
+ end
73
+
74
+ resp
75
+ end
76
+
77
+ # Validate base64 string
78
+ def self.valid_base64_str(b_str)
79
+ return false if b_str.length % 4 != 0
80
+ b_str.each_char.all? { |m| CHARSET.include?(m) }
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,47 @@
1
+ require_relative '../model/api_config'
2
+
3
+ module K3CloudWebapiSdk
4
+ module Util
5
+ module ConfigUtil
6
+ # Initialize configuration from file
7
+ def self.init_config(config_path, config_node)
8
+ raise 'Init config failed: Lack of config path!' if config_path.nil? || config_path.empty?
9
+ raise 'Init config failed: Lack of config node!' if config_node.nil? || config_node.empty?
10
+
11
+ # For Ruby, we'll use a simple YAML-like format or ENV variables
12
+ # In a real implementation, you'd use a config file parser
13
+ assign_api_config = Model::ApiConfig.new
14
+
15
+ # TODO: Implement file parsing for .ini or .yaml files
16
+ # For now, return empty config
17
+ assign_api_config
18
+ end
19
+
20
+ # Initialize configuration by parameters
21
+ def self.init_config_by_params(acct_id, user_name, app_id, app_secret, server_url,
22
+ lcid = 2052, org_num = 0, connect_timeout = 120,
23
+ request_timeout = 120, proxy = '')
24
+ assign_api_config = Model::ApiConfig.new
25
+
26
+ if !server_url.nil? && !server_url.empty?
27
+ assign_api_config.server_url = server_url
28
+ else
29
+ # Cancel default old gateway, require url input by Ann 2025-01-15
30
+ raise 'ServerUrl is required'
31
+ end
32
+
33
+ assign_api_config.dcid = acct_id
34
+ assign_api_config.user_name = user_name
35
+ assign_api_config.app_id = app_id
36
+ assign_api_config.app_secret = app_secret
37
+ assign_api_config.lcid = lcid > 0 ? lcid : 2052
38
+ assign_api_config.org_num = org_num > 0 ? org_num : 0
39
+ assign_api_config.connect_timeout = connect_timeout > 0 ? connect_timeout : 120
40
+ assign_api_config.request_timeout = request_timeout > 0 ? request_timeout : 120
41
+ assign_api_config.proxy = proxy
42
+
43
+ assign_api_config
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,32 @@
1
+ require_relative 'base64_util'
2
+
3
+ module K3CloudWebapiSdk
4
+ module Util
5
+ module EncodeUtil
6
+ # Decode application secret
7
+ def self.decode_app_secret(app_secret)
8
+ return '' if app_secret.length != 32
9
+
10
+ base64_decode = Base64Util.decode(app_secret)
11
+ base64_xor = xor_code(base64_decode)
12
+ Base64Util.encode(base64_xor.pack('C*'))
13
+ end
14
+
15
+ # Encode character using ROT13-like cipher
16
+ def self.encode_char(ch)
17
+ if ch =~ /[a-z]/
18
+ ((ch.ord - 97 + 13) % 26 + 97).chr
19
+ elsif ch =~ /[A-Z]/
20
+ ((ch.ord - 65 + 13) % 26 + 65).chr
21
+ else
22
+ ch
23
+ end
24
+ end
25
+
26
+ # ROT function
27
+ def self.rot(s)
28
+ s.chars.map { |c| encode_char(c) }.join('')
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,15 @@
1
+ require 'base64'
2
+ require 'openssl'
3
+
4
+ module K3CloudWebapiSdk
5
+ module Util
6
+ module HmacUtil
7
+ # Calculate HMAC-SHA256 signature (Ruby equivalent of Python's hmac_util.py)
8
+ def self.hmac_sha256(content, sign_key, encode_format = 'utf-8')
9
+ digest = OpenSSL::HMAC.digest('SHA256', sign_key.encode(encode_format), content.encode(encode_format))
10
+ sign_hex = digest.unpack1('H*')
11
+ Base64.strict_encode64([sign_hex].pack('H*'))
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ require 'json'
2
+ require 'rest-client'
3
+ require 'base64'
4
+ require 'openssl'
5
+ require 'securerandom'
6
+
7
+ # Load all modules
8
+ Dir[File.expand_path('../**/*.rb', __FILE__)].each do |file|
9
+ require_relative file.sub(/.*\/lib\//, '')
10
+ end
11
+
12
+ module K3CloudWebapiSdk
13
+ VERSION = "8.2.0"
14
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: k3cloud_webapi_sdk
3
+ version: !ruby/object:Gem::Version
4
+ version: 8.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Converted from Python SDK
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rest-client
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '2.1'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '2.1'
26
+ - !ruby/object:Gem::Dependency
27
+ name: json
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '1.8'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '1.8'
40
+ - !ruby/object:Gem::Dependency
41
+ name: rake
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '13.0'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '13.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rspec
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '3.0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.0'
68
+ description: Ruby SDK for K3Cloud API integration
69
+ email:
70
+ - support@kingdee.com
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - lib/k3cloud_webapi_sdk.rb
76
+ - lib/k3cloud_webapi_sdk/const/const_define.rb
77
+ - lib/k3cloud_webapi_sdk/const/header_param.rb
78
+ - lib/k3cloud_webapi_sdk/core/webapi_client.rb
79
+ - lib/k3cloud_webapi_sdk/main.rb
80
+ - lib/k3cloud_webapi_sdk/model/api_config.rb
81
+ - lib/k3cloud_webapi_sdk/model/cookie.rb
82
+ - lib/k3cloud_webapi_sdk/model/cookie_store.rb
83
+ - lib/k3cloud_webapi_sdk/model/identity.rb
84
+ - lib/k3cloud_webapi_sdk/model/query_param.rb
85
+ - lib/k3cloud_webapi_sdk/util/base64_util.rb
86
+ - lib/k3cloud_webapi_sdk/util/config_util.rb
87
+ - lib/k3cloud_webapi_sdk/util/encode_util.rb
88
+ - lib/k3cloud_webapi_sdk/util/hmac_util.rb
89
+ homepage: https://github.com/kingdee/k3cloud-ruby-sdk
90
+ licenses:
91
+ - MIT
92
+ metadata: {}
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubygems_version: 3.6.9
108
+ specification_version: 4
109
+ summary: K3Cloud API Ruby SDK
110
+ test_files: []