From a73c1179141a94a0ab6f8d3097205125c1d4e002 Mon Sep 17 00:00:00 2001 From: Aditya Agarwal Date: Thu, 11 Dec 2025 15:56:09 +0100 Subject: [PATCH 1/5] fix upload endpoint --- .github/workflows/release.yml | 3 - lib/getstream_ruby/client.rb | 85 +++++++++++++++++++++++ spec/integration/feed_integration_spec.rb | 33 +++++++++ 3 files changed, 118 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index da82256..07981d4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,9 +18,6 @@ on: type: string push: branches: [ main, master ] - paths: - - 'lib/getstream_ruby/version.rb' - - 'getstream-ruby.gemspec' jobs: create-release-pr: diff --git a/lib/getstream_ruby/client.rb b/lib/getstream_ruby/client.rb index 3d2c504..b496d3e 100644 --- a/lib/getstream_ruby/client.rb +++ b/lib/getstream_ruby/client.rb @@ -2,6 +2,7 @@ require 'faraday' require 'faraday/retry' +require 'faraday/multipart' require 'json' require 'jwt' require_relative 'generated/base_model' @@ -86,6 +87,12 @@ def make_request(method, path, query_params: nil, body: nil) def request(method, path, data = {}) # Add API key to query parameters query_params = { api_key: @configuration.api_key } + + # Check if this is a file upload request that needs multipart + if multipart_request?(data) + return make_multipart_request(method, path, query_params, data) + end + response = @connection.send(method) do |req| req.url path, query_params @@ -105,6 +112,7 @@ def request(method, path, data = {}) def build_connection Faraday.new(url: @configuration.base_url) do |conn| + conn.request :multipart conn.request :retry, { max: 3, interval: 0.05, @@ -159,6 +167,83 @@ def handle_response(response) end end + def multipart_request?(data) + return false if data.nil? || data == {} + + # Check if data is a FileUploadRequest or ImageUploadRequest + data.is_a?(GetStream::Generated::Models::FileUploadRequest) || + data.is_a?(GetStream::Generated::Models::ImageUploadRequest) + end + + def make_multipart_request(method, path, query_params, data) + # Build multipart form data + payload = {} + + # Handle file field + if data.file.nil? || data.file.empty? + raise APIError, "file name must be provided" + end + + file_path = data.file + unless File.exist?(file_path) + raise APIError, "file not found: #{file_path}" + end + + # Determine content type + content_type = detect_content_type(file_path) + + # Add file as multipart (FilePart handles file opening/closing) + payload[:file] = Faraday::Multipart::FilePart.new( + file_path, + content_type, + File.basename(file_path) + ) + + # Add user field if present (as JSON string) + if data.user + user_json = data.user.to_json + payload[:user] = user_json + end + + # Add upload_sizes field for ImageUploadRequest (as JSON string) + if data.is_a?(GetStream::Generated::Models::ImageUploadRequest) && data.upload_sizes + upload_sizes_json = data.upload_sizes.to_json + payload[:upload_sizes] = upload_sizes_json + end + + response = @connection.send(method) do |req| + req.url path, query_params + req.headers['Authorization'] = generate_auth_header + req.headers['stream-auth-type'] = 'jwt' + req.headers['X-Stream-Client'] = user_agent + req.body = payload + end + + handle_response(response) + rescue Faraday::Error => e + raise APIError, "Request failed: #{e.message}" + end + + def detect_content_type(file_path) + ext = File.extname(file_path).downcase + case ext + when '.png' + 'image/png' + when '.jpg', '.jpeg' + 'image/jpeg' + when '.gif' + 'image/gif' + when '.pdf' + 'application/pdf' + when '.txt' + 'text/plain' + when '.json' + 'application/json' + else + 'application/octet-stream' + end + end + end end diff --git a/spec/integration/feed_integration_spec.rb b/spec/integration/feed_integration_spec.rb index acdec80..53236b8 100644 --- a/spec/integration/feed_integration_spec.rb +++ b/spec/integration/feed_integration_spec.rb @@ -825,6 +825,39 @@ end + describe 'File Upload' do + + it 'uploads a file using multipart form data' do + puts "\nšŸ“¤ Testing file upload..." + + # Get the path to the test file (relative to project root) + test_file_path = File.join(__dir__, '..', '..', 'test', 'upload-test.png') + expect(File.exist?(test_file_path)).to be true, "Test file not found: #{test_file_path}" + + # Create file upload request + file_upload_request = GetStream::Generated::Models::FileUploadRequest.new( + file: test_file_path, + user: GetStream::Generated::Models::OnlyUserID.new(id: test_user_id_1) + ) + + # Upload the file + upload_response = client.common.upload_file(file_upload_request) + + expect(upload_response).to be_a(GetStreamRuby::StreamResponse) + expect(upload_response.file).not_to be_nil + expect(upload_response.file).to be_a(String) + expect(upload_response.file).not_to be_empty + + puts "āœ… File uploaded successfully" + puts " File URL: #{upload_response.file}" + puts " Thumbnail URL: #{upload_response.thumb_url}" if upload_response.thumb_url + + # Verify the URL is a valid URL + expect(upload_response.file).to match(/^https?:\/\//) + end + + end + describe 'Real World Usage Demo' do it 'demonstrates real-world usage patterns' do From e252574c75253269c8064faa201b8e13e9a93539 Mon Sep 17 00:00:00 2001 From: Aditya Agarwal Date: Thu, 11 Dec 2025 16:06:02 +0100 Subject: [PATCH 2/5] fix import --- Gemfile | 2 +- getstream-ruby.gemspec | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 0f70349..5e8accf 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' -gemspec +gemspec name: 'getstream-ruby' group :development, :test do diff --git a/getstream-ruby.gemspec b/getstream-ruby.gemspec index b952e1d..abd861c 100644 --- a/getstream-ruby.gemspec +++ b/getstream-ruby.gemspec @@ -20,6 +20,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'dotenv', '~> 2.0' spec.add_dependency 'faraday', '~> 2.0' + spec.add_dependency 'faraday-multipart', '~> 1.0' spec.add_dependency 'faraday-retry', '~> 2.0' spec.add_dependency 'json', '~> 2.0' spec.add_dependency 'jwt', '~> 2.0' From 2e0ceb2d1c2f182da5b038aec925462d3a5fbbcb Mon Sep 17 00:00:00 2001 From: Aditya Agarwal Date: Thu, 11 Dec 2025 16:08:51 +0100 Subject: [PATCH 3/5] add sample upload --- spec/integration/upload-test.png | Bin 0 -> 19585 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 spec/integration/upload-test.png diff --git a/spec/integration/upload-test.png b/spec/integration/upload-test.png new file mode 100644 index 0000000000000000000000000000000000000000..c1876c33db9b213f1902928b114ad94fc8aa7d59 GIT binary patch literal 19585 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 literal 0 HcmV?d00001 From af72a995ac47549c02fe4d08494bbf13c4c79f09 Mon Sep 17 00:00:00 2001 From: Aditya Agarwal Date: Thu, 11 Dec 2025 16:14:32 +0100 Subject: [PATCH 4/5] fix test --- spec/integration/feed_integration_spec.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/spec/integration/feed_integration_spec.rb b/spec/integration/feed_integration_spec.rb index 53236b8..42b474f 100644 --- a/spec/integration/feed_integration_spec.rb +++ b/spec/integration/feed_integration_spec.rb @@ -830,9 +830,11 @@ it 'uploads a file using multipart form data' do puts "\nšŸ“¤ Testing file upload..." - # Get the path to the test file (relative to project root) - test_file_path = File.join(__dir__, '..', '..', 'test', 'upload-test.png') - expect(File.exist?(test_file_path)).to be true, "Test file not found: #{test_file_path}" + # Get the path to the test file (in the same directory as the spec) + test_file_path = File.join(__dir__, 'upload-test.png') + unless File.exist?(test_file_path) + raise "Test file not found: #{test_file_path}" + end # Create file upload request file_upload_request = GetStream::Generated::Models::FileUploadRequest.new( From 1ee6e8b5baa74fb61ebfb1c024ab38df919315c6 Mon Sep 17 00:00:00 2001 From: Aditya Agarwal Date: Thu, 11 Dec 2025 16:16:53 +0100 Subject: [PATCH 5/5] fix lint --- lib/getstream_ruby/client.rb | 38 ++++++++++------------- spec/integration/feed_integration_spec.rb | 14 ++++----- 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/lib/getstream_ruby/client.rb b/lib/getstream_ruby/client.rb index b496d3e..f7bc9a2 100644 --- a/lib/getstream_ruby/client.rb +++ b/lib/getstream_ruby/client.rb @@ -87,12 +87,10 @@ def make_request(method, path, query_params: nil, body: nil) def request(method, path, data = {}) # Add API key to query parameters query_params = { api_key: @configuration.api_key } - + # Check if this is a file upload request that needs multipart - if multipart_request?(data) - return make_multipart_request(method, path, query_params, data) - end - + return make_multipart_request(method, path, query_params, data) if multipart_request?(data) + response = @connection.send(method) do |req| req.url path, query_params @@ -169,7 +167,7 @@ def handle_response(response) def multipart_request?(data) return false if data.nil? || data == {} - + # Check if data is a FileUploadRequest or ImageUploadRequest data.is_a?(GetStream::Generated::Models::FileUploadRequest) || data.is_a?(GetStream::Generated::Models::ImageUploadRequest) @@ -178,47 +176,45 @@ def multipart_request?(data) def make_multipart_request(method, path, query_params, data) # Build multipart form data payload = {} - + # Handle file field - if data.file.nil? || data.file.empty? - raise APIError, "file name must be provided" - end - + raise APIError, 'file name must be provided' if data.file.nil? || data.file.empty? + file_path = data.file - unless File.exist?(file_path) - raise APIError, "file not found: #{file_path}" - end - + raise APIError, "file not found: #{file_path}" unless File.exist?(file_path) + # Determine content type content_type = detect_content_type(file_path) - + # Add file as multipart (FilePart handles file opening/closing) payload[:file] = Faraday::Multipart::FilePart.new( file_path, content_type, - File.basename(file_path) + File.basename(file_path), ) - + # Add user field if present (as JSON string) if data.user user_json = data.user.to_json payload[:user] = user_json end - + # Add upload_sizes field for ImageUploadRequest (as JSON string) if data.is_a?(GetStream::Generated::Models::ImageUploadRequest) && data.upload_sizes upload_sizes_json = data.upload_sizes.to_json payload[:upload_sizes] = upload_sizes_json end - + response = @connection.send(method) do |req| + req.url path, query_params req.headers['Authorization'] = generate_auth_header req.headers['stream-auth-type'] = 'jwt' req.headers['X-Stream-Client'] = user_agent req.body = payload + end - + handle_response(response) rescue Faraday::Error => e raise APIError, "Request failed: #{e.message}" diff --git a/spec/integration/feed_integration_spec.rb b/spec/integration/feed_integration_spec.rb index 42b474f..703fff9 100644 --- a/spec/integration/feed_integration_spec.rb +++ b/spec/integration/feed_integration_spec.rb @@ -828,34 +828,34 @@ describe 'File Upload' do it 'uploads a file using multipart form data' do + puts "\nšŸ“¤ Testing file upload..." # Get the path to the test file (in the same directory as the spec) test_file_path = File.join(__dir__, 'upload-test.png') - unless File.exist?(test_file_path) - raise "Test file not found: #{test_file_path}" - end + raise "Test file not found: #{test_file_path}" unless File.exist?(test_file_path) # Create file upload request file_upload_request = GetStream::Generated::Models::FileUploadRequest.new( file: test_file_path, - user: GetStream::Generated::Models::OnlyUserID.new(id: test_user_id_1) + user: GetStream::Generated::Models::OnlyUserID.new(id: test_user_id_1), ) # Upload the file upload_response = client.common.upload_file(file_upload_request) - + expect(upload_response).to be_a(GetStreamRuby::StreamResponse) expect(upload_response.file).not_to be_nil expect(upload_response.file).to be_a(String) expect(upload_response.file).not_to be_empty - - puts "āœ… File uploaded successfully" + + puts 'āœ… File uploaded successfully' puts " File URL: #{upload_response.file}" puts " Thumbnail URL: #{upload_response.thumb_url}" if upload_response.thumb_url # Verify the URL is a valid URL expect(upload_response.file).to match(/^https?:\/\//) + end end