Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ on:
type: string
push:
branches: [ main, master ]
paths:
- 'lib/getstream_ruby/version.rb'
- 'getstream-ruby.gemspec'

jobs:
create-release-pr:
Expand Down
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

source 'https://rubygems.org'

gemspec
gemspec name: 'getstream-ruby'

group :development, :test do

Expand Down
1 change: 1 addition & 0 deletions getstream-ruby.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
81 changes: 81 additions & 0 deletions lib/getstream_ruby/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

require 'faraday'
require 'faraday/retry'
require 'faraday/multipart'
require 'json'
require 'jwt'
require_relative 'generated/base_model'
Expand Down Expand Up @@ -86,6 +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
return make_multipart_request(method, path, query_params, data) if multipart_request?(data)

response = @connection.send(method) do |req|

req.url path, query_params
Expand All @@ -105,6 +110,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,
Expand Down Expand Up @@ -159,6 +165,81 @@ 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
raise APIError, 'file name must be provided' if data.file.nil? || data.file.empty?

file_path = data.file
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),
)

# 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
35 changes: 35 additions & 0 deletions spec/integration/feed_integration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,41 @@

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 (in the same directory as the spec)
test_file_path = File.join(__dir__, 'upload-test.png')
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),
)

# 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
Expand Down
Binary file added spec/integration/upload-test.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.