diff --git a/Gemfile b/Gemfile index bd32b1897..09d185771 100644 --- a/Gemfile +++ b/Gemfile @@ -29,6 +29,7 @@ group :development, :test do gem 'rspec-rails', '2.11.0' gem 'guard-rspec', '0.5.5' gem 'jasmine' + gem 'pry' end # Gems used only for assets and not required diff --git a/app/controllers/api_music_sessions_controller.rb b/app/controllers/api_music_sessions_controller.rb index f65a393b2..a177d5056 100644 --- a/app/controllers/api_music_sessions_controller.rb +++ b/app/controllers/api_music_sessions_controller.rb @@ -1,5 +1,23 @@ class ApiMusicSessionsController < ApplicationController + # TODO: roll these into a base ApiController class?s + rescue_from 'JamRuby::StateError' do |exception| + @exception = exception + render "errors/state_error.rabl", :status => 500 + end + rescue_from 'JamRuby::JamArgrumentError' do |exception| + @exception = exception + render "errors/jam_argument_error.rabl", :status => 500 + end + rescue_from 'JamRuby::PermissionError' do |exception| + @exception = exception + render "errors/permission_error.rabl", :status => 500 + end + + rescue_from 'ActiveRecord::RecordNotFound' do |exception| + render :status => 404 + end + # have to be signed in currently to see this screen before_filter :signed_in_user @@ -8,6 +26,7 @@ class ApiMusicSessionsController < ApplicationController def initialize @mq_router = MQRouter.new @message_factory = MessageFactory.new + end def index @@ -15,10 +34,26 @@ class ApiMusicSessionsController < ApplicationController end def create - @music_session = MusicSession.new() - @music_session.creator = current_user - @music_session.description = params[:description] - @music_session.save + ConnectionManager.active_record_transaction do |connection_manager| + + client_id = params[:client_id] + + if client_id.nil? + raise JamArgumentError, "client_id must be specified" + end + + @music_session = MusicSession.new() + @music_session.creator = current_user + @music_session.description = params[:description] + saved = @music_session.save + + if saved + # auto-join this user into the newly created session + connection_manager.join_music_session(current_user.id, client_id, @music_session.id) + end + end + + respond_with @music_session, responder: ApiResponder, :location => api_session_detail_url(@music_session) end @@ -34,33 +69,50 @@ class ApiMusicSessionsController < ApplicationController end def participant_show - @music_session_client = MusicSessionClient.find(params[:id]) + @connection = Connection.find(params[:id]) end def participant_create - @music_session = MusicSession.find(params[:id]) + @music_session = nil + @connection = nil - client_id = params[:client_id] - @music_session_client = MusicSessionClient.new() - @music_session_client.ip_address = params[:ip_address] - @music_session_client.client_id = client_id - @music_session_client.music_session = @music_session - @music_session_client.user = current_user - saved = @music_session_client.save + ConnectionManager.active_record_transaction do |connection_manager| + @music_session = MusicSession.find(params[:id]) - if saved - # send out notification to queue to the rest of the session - user_joined = @message_factory.user_joined_music_session(current_user.id, current_user.name) - @mq_router.user_publish_to_session(@music_session, current_user, user_joined, sender = {:client_id => client_id}) + if @music_session.nil? + raise JamArgumentError, "no session found" + end + + client_id = params[:client_id] + + connection_manager.join_music_session(current_user.id, client_id, @music_session.id) + + @connection = Connection.find_by_client_id(client_id) end - respond_with @music_session_client, responder: ApiResponder, :location => api_session_participant_detail_url(@music_session_client) + # send out notification to queue to the rest of the session + user_joined = @message_factory.user_joined_music_session(current_user.id, current_user.name) + @mq_router.user_publish_to_session(@music_session, current_user, user_joined, sender = {:client_id => @connection.client_id}) + + respond_with @connection, responder: ApiResponder, :location => api_session_participant_detail_url(@connection.client_id) end def participant_delete - @music_session_client = MusicSessionClient.find(params[:id]) - @music_session_client.delete - respond_with @music_session_client, responder: ApiResponder + ConnectionManager.active_record_transaction do |connection_manager| + @connection = Connection.find_by_client_id(params[:client_id]) + + if @connection.nil? + raise JamArgumentError, "no client found with specified client_id #{id}" + end + + if @connection.user.id != current_user.id + raise PermissionError, "you do not own this connection" + end + + connection_manager.leave_music_session(current_user.id, @connection.client_id, @connection.music_session_id) + end + + respond_with @connection, responder: ApiResponder end end diff --git a/app/views/api_music_sessions/create.rabl b/app/views/api_music_sessions/create.rabl new file mode 100644 index 000000000..e34b6943d --- /dev/null +++ b/app/views/api_music_sessions/create.rabl @@ -0,0 +1,3 @@ +object @music_session + +extends "api_music_sessions/show" \ No newline at end of file diff --git a/app/views/api_music_sessions/member_show.rabl b/app/views/api_music_sessions/member_show.rabl index d6959b545..175084104 100644 --- a/app/views/api_music_sessions/member_show.rabl +++ b/app/views/api_music_sessions/member_show.rabl @@ -1,3 +1,3 @@ -object @music_session_client +object @connection attributes :id, :ip_address, :client_id \ No newline at end of file diff --git a/app/views/api_music_sessions/show.rabl b/app/views/api_music_sessions/show.rabl index 518a4eb75..e8f52a010 100644 --- a/app/views/api_music_sessions/show.rabl +++ b/app/views/api_music_sessions/show.rabl @@ -2,7 +2,7 @@ object @music_session attributes :id, :description -child(:music_session_clients => :participants) { +child(:connections => :participants) { collection @music_sessions, :object_root => false attributes :id, :ip_address, :client_id } diff --git a/app/views/errors/jam_argument_error.rabl b/app/views/errors/jam_argument_error.rabl new file mode 100644 index 000000000..adeea51da --- /dev/null +++ b/app/views/errors/jam_argument_error.rabl @@ -0,0 +1,7 @@ +object @exception + +attributes :message + +node "type" do + "ArgumentError" +end \ No newline at end of file diff --git a/app/views/errors/permission_error.rabl b/app/views/errors/permission_error.rabl new file mode 100644 index 000000000..b3132a738 --- /dev/null +++ b/app/views/errors/permission_error.rabl @@ -0,0 +1,7 @@ +object @exception + +attributes :message + +node "type" do + "PermissionError" +end \ No newline at end of file diff --git a/app/views/errors/state_error.rabl b/app/views/errors/state_error.rabl new file mode 100644 index 000000000..9aab70130 --- /dev/null +++ b/app/views/errors/state_error.rabl @@ -0,0 +1,7 @@ +object @exception + +attributes :message + +node "type" do + "StateError" +end \ No newline at end of file diff --git a/config/database.yml b/config/database.yml index 6c9631f21..644020af3 100644 --- a/config/database.yml +++ b/config/database.yml @@ -25,8 +25,11 @@ test: &test timeout: 5000 production: - adapter: sqlite3 - database: db/production.sqlite3 + adapter: postgresql + database: jam + username: postgres + password: postgres + host: localhost pool: 5 timeout: 5000 diff --git a/config/routes.rb b/config/routes.rb index 2b4a66d46..aad155e5e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -26,8 +26,8 @@ SampleApp::Application.routes.draw do scope '/api' do # music sessions match '/sessions/:id/participants' => 'api_music_sessions#participant_create', :via => :post - match '/participants/:id' => 'api_music_sessions#participant_show', :via => :get, :as => 'api_session_participant_detail' - match '/participants/:id' => 'api_music_sessions#participant_delete', :via => :delete + match '/participants/:client_id' => 'api_music_sessions#participant_show', :via => :get, :as => 'api_session_participant_detail' + match '/participants/:client_id' => 'api_music_sessions#participant_delete', :via => :delete match '/sessions/:id' => 'api_music_sessions#show', :via => :get, :as => 'api_session_detail' match '/sessions/:id' => 'api_music_sessions#delete', :via => :delete match '/sessions' => 'api_music_sessions#index', :via => :get diff --git a/spec/factories.rb b/spec/factories.rb index 649e5259f..2fb64bde0 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -10,4 +10,8 @@ FactoryGirl.define do end end + factory :connection, :class => JamRuby::Connection do + + end + end diff --git a/spec/requests/music_session_pages_spec.rb b/spec/requests/music_session_pages_spec.rb index c24a6fc37..303a93c07 100644 --- a/spec/requests/music_session_pages_spec.rb +++ b/spec/requests/music_session_pages_spec.rb @@ -23,7 +23,10 @@ describe "Music Session API ", :type => :api do it "should create session" do # create the session - post '/api/sessions.json', '{"description" : "a session"}', "CONTENT_TYPE" => 'application/json' + # create a client connection + + client = FactoryGirl.create(:connection, :user => user, :ip_address => "1.1.1.1", :client_id => "1") + post '/api/sessions.json', '{"description" : "a session", "client_id" : "' + client.client_id + '"}', "CONTENT_TYPE" => 'application/json' last_response.status.should eql(201) # now fetch it's data @@ -44,7 +47,9 @@ describe "Music Session API ", :type => :api do list.length.should == 0 # create the session - post '/api/sessions.json', '{"description" : "a session"}', "CONTENT_TYPE" => 'application/json' + client = FactoryGirl.create(:connection, :user => user, :ip_address => "1.1.1.1", :client_id => "2") + post '/api/sessions.json', '{"description" : "a session", "client_id" : "' + client.client_id + '"}', "CONTENT_TYPE" => 'application/json' + last_response.status.should eql(201) # now fetch it's data get last_response.headers["Location"] @@ -65,43 +70,33 @@ describe "Music Session API ", :type => :api do it "should add/remove member from session" do # create the session - post '/api/sessions.json', '{"description" : "a session"}', "CONTENT_TYPE" => 'application/json' + client = FactoryGirl.create(:connection, :user => user, :ip_address => "1.1.1.1", :client_id => "3") + post '/api/sessions.json', '{"description" : "a session", "client_id" : "' + client.client_id + '"}', "CONTENT_TYPE" => 'application/json' + last_response.status.should eql(201) # now fetch it's data get last_response.headers["Location"] music_session = JSON.parse(last_response.body) - music_session["participants"].length.should == 0 - # create a member - post "/api/sessions/#{music_session["id"]}/participants.json", '{ "ip_address" : "1.2.3.4", "client_id" : "1" }', "CONTENT_TYPE" => 'application/json' - last_response.status.should eql(201) - - musician = JSON.parse(last_response.body) - - # re-fetch the session now that there is a musician - get "/api/sessions/#{music_session["id"]}.json" - music_session = JSON.parse(last_response.body) - - # there should be one musician in the response + # and the creator should be in the session music_session["participants"].length.should == 1 musician = music_session["participants"][0] # and that musician should have the same IP address - musician["ip_address"].should == "1.2.3.4" + musician["ip_address"].should == "1.1.1.1" - # and that musician should have the same IP address - musician["client_id"].should == "1" + # and that musician should have the correct IP address + musician["client_id"].should == "3" # now delete that musician - delete "/api/participants/#{musician["id"]}.json", '', "CONTENT_TYPE" => 'application/json' + delete "/api/participants/#{musician["client_id"]}.json", '', "CONTENT_TYPE" => 'application/json' last_response.status.should eql(204) - # re-fetch the session now that there is not a musician + # re-fetch the session now that there is not a musician; + # we auto-delete sessions when the last person leaves get "/api/sessions/#{music_session["id"]}.json" - music_session = JSON.parse(last_response.body) + last_response.status.should eql(404) - # there should be no musicians in the response - music_session["participants"].length.should == 0 end