kinetic_sdk 5.0.30 → 7.0.0.rc1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 054f33cf19229075e84310cbec30fd1e7273dcb7b170ca72b47ce3ac2e4247e1
4
- data.tar.gz: f0f73ed401f108470103e98e2ac7463199cfcd499127cc9e49123a882dee6000
3
+ metadata.gz: c479e6e6a7733c2bfa20d92f03230037c9733f6c58c804c38695776572d1e1d6
4
+ data.tar.gz: fc8d8219f4bb8e7f3f1655d259056f19cdb1e7d0606e7b315e775f4a161a041b
5
5
  SHA512:
6
- metadata.gz: 8288d007b340bf2f81b05b2504ff4a2af7892966ea5319910916aa5566e32897b8353ccc4963df962cb8f8dbc6460b1e3c25e2586ad3a049d60f76ad441439da
7
- data.tar.gz: 9f274fe9504a7121b73dfe89d6fa90a87e1154bf2a1e30503726c7847793e4323ce2edf3357372f6bdfe9f657a62e2a28d38d636e3b029ccd56b25d3372ccdf9
6
+ metadata.gz: fd3128429fdd4e411786cbbf5b3d99bd113897f999f35c24624d19477b7eb0174ee289f3b7a4a356ae6367fc5f6d305c5edcd64c47bc8a2de123474b8bbe6ff9
7
+ data.tar.gz: 79cd820bac2d3bc3b38bd24cd079f1c01a0e3116b3d6e8caef5988535e9b3c3c46562bf540d695de6a5586646dcc9c71632cdecd26095a8fdb5cb44a0a96c6be
data/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Change Log
2
2
 
3
+
4
+ ## [7.0.0-rc1](https://github.com/kineticdata/kinetic-sdk-rb/tree/7.0.0-rc1) (2026-08-31)
5
+
6
+ **Breaking changes:**
7
+
8
+ - This release is tied to version 7 of the Kinetic Platform, and is not compatible with
9
+ earlier platform versions.
10
+ - The Core OAuth authorization routes moved from `/app/oauth` to `/app/oauth2`. The
11
+ authorization code and token requests made by `KineticSdk::Core#jwt_token` - used by the
12
+ Discussions and Integrator SDKs to authenticate - now use the `/app/oauth2` routes.
13
+
14
+ **Implemented enhancements:**
15
+
16
+ - Raise a descriptive error when the authorize endpoint redirects without an authorization
17
+ code, instead of passing an invalid code along to the token endpoint.
18
+
19
+
20
+ ## [5.0.31](https://github.com/kineticdata/kinetic-sdk-rb/tree/5.0.31) (2026-03-06)
21
+
22
+ **Implemented enhancements:**
23
+
24
+ - Added thread support for exporting.
25
+
26
+
3
27
  ## [5.0.30](https://github.com/kineticdata/kinetic-sdk-rb/tree/5.0.30) (2025-10-02)
4
28
 
5
29
  **Implemented enhancements:**
data/kinetic_sdk.gemspec CHANGED
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
23
23
 
24
24
  spec.add_development_dependency "bundler", ">= 1.17"
25
25
  spec.add_development_dependency "rake", ">= 13.0.1"
26
- spec.add_development_dependency "yard", "~> 0.9.36"
26
+ spec.add_development_dependency "yard", "~> 0.9.45"
27
27
 
28
28
  spec.metadata["yard.run"] = "yri"
29
29
  end
@@ -14,7 +14,7 @@ module KineticSdk
14
14
  # - +status+
15
15
  # - +submissionLabelExpression+
16
16
  # - +type+
17
- # - +attributes+
17
+ # - +attributes
18
18
  # - +bridgedResources+
19
19
  # - +pages+
20
20
  # - +securityPolicies+
@@ -95,6 +95,27 @@ module KineticSdk
95
95
  put("#{@api_url}/kapps/#{kapp_slug}/forms/#{form_slug}", properties, headers)
96
96
  end
97
97
 
98
+ # Update a Form
99
+ #
100
+ # @param kapp_slug [String] slug of the Kapp the form belongs to
101
+ # @param form_slug [String] slug of the form
102
+ # @param properties [Hash] form properties to update
103
+ # - +anonymous+
104
+ # - +customHeadContent+
105
+ # - +description+
106
+ # - +name+
107
+ # - +notes+
108
+ # - +slug+
109
+ # - +status+
110
+ # - +submissionLabelExpression+
111
+ # - +type+
112
+ # - +attributes+
113
+ # - +bridgedResources+
114
+ # - +pages+
115
+ # - +securityPolicies+
116
+ # @param headers [Hash] hash of headers to send, default is basic authentication and accept JSON content type
117
+ # @return [KineticSdk::Utils::KineticHttpResponse] object, with +code+, +message+, +content_string+, and +content+ properties
118
+
98
119
 
99
120
  # Find Form workflows
100
121
  #
@@ -12,7 +12,7 @@ module KineticSdk
12
12
  jwt_code = jwt_code(client_id, headers)
13
13
  # retrieve the jwt token
14
14
  @logger.info("Retrieving JWT authorization token")
15
- url = "#{@server}/app/oauth/token?grant_type=authorization_code&response_type=token&client_id=#{client_id}&code=#{jwt_code}"
15
+ url = "#{@server}/app/oauth2/token?grant_type=authorization_code&response_type=token&client_id=#{client_id}&code=#{jwt_code}"
16
16
  token_headers = header_accept_json.merge(header_basic_auth(client_id, client_secret))
17
17
  response = post(url, {}, token_headers, { :max_redirects => 0 })
18
18
 
@@ -34,7 +34,7 @@ module KineticSdk
34
34
  # @return [KineticSdk::Utils::KineticHttpResponse] object, with +code+, +message+, +content_string+, and +content+ properties
35
35
  def jwt_code(client_id, headers = default_headers)
36
36
  @logger.info("Retrieving JWT authorization code")
37
- url = "#{@server}/app/oauth/authorize?grant_type=authorization_code&response_type=code&client_id=#{client_id}"
37
+ url = "#{@server}/app/oauth2/authorize?grant_type=authorization_code&response_type=code&client_id=#{client_id}"
38
38
  response = post(url, {}, headers, { :max_redirects => -1 })
39
39
 
40
40
  if response.status == 401
@@ -43,6 +43,8 @@ module KineticSdk
43
43
  location = response.headers["location"]
44
44
  if location.nil?
45
45
  raise StandardError.new "Unable to retrieve code: #{response.inspect}"
46
+ elsif !location.include?("?code=")
47
+ raise StandardError.new "Unable to retrieve code, the authorize endpoint redirected to #{location} without an authorization code."
46
48
  else
47
49
  location.split("?code=").last.split("#/").first
48
50
  end
@@ -1,4 +1,5 @@
1
1
  module KineticSdk
2
+ MAX_THREADS = 30
2
3
  class Core
3
4
 
4
5
  # Add an attribute value to the space, or update an attribute if it already exists
@@ -86,7 +87,14 @@ module KineticSdk
86
87
  "space.webhooks.{name}",
87
88
  )
88
89
  core_data = find_space({ 'export' => true }, headers).content
89
- process_export(@options[:export_directory], export_shape, core_data)
90
+ pool = Concurrent::FixedThreadPool.new(MAX_THREADS) # Adjust thread count based on your needs
91
+ begin
92
+ process_export(@options[:export_directory], export_shape, core_data, '', executor: pool)
93
+ ensure
94
+ pool.shutdown
95
+ pool.wait_for_termination
96
+ end
97
+
90
98
  export_workflows(headers)
91
99
  @logger.info("Finished exporting space definition to #{@options[:export_directory]}.")
92
100
  end
@@ -106,50 +114,68 @@ module KineticSdk
106
114
  raise StandardError.new "An export directory must be defined to export workflows." if @options[:export_directory].nil?
107
115
  @logger.info("Exporting workflows to #{@options[:export_directory]}.")
108
116
 
109
- # space workflows
110
- space_workflows = find_space_workflows({ "include" => "details" }, headers).content["workflows"] || []
111
- space_workflows.select { |wf| !wf["event"].nil? }.each do |workflow|
112
- evt = workflow["event"].to_s.slugify
113
- name = workflow["name"].to_s.slugify
114
- if evt.empty? || name.empty?
115
- raise "Some workflows are currently in an orphaned or missing state. You can open the Workflows tab for the space in the space console, and run the repair to attempt to resolve this issue."
116
- end
117
- @logger.info(workflow)
118
- filename = "#{File.join(@options[:export_directory], "space", "workflows", evt, name)}.json"
119
- workflow_json = find_space_workflow(workflow["id"], {}, headers).content["treeJson"]
120
- write_object_to_file(filename, workflow_json)
121
- end
122
-
123
- space_content = find_space({ 'include' => "kapps.details,kapps.forms.details" }).content["space"]
124
-
125
- # kapp workflows
126
- space_content["kapps"].each do |kapp|
127
- kapp_workflows = find_kapp_workflows(kapp["slug"], {}, headers).content["workflows"] || []
128
- kapp_workflows.select { |wf| !wf["event"].nil? }.each do |workflow|
129
- evt = workflow["event"].to_s.slugify
130
- name = workflow["name"].to_s.slugify
131
- if evt.empty? || name.empty?
132
- raise "Some workflows are currently in an orphaned or missing state. You can open the Workflows tab for the #{kapp["name"]} kapp in the space console, and run the repair to attempt to resolve this issue."
117
+ #Define Thread settings
118
+ pool = Concurrent::FixedThreadPool.new(MAX_THREADS)
119
+ futures = []
120
+ begin
121
+ # space workflows
122
+ space_workflows = find_space_workflows({ "include" => "details" }, headers).content["workflows"] || []
123
+ space_workflows.select { |wf| !wf["event"].nil? }.each do |workflow|
124
+ futures << Concurrent::Future.execute(executor: pool) do
125
+ begin
126
+ evt = workflow["event"].to_s.slugify
127
+ name = workflow["name"].to_s.slugify
128
+ if evt.empty? || name.empty?
129
+ raise "Some workflows are currently in an orphaned or missing state. You can open the Workflows tab for the space in the space console, and run the repair to attempt to resolve this issue."
130
+ end
131
+ @logger.info(workflow)
132
+ filename = "#{File.join(@options[:export_directory], "space", "workflows", evt, name)}.json"
133
+ workflow_json = find_space_workflow(workflow["id"], {}, headers).content["treeJson"]
134
+ write_object_to_file(filename, workflow_json)
135
+ end
133
136
  end
134
- filename = "#{File.join(@options[:export_directory], "space", "kapps", kapp["slug"], "workflows", evt, name)}.json"
135
- workflow_json = find_kapp_workflow(kapp["slug"], workflow["id"], {}, headers).content["treeJson"]
136
- write_object_to_file(filename, workflow_json)
137
137
  end
138
-
139
- # form workflows
140
- kapp["forms"].each do |form|
141
- form_workflows = find_form_workflows(kapp["slug"], form["slug"], {}, headers).content["workflows"] || []
142
- form_workflows.select { |wf| !wf["event"].nil? }.each do |workflow|
143
- evt = workflow["event"].to_s.slugify
144
- name = workflow["name"].to_s.slugify
145
- if evt.empty? || name.empty?
146
- raise "Some workflows are currently in an orphaned or missing state. You can open the Workflows tab for the #{kapp["name"]} > #{form["name"]} form in the space console, and run the repair to attempt to resolve this issue."
138
+ futures.each(&:value)
139
+
140
+
141
+ space_content = find_space({ 'include' => "kapps.details,kapps.forms.details" }).content["space"]
142
+
143
+ # kapp workflows
144
+ space_content["kapps"].each do |kapp|
145
+ futures << Concurrent::Future.execute(executor: pool) do
146
+ kapp_workflows = find_kapp_workflows(kapp["slug"], {}, headers).content["workflows"] || []
147
+ kapp_workflows.select { |wf| !wf["event"].nil? }.each do |workflow|
148
+ evt = workflow["event"].to_s.slugify
149
+ name = workflow["name"].to_s.slugify
150
+ if evt.empty? || name.empty?
151
+ raise "Some workflows are currently in an orphaned or missing state. You can open the Workflows tab for the #{kapp["name"]} kapp in the space console, and run the repair to attempt to resolve this issue."
152
+ end
153
+ filename = "#{File.join(@options[:export_directory], "space", "kapps", kapp["slug"], "workflows", evt, name)}.json"
154
+ workflow_json = find_kapp_workflow(kapp["slug"], workflow["id"], {}, headers).content["treeJson"]
155
+ write_object_to_file(filename, workflow_json)
147
156
  end
148
- filename = "#{File.join(@options[:export_directory], "space", "kapps", kapp["slug"], "forms", form["slug"], "workflows", evt, name)}.json"
149
- workflow_json = find_form_workflow(kapp["slug"], form["slug"], workflow["id"], {}, headers).content["treeJson"]
150
- write_object_to_file(filename, workflow_json)
151
157
  end
158
+ futures << Concurrent::Future.execute(executor: pool) do
159
+ # form workflows
160
+ kapp["forms"].each do |form|
161
+ form_workflows = find_form_workflows(kapp["slug"], form["slug"], {}, headers).content["workflows"] || []
162
+ form_workflows.select { |wf| !wf["event"].nil? }.each do |workflow|
163
+ evt = workflow["event"].to_s.slugify
164
+ name = workflow["name"].to_s.slugify
165
+ if evt.empty? || name.empty?
166
+ raise "Some workflows are currently in an orphaned or missing state. You can open the Workflows tab for the #{kapp["name"]} > #{form["name"]} form in the space console, and run the repair to attempt to resolve this issue."
167
+ end
168
+ filename = "#{File.join(@options[:export_directory], "space", "kapps", kapp["slug"], "forms", form["slug"], "workflows", evt, name)}.json"
169
+ workflow_json = find_form_workflow(kapp["slug"], form["slug"], workflow["id"], {}, headers).content["treeJson"]
170
+ write_object_to_file(filename, workflow_json)
171
+ end
172
+ end
173
+ end #End thread
152
174
  end
175
+ ensure
176
+ futures.each(&:value)
177
+ pool.shutdown
178
+ pool.wait_for_termination
153
179
  end
154
180
  end
155
181
 
@@ -163,6 +189,7 @@ module KineticSdk
163
189
  get("#{@api_url}/space", params, headers)
164
190
  end
165
191
 
192
+
166
193
  # Imports a full space definition from the export_directory, except for workflows. Those must be imported separately after
167
194
  # the Kinetic Platform source exists in task.
168
195
  #
@@ -361,5 +388,8 @@ module KineticSdk
361
388
  post("#{@api_url}/workflows/#{workflow_id}", payload, headers)
362
389
  end
363
390
 
391
+
392
+
393
+
364
394
  end
365
395
  end
@@ -1,6 +1,5 @@
1
1
  module KineticSdk
2
2
  class Task
3
-
4
3
  # Delete a Handler
5
4
  #
6
5
  # @param definition_id [String] the handler definition id
@@ -73,6 +72,28 @@ module KineticSdk
73
72
  end
74
73
  end
75
74
 
75
+ def import_handlers_threaded(force_overwrite=false, headers=header_basic_auth)
76
+ raise StandardError.new "An export directory must be defined to import handlers from." if @options[:export_directory].nil?
77
+ @logger.info("Importing all Handlers from Export Directory")
78
+ pool = Concurrent::FixedThreadPool.new(MAX_THREADS)
79
+ mutex = Mutex.new
80
+ promises = []
81
+
82
+
83
+ Dir["#{@options[:export_directory]}/handlers/*.zip"].sort.each do |file|
84
+ promises << Concurrent::Promise.execute(executor: pool) do
85
+ begin
86
+ handler_file = File.new(file, "rb")
87
+ import_handler(handler_file, force_overwrite, headers)
88
+ rescue
89
+ end
90
+ end
91
+ end
92
+ promises.each(&:wait!)
93
+ pool.shutdown
94
+ pool.wait_for_termination
95
+ end
96
+
76
97
  # Modifies the properties and info values for a handler
77
98
  #
78
99
  # @param definition_id [String] the definition id of the handler
@@ -29,7 +29,9 @@ module KineticSdk
29
29
  # @param headers [Hash] hash of headers to send, default is none
30
30
  # @return [KineticSdk::Utils::KineticHttpResponse] object, with +code+, +message+, +content_string+, and +content+ properties
31
31
  def server_info(headers={})
32
- get(@api_url, {}, headers)
32
+ @server_info_mutex.synchronize do
33
+ @cached_server_info ||= get(@api_url, {}, headers)
34
+ end
33
35
  end
34
36
 
35
37
  end
@@ -1,6 +1,5 @@
1
1
  module KineticSdk
2
2
  class Task
3
-
4
3
  # Delete a tree.
5
4
  #
6
5
  # @param tree [String|Hash] either the tree title, or a hash consisting of component names
@@ -147,7 +146,63 @@ module KineticSdk
147
146
  @logger.info("Importing all Trees from Export Directory")
148
147
  Dir["#{@options[:export_directory]}/sources/**/*.xml"].sort.each do |file|
149
148
  tree_file = File.new(file, "rb")
150
- import_tree(tree_file, force_overwrite, headers)
149
+ match = compare_trees(tree_file)
150
+ if !match
151
+ import_tree(tree_file, force_overwrite, headers)
152
+ else
153
+ #Matching trees, skipping
154
+ end
155
+ end
156
+ end
157
+ def import_trees_threaded(force_overwrite=false, headers=header_basic_auth)
158
+ raise StandardError.new "An export directory must be defined to import trees from." if @options[:export_directory].nil?
159
+ @logger.info("Importing all Trees from Export Directory")
160
+ pool = Concurrent::FixedThreadPool.new(MAX_THREADS)
161
+ mutex = Mutex.new
162
+ promises = []
163
+
164
+ Dir["#{@options[:export_directory]}/sources/**/*.xml"].sort.each do |file|
165
+ promises << Concurrent::Promise.execute(executor: pool) do
166
+ begin
167
+
168
+ tree_file = File.new(file, "rb")
169
+ match = compare_trees(tree_file)
170
+ if !match
171
+ import_tree(tree_file, force_overwrite, headers)
172
+ else
173
+ #Matching trees, skipping
174
+ end
175
+ rescue
176
+ end
177
+
178
+ end
179
+ end
180
+ promises.each(&:wait!)
181
+ pool.shutdown
182
+ pool.wait_for_termination
183
+ end
184
+ # Import a tree
185
+ #
186
+ # If the tree already exists on the server, this will fail unless forced
187
+ # to overwrite.
188
+ #
189
+ # The source named in the tree content must also exist on the server, or
190
+ # the import will fail.
191
+ #
192
+ # @param tree [String] content from tree file
193
+ # @param force_overwrite [Boolean] whether to overwrite a tree if it exists, default is false
194
+ # @param headers [Hash] hash of headers to send, default is basic authentication
195
+ # @return [KineticSdk::Utils::KineticHttpResponse] object, with +code+, +message+, +content_string+, and +content+ properties
196
+ def compare_trees(tree, headers=header_basic_auth)
197
+ definitionId = REXML::XPath.first(tree, "//definitionId")&.text
198
+ @logger.info("Querying for Tree #{definitionId}")
199
+ response = find_tree(definitionId, { "include" => "details,export" })
200
+ @logger.info("Comparing new and old tree")
201
+ prev_tree = response.content
202
+ if prev_tree == tree
203
+ return true
204
+ else
205
+ return false
151
206
  end
152
207
  end
153
208
 
@@ -183,6 +238,28 @@ module KineticSdk
183
238
  end
184
239
  end
185
240
 
241
+ def import_routines_threaded(force_overwrite=false, headers=header_basic_auth)
242
+ raise StandardError.new "An export directory must be defined to import trees from." if @options[:export_directory].nil?
243
+ @logger.info("Importing all Routines from Export Directory")
244
+ pool = Concurrent::FixedThreadPool.new(MAX_THREADS)
245
+ mutex = Mutex.new
246
+ promises = []
247
+
248
+ Dir["#{@options[:export_directory]}/routines/*.xml"].sort.each do |file|
249
+ promises << Concurrent::Promise.execute(executor: pool) do
250
+ begin
251
+ routine_file = File.new(file, "rb")
252
+ import_routine(routine_file, force_overwrite, headers)
253
+ rescue
254
+ end
255
+ end
256
+ end
257
+ promises.each(&:wait!)
258
+ pool.shutdown
259
+ pool.wait_for_termination
260
+
261
+ end
262
+
186
263
  # Find a single tree by title (Source Name :: Group Name :: Tree Name)
187
264
  #
188
265
  # @param title [String] The tree title
@@ -69,6 +69,8 @@ module KineticSdk
69
69
  options = {}
70
70
  @config_user = {}
71
71
  @server = nil
72
+ @server_info_mutex = Mutex.new
73
+ @cached_server_info = nil
72
74
 
73
75
  # process the configuration file if it was provided
74
76
  unless opts[:config_file].nil?
@@ -97,6 +97,18 @@ module KineticSdk
97
97
  File.open(filename, 'w') { |file| file.write(JSON.pretty_generate(file_contents)) }
98
98
  end
99
99
 
100
+ def write_object_to_file_threaded(filename, file_contents, mutex=nil)
101
+ # Create Folder if not exists
102
+ dir_path = File.dirname(filename)
103
+ if mutex
104
+ mutex.synchronize { FileUtils.mkdir_p(dir_path, :mode => 0700) }
105
+ else
106
+ FileUtils.mkdir_p(dir_path, :mode => 0700)
107
+ end
108
+ # Write File
109
+ File.open(filename, 'w') { |file| file.write(JSON.pretty_generate(file_contents)) }
110
+ end
111
+
100
112
  # Processes and writes data exported from the core service
101
113
  #
102
114
  # @param core_path [String] the root folder path to write the data to
@@ -104,7 +116,84 @@ module KineticSdk
104
116
  # @param object [Hash] the object being processed
105
117
  # @param object_path [String] the path of the object being processed (used recursively)
106
118
  # @return nil
107
- def process_export(core_path, export_shape, object, object_path='')
119
+
120
+ def process_export(core_path, export_shape, object, object_path='', executor: nil)
121
+ # Prepare metadata (same as before)
122
+ child_objects = {}
123
+ file_contents = {}
124
+
125
+ if object.kind_of?(Array)
126
+ file_contents = object
127
+ else
128
+ object.each do |key, value|
129
+ child_object_path = object_path.empty? ? key : "#{object_path}.#{key}"
130
+
131
+ if should_extract(child_object_path, export_shape)
132
+ if value.kind_of?(Array)
133
+ variable = get_variable_property(export_shape, child_object_path)
134
+ if variable.nil?
135
+ child_objects[child_object_path] = value
136
+ else
137
+ value.each do |item|
138
+ child_objects["#{child_object_path}.#{item[variable]}"] = item
139
+ end
140
+ end
141
+ else
142
+ child_objects[child_object_path] = value
143
+ end
144
+ else
145
+ file_contents[key] = value
146
+ end
147
+ end
148
+ end
149
+
150
+ if object_path != '' && !file_contents.empty?
151
+ filename = "#{core_path}/#{object_path.gsub('/', '').gsub('\\', '').gsub('.', '/').gsub(/::/, '-').gsub(/[^ a-zA-Z0-9_\-\/]/, '')}.json"
152
+ write_object_to_file(filename, file_contents)
153
+ end
154
+
155
+ # Thread at key levels: root, kapps, and forms
156
+ should_thread = executor && child_objects.size > 1 && should_parallelize?(object_path)
157
+
158
+ if should_thread
159
+ puts " -> Spawning #{child_objects.size} threads for: #{object_path}"
160
+ futures = child_objects.map do |key, child_object|
161
+ Concurrent::Future.execute(executor: executor) do
162
+ puts " [Thread #{Thread.current.object_id}] Starting: #{key}"
163
+ process_export(core_path, export_shape, child_object, key, executor: executor)
164
+ puts " [Thread #{Thread.current.object_id}] Finished: #{key}"
165
+ end
166
+ end
167
+ futures.each(&:value)
168
+ else
169
+ child_objects.each do |key, child_object|
170
+ process_export(core_path, export_shape, child_object, key, executor: executor)
171
+ end
172
+ end
173
+ end
174
+
175
+ def should_parallelize?(object_path)
176
+ # Thread at these specific levels
177
+ parallelize_patterns = [
178
+ /^$/, # Root level
179
+ /^space\.kapps$/, # All kapps
180
+ /^space\.kapps\.[^.]+$/, # Within each kapp (forms, webhooks, etc)
181
+ /^space\.kapps\.[^.]+\.forms$/, # All forms within a kapp
182
+ /^space\.teams$/, # All teams
183
+ /^space\.bridges$/, # All bridges
184
+ /^space\.webApis$/, # All webApis
185
+ /^space\.webhooks$/, # All webhooks
186
+ ]
187
+
188
+ matches = parallelize_patterns.any? { |pattern| object_path =~ pattern }
189
+
190
+ # DEBUG
191
+ puts " Checking parallelize for: '#{object_path}' -> #{matches}"
192
+
193
+ matches
194
+ end
195
+
196
+ def process_export_old(core_path, export_shape, object, object_path='', executor: nil)
108
197
  # Prepare metadata
109
198
  child_objects = {}
110
199
  file_contents = {}
@@ -150,14 +239,22 @@ module KineticSdk
150
239
  write_object_to_file(filename, file_contents)
151
240
  end
152
241
 
153
-
154
- # For each of the child objects
155
- child_objects.each do |key, child_object|
156
- # Process the export for that object (recursively)
157
- process_export(core_path, export_shape, child_object, key)
242
+ if executor && child_objects.size > 0
243
+ futures = child_objects.map do |key, child_object|
244
+ Concurrent::Future.execute(executor: executor) do
245
+ # Recursive calls don't use threading
246
+ process_export(core_path, export_shape, child_object, key)
247
+ end
248
+ end
249
+ futures.each do |future|
250
+ future.value # This will re-raise any exceptions that occurred
251
+ end
252
+ else
253
+ child_objects.each do |key, child_object|
254
+ process_export(core_path, export_shape, child_object, key)
255
+ end
158
256
  end
159
257
  end
160
-
161
258
  end
162
259
  end
163
260
  end
@@ -3,5 +3,5 @@ module KineticSdk
3
3
  # Version of Kinetic SDK
4
4
  #
5
5
  # @return [String] Version of the SDK
6
- VERSION = "5.0.30"
6
+ VERSION = "7.0.0.rc1"
7
7
  end
data/lib/kinetic_sdk.rb CHANGED
@@ -2,6 +2,7 @@ require 'fileutils'
2
2
  require 'json'
3
3
  require 'uri'
4
4
  require 'yaml'
5
+ require 'concurrent-ruby'
5
6
 
6
7
  pwd = File.expand_path(File.dirname(__FILE__))
7
8
  gemdir = File.expand_path(File.join(pwd, '..', 'gems'))
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kinetic_sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.30
4
+ version: 7.0.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kinetic Data
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-10-02 00:00:00.000000000 Z
11
+ date: 2026-08-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: slugify
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: 0.9.36
89
+ version: 0.9.45
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: 0.9.36
96
+ version: 0.9.45
97
97
  description:
98
98
  email:
99
99
  - support@kineticdata.com
@@ -708,9 +708,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
708
708
  version: '0'
709
709
  required_rubygems_version: !ruby/object:Gem::Requirement
710
710
  requirements:
711
- - - ">="
711
+ - - ">"
712
712
  - !ruby/object:Gem::Version
713
- version: '0'
713
+ version: 1.3.1
714
714
  requirements: []
715
715
  rubygems_version: 3.3.3
716
716
  signing_key: