Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add support for the channels REST API endpoint.

  • Loading branch information...
commit 419caebafc3f6566e29a76a4b3a5cdd7b54b5fc0 1 parent 82341d2
@tristandunn authored
View
72 features/channel.feature
@@ -0,0 +1,72 @@
+@javascript
+Feature: Requesting channel information
+
+ Background:
+ Given I am connected
+
+ Scenario: Requesting all channels
+ When I request "/channels"
+ Then I should receive the following JSON:
+ """
+ { "channels" : {} }
+ """
+ When I subscribe to the "chat-message" channel
+ And I request "/channels"
+ Then I should receive the following JSON:
+ """
+ { "channels" : {
+ "chat-message" : {}
+ }
+ }
+ """
+ When I subscribe to the "presence-game-1" channel with presence events
+ And I request "/channels"
+ Then I should receive the following JSON:
+ """
+ { "channels" : {
+ "chat-message" : {},
+ "presence-game-1" : {}
+ }
+ }
+ """
+
+ Scenario: Requesting all channels, with a filter
+ Given I subscribe to the "chat-message" channel
+ And I subscribe to the "presence-game-1" channel with presence events
+ When I request "/channels" with the following options:
+ | filter_by_prefix |
+ | chat |
+ Then I should receive the following JSON:
+ """
+ { "channels" : {
+ "chat-message" : {}
+ }
+ }
+ """
+
+ Scenario: Requesting all channels, with a valid filter and info attributes
+ Given I subscribe to the "chat-message" channel
+ And I subscribe to the "presence-game-1" channel with presence events
+ When I request "/channels" with the following options:
+ | filter_by_prefix | info |
+ | presence- | user_count |
+ Then I should receive the following JSON:
+ """
+ { "channels" : {
+ "presence-game-1" : {
+ "user_count" : 1
+ }
+ }
+ }
+ """
+
+ Scenario: Requesting all channels, with an invalid filter and info attributes
+ Given I subscribe to the "chat-message" channel
+ And I subscribe to the "presence-game-1" channel with presence events
+ When I request "/channels" with the following options:
+ | filter_by_prefix | info |
+ | chat- | user_count |
+ Then I should receive the following error:
+ """
+ user_count may only be requested for presence channels - please supply filter_by_prefix begining with presence-
+ """
View
29 features/step_definitions/api_steps.rb
@@ -0,0 +1,29 @@
+When %{I request "$path"} do |path|
+ wait do
+ @response = Pusher.get(path)
+ end
+end
+
+When %{I request "$path" with the following options:} do |path, table|
+ wait do
+ begin
+ @response = Pusher.get(path, table.hashes.first)
+ rescue => error
+ @error = error
+ end
+ end
+end
+
+Then %{I should receive the following JSON:} do |string|
+ expected = MultiJson.load(string)
+ expected = expected.inject({}) do |result, (key, value)|
+ result[key.to_sym] = value
+ result
+ end
+
+ @response.should == expected
+end
+
+Then %{I should receive the following error:} do |string|
+ @error.message.should include(string.strip)
+end
View
56 lib/pusher-fake/server/application.rb
@@ -1,20 +1,66 @@
module PusherFake
module Server
class Application
- # Process an API request by emitting the event with data to the
- # requested channels.
+ CHANNEL_FILTER_ERROR = "user_count may only be requested for presence channels - " +
+ "please supply filter_by_prefix begining with presence-".freeze
+
+ # Process an API request.
#
# @param [Hash] environment The request environment.
# @return [Rack::Response] A successful response.
def self.call(environment)
- request = Rack::Request.new(environment)
- event = MultiJson.load(request.body.read)
+ id = PusherFake.configuration.app_id
+ request = Rack::Request.new(environment)
+ response = case request.path
+ when %r{/apps/#{id}/events}
+ events(request)
+ when %r{/apps/#{id}/channels}
+ channels(request)
+ end
+
+ Rack::Response.new(MultiJson.dump(response)).finish
+ rescue => error
+ Rack::Response.new(error.message, 400).finish
+ end
+ # Emit an event with data to the requested channel(s).
+ #
+ # @param [Rack::Request] request The HTTP request.
+ # @return [Hash] An empty hash.
+ def self.events(request)
+ event = MultiJson.load(request.body.read)
event["channels"].each do |channel|
Channel.factory(channel).emit(event["name"], event["data"], socket_id: event["socket_id"])
end
- Rack::Response.new("{}").finish
+ {}
+ end
+
+ # Returns a hash of occupied channels, optionally filtering with a prefix.
+ #
+ # When filtering to presence chanenls, the user count maybe also be requested.
+ #
+ # @param [Rack::Request] request The HTTP request.
+ # @return [Hash] A hash of occupied channels.
+ def self.channels(request)
+ info = request.params["info"].to_s.split(",")
+ prefix = request.params["filter_by_prefix"].to_s
+
+ if info.include?("user_count") && prefix != "presence-"
+ raise CHANNEL_FILTER_ERROR
+ end
+
+ filter = Regexp.new(%r{\A#{prefix}})
+ channels = PusherFake::Channel.channels || {}
+ channels.inject({ channels: {} }) do |result, (name, channel)|
+ unless filter && name !~ filter
+ channels = result[:channels]
+ channels[name] = {}
+ channels[name][:user_count] = channel.connections.length if info.include?("user_count")
+ end
+
+ result
+ end
end
end
end
View
218 spec/lib/pusher-fake/server/application_spec.rb
@@ -1,17 +1,10 @@
require "spec_helper"
-describe PusherFake::Server::Application, ".call" do
- let(:body) { stub(read: json) }
- let(:data) { mock }
- let(:json) { mock }
- let(:name) { "event-name" }
- let(:event) { { "channels" => channels, "name" => name, "data" => data, "socket_id" => socket_id } }
- let(:request) { stub(body: body) }
- let(:channels) { ["channel-1", "channel-2"] }
+shared_examples_for "an API request" do
+ let(:hash) { mock }
+ let(:string) { mock }
+ let(:request) { stub(path: path) }
let(:response) { mock }
- let(:channel_1) { stub(emit: true) }
- let(:channel_2) { stub(emit: true) }
- let(:socket_id) { stub }
let(:environment) { mock }
subject { PusherFake::Server::Application }
@@ -19,11 +12,9 @@
before do
response.stubs(finish: response)
- MultiJson.stubs(load: event)
+ MultiJson.stubs(dump: string)
Rack::Request.stubs(new: request)
Rack::Response.stubs(new: response)
- PusherFake::Channel.stubs(:factory).with(channels[0]).returns(channel_1)
- PusherFake::Channel.stubs(:factory).with(channels[1]).returns(channel_2)
end
it "creates a request" do
@@ -31,28 +22,85 @@
Rack::Request.should have_received(:new).with(environment)
end
- it "parses the request body as JSON" do
+ it "dumps the response hash to JSON" do
subject.call(environment)
- MultiJson.should have_received(:load).with(json)
+ MultiJson.should have_received(:dump).with(hash)
end
- it "creates channels by name" do
+ it "creates a Rack response with the response JSON" do
subject.call(environment)
+ Rack::Response.should have_received(:new).with(string)
+ end
- channels.each do |channel|
- PusherFake::Channel.should have_received(:factory).with(channel)
+ it "finishes the response" do
+ subject.call(environment)
+ response.should have_received(:finish).with()
+ end
+
+ it "returns the response" do
+ subject.call(environment).should == response
+ end
+end
+
+describe PusherFake::Server::Application, ".call, for triggering events" do
+ it_should_behave_like "an API request" do
+ let(:id) { PusherFake.configuration.app_id }
+ let(:path) { "/apps/#{id}/events" }
+
+ before do
+ subject.stubs(events: hash)
+ end
+
+ it "emits events" do
+ subject.call(environment)
+ subject.should have_received(:events).with(request)
end
end
+end
- it "emits the event to the channels" do
+describe PusherFake::Server::Application, ".call, for retrieving occupied channels" do
+ it_should_behave_like "an API request" do
+ let(:id) { PusherFake.configuration.app_id }
+ let(:path) { "/apps/#{id}/channels" }
+
+ before do
+ subject.stubs(channels: hash)
+ end
+
+ it "filters the occupied channels" do
+ subject.call(environment)
+ subject.should have_received(:channels).with(request)
+ end
+ end
+end
+
+describe PusherFake::Server::Application, ".call, raising an error" do
+ let(:id) { PusherFake.configuration.app_id }
+ let(:path) { "/apps/#{id}/channels" }
+ let(:message) { "Example error message." }
+ let(:request) { stub(path: path) }
+ let(:response) { mock }
+ let(:environment) { mock }
+
+ subject { PusherFake::Server::Application }
+
+ before do
+ subject.stubs(:channels).raises(message)
+
+ response.stubs(finish: response)
+
+ Rack::Request.stubs(new: request)
+ Rack::Response.stubs(new: response)
+ end
+
+ it "creates a request" do
subject.call(environment)
- channel_1.should have_received(:emit).with(name, data, socket_id: socket_id)
- channel_2.should have_received(:emit).with(name, data, socket_id: socket_id)
+ Rack::Request.should have_received(:new).with(environment)
end
- it "creates a Rack response with an empty JSON object" do
+ it "creates a Rack response with the error message" do
subject.call(environment)
- Rack::Response.should have_received(:new).with("{}")
+ Rack::Response.should have_received(:new).with(message, 400)
end
it "finishes the response" do
@@ -64,3 +112,125 @@
subject.call(environment).should == response
end
end
+
+describe PusherFake::Server::Application, ".events" do
+ let(:body) { stub(read: json) }
+ let(:data) { mock }
+ let(:json) { mock }
+ let(:name) { "event-name" }
+ let(:event) { { "channels" => channels, "name" => name, "data" => data, "socket_id" => socket_id } }
+ let(:request) { stub(body: body) }
+ let(:channels) { ["channel-1", "channel-2"] }
+ let(:channel_1) { stub(emit: true) }
+ let(:channel_2) { stub(emit: true) }
+ let(:socket_id) { stub }
+
+ subject { PusherFake::Server::Application }
+
+ before do
+ MultiJson.stubs(load: event)
+ PusherFake::Channel.stubs(:factory).with(channels[0]).returns(channel_1)
+ PusherFake::Channel.stubs(:factory).with(channels[1]).returns(channel_2)
+ end
+
+ it "parses the request body as JSON" do
+ subject.events(request)
+ MultiJson.should have_received(:load).with(json)
+ end
+
+ it "creates channels by name" do
+ subject.events(request)
+
+ channels.each do |channel|
+ PusherFake::Channel.should have_received(:factory).with(channel)
+ end
+ end
+
+ it "emits the event to the channels" do
+ subject.events(request)
+
+ channel_1.should have_received(:emit).with(name, data, socket_id: socket_id)
+ channel_2.should have_received(:emit).with(name, data, socket_id: socket_id)
+ end
+end
+
+describe PusherFake::Server::Application, ".channels, requesting all channels" do
+ let(:request) { stub(params: {}) }
+ let(:channels) { { "channel-1" => mock, "channel-2" => mock } }
+
+ subject { PusherFake::Server::Application }
+
+ before do
+ PusherFake::Channel.stubs(channels: channels)
+ end
+
+ it "returns a hash of all the channels" do
+ subject.channels(request).should == {
+ channels: {
+ "channel-1" => {},
+ "channel-2" => {}
+ }
+ }
+ end
+end
+
+describe PusherFake::Server::Application, ".channels, requesting channels with a filter" do
+ let(:params) { { "filter_by_prefix" => "public-" } }
+ let(:request) { stub(params: params) }
+ let(:channels) { { "public-1" => mock, "presence-1" => mock } }
+
+ subject { PusherFake::Server::Application }
+
+ before do
+ PusherFake::Channel.stubs(channels: channels)
+ end
+
+ it "returns a hash of the channels matching the filter" do
+ subject.channels(request).should == { channels: { "public-1" => {} } }
+ end
+end
+
+describe PusherFake::Server::Application, ".channels, requesting user count for channels with a filter" do
+ let(:params) { { "filter_by_prefix" => "presence-", "info" => "user_count" } }
+ let(:request) { stub(params: params) }
+ let(:channel) { stub(connections: [mock, mock]) }
+ let(:channels) { { "public-1" => mock, "presence-1" => channel } }
+
+ subject { PusherFake::Server::Application }
+
+ before do
+ PusherFake::Channel.stubs(channels: channels)
+ end
+
+ it "returns a hash of the channels matching the filter and include the user count" do
+ subject.channels(request).should == { channels: { "presence-1" => { user_count: 2 } } }
+ end
+end
+
+describe PusherFake::Server::Application, ".channels, requesting all channels with no channels occupied" do
+ let(:request) { stub(params: {}) }
+ let(:channels) { nil }
+
+ subject { PusherFake::Server::Application }
+
+ before do
+ PusherFake::Channel.stubs(channels: channels)
+ end
+
+ it "returns a hash of no channels" do
+ subject.channels(request).should == { channels: {} }
+ end
+end
+
+describe PusherFake::Server::Application, ".channels, requesting a user count on a non-presence channel" do
+ let(:params) { { "filter_by_prefix" => "public-", "info" => "user_count" } }
+ let(:request) { stub(params: params) }
+
+ subject { PusherFake::Server::Application }
+
+ it "raises an error" do
+ lambda {
+ subject.channels(request)
+ }.should raise_error(subject::CHANNEL_FILTER_ERROR)
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.