Giter VIP home page Giter VIP logo

stream-ruby's Introduction

Official Ruby SDK for Stream Feeds

build Gem Version

Official Ruby API client for Stream Feeds, a web service for building scalable newsfeeds and activity streams.
Explore the docs »

Code Samples · Report Bug · Request Feature

📝 About Stream

💡 This is a library for the Feeds product. The Chat SDKs can be found here.

You can sign up for a Stream account at our Get Started page.

You can use this library to access Feeds API endpoints server-side.

For the client-side integrations (web and mobile) have a look at the JavaScript, iOS and Android SDK libraries (docs). This API Client project requires Ruby 2.5.x at a minimum.

💡 We have a Rails integration available here.

⚙️ Installation

gem install 'stream-ruby'

📚 Full documentation

Documentation for this Ruby client are available at the Stream website.

✨ Getting started

# Instantiate a new client to connect to us east API endpoint
require 'stream'
client = Stream::Client.new('YOUR_API_KEY', 'API_KEY_SECRET', 'APP_ID', location: 'us-east')

# Find your API keys here https://getstream.io/dashboard/

# Instantiate a feed object
user_feed_1 = client.feed('user', '1')

# Get activities from 5 to 10 (slow pagination)
result = user_feed_1.get(limit: 5, offset: 5)
# (Recommended & faster) Filter on an id less than the given UUID
result =
  user_feed_1.get(limit: 5, id_lt: 'e561de8f-00f1-11e4-b400-0cc47a024be0')

# Create a new activity
activity_data = { actor: 1, verb: 'tweet', object: 1, foreign_id: 'tweet:1' }
activity_response = user_feed_1.add_activity(activity_data)
# Create a bit more complex activity
activity_data = {
  actor: 1,
  verb: 'tweet',
  object: 1,
  foreign_id: 'tweet:1',
  course: { name: 'Golden Gate park', distance: 10 },
  participants: %w[Thierry Tommaso],
  started_at: DateTime.now
}
activity_response = user_feed_1.add_activity(activity_data)

# Update an existing activity (requires both :foreign_id and :time fields)
activity_data = {
  actor: 1,
  verb: 'tweet',
  object: 1,
  foreign_id: 'tweet:1',
  popularity: 100,
  time: '2016-05-13T16:12:30'
}
client.update_activity(activity_data)

# Update activities
client.update_activities([activity_data])

# Remove an activity by its id
user_feed_1.remove_activity('e561de8f-00f1-11e4-b400-0cc47a024be0')

# Remove activities by their foreign_id
user_feed_1.remove_activity('tweet:1', foreign_id: true)

# Follow another feed
user_feed_1.follow('flat', '42')

# Stop following another feed
user_feed_1.unfollow('flat', '42')

# Batch adding activities
activities = [
  [actor: '1', verb: 'tweet', object: '1'],
  [actor: '2', verb: 'like', object: '3']
]
user_feed_1.add_activities(activities)

# Batch following many feeds (requires ruby 2.1 or later)
follows = [
  { source: 'flat:1', target: 'user:1' },
  { source: 'flat:1', target: 'user:2' },
  { source: 'flat:1', target: 'user:3' }
]
client.follow_many(follows)

# Add an activity and push it to other feeds too using the `to` field
data = [actor_id: '1', verb: 'like', object_id: '3', to: %w[user:44 user:45]]
user_feed_1.add_activity(data)

# Updating parts of an activity
set = {
  'product.price': 19.99, 'shares': { 'facebook': '...', 'twitter': '...' }
}
unset = %w[daily_likes popularity]
# ...by ID
client.activity_partial_update(
  id: '54a60c1e-4ee3-494b-a1e3-50c06acb5ed4', set: set, unset: unset
)
# ...or by combination of foreign ID and time
client.activity_partial_update(
  foreign_id: 'product:123',
  time: '2016-11-10T13:20:00.000000',
  set: set,
  unset: unset
)

# Generating tokens for client side usage
token = user_feed_1.readonly_token

# Javascript client side feed initialization
user1 = client.feed('user', '1', '{{ token }}')

# Retrieve first 10 followers of a feed
user_feed_1.followers(0, 10)

# Retrieve followers from 10 to 20
user_feed_1.followers(10, 10)

# Retrieve 10 feeds followed by user_feed_1
user_feed_1.following(10)

# Retrieve 10 feeds followed by user_feed_1 starting from the 11th
user_feed_1.following(10, 10)

# Check if user_feed_1 follows specific feeds
user_feed_1.following(0, 2, filter = %w[user:42 user:43])

# Add one activity to many feeds in one request
feeds = %w[flat:1 flat:2 flat:3 flat:4]
activity = {
  actor: 'User:2', verb: 'pin', object: 'Place:42', target: 'Board:1'
}
client.add_to_many(activity, feeds)

# Retrive open graph information
client.og('https://google.com')

✍️ Contributing

Project is licensed under the BSD 3-Clause.

We welcome code changes that improve this library or fix a problem, please make sure to follow all best practices and add tests if applicable before submitting a Pull Request on Github. We are very happy to merge your code in the official repository. Make sure to sign our Contributor License Agreement (CLA) first. See our license file for more details.

🧑‍💻 We are hiring!

We've recently closed a $38 million Series B funding round and we keep actively growing. Our APIs are used by more than a billion end-users, and you'll have a chance to make a huge impact on the product within a team of the strongest engineers all over the world.

Check out our current openings and apply via Stream's website.

stream-ruby's People

Contributors

aldesantis avatar capripot avatar dwightgunning avatar fcastellanos avatar ferhatelmas avatar github-actions[bot] avatar groyoh avatar iandouglas avatar itsmeadi avatar jeltef avatar jkeam avatar kennym avatar linell avatar marco-ulge avatar matthisk avatar mircea-cosbuc avatar mscoutermarsh avatar parndt avatar peterdeme avatar pterk avatar quekshuy avatar rossta avatar rromanchuk avatar ruggi avatar tbarbugli avatar tschellenbach avatar vagruchi avatar victorfgs avatar volksport avatar wyattisimo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

stream-ruby's Issues

DELETE operations need feature parity

I understand that there are probably technical complexities which make these operations very difficult to provide

For example, this needs parity with unfollow_many(follows)

# Batch following many feeds (requires ruby 2.1 or later)
follows = [
  {:source => 'flat:1', :target => 'user:1'},
  {:source => 'flat:1', :target => 'user:2'},
  {:source => 'flat:1', :target => 'user:3'},
]
client.follow_many(follows)

and user_feed_1.remove_activities(activities)

# Batch adding activities
activities = [
    [:actor => '1', :verb => 'tweet', :object => '1'],
    [:actor => '2', :verb => 'like', :object => '3']
]
user_feed_1.add_activities(activities)

I've seen a lot of questions where the response is usually ¯_(ツ)_/¯, or just to orphan the data. The biggest use case is user deletion/deactivation. To actually delete the users footprint is an incredibly painful operation with thousands and thousands of api calls required depending on the user.

expose signing

the readme and code should explain how to generate a token for usage in the client side API

Asynchronous HTTP client

Are there any plans on implementing an asynchronous HTTP client, as right now the client can be a real bottleneck in a web application if requests aren't moved into the background?

[BUG] Stream client Feed#followers returning HTTParty object not array

I am having this weird issue I can only reproduce on an app deployed with Heroku.

I am calling the Stream::Feed#followers and Stream::Feed#following methods, but they return an HTTParty object instead of an array as it does on my local machine.

E.g.:

irb(main):002:0> STREAM_CLIENT.feed("user", "139052").following
=> #<HTTParty::Response:0x7f05820da1b8 parsed_response={"duration"=>"12ms", "results"=>[]}, @response=#<Net::HTTPOK 200 OK readbody=true>, @headers={"allow"=>["GET, POST, HEAD, OPTIONS"], "cache-control"=>["max-age=0"], "content-type"=>["application/json"], "date"=>["Fri, 15 Jan 2016 00:08:51 GMT"], "server"=>["nginx/1.4.6 (Ubuntu)"], "vary"=>["Accept"], "content-length"=>["35"], "connection"=>["Close"]}>

Any ideas of what can be going wrong?

Need clarification on add to many

Let's say we're in a game lobby, and i'm modeling comment activity. Normally when a user would leave a comment i would add that activity to their user feed, and then utilize the to: field to add this activity to relevant users (in this scenario all users in the lobby).

So now anyone following that user will see this story in their newsfeed, and those directly involved will also get the notifications.

Now in some situations, i do not send this activity to the users feed because because of a privacy preference, so instead i take the activity and use the add_to_many (where as before i would attach it to the to field) and send them to the notification feeds, which is what i want.

https://stackoverflow.com/questions/46435072/does-batch-activity-add-trigger-on-firehose

After reading this it looks like my notification firehose don't get these updates per design, although I still don't really get why.

So instead of this:

to_array = push_users.pluck(:id).map {|id| "notification:#{id}" }
stream_client.add_to_many(activity_data, to_array)

I now have to do this in order to get the firehose.

push_users.pluck(:id).each do |id| 
  notification_feed = stream_client.feed('notification', id)  
  notification_feed.add_activity(activity_data)  
end

The problem is, I have decoupled my push logic and made the getstream firehose my clearing house for literal APN pushes, so any activity i want to push to multiple notification feeds has to be done individually, given that the activity cannot be added to the users feed.

Can we relax the JWT dependency a bit?

Bundler could not find compatible versions for gem "jwt":
  In Gemfile:
    firebase_id_token was resolved to 2.3.0, which depends on
      jwt (>= 2.1.0, ~> 2.1)

    stream-ruby was resolved to 2.7.0, which depends on
      jwt (~> 1.5.2)

795: unexpected token at

Using the following code to post to my stream, while in @text parameter there is a text in Hebrew submitted by users:

activity_data = {:actor => @user_id, :verb => 'post', :object => 1, :post => @text}
activity_response = @user_feed.add_activity(activity_data)

Every time a text has break line in it, it rises the following exception on your side:
795: unexpected token at '{ "text" : "ש��� ��ר.......!" }'

Timeouts

Users have reported Net::ReadTimeout or Net::OpenTimeout in rare cases. Maybe our connection defaults can be improved?

Tests are not passing

Some tests are consistently failing (while others are inconsistently passing). Either the service is having inconsistencies/problems, or perhaps the lack of test tear downs/mocks is causing some of these failures?

Firstly, having one key/secret combo means that anyone running the tests simultaneously are creating race conditions that could potentially cause them to fail (depending on the current and future test logic).

Second, the overlap of feed keys makes this even more likely. You could start with randomized feed keys in the setup (while still using the live service), and completely remove all data from them during the teardown, which would make a collision less likely.

But ultimately, the library shouldn't be testing the actual API (they should have their own tests), it should be testing the input/output. That probably means mocking the actual API results (maybe even programmatically during the setup).

Just some thoughts...

-r

Best way to initialize?

Hi there! This is probably a very newbie question but I searched like whole internet and can't figure it out. So, I have stream.rb in config/initializers with:

require 'stream'
stream = Stream::Client.new(Rails.application.secrets.stream_key, Rails.application.secrets.stream_secret, Rails.application.secrets.stream_id, location: 'eu-central')

The problems is reaching this stream variable from controllers/models. Is there like a common practice on how to do this? I know this is probably simpler with stream-rails but it doesn't seem like its working with rails5 so I decided to go with ruby version.

Warning on the regular expression format

Reported by A David Thyresson (Stattleship.com)

/Users/dthyresson/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/stream-ruby-2.1.2/lib/stream/client.rb:8: warning: nested repeat operator '?' and '' was replaced with '' in regular expression

08:04:13 web.1 | [32068] * Preloading application
08:04:15 web.1 | /Users/dthyresson/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/stream-ruby-2.1.2/lib/stream/client.rb:8: warning: nested repeat operator '?' and '' was replaced with '' in reg

JSON Parse Error

For my rails-api Rails App I am getting the following error with both the stream-ruby and stream-rails gems.

Environment:

  • Ruby 2.3.1
  • Rails 5.0.0.1

This error occurs whenever trying to create an activity, either as part of creating an ActiveRecord Object when include StreamRails::Activity is on the model, or simply creating an activity in the console with:

response = user_feed.add_activity(data)

JSON::ParserError: 743: unexpected token at ''
	from /Users/tom/.gem/ruby/2.3.1/gems/json-2.0.2/lib/json/common.rb:156:in `parse'
	from /Users/tom/.gem/ruby/2.3.1/gems/json-2.0.2/lib/json/common.rb:156:in `parse'
	from /Users/tom/.gem/ruby/2.3.1/gems/stream-ruby-2.6.0/lib/stream/client.rb:190:in `_build_error_message'
	from /Users/tom/.gem/ruby/2.3.1/gems/stream-ruby-2.6.0/lib/stream/client.rb:177:in `block in call'
	from /Users/tom/.gem/ruby/2.3.1/gems/faraday-0.15.0/lib/faraday/response.rb:61:in `on_complete'
	from /Users/tom/.gem/ruby/2.3.1/gems/stream-ruby-2.6.0/lib/stream/client.rb:166:in `call'
	from /Users/tom/.gem/ruby/2.3.1/gems/faraday-0.15.0/lib/faraday/rack_builder.rb:143:in `build_response'
	from /Users/tom/.gem/ruby/2.3.1/gems/faraday-0.15.0/lib/faraday/connection.rb:387:in `run_request'
	from /Users/tom/.gem/ruby/2.3.1/gems/stream-ruby-2.6.0/lib/stream/client.rb:150:in `make_http_request'
	from /Users/tom/.gem/ruby/2.3.1/gems/stream-ruby-2.6.0/lib/stream/client.rb:115:in `make_request'
	from /Users/tom/.gem/ruby/2.3.1/gems/stream-ruby-2.6.0/lib/stream/feed.rb:67:in `add_activity'
	from (irb):21
	from /Users/tom/.gem/ruby/2.3.1/gems/railties-5.0.0.1/lib/rails/commands/console.rb:65:in `start'
	from /Users/tom/.gem/ruby/2.3.1/gems/railties-5.0.0.1/lib/rails/commands/console_helper.rb:9:in `start'
	from /Users/tom/.gem/ruby/2.3.1/gems/railties-5.0.0.1/lib/rails/commands/commands_tasks.rb:78:in `console'
	from /Users/tom/.gem/ruby/2.3.1/gems/railties-5.0.0.1/lib/rails/commands/commands_tasks.rb:49:in `run_command!'
... 6 levels...
	from /Users/tom/.gem/ruby/2.3.1/gems/activesupport-5.0.0.1/lib/active_support/dependencies.rb:287:in `load'
	from /Users/tom/.gem/ruby/2.3.1/gems/activesupport-5.0.0.1/lib/active_support/dependencies.rb:287:in `block in load'
	from /Users/tom/.gem/ruby/2.3.1/gems/activesupport-5.0.0.1/lib/active_support/dependencies.rb:259:in `load_dependency'
	from /Users/tom/.gem/ruby/2.3.1/gems/activesupport-5.0.0.1/lib/active_support/dependencies.rb:287:in `load'
	from /Users/tom/.gem/ruby/2.3.1/gems/spring-1.7.2/lib/spring/commands/rails.rb:6:in `call'
	from /Users/tom/.gem/ruby/2.3.1/gems/spring-1.7.2/lib/spring/command_wrapper.rb:38:in `call'
	from /Users/tom/.gem/ruby/2.3.1/gems/spring-1.7.2/lib/spring/application.rb:191:in `block in serve'
	from /Users/tom/.gem/ruby/2.3.1/gems/spring-1.7.2/lib/spring/application.rb:161:in `fork'
	from /Users/tom/.gem/ruby/2.3.1/gems/spring-1.7.2/lib/spring/application.rb:161:in `serve'
	from /Users/tom/.gem/ruby/2.3.1/gems/spring-1.7.2/lib/spring/application.rb:131:in `block in run'
	from /Users/tom/.gem/ruby/2.3.1/gems/spring-1.7.2/lib/spring/application.rb:125:in `loop'
	from /Users/tom/.gem/ruby/2.3.1/gems/spring-1.7.2/lib/spring/application.rb:125:in `run'
	from /Users/tom/.gem/ruby/2.3.1/gems/spring-1.7.2/lib/spring/application/boot.rb:19:in `<top (required)>'
	from /Users/tom/.rubies/ruby-2.3.1/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require'
	from /Users/tom/.rubies/ruby-2.3.1/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require'
	from -e:1:in `<main>'

get feed request responds with HTML instead of json

I am trying to retrieve a feed from getstream.io but it seems that the client is having trouble parsing the response because it is in HTML. Also not sure why the response says that the resource is not found, when the feed exists checking from the feed explorer on the getstream dashboard.

(pry)> stream_client.feed('user', self.id).get
JSON::ParserError: lexical error: invalid char in json text.
                                   <html>  <head><title>302 Found<
                 (right here) ------^

I tried prying the gem and the error happens at /lib/stream/feed.rb upon the @client.make_request call

def get(params = {})
  uri = "/feed/#{@feed_url}/"
  if params[:mark_read] && params[:mark_read].is_a?(Array)
    params[:mark_read] = params[:mark_read].join(',')
  end
  if params[:mark_seen] && params[:mark_seen].is_a?(Array)
    params[:mark_seen] = params[:mark_seen].join(',')
  end
  auth_token = create_jwt_token('feed', 'read')

  @client.make_request(:get, uri, auth_token, params)
end

consistency followFeed vs follow

python, js and ruby do it like this

Get the feed object for aggregated 3 and follow user 1

aggregated3 = client.feed('aggregated:3')
aggregated3.follow('user:1')

vs

// Get the feed object for aggregated 3 and follow user 1
$aggregated3 = $client->feed('aggregated:3');
$aggregated3->followFeed('user:1');

auth naming

secret = the api secret
token = a token for accessing a feed (generated from the secret)
authorization = the header sent for authorization combining the feed name and the token

validation error from APIs

seems like in some cases we don't include the full error message from the APIs (eg. when sending an invalid verb such as "this_is_not_good")

Batch follow example should be in separate hashes

Batch following many feeds (requires ruby 2.1 or later)

follows = [{
:source => 'flat:1', :target => 'user:1',
:source => 'flat:1', :target => 'user:2',
:source => 'flat:1', :target => 'user:3'
}]

should be:
follows = [
{:source => 'timeline:1', :target => 'user:1'},
{:source => 'timeline:1', :target => 'user:2'},
{:source => 'timeline:1', :target => 'user:3'}
]

Otherwise the keys will just get overwritten.

Able to loosen gem requirements?

Are you able to allow faraday versions >= 0.9, and not limit jwt to exactly 1.5.2 (maybe allowing >= 1.0)?

I've had to go back to to version 2.2.5 version of this gem to avoid jwt and faraday conflicts with other gems we are using.

Unfollow keep history parameters is failing

self_feed.unfollow(feed_name, 123, keep_history: true) is returning error
*** ArgumentError Exception: wrong number of arguments (3 for 2).

In the method declaration, def unfollow(target_feed_slug, target_user_id, keep_history = false) no longer has the symbol argument so the documentation is outdated; however, running this:

self_feed.unfollow(feed_name, 123, true) still returns the exact same argument error.
*** ArgumentError Exception: wrong number of arguments (3 for 2)

Without the keep_history argument, I receive a proper response from the API.
#<HTTParty::Response:0x7fa8d827a910 parsed_response={"duration"=>"44ms"}, @response=#<Net::HTTPOK 200 OK readbody=true>, @headers={"allow"=>["DELETE, OPTIONS"], "content-type"=>["application/json"], "date"=>["Thu, 29 Sep 2016 20:01:17 GMT"], "server"=>["nginx"], "vary"=>["Accept"], "content-length"=>["20"], "connection"=>["Close"]}>

Activities migration

Is there any way to update all activities? I'd like to add an information in them, but I don't know how to retrieve/update all

Hash.to_json

Examples are failing with NoMethodError: undefined method `to_json' for {}:Hash.
Probably you should refer to ActiveSupport dependency somewhere

Gem 'jwt' breaks Ruby 1.9.3 support

The latest commits have broken support for Ruby 1.9.3, because the gem jwt doesn't support it. See here: https://travis-ci.org/GetStream/stream-ruby/jobs/115152833

/home/travis/.rvm/rubies/ruby-1.9.3-p551/bin/ruby -S rspec ./spec/client_spec.rb ./spec/errors_spec.rb ./spec/feed_spec.rb ./spec/integration_spec.rb ./spec/signer_spec.rb ./spec/stream_spec.rb
/home/travis/.rvm/gems/ruby-1.9.3-p551/gems/jwt-1.5.3/lib/jwt/decode.rb:2:in `require': /home/travis/.rvm/gems/ruby-1.9.3-p551/gems/jwt-1.5.3/lib/jwt/verify.rb:7: unknown type of %string (SyntaxError)
      %i[verify_aud verify_expiration ...
         ^
/home/travis/.rvm/gems/ruby-1.9.3-p551/gems/jwt-1.5.3/lib/jwt/verify.rb:7: syntax error, unexpected $end
      %i[verify_aud verify_expiration ...
         ^
    from /home/travis/.rvm/gems/ruby-1.9.3-p551/gems/jwt-1.5.3/lib/jwt/decode.rb:2:in `<top (required)>'
    from /home/travis/.rvm/gems/ruby-1.9.3-p551/gems/jwt-1.5.3/lib/jwt.rb:3:in `require'
    from /home/travis/.rvm/gems/ruby-1.9.3-p551/gems/jwt-1.5.3/lib/jwt.rb:3:in `<top (required)>'

Delete feed API does not exist

The readme and gym support a Feed#delete method but I can't find it in the docs and it 404s.

Stream::StreamApiResponseException: DELETE https://eu-west-api.stream-io-api.com/api/v1.0/feed/gym/511/?api_key=[redacted]: 404: url not found

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.