Skip to content

Commit fe38a50

Browse files
authored
[Ruby][faraday] fix response streaming (#7734)
* test file return * fix stream response * use options * use local var * fix nil tempfile * fix tempfile * fix tempfile * use stream * check content * open temp file * test ruby file download * fix stream data * test faraday * catch connection error * catch Faraday::ConnectionFailed * catch all error * use sream * refactor * fi download file in faraday * local fix * fi streaming download * undo changess to spec, test * undo changes to spec
1 parent 37743c0 commit fe38a50

File tree

8 files changed

+251
-191
lines changed

8 files changed

+251
-191
lines changed

modules/openapi-generator/src/main/resources/ruby-client/api_client.mustache

Lines changed: 13 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,20 @@ module {{moduleName}}
6666

6767
# handle file downloading - return the File instance processed in request callbacks
6868
# note that response body is empty when the file is written in chunks in request on_body callback
69+
{{^isFaraday}}
6970
return @tempfile if return_type == 'File'
71+
{{/isFaraday}}
72+
{{#isFaraday}}
73+
if return_type == 'File'
74+
@tempfile.write(@stream)
75+
@tempfile.close
76+
@config.logger.info "Temp file written to #{@tempfile.path}, please copy the file to a proper folder "\
77+
"with e.g. `FileUtils.cp(tempfile.path, '/new/file/path')` otherwise the temp file "\
78+
"will be deleted automatically with GC. It's also recommended to delete the temp file "\
79+
"explicitly with `tempfile.delete`"
80+
return @tempfile
81+
end
82+
{{/isFaraday}}
7083

7184
return nil if body.nil? || body.empty?
7285

@@ -131,44 +144,6 @@ module {{moduleName}}
131144
end
132145
end
133146

134-
# Save response body into a file in (the defined) temporary folder, using the filename
135-
# from the "Content-Disposition" header if provided, otherwise a random filename.
136-
# The response body is written to the file in chunks in order to handle files which
137-
# size is larger than maximum Ruby String or even larger than the maximum memory a Ruby
138-
# process can use.
139-
#
140-
# @see Configuration#temp_folder_path
141-
def download_file(request)
142-
tempfile = nil
143-
encoding = nil
144-
request.on_headers do |response|
145-
content_disposition = response.headers['Content-Disposition']
146-
if content_disposition && content_disposition =~ /filename=/i
147-
filename = content_disposition[/filename=['"]?([^'"\s]+)['"]?/, 1]
148-
prefix = sanitize_filename(filename)
149-
else
150-
prefix = 'download-'
151-
end
152-
prefix = prefix + '-' unless prefix.end_with?('-')
153-
encoding = response.body.encoding
154-
tempfile = Tempfile.open(prefix, @config.temp_folder_path, encoding: encoding)
155-
@tempfile = tempfile
156-
end
157-
request.on_body do |chunk|
158-
chunk.force_encoding(encoding)
159-
tempfile.write(chunk)
160-
end
161-
request.on_complete do |response|
162-
if tempfile
163-
tempfile.close
164-
@config.logger.info "Temp file written to #{tempfile.path}, please copy the file to a proper folder "\
165-
"with e.g. `FileUtils.cp(tempfile.path, '/new/file/path')` otherwise the temp file "\
166-
"will be deleted automatically with GC. It's also recommended to delete the temp file "\
167-
"explicitly with `tempfile.delete`"
168-
end
169-
end
170-
end
171-
172147
# Sanitize filename by removing path.
173148
# e.g. ../../sun.gif becomes sun.gif
174149
#

modules/openapi-generator/src/main/resources/ruby-client/api_client_faraday_partial.mustache

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,41 @@
127127
end
128128
data
129129
end
130+
131+
# Save response body into a file in (the defined) temporary folder, using the filename
132+
# from the "Content-Disposition" header if provided, otherwise a random filename.
133+
# The response body is written to the file in chunks in order to handle files which
134+
# size is larger than maximum Ruby String or even larger than the maximum memory a Ruby
135+
# process can use.
136+
#
137+
# @see Configuration#temp_folder_path
138+
def download_file(request)
139+
tempfile = nil
140+
encoding = nil
141+
request.headers do |response|
142+
content_disposition = response.headers['Content-Disposition']
143+
if content_disposition && content_disposition =~ /filename=/i
144+
filename = content_disposition[/filename=['"]?([^'"\s]+)['"]?/, 1]
145+
prefix = sanitize_filename(filename)
146+
else
147+
prefix = 'download-'
148+
end
149+
prefix = prefix + '-' unless prefix.end_with?('-')
150+
encoding = response.body.encoding
151+
tempfile = Tempfile.open(prefix, @config.temp_folder_path, encoding: encoding)
152+
@tempfile = tempfile
153+
end
154+
155+
if tempfile.nil?
156+
tempfile = Tempfile.open('download-', @config.temp_folder_path)
157+
@tempfile = tempfile
158+
end
159+
160+
@stream = []
161+
162+
# handle streaming Responses
163+
request.options.on_data = Proc.new do |chunk, overall_received_bytes|
164+
@stream << chunk
165+
end
166+
167+
end

modules/openapi-generator/src/main/resources/ruby-client/api_client_typhoeus_partial.mustache

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,41 @@
113113
end
114114
data
115115
end
116+
117+
# Save response body into a file in (the defined) temporary folder, using the filename
118+
# from the "Content-Disposition" header if provided, otherwise a random filename.
119+
# The response body is written to the file in chunks in order to handle files which
120+
# size is larger than maximum Ruby String or even larger than the maximum memory a Ruby
121+
# process can use.
122+
#
123+
# @see Configuration#temp_folder_path
124+
def download_file(request)
125+
tempfile = nil
126+
encoding = nil
127+
request.on_headers do |response|
128+
content_disposition = response.headers['Content-Disposition']
129+
if content_disposition && content_disposition =~ /filename=/i
130+
filename = content_disposition[/filename=['"]?([^'"\s]+)['"]?/, 1]
131+
prefix = sanitize_filename(filename)
132+
else
133+
prefix = 'download-'
134+
end
135+
prefix = prefix + '-' unless prefix.end_with?('-')
136+
encoding = response.body.encoding
137+
tempfile = Tempfile.open(prefix, @config.temp_folder_path, encoding: encoding)
138+
@tempfile = tempfile
139+
end
140+
request.on_body do |chunk|
141+
chunk.force_encoding(encoding)
142+
tempfile.write(chunk)
143+
end
144+
request.on_complete do |response|
145+
if tempfile
146+
tempfile.close
147+
@config.logger.info "Temp file written to #{tempfile.path}, please copy the file to a proper folder "\
148+
"with e.g. `FileUtils.cp(tempfile.path, '/new/file/path')` otherwise the temp file "\
149+
"will be deleted automatically with GC. It's also recommended to delete the temp file "\
150+
"explicitly with `tempfile.delete`"
151+
end
152+
end
153+
end

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,6 +1192,7 @@
11921192
<!-- clients -->
11931193
<!--<module>samples/client/petstore/perl</module>
11941194
<module>samples/client/petstore/bash</module>-->
1195+
<module>samples/client/petstore/ruby-faraday</module>
11951196
<module>samples/client/petstore/ruby</module>
11961197
<module>samples/client/petstore/c</module>
11971198
<module>samples/client/petstore/cpp-qt5</module>

samples/client/petstore/ruby-faraday/lib/petstore/api_client.rb

Lines changed: 47 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,44 @@ def build_request_body(header_params, form_params, body)
172172
data
173173
end
174174

175+
# Save response body into a file in (the defined) temporary folder, using the filename
176+
# from the "Content-Disposition" header if provided, otherwise a random filename.
177+
# The response body is written to the file in chunks in order to handle files which
178+
# size is larger than maximum Ruby String or even larger than the maximum memory a Ruby
179+
# process can use.
180+
#
181+
# @see Configuration#temp_folder_path
182+
def download_file(request)
183+
tempfile = nil
184+
encoding = nil
185+
request.headers do |response|
186+
content_disposition = response.headers['Content-Disposition']
187+
if content_disposition && content_disposition =~ /filename=/i
188+
filename = content_disposition[/filename=['"]?([^'"\s]+)['"]?/, 1]
189+
prefix = sanitize_filename(filename)
190+
else
191+
prefix = 'download-'
192+
end
193+
prefix = prefix + '-' unless prefix.end_with?('-')
194+
encoding = response.body.encoding
195+
tempfile = Tempfile.open(prefix, @config.temp_folder_path, encoding: encoding)
196+
@tempfile = tempfile
197+
end
198+
199+
if tempfile.nil?
200+
tempfile = Tempfile.open('download-', @config.temp_folder_path)
201+
@tempfile = tempfile
202+
end
203+
204+
@stream = []
205+
206+
# handle streaming Responses
207+
request.options.on_data = Proc.new do |chunk, overall_received_bytes|
208+
@stream << chunk
209+
end
210+
211+
end
212+
175213
# Check if the given MIME is a JSON MIME.
176214
# JSON MIME examples:
177215
# application/json
@@ -193,7 +231,15 @@ def deserialize(response, return_type)
193231

194232
# handle file downloading - return the File instance processed in request callbacks
195233
# note that response body is empty when the file is written in chunks in request on_body callback
196-
return @tempfile if return_type == 'File'
234+
if return_type == 'File'
235+
@tempfile.write(@stream)
236+
@tempfile.close
237+
@config.logger.info "Temp file written to #{@tempfile.path}, please copy the file to a proper folder "\
238+
"with e.g. `FileUtils.cp(tempfile.path, '/new/file/path')` otherwise the temp file "\
239+
"will be deleted automatically with GC. It's also recommended to delete the temp file "\
240+
"explicitly with `tempfile.delete`"
241+
return @tempfile
242+
end
197243

198244
return nil if body.nil? || body.empty?
199245

@@ -258,44 +304,6 @@ def convert_to_type(data, return_type)
258304
end
259305
end
260306

261-
# Save response body into a file in (the defined) temporary folder, using the filename
262-
# from the "Content-Disposition" header if provided, otherwise a random filename.
263-
# The response body is written to the file in chunks in order to handle files which
264-
# size is larger than maximum Ruby String or even larger than the maximum memory a Ruby
265-
# process can use.
266-
#
267-
# @see Configuration#temp_folder_path
268-
def download_file(request)
269-
tempfile = nil
270-
encoding = nil
271-
request.on_headers do |response|
272-
content_disposition = response.headers['Content-Disposition']
273-
if content_disposition && content_disposition =~ /filename=/i
274-
filename = content_disposition[/filename=['"]?([^'"\s]+)['"]?/, 1]
275-
prefix = sanitize_filename(filename)
276-
else
277-
prefix = 'download-'
278-
end
279-
prefix = prefix + '-' unless prefix.end_with?('-')
280-
encoding = response.body.encoding
281-
tempfile = Tempfile.open(prefix, @config.temp_folder_path, encoding: encoding)
282-
@tempfile = tempfile
283-
end
284-
request.on_body do |chunk|
285-
chunk.force_encoding(encoding)
286-
tempfile.write(chunk)
287-
end
288-
request.on_complete do |response|
289-
if tempfile
290-
tempfile.close
291-
@config.logger.info "Temp file written to #{tempfile.path}, please copy the file to a proper folder "\
292-
"with e.g. `FileUtils.cp(tempfile.path, '/new/file/path')` otherwise the temp file "\
293-
"will be deleted automatically with GC. It's also recommended to delete the temp file "\
294-
"explicitly with `tempfile.delete`"
295-
end
296-
end
297-
end
298-
299307
# Sanitize filename by removing path.
300308
# e.g. ../../sun.gif becomes sun.gif
301309
#

samples/client/petstore/ruby/lib/petstore/api_client.rb

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,44 @@ def build_request_body(header_params, form_params, body)
156156
data
157157
end
158158

159+
# Save response body into a file in (the defined) temporary folder, using the filename
160+
# from the "Content-Disposition" header if provided, otherwise a random filename.
161+
# The response body is written to the file in chunks in order to handle files which
162+
# size is larger than maximum Ruby String or even larger than the maximum memory a Ruby
163+
# process can use.
164+
#
165+
# @see Configuration#temp_folder_path
166+
def download_file(request)
167+
tempfile = nil
168+
encoding = nil
169+
request.on_headers do |response|
170+
content_disposition = response.headers['Content-Disposition']
171+
if content_disposition && content_disposition =~ /filename=/i
172+
filename = content_disposition[/filename=['"]?([^'"\s]+)['"]?/, 1]
173+
prefix = sanitize_filename(filename)
174+
else
175+
prefix = 'download-'
176+
end
177+
prefix = prefix + '-' unless prefix.end_with?('-')
178+
encoding = response.body.encoding
179+
tempfile = Tempfile.open(prefix, @config.temp_folder_path, encoding: encoding)
180+
@tempfile = tempfile
181+
end
182+
request.on_body do |chunk|
183+
chunk.force_encoding(encoding)
184+
tempfile.write(chunk)
185+
end
186+
request.on_complete do |response|
187+
if tempfile
188+
tempfile.close
189+
@config.logger.info "Temp file written to #{tempfile.path}, please copy the file to a proper folder "\
190+
"with e.g. `FileUtils.cp(tempfile.path, '/new/file/path')` otherwise the temp file "\
191+
"will be deleted automatically with GC. It's also recommended to delete the temp file "\
192+
"explicitly with `tempfile.delete`"
193+
end
194+
end
195+
end
196+
159197
# Check if the given MIME is a JSON MIME.
160198
# JSON MIME examples:
161199
# application/json
@@ -242,44 +280,6 @@ def convert_to_type(data, return_type)
242280
end
243281
end
244282

245-
# Save response body into a file in (the defined) temporary folder, using the filename
246-
# from the "Content-Disposition" header if provided, otherwise a random filename.
247-
# The response body is written to the file in chunks in order to handle files which
248-
# size is larger than maximum Ruby String or even larger than the maximum memory a Ruby
249-
# process can use.
250-
#
251-
# @see Configuration#temp_folder_path
252-
def download_file(request)
253-
tempfile = nil
254-
encoding = nil
255-
request.on_headers do |response|
256-
content_disposition = response.headers['Content-Disposition']
257-
if content_disposition && content_disposition =~ /filename=/i
258-
filename = content_disposition[/filename=['"]?([^'"\s]+)['"]?/, 1]
259-
prefix = sanitize_filename(filename)
260-
else
261-
prefix = 'download-'
262-
end
263-
prefix = prefix + '-' unless prefix.end_with?('-')
264-
encoding = response.body.encoding
265-
tempfile = Tempfile.open(prefix, @config.temp_folder_path, encoding: encoding)
266-
@tempfile = tempfile
267-
end
268-
request.on_body do |chunk|
269-
chunk.force_encoding(encoding)
270-
tempfile.write(chunk)
271-
end
272-
request.on_complete do |response|
273-
if tempfile
274-
tempfile.close
275-
@config.logger.info "Temp file written to #{tempfile.path}, please copy the file to a proper folder "\
276-
"with e.g. `FileUtils.cp(tempfile.path, '/new/file/path')` otherwise the temp file "\
277-
"will be deleted automatically with GC. It's also recommended to delete the temp file "\
278-
"explicitly with `tempfile.delete`"
279-
end
280-
end
281-
end
282-
283283
# Sanitize filename by removing path.
284284
# e.g. ../../sun.gif becomes sun.gif
285285
#

0 commit comments

Comments
 (0)