From d11261c5c6681f0398567c5d459060bcefb3c482 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 19 May 2014 16:57:08 -0500 Subject: [PATCH 1/7] * VRFS-1730 - websocket on web layout --- web/app/assets/javascripts/JamServer.js | 31 ++++------ web/app/assets/javascripts/application.js | 1 + .../javascripts/fakeJamClientRecordings.js | 12 ++-- web/app/assets/javascripts/sessionList.js | 3 +- web/app/assets/javascripts/sessionModel.js | 18 ++++-- web/app/assets/javascripts/utils.js | 43 ++++++++++++++ web/app/assets/javascripts/web/web.js | 5 ++ web/app/assets/stylesheets/web/web.css | 2 + web/app/controllers/application_controller.rb | 5 ++ web/app/controllers/clients_controller.rb | 16 ------ web/app/controllers/spikes_controller.rb | 6 ++ web/app/helpers/client_helper.rb | 28 +++++++++ web/app/views/clients/index.html.erb | 56 +----------------- web/app/views/layouts/web.html.erb | 18 ++++++ web/app/views/spikes/websocket.html.erb | 57 +++++++++++++++++++ web/config/routes.rb | 2 +- web/spec/features/landing_spec.rb | 6 +- web/spec/features/reconnect_spec.rb | 2 +- web/spec/features/recordings_spec.rb | 6 +- web/spec/support/utilities.rb | 2 +- 20 files changed, 211 insertions(+), 108 deletions(-) create mode 100644 web/app/views/spikes/websocket.html.erb diff --git a/web/app/assets/javascripts/JamServer.js b/web/app/assets/javascripts/JamServer.js index 8f3a6c2ef..44321fd23 100644 --- a/web/app/assets/javascripts/JamServer.js +++ b/web/app/assets/javascripts/JamServer.js @@ -9,10 +9,12 @@ var logger = context.JK.logger; var msg_factory = context.JK.MessageFactory; + var rest = context.JK.Rest(); + // Let socket.io know where WebSocketMain.swf is context.WEB_SOCKET_SWF_LOCATION = "assets/flash/WebSocketMain.swf"; - context.JK.JamServer = function (app) { + context.JK.JamServer = function (app, activeElementEvent) { // uniquely identify the websocket connection var channelId = null; @@ -50,6 +52,8 @@ var $templateDisconnected = null; var $currentDisplay = null; + var $self = $(this); + var server = {}; server.socket = {}; server.signedIn = false; @@ -59,7 +63,6 @@ server.socketClosedListeners = []; server.connected = false; - function heartbeatStateReset() { lastHeartbeatSentTime = null; lastHeartbeatAckTime = null; @@ -72,10 +75,6 @@ freezeInteraction = activeElementVotes && ((activeElementVotes.dialog && activeElementVotes.dialog.freezeInteraction === true) || (activeElementVotes.screen && activeElementVotes.screen.freezeInteraction === true)); - if (!initialConnect) { - context.JK.CurrentSessionModel.onWebsocketDisconnected(in_error); - } - if (in_error) { reconnectAttempt = 0; $currentDisplay = renderDisconnected(); @@ -108,11 +107,11 @@ server.reconnecting = true; - var result = app.activeElementEvent('beforeDisconnect'); + var result = activeElementEvent('beforeDisconnect'); initiateReconnect(result, in_error); - app.activeElementEvent('afterDisconnect'); + activeElementEvent('afterDisconnect'); // notify anyone listening that the socket closed var len = server.socketClosedListeners.length; @@ -193,14 +192,12 @@ heartbeatAckCheckInterval = context.setInterval(_heartbeatAckCheck, 1000); lastHeartbeatAckTime = new Date(new Date().getTime() + heartbeatMS); // add a little forgiveness to server for initial heartbeat connectDeferred.resolve(); - app.activeElementEvent('afterConnect', payload); + activeElementEvent('afterConnect', payload); } function heartbeatAck(header, payload) { lastHeartbeatAckTime = new Date(); - - context.JK.CurrentSessionModel.trackChanges(header, payload); } function registerLoginAck() { @@ -272,11 +269,7 @@ // TODO: tell certain elements that we've reconnected } else { - // this path is the 'in session' path, where we actually reload the page - context.JK.CurrentSessionModel.leaveCurrentSession() - .always(function () { - window.location.reload(); - }); + window.location.reload(); } server.reconnecting = false; }); @@ -455,10 +448,10 @@ } connectDeferred = new $.Deferred(); channelId = context.JK.generateUUID(); // create a new channel ID for every websocket connection - logger.log("connecting websocket, channel_id: " + channelId); - var uri = context.JK.websocket_gateway_uri + '?channel_id=' + channelId; // Set in index.html.erb. - //var uri = context.gon.websocket_gateway_uri; // Leaving here for now, as we're looking for a better solution. + var uri = context.gon.websocket_gateway_uri + '?channel_id=' + channelId; // Set in index.html.erb. + + logger.debug("connecting websocket: " + uri); server.socket = new context.WebSocket(uri); server.socket.onopen = server.onOpen; diff --git a/web/app/assets/javascripts/application.js b/web/app/assets/javascripts/application.js index ec1dee5c6..f9d230ae6 100644 --- a/web/app/assets/javascripts/application.js +++ b/web/app/assets/javascripts/application.js @@ -34,6 +34,7 @@ //= require AAA_Log //= require globals //= require AAB_message_factory +//= require jam_rest //= require AAC_underscore //= require utils //= require custom_controls diff --git a/web/app/assets/javascripts/fakeJamClientRecordings.js b/web/app/assets/javascripts/fakeJamClientRecordings.js index b48ad335b..5fd412e0b 100644 --- a/web/app/assets/javascripts/fakeJamClientRecordings.js +++ b/web/app/assets/javascripts/fakeJamClientRecordings.js @@ -137,14 +137,16 @@ } function onStopRecording(from, payload) { - logger.debug("received stop recording request from " + from); + logger.debug("received stop recording request from " + from); - // TODO check recordingId, and if currently recording - // we should return success if we are currently recording, or if we were already asked to stop for this recordingId - // this means we should keep a list of the last N recordings that we've seen, rather than just keeping the current - context.JK.JamServer.sendP2PMessage(from, JSON.stringify(p2pMessageFactory.stopRecordingAck(payload.recordingId, true))); + // TODO check recordingId, and if currently recording + // we should return success if we are currently recording, or if we were already asked to stop for this recordingId + // this means we should keep a list of the last N recordings that we've seen, rather than just keeping the current + context.JK.JamServer.sendP2PMessage(from, JSON.stringify(p2pMessageFactory.stopRecordingAck(payload.recordingId, true))); + if(stopRecordingResultCallbackName) { eval(stopRecordingResultCallbackName).call(this, payload.recordingId, {success:payload.success, reason:payload.reason, detail:from}); + } } function onStopRecordingAck(from, payload) { diff --git a/web/app/assets/javascripts/sessionList.js b/web/app/assets/javascripts/sessionList.js index 097d05d0c..b86bc1919 100644 --- a/web/app/assets/javascripts/sessionList.js +++ b/web/app/assets/javascripts/sessionList.js @@ -90,8 +90,7 @@ // loop through the tracks to get the instruments for (j=0; j < participant.tracks.length; j++) { var track = participant.tracks[j]; - logger.debug("Find:Finding instruments. Participant tracks:"); - logger.debug(participant.tracks); + logger.debug("Find:Finding instruments. Participant tracks:", participant.tracks); var inst = '../assets/content/icon_instrument_default24.png'; if (track.instrument_id in instrument_logo_map) { inst = instrument_logo_map[track.instrument_id]; diff --git a/web/app/assets/javascripts/sessionModel.js b/web/app/assets/javascripts/sessionModel.js index 791503e06..06cf02d0f 100644 --- a/web/app/assets/javascripts/sessionModel.js +++ b/web/app/assets/javascripts/sessionModel.js @@ -23,7 +23,9 @@ var participantsEverSeen = {}; var $self = $(this); - function id() { + server.registerOnSocketClosed(onWebsocketDisconnected); + + function id() { return currentSession ? currentSession.id : null; } @@ -91,6 +93,8 @@ server.registerMessageCallback(context.JK.MessageType.SESSION_JOIN, trackChanges); server.registerMessageCallback(context.JK.MessageType.SESSION_DEPART, trackChanges); server.registerMessageCallback(context.JK.MessageType.TRACKS_CHANGED, trackChanges); + server.registerMessageCallback(context.JK.MessageType.HEARTBEAT_ACK, trackChanges); + $(document).trigger('jamkazam.session_started', {session: {id: sessionId}}); }) .fail(function() { @@ -107,12 +111,14 @@ server.unregisterMessageCallback(context.JK.MessageType.SESSION_JOIN, trackChanges); server.unregisterMessageCallback(context.JK.MessageType.SESSION_DEPART, trackChanges); server.unregisterMessageCallback(context.JK.MessageType.TRACKS_CHANGED, trackChanges); + server.unregisterMessageCallback(context.JK.MessageType.HEARTBEAT_ACK, trackChanges); //server.unregisterMessageCallback(context.JK.MessageType.MUSICIAN_SESSION_DEPART, refreshCurrentSession); // leave the session right away without waiting on REST. Why? If you can't contact the server, or if it takes a long // time, for that entire duration you'll still be sending voice data to the other users. // this may be bad if someone decides to badmouth others in the left-session during this time - logger.debug("calling jamClient.LeaveSession for clientId=" + clientId); + console.trace(); + logger.debug("performLeaveSession: calling jamClient.LeaveSession for clientId=" + clientId); client.LeaveSession({ sessionID: currentSessionId }); leaveSessionRest(currentSessionId) .done(function() { @@ -377,10 +383,11 @@ } function onWebsocketDisconnected(in_error) { - - // kill the streaming of the session immediately - logger.debug("calling jamClient.LeaveSession for clientId=" + clientId); + // kill the streaming of the session immediately + if(currentSessionId) { + logger.debug("onWebsocketDisconnect: calling jamClient.LeaveSession for clientId=" + clientId); client.LeaveSession({ sessionID: currentSessionId }); + } } // returns a deferred object @@ -423,7 +430,6 @@ this.getCurrentOrLastSession = function() { return currentOrLastSession; }; - this.trackChanges = trackChanges; this.getParticipant = function(clientId) { return participantsEverSeen[clientId] }; diff --git a/web/app/assets/javascripts/utils.js b/web/app/assets/javascripts/utils.js index fd689608d..2203d6ffb 100644 --- a/web/app/assets/javascripts/utils.js +++ b/web/app/assets/javascripts/utils.js @@ -607,6 +607,49 @@ doneYet(); }; + context.JK.initJamClient = function() { + // If no jamClient (when not running in native client) + // create a fake one. + if (!(window.jamClient)) { + var p2pMessageFactory = new JK.FakeJamClientMessages(); + window.jamClient = new JK.FakeJamClient(JK.app, p2pMessageFactory); + window.jamClient.SetFakeRecordingImpl(new JK.FakeJamClientRecordings(JK.app, jamClient, p2pMessageFactory)); + } + else if(false) { // set to true to time long running bridge calls + var originalJamClient = window.jamClient; + var interceptedJamClient = {}; + $.each(Object.keys(originalJamClient), function(i, key) { + if(key.indexOf('(') > -1) { + // this is a method. time it + var jsKey = key.substring(0, key.indexOf('(')) + console.log("replacing " + jsKey) + interceptedJamClient[jsKey] = function() { + var original = originalJamClient[key] + var start = new Date(); + if(key == "FTUEGetDevices()") { + var returnVal = eval('originalJamClient.FTUEGetDevices(' + arguments[0] + ')'); + } + else { + var returnVal = original.apply(originalJamClient, arguments); + } + var time = new Date().getTime() - start.getTime(); + if(time >= 0) { // if 0, you'll see ALL bridge calls. If you set it to a higher value, you'll only see calls that are beyond that threshold + console.error(time + "ms jamClient." + jsKey + ' returns=', returnVal); + } + + return returnVal; + } + } + else { + // we need to intercept properties... but how? + } + }); + + + window.jamClient = interceptedJamClient; + } + + } context.JK.clientType = function () { return context.jamClient.IsNativeClient() ? 'client' : 'browser'; } diff --git a/web/app/assets/javascripts/web/web.js b/web/app/assets/javascripts/web/web.js index 6323e331e..f79dba4a7 100644 --- a/web/app/assets/javascripts/web/web.js +++ b/web/app/assets/javascripts/web/web.js @@ -55,3 +55,8 @@ //= require web/sessions //= require web/recordings //= require web/welcome +//= require banner +//= require fakeJamClient +//= require fakeJamClientMessages +//= require fakeJamClientRecordings +//= require JamServer diff --git a/web/app/assets/stylesheets/web/web.css b/web/app/assets/stylesheets/web/web.css index d8945d48d..c06b72a33 100644 --- a/web/app/assets/stylesheets/web/web.css +++ b/web/app/assets/stylesheets/web/web.css @@ -1,4 +1,6 @@ /** +*= require client/banner +*= require client/jamServer *= require client/ie *= require client/jamkazam *= require easydropdown diff --git a/web/app/controllers/application_controller.rb b/web/app/controllers/application_controller.rb index 3954175af..29e294ac5 100644 --- a/web/app/controllers/application_controller.rb +++ b/web/app/controllers/application_controller.rb @@ -3,10 +3,15 @@ class ApplicationController < ActionController::Base protect_from_forgery include ApplicationHelper include SessionsHelper + include ClientHelper # inject username/email into bugsnag data before_bugsnag_notify :add_user_info_to_bugsnag + before_filter do + gon_setup + end + before_filter do if params[AffiliatePartner::PARAM_REFERRAL].present? && current_user.nil? if cookies[AffiliatePartner::PARAM_COOKIE].blank? diff --git a/web/app/controllers/clients_controller.rb b/web/app/controllers/clients_controller.rb index 0f89b42de..637688b11 100644 --- a/web/app/controllers/clients_controller.rb +++ b/web/app/controllers/clients_controller.rb @@ -12,22 +12,6 @@ class ClientsController < ApplicationController return end - # use gon to pass variables into javascript - gon.websocket_gateway_uri = Rails.application.config.websocket_gateway_uri - gon.check_for_client_updates = Rails.application.config.check_for_client_updates - gon.fp_apikey = Rails.application.config.filepicker_rails.api_key - gon.fp_upload_dir = Rails.application.config.filepicker_upload_dir - gon.allow_force_native_client = Rails.application.config.allow_force_native_client - - # is this the native client or browser? - @nativeClient = is_native_client? - - # let javascript have access to the server's opinion if this is a native client - gon.isNativeClient = @nativeClient - - gon.use_cached_session_scores = Rails.application.config.use_cached_session_scores - gon.allow_both_find_algos = Rails.application.config.allow_both_find_algos - render :layout => 'client' end diff --git a/web/app/controllers/spikes_controller.rb b/web/app/controllers/spikes_controller.rb index 75cde8a49..ff31a9511 100644 --- a/web/app/controllers/spikes_controller.rb +++ b/web/app/controllers/spikes_controller.rb @@ -5,6 +5,8 @@ class SpikesController < ApplicationController + include ClientHelper + def facebook_invite end @@ -24,4 +26,8 @@ class SpikesController < ApplicationController def launch_app render :layout => 'web' end + + def websocket + render :layout => false + end end diff --git a/web/app/helpers/client_helper.rb b/web/app/helpers/client_helper.rb index 319b1456b..045b50636 100644 --- a/web/app/helpers/client_helper.rb +++ b/web/app/helpers/client_helper.rb @@ -15,4 +15,32 @@ module ClientHelper is_native_client end + + def gon_setup + + gon.root_url = root_url + # use gon to pass variables into javascript + if Rails.env == "development" + # if in development mode, we assume you are running websocket-gateway + # on the same host as you hit your server. + gon.websocket_gateway_uri = "ws://" + request.host + ":6767/websocket"; + else + # but in any other mode, just use config + gon.websocket_gateway_uri = Rails.application.config.websocket_gateway_uri + end + + gon.check_for_client_updates = Rails.application.config.check_for_client_updates + gon.fp_apikey = Rails.application.config.filepicker_rails.api_key + gon.fp_upload_dir = Rails.application.config.filepicker_upload_dir + gon.allow_force_native_client = Rails.application.config.allow_force_native_client + + # is this the native client or browser? + @nativeClient = is_native_client? + + # let javascript have access to the server's opinion if this is a native client + gon.isNativeClient = @nativeClient + + gon.use_cached_session_scores = Rails.application.config.use_cached_session_scores + gon.allow_both_find_algos = Rails.application.config.allow_both_find_algos + end end \ No newline at end of file diff --git a/web/app/views/clients/index.html.erb b/web/app/views/clients/index.html.erb index 28836cda8..9b1b8cbfc 100644 --- a/web/app/views/clients/index.html.erb +++ b/web/app/views/clients/index.html.erb @@ -73,18 +73,7 @@ JK = JK || {}; - JK.root_url = "<%= root_url %>" - - <% if Rails.env == "development" %> - // if in development mode, we assume you are running websocket-gateway - // on the same host as you hit your server. - JK.websocket_gateway_uri = "ws://" + location.hostname + ":6767/websocket"; - <% else %> - // but in any other mode, just trust the config coming through gon - JK.websocket_gateway_uri = gon.websocket_gateway_uri - <% end %> - if (console) { console.log("websocket_gateway_uri:" + JK.websocket_gateway_uri); } - + JK.root_url = gon.root_url // If no trackVolumeObject (when not running in native client) // create a fake one. @@ -263,49 +252,10 @@ } JK.app = JK.JamKazam(); - var jamServer = new JK.JamServer(JK.app); + var jamServer = new JK.JamServer(JK.app, function(event_type) {JK.app.activeElementEvent(event_type)}); jamServer.initialize(); - // If no jamClient (when not running in native client) - // create a fake one. - if (!(window.jamClient)) { - var p2pMessageFactory = new JK.FakeJamClientMessages(); - window.jamClient = new JK.FakeJamClient(JK.app, p2pMessageFactory); - window.jamClient.SetFakeRecordingImpl(new JK.FakeJamClientRecordings(JK.app, jamClient, p2pMessageFactory)); - } - else if(false) { // set to true to time long running bridge calls - var originalJamClient = window.jamClient; - var interceptedJamClient = {}; - $.each(Object.keys(originalJamClient), function(i, key) { - if(key.indexOf('(') > -1) { - // this is a method. time it - var jsKey = key.substring(0, key.indexOf('(')) - console.log("replacing " + jsKey) - interceptedJamClient[jsKey] = function() { - var original = originalJamClient[key] - var start = new Date(); - if(key == "FTUEGetDevices()") { - var returnVal = eval('originalJamClient.FTUEGetDevices(' + arguments[0] + ')'); - } - else { - var returnVal = original.apply(originalJamClient, arguments); - } - var time = new Date().getTime() - start.getTime(); - if(time >= 0) { // if 0, you'll see ALL bridge calls. If you set it to a higher value, you'll only see calls that are beyond that threshold - console.error(time + "ms jamClient." + jsKey + ' returns=', returnVal); - } - - return returnVal; - } - } - else { - // we need to intercept properties... but how? - } - }); - - - window.jamClient = interceptedJamClient; - } + JK.initJamClient(); // Let's get things rolling... if (JK.currentUserId) { diff --git a/web/app/views/layouts/web.html.erb b/web/app/views/layouts/web.html.erb index 2515ae9da..60b500f40 100644 --- a/web/app/views/layouts/web.html.erb +++ b/web/app/views/layouts/web.html.erb @@ -70,6 +70,9 @@ <%= render "clients/footer" %> + <%= render "clients/banner" %> + <%= render "clients/banners/disconnected" %> + <%= render "clients/jamServer" %> <%= render "clients/invitationDialog" %> <%= render "users/signupDialog" %> <%= render "users/signinDialog" %> @@ -103,6 +106,12 @@ <% end %> JK.app = JK.JamKazam(); + var jamServer = new JK.JamServer(JK.app, $.noop); + jamServer.initialize(); + + // JamServer.connect needs the jamClient to be initialized + JK.initJamClient(); + JK.app.initialize({inClient: false, layoutOpts: {layoutFooter: false, sizeOverlayToContent: true}}); var facebookHelper = new JK.FacebookHelper(JK.app); @@ -125,6 +134,15 @@ videoDialog.initialize(); JK.bindHoverEvents(); + + + JK.JamServer.connect() // singleton here defined in JamServer.js + .done(function() { + console.log("websocket connected") + }) + .fail(function() { + console.log("websocket failed to connect") + }); }) diff --git a/web/app/views/spikes/websocket.html.erb b/web/app/views/spikes/websocket.html.erb new file mode 100644 index 000000000..e75e00919 --- /dev/null +++ b/web/app/views/spikes/websocket.html.erb @@ -0,0 +1,57 @@ + +<%= javascript_include_tag "jamServer" %> + +<%= include_gon %> + +<%= render "clients/banner" %> +<%= render "clients/banners/disconnected" %> +<%= render "clients/jamServer" %> + + +<%= stylesheet_link_tag "client/banner", media: "all" %> +<%= stylesheet_link_tag "client/jamServer", media: "all" %> + + \ No newline at end of file diff --git a/web/config/routes.rb b/web/config/routes.rb index c7d162cf7..bd6961167 100644 --- a/web/config/routes.rb +++ b/web/config/routes.rb @@ -80,7 +80,7 @@ SampleApp::Application.routes.draw do # route to spike controller (proof-of-concepts) match '/facebook_invite', to: 'spikes#facebook_invite' match '/launch_app', to: 'spikes#launch_app' - + match '/websocket', to: 'spikes#websocket' # junk pages match '/help', to: 'static_pages#help' diff --git a/web/spec/features/landing_spec.rb b/web/spec/features/landing_spec.rb index 0d42a2680..ff192b975 100644 --- a/web/spec/features/landing_spec.rb +++ b/web/spec/features/landing_spec.rb @@ -23,8 +23,10 @@ describe "Landing", :js => true, :type => :feature, :capybara_feature => true do end it "footer links work" do - first('#footer-links a').trigger(:click) - should have_selector('h1', text: 'About Us') + first('#footer-links a', text: 'about').trigger(:click) + page.within_window page.driver.window_handles.last do + should have_selector('h1', text: 'About Us') + end end end diff --git a/web/spec/features/reconnect_spec.rb b/web/spec/features/reconnect_spec.rb index ce822ba51..0ff6752fe 100644 --- a/web/spec/features/reconnect_spec.rb +++ b/web/spec/features/reconnect_spec.rb @@ -117,7 +117,7 @@ describe "Reconnect", :js => true, :type => :feature, :capybara_feature => true # but.. after a few seconds, it should reconnect on it's own page.should_not have_selector('h2', text: 'Disconnected from Server') - find('h1', text:'session') + find('div[layout-id="session"] h1', text:'session') end end end diff --git a/web/spec/features/recordings_spec.rb b/web/spec/features/recordings_spec.rb index 5b005acfa..c81a0f493 100644 --- a/web/spec/features/recordings_spec.rb +++ b/web/spec/features/recordings_spec.rb @@ -47,10 +47,11 @@ describe "Session Recordings", :js => true, :type => :feature, :capybara_feature # confirms that if someone leaves 'ugly' (without calling 'Leave' REST API), that the recording is junked it "creator starts and then abruptly leave" do + pending "shows 'recording finished'" start_recording_with(creator, [joiner1]) in_client(creator) do - visit "/downloads" # kills websocket, looking like an abrupt leave + close_websocket end in_client(joiner1) do @@ -80,10 +81,11 @@ describe "Session Recordings", :js => true, :type => :feature, :capybara_feature # confirms that if someone leaves 'ugly' (without calling 'Leave' REST API), that the recording is junked with 3 participants it "creator starts and then abruptly leave with 3 participants" do + pending "shows 'recording finished'" start_recording_with(creator, [joiner1, joiner2]) in_client(creator) do - visit "/downloads" # kills websocket, looking like an abrupt leave + close_websocket end in_client(joiner1) do diff --git a/web/spec/support/utilities.rb b/web/spec/support/utilities.rb index 62aa1c5d4..998b75962 100644 --- a/web/spec/support/utilities.rb +++ b/web/spec/support/utilities.rb @@ -479,4 +479,4 @@ def garbage length output = '' length.times { output << special_characters.sample } output.slice(0, length) -end \ No newline at end of file +end From 96f62b1364f5bf2d9370f32dbf9768af5a9488ec Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Tue, 20 May 2014 02:45:00 -0400 Subject: [PATCH 2/7] VRFS-1690 VRFS-1691 VRFS-1692 RSVP API work --- ruby/lib/jam_ruby/models/notification.rb | 12 +-- ruby/lib/jam_ruby/models/rsvp_request.rb | 88 ++++++++++++++++--- ruby/lib/jam_ruby/models/rsvp_slot.rb | 4 + .../jam_ruby/models/session_info_comment.rb | 9 +- .../spec/jam_ruby/models/notification_spec.rb | 15 ++-- .../api_rsvp_requests_controller.rb | 46 +++++++++- web/config/routes.rb | 2 +- web/spec/requests/rsvp_requests_api_spec.rb | 67 ++++++++++++++ 8 files changed, 212 insertions(+), 31 deletions(-) create mode 100644 web/spec/requests/rsvp_requests_api_spec.rb diff --git a/ruby/lib/jam_ruby/models/notification.rb b/ruby/lib/jam_ruby/models/notification.rb index 5c0f55bf2..9fbb8bf57 100644 --- a/ruby/lib/jam_ruby/models/notification.rb +++ b/ruby/lib/jam_ruby/models/notification.rb @@ -769,7 +769,7 @@ module JamRuby return if music_session.nil? - rsvp_requests = RsvpRequest.requests_by_session(music_session) + rsvp_requests = RsvpRequest.index(music_session) rsvp_requests.each do |rsvp| target_user = rsvp.user @@ -810,7 +810,7 @@ module JamRuby return if music_session.nil? - rsvp_requests = RsvpRequest.requests_by_session(music_session) + rsvp_requests = RsvpRequest.index(music_session) rsvp_requests.each do |rsvp| target_user = rsvp.user @@ -851,7 +851,7 @@ module JamRuby return if music_session.nil? - rsvp_requests = RsvpRequest.requests_by_session(music_session) + rsvp_requests = RsvpRequest.index(music_session) rsvp_requests.each do |rsvp| target_user = rsvp.user @@ -888,15 +888,15 @@ module JamRuby end end - def send_scheduled_session_comment(music_session, comment) + def send_scheduled_session_comment(music_session, creator, comment) return if music_session.nil? || comment.blank? - rsvp_requests = RsvpRequest.requests_by_session(music_session) + rsvp_requests = RsvpRequest.index(music_session) rsvp_requests.each do |rsvp| target_user = rsvp.user - source_user = music_session.creator + source_user = creator notification = Notification.new notification.description = NotificationTypes::SCHEDULED_SESSION_CANCELLED diff --git a/ruby/lib/jam_ruby/models/rsvp_request.rb b/ruby/lib/jam_ruby/models/rsvp_request.rb index 5f0ec12b8..84dce5c8d 100644 --- a/ruby/lib/jam_ruby/models/rsvp_request.rb +++ b/ruby/lib/jam_ruby/models/rsvp_request.rb @@ -4,19 +4,10 @@ module JamRuby belongs_to :user, :class_name => "JamRuby::User" has_many :rsvp_request_rsvp_slots, :class_name => "JamRuby::RsvpRequestRsvpSlot" has_many :rsvp_slots, :class_name => "JamRuby::RsvpSlot", :through => :rsvp_requests_rsvp_slots - - # validates :message, length: {maximum: 1000}, no_profanity: true validates :canceled, :inclusion => {:in => [nil, true, false]} - # def self.create(params) - # # slot_ids = - # rsvp = RsvpRequest.new - # # rsv - # rsvp.save - # end - - def self.requests_by_session(session, user = nil) + def self.index(session, user = nil) query = RsvpRequest .includes(:user) .joins( @@ -40,6 +31,83 @@ module JamRuby return query end + def self.create(params, user) + if rsvp_request.user_id != user.id + raise PermissionError, "Only a session invitee create the RSVP." + + RsvpRequest.transaction do + rsvp = RsvpRequest.new + + if params[:slot_ids].blank? + raise JamRuby::JamArgumentError.new("You must select at least 1 slot.") + end + + slot_ids = params[:slot_ids] + + slot_ids.each do |id| + end + rsvp.save + + Notification.send_scheduled_session_rsvp() + end + end + + def self.update(params) + + rsvp_request_id = params[:id] + + if !params[:decision].blank? + case params[:decision] + when "accept" + slot_ids = params[:slot_ids] + + slot_ids.each do |id| + request_slot = RsvpRequestRsvpSlot.where("rsvp_request_id = '#{rsvp_request_id}' AND rsvp_slot_id = '#{id}'").first + request_slot.rsvp_request_id = rsvp_request_id + request_slot.rsvp_slot_id = id + request_slot.chosen = true + request_slot.save + end + + # send notification + Notification.send_scheduled_session_rsvp_approved(music_session, user) + + when "reject" + + end + + else + raise JamRuby::JamArgumentError.new("Invalid request.") + end + end + + def self.cancel(rsvp_request, music_session, user, message) + if music_session.creator.id != user.id && rsvp_request.user_id != user.id + raise PermissionError, "Only the session organizer or RSVP creator can cancel the RSVP." + + RsvpRequest.transaction do + + # mark corresponding slot's chosen field as false + rsvp_request_slots = RsvpRequestRsvpSlot.find("rsvp_request_id = '#{rsvp_request.id}'") + + rsvp_request_slots.each do |slot| + if slot.chosen + slot.chosen = false + slot.save + end + end + + # send notification + if music_session.creator.id == user.id + Notification.send_scheduled_session_rsvp_cancelled_org(music_session, user) + else + Notification.send_scheduled_session_rsvp_cancelled(music_session, user) + end + + Notification.send_scheduled_session_comment(music_session, user, message) + end + end + # XXX we need to validate that only one RsvpRequest.chosen = true for a given RsvpSlot # in other words, you can have many requests to a slot, but only 0 or 1 rsvp_request.chosen = true) end diff --git a/ruby/lib/jam_ruby/models/rsvp_slot.rb b/ruby/lib/jam_ruby/models/rsvp_slot.rb index 274cf21bd..a0a5f3b86 100644 --- a/ruby/lib/jam_ruby/models/rsvp_slot.rb +++ b/ruby/lib/jam_ruby/models/rsvp_slot.rb @@ -7,5 +7,9 @@ module JamRuby has_many :rsvp_requests, :class_name => "JamRuby::RsvpRequest", :through => :rsvp_requests_rsvp_slots # TODO: validates :proficiency_level + + def is_chosen + RsvpRequestRsvpSlot.exists?("chosen=true AND rsvp_slot_id='#{self.id}") + end end end diff --git a/ruby/lib/jam_ruby/models/session_info_comment.rb b/ruby/lib/jam_ruby/models/session_info_comment.rb index 58e7b75eb..fa0833807 100644 --- a/ruby/lib/jam_ruby/models/session_info_comment.rb +++ b/ruby/lib/jam_ruby/models/session_info_comment.rb @@ -7,13 +7,10 @@ module JamRuby default_scope order('created_at DESC') - belongs_to(:music_session, - :class_name => "JamRuby::MusicSession", - :foreign_key => "music_session_id") + belongs_to(:music_session, :class_name => "JamRuby::MusicSession", :foreign_key => "music_session_id") + belongs_to(:user, :class_name => "JamRuby::User", :foreign_key => "creator_id") - belongs_to(:user, - :class_name => "JamRuby::User", - :foreign_key => "creator_id") + # validates :comment, length: {maximum: 1000}, no_profanity: true end end \ No newline at end of file diff --git a/ruby/spec/jam_ruby/models/notification_spec.rb b/ruby/spec/jam_ruby/models/notification_spec.rb index 8b33f3122..bbb9335f5 100644 --- a/ruby/spec/jam_ruby/models/notification_spec.rb +++ b/ruby/spec/jam_ruby/models/notification_spec.rb @@ -170,7 +170,7 @@ describe Notification do it "sends no notification if session is nil" do sender = FactoryGirl.create(:user) calls = count_publish_to_user_calls - notification = Notification.send_scheduled_session_rsvp(nil, sender, nil) + notification = Notification.send_scheduled_session_rsvp(nil, sender, 'Blah', nil) UserMailer.deliveries.length.should == 0 calls[:count].should == 0 @@ -179,7 +179,7 @@ describe Notification do it "sends no notification if user is nil" do session = FactoryGirl.create(:music_session) calls = count_publish_to_user_calls - notification = Notification.send_scheduled_session_rsvp(session, nil, nil) + notification = Notification.send_scheduled_session_rsvp(session, nil, 'Blah', nil) UserMailer.deliveries.length.should == 0 calls[:count].should == 0 @@ -222,7 +222,7 @@ describe Notification do it "sends no notification if session is nil" do sender = FactoryGirl.create(:user) calls = count_publish_to_user_calls - notification = Notification.send_scheduled_session_rsvp_cancelled(nil, sender) + notification = Notification.send_scheduled_session_rsvp_cancelled(nil, sender, 'Blah') UserMailer.deliveries.length.should == 0 calls[:count].should == 0 @@ -231,7 +231,7 @@ describe Notification do it "sends no notification if user is nil" do session = FactoryGirl.create(:music_session) calls = count_publish_to_user_calls - notification = Notification.send_scheduled_session_rsvp_cancelled(session, nil) + notification = Notification.send_scheduled_session_rsvp_cancelled(session, nil, 'Blah') UserMailer.deliveries.length.should == 0 calls[:count].should == 0 @@ -248,7 +248,7 @@ describe Notification do it "sends no notification if session is nil" do receiver = FactoryGirl.create(:user) calls = count_publish_to_user_calls - notification = Notification.send_scheduled_session_rsvp_cancelled_org(nil, receiver) + notification = Notification.send_scheduled_session_rsvp_cancelled_org(nil, receiver, 'Blah') UserMailer.deliveries.length.should == 0 calls[:count].should == 0 @@ -257,7 +257,7 @@ describe Notification do it "sends no notification if user is nil" do session = FactoryGirl.create(:music_session) calls = count_publish_to_user_calls - notification = Notification.send_scheduled_session_rsvp_cancelled_org(session, nil) + notification = Notification.send_scheduled_session_rsvp_cancelled_org(session, nil, 'Blah') UserMailer.deliveries.length.should == 0 calls[:count].should == 0 @@ -355,9 +355,10 @@ describe Notification do end it "sends no notification if comment is empty" do + sender = FactoryGirl.create(:user) session = FactoryGirl.create(:music_session) calls = count_publish_to_user_calls - notification = Notification.send_scheduled_session_comment(session, '') + notification = Notification.send_scheduled_session_comment(session, sender, '') UserMailer.deliveries.length.should == 0 calls[:count].should == 0 diff --git a/web/app/controllers/api_rsvp_requests_controller.rb b/web/app/controllers/api_rsvp_requests_controller.rb index 167fd4da7..2940d5402 100644 --- a/web/app/controllers/api_rsvp_requests_controller.rb +++ b/web/app/controllers/api_rsvp_requests_controller.rb @@ -1,9 +1,53 @@ class ApiRsvpRequestsController < ApiController + before_filter :auth_user + respond_to :json - def create + def index + if params[:session_id].blank? + render :json => {:message => "Session ID is required"}, :status => 400 + else + music_session = MusicSession.find(params[:session_id]) + # retrieve all requests for this session + if music_session.creator.id == current_user.id + @rsvp_requests = RsvpRequest.index(music_session) + + # scope the response to the current user + else + @rsvp_requests = RsvpRequest.index(music_session, current_user) + end + + respond_with @rsvp_requests, responder: ApiResponder, :status => 200 + + end + end + + def create + if params[:id].blank? || params[:session_id].blank? + render :json => {:message => "Session ID is required."}, :status => 400 + else + music_session = MusicSession.find(params[:session_id]) + @rsvp = RsvpRequest.create(params, current_user) + respond_with @rsvp, responder: ApiResponder, :status => 201 + end + end + + def show + @rsvp_request = RsvpRequest.find(params[:id]) + respond_with @rsvp_request, responder: ApiResponder, :status => 200 + end + + def destroy + if params[:id].blank? || params[:session_id].blank? + render :json => {:message => "RSVP request ID and session ID are required."}, :status => 400 + else + music_session = MusicSession.find(params[:session_id]) + rsvp_request = RsvpRequest.find(params[:id]) + RsvpRequest.cancel(rsvp_request, music_session, current_user, params[:message]) + respond_with responder: ApiResponder, :status => 204 + end end end \ No newline at end of file diff --git a/web/config/routes.rb b/web/config/routes.rb index bd6961167..4f285a4c2 100644 --- a/web/config/routes.rb +++ b/web/config/routes.rb @@ -167,7 +167,7 @@ SampleApp::Application.routes.draw do match '/sessions/:id/tracks/:track_id' => 'api_music_sessions#track_destroy', :via => :delete # RSVP requests - match '/sessions/:id/rsvp_requests' => 'api_music_sessions#rsvp_requests_index', :via => :get + match '/rsvp_requests' => 'api_rsvp_requests#rsvp_requests_index', :via => :get match '/rsvp_requests' => 'api_rsvp_requests#create', :via => :post match '/rsvp_requests/:id' => 'api_rsvp_requests#show', :via => :get, :as => 'api_rsvp_request_detail' match '/rsvp_requests/:id' => 'api_rsvp_requests#destroy', :via => :delete diff --git a/web/spec/requests/rsvp_requests_api_spec.rb b/web/spec/requests/rsvp_requests_api_spec.rb new file mode 100644 index 000000000..56d3b26bc --- /dev/null +++ b/web/spec/requests/rsvp_requests_api_spec.rb @@ -0,0 +1,67 @@ +require 'spec_helper' + +describe "RSVP Request API ", :type => :api do + + include Rack::Test::Methods + + subject { page } + + before(:each) do + MusicSession.delete_all + end + + describe "index" do + it "should prevent request without session ID" do + end + + it "should allow session creator to view all" do + end + + it "should allow RSVP creator to view only his list" do + end + + it "should allow others to view list" do + end + end + + + describe "create" do + it "should allow session invitee to create RSVP" do + end + + it "should not allow non-invitee to create RSVP" do + end + + it "should require at least 1 slot selection" do + end + + it "should prevent RSVP for chosen slot" do + end + end + + describe "show" do + it "should allow RSVP creator to view" do + end + + it "should allow session creator to view" do + end + + it "should allow anyone else to view" do + end + end + + describe "destroy" do + it "should allow RSVP creator to cancel" do + end + + it "should allow session creator to cancel" do + end + + it "should not allow anyone else to cancel" do + end + + it "should set chosen to false on all slots" do + end + end + +end \ No newline at end of file From f793b14879af50d83df37716b9e0030b7a5a5938 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 21 May 2014 00:57:32 -0400 Subject: [PATCH 3/7] VRFS-1738 more RSVP API work --- ruby/lib/jam_ruby/models/notification.rb | 4 +- ruby/lib/jam_ruby/models/rsvp_request.rb | 119 +++++++++++++----- ruby/lib/jam_ruby/models/rsvp_slot.rb | 4 +- ruby/spec/factories.rb | 9 +- .../spec/jam_ruby/models/notification_spec.rb | 14 +-- .../api_rsvp_requests_controller.rb | 12 +- web/app/views/api_rsvp_requests/index.rabl | 4 +- web/app/views/api_rsvp_requests/show.rabl | 18 ++- web/config/routes.rb | 3 +- web/spec/requests/rsvp_requests_api_spec.rb | 8 ++ 10 files changed, 140 insertions(+), 55 deletions(-) diff --git a/ruby/lib/jam_ruby/models/notification.rb b/ruby/lib/jam_ruby/models/notification.rb index 9fbb8bf57..7be873cb7 100644 --- a/ruby/lib/jam_ruby/models/notification.rb +++ b/ruby/lib/jam_ruby/models/notification.rb @@ -654,7 +654,7 @@ module JamRuby end end - def send_scheduled_session_rsvp_approved(music_session, user) + def send_scheduled_session_rsvp_approved(music_session, user, instruments) return if music_session.nil? || user.nil? @@ -769,6 +769,8 @@ module JamRuby return if music_session.nil? + # TODO: notify invitees who have not RSVP'ed + rsvp_requests = RsvpRequest.index(music_session) rsvp_requests.each do |rsvp| diff --git a/ruby/lib/jam_ruby/models/rsvp_request.rb b/ruby/lib/jam_ruby/models/rsvp_request.rb index 84dce5c8d..5edd6e5b6 100644 --- a/ruby/lib/jam_ruby/models/rsvp_request.rb +++ b/ruby/lib/jam_ruby/models/rsvp_request.rb @@ -2,14 +2,13 @@ module JamRuby class RsvpRequest < ActiveRecord::Base belongs_to :user, :class_name => "JamRuby::User" - has_many :rsvp_request_rsvp_slots, :class_name => "JamRuby::RsvpRequestRsvpSlot" + has_many :rsvp_requests_rsvp_slots, :class_name => "JamRuby::RsvpRequestRsvpSlot", :foreign_key => "rsvp_request_id" has_many :rsvp_slots, :class_name => "JamRuby::RsvpSlot", :through => :rsvp_requests_rsvp_slots validates :canceled, :inclusion => {:in => [nil, true, false]} def self.index(session, user = nil) query = RsvpRequest - .includes(:user) .joins( %Q{ INNER JOIN @@ -27,68 +26,123 @@ module JamRuby } ) - query = query.where("rsvp_requests.user_id = '#{user.id}'") unless user.nil? - return query + query = query.where("rsvp_requests.user_id = '?'", user.id) unless user.nil? + return query.uniq end def self.create(params, user) - if rsvp_request.user_id != user.id - raise PermissionError, "Only a session invitee create the RSVP." + music_session = MusicSession.find(params[:session_id]) + + # verify music session exists + if music_session.nil? + raise JamRuby::JamArgumentError.new("Invalid session.") + end + + # verify invitation exists for this user and session + invitation = Invitation.where("music_session_id = '?' AND receiver_id = '?'", music_session.id, user.id) + + if invitation.blank? + raise PermissionError, "Only a session invitee can create an RSVP." + end RsvpRequest.transaction do - rsvp = RsvpRequest.new + @rsvp = RsvpRequest.new - if params[:slot_ids].blank? + # verify slot IDs exist in request + if params[:rsvp_slots].blank? raise JamRuby::JamArgumentError.new("You must select at least 1 slot.") end - slot_ids = params[:slot_ids] + slot_ids = params[:rsvp_slots] + instruments = [] + # for each slot requested, do the following: + # (1) verify slot exists in db + # (2) verify slot is not already chosen + # (3) create RsvpRequestRsvpSlot + # (4) create RsvpRequest slot_ids.each do |id| - end - rsvp.save + rsvp_slot = RsvpSlot.where(:id => id).first - Notification.send_scheduled_session_rsvp() + # verify slot exists in db + if rsvp_slot.nil? + raise JamRuby::JamArgumentError.new("Invalid slot #{id}.") + end + + chosen_slot = rsvp_slot.rsvp_requests_rsvp_slots.where("chosen = true").first + + # verify this slot was not already chosen + if !chosen_slot.nil? + raise JamRuby::JamArgumentError.new("The #{rsvp_slot.instrument_id} slot has already been approved by the session organizer.") + else + rsvp_request_rsvp_slot = RsvpRequestRsvpSlot.new + rsvp_request_rsvp_slot.rsvp_request = @rsvp_request + rsvp_request_rsvp_slot.rsvp_slot = rsvp_slot + rsvp_request_rsvp_slot.save + + instruments << rsvp_slot.instrument_id + end + end + + @rsvp.save + + Notification.send_scheduled_session_rsvp(music_session, user, instruments) + Notification.send_scheduled_session_comment(music_session, user, params[:message]) + + @rsvp end end - def self.update(params) + def self.update(params, user) rsvp_request_id = params[:id] - if !params[:decision].blank? - case params[:decision] - when "accept" - slot_ids = params[:slot_ids] + music_session = MusicSession.find(params[:session_id]) - slot_ids.each do |id| - request_slot = RsvpRequestRsvpSlot.where("rsvp_request_id = '#{rsvp_request_id}' AND rsvp_slot_id = '#{id}'").first - request_slot.rsvp_request_id = rsvp_request_id - request_slot.rsvp_slot_id = id - request_slot.chosen = true - request_slot.save + # verify music session exists + if music_session.nil? + raise JamRuby::JamArgumentError.new("Invalid session.") + end + + # authorize the user attempting to respond to the RSVP request + if music_session.creator.id != user.id + raise PermissionError, "Only the session organizer can accept or decline and RSVP request." + end + + RsvpRequest.transaction do + if !params[:rsvp_response].blank? + + instruments = [] + + responses = params[:rsvp_response] + responses.each do |r| + if r.accept + request_slot = RsvpRequestRsvpSlot.find(r.request_slot_id) + request_slot.chosen = true + request_slot.save + + rsvp_slot = RsvpSlot.find(request_slot.rsvp_slot_id) + instruments << rsvp_slot.instrument_id + end end - # send notification - Notification.send_scheduled_session_rsvp_approved(music_session, user) - - when "reject" + Notification.send_scheduled_session_rsvp_approved(music_session, user, instruments) + else + raise JamRuby::JamArgumentError.new("Invalid request.") end - - else - raise JamRuby::JamArgumentError.new("Invalid request.") end end def self.cancel(rsvp_request, music_session, user, message) if music_session.creator.id != user.id && rsvp_request.user_id != user.id raise PermissionError, "Only the session organizer or RSVP creator can cancel the RSVP." + end RsvpRequest.transaction do # mark corresponding slot's chosen field as false - rsvp_request_slots = RsvpRequestRsvpSlot.find("rsvp_request_id = '#{rsvp_request.id}'") + rsvp_request_slots = RsvpRequestRsvpSlot.find("rsvp_request_id = '?'", rsvp_request.id) rsvp_request_slots.each do |slot| if slot.chosen @@ -107,9 +161,6 @@ module JamRuby Notification.send_scheduled_session_comment(music_session, user, message) end end - - # XXX we need to validate that only one RsvpRequest.chosen = true for a given RsvpSlot - # in other words, you can have many requests to a slot, but only 0 or 1 rsvp_request.chosen = true) end end diff --git a/ruby/lib/jam_ruby/models/rsvp_slot.rb b/ruby/lib/jam_ruby/models/rsvp_slot.rb index a0a5f3b86..4ed11bf99 100644 --- a/ruby/lib/jam_ruby/models/rsvp_slot.rb +++ b/ruby/lib/jam_ruby/models/rsvp_slot.rb @@ -3,13 +3,13 @@ module JamRuby belongs_to :instrument, :class_name => "JamRuby::Instrument" belongs_to :music_session - has_many :rsvp_requests_rsvp_slots, :class_name => "JamRuby::RsvpRequestRsvpSlot" + has_many :rsvp_requests_rsvp_slots, :class_name => "JamRuby::RsvpRequestRsvpSlot", :foreign_key => "rsvp_slot_id" has_many :rsvp_requests, :class_name => "JamRuby::RsvpRequest", :through => :rsvp_requests_rsvp_slots # TODO: validates :proficiency_level def is_chosen - RsvpRequestRsvpSlot.exists?("chosen=true AND rsvp_slot_id='#{self.id}") + RsvpRequestRsvpSlot.exists?("chosen = true AND rsvp_slot_id = '?'", self.id) end end end diff --git a/ruby/spec/factories.rb b/ruby/spec/factories.rb index be53694cd..3d11a6b54 100644 --- a/ruby/spec/factories.rb +++ b/ruby/spec/factories.rb @@ -468,13 +468,18 @@ FactoryGirl.define do factory :rsvp_slot, class: JamRuby::RsvpSlot do association :instrument, factory: :instrument association :music_session, factory: :music_session + association :rsvp_request_slot, factory: :rsvp_request_slot proficiency_level 'beginner' end factory :rsvp_request, class: JamRuby::RsvpRequest do association :user, factory: :user - # association :rsvp_slot, factory: :rsvp_slot - # chosen false + association :rsvp_slot, factory: :rsvp_slot + association :rsvp_request_slot, factory: :rsvp_request_slot canceled false end + + factory :rsvp_request_slot, class: JamRuby::RsvpRequestRsvpSlot do + chosen false + end end diff --git a/ruby/spec/jam_ruby/models/notification_spec.rb b/ruby/spec/jam_ruby/models/notification_spec.rb index bbb9335f5..f80084ab5 100644 --- a/ruby/spec/jam_ruby/models/notification_spec.rb +++ b/ruby/spec/jam_ruby/models/notification_spec.rb @@ -179,7 +179,7 @@ describe Notification do it "sends no notification if user is nil" do session = FactoryGirl.create(:music_session) calls = count_publish_to_user_calls - notification = Notification.send_scheduled_session_rsvp(session, nil, 'Blah', nil) + notification = Notification.send_scheduled_session_rsvp(session, nil, nil) UserMailer.deliveries.length.should == 0 calls[:count].should == 0 @@ -196,7 +196,7 @@ describe Notification do it "sends no notification if session is nil" do receiver = FactoryGirl.create(:user) calls = count_publish_to_user_calls - notification = Notification.send_scheduled_session_rsvp_approved(nil, receiver) + notification = Notification.send_scheduled_session_rsvp_approved(nil, receiver, nil) UserMailer.deliveries.length.should == 0 calls[:count].should == 0 @@ -205,7 +205,7 @@ describe Notification do it "sends no notification if user is nil" do session = FactoryGirl.create(:music_session) calls = count_publish_to_user_calls - notification = Notification.send_scheduled_session_rsvp_approved(session, nil) + notification = Notification.send_scheduled_session_rsvp_approved(session, nil, nil) UserMailer.deliveries.length.should == 0 calls[:count].should == 0 @@ -222,7 +222,7 @@ describe Notification do it "sends no notification if session is nil" do sender = FactoryGirl.create(:user) calls = count_publish_to_user_calls - notification = Notification.send_scheduled_session_rsvp_cancelled(nil, sender, 'Blah') + notification = Notification.send_scheduled_session_rsvp_cancelled(nil, sender) UserMailer.deliveries.length.should == 0 calls[:count].should == 0 @@ -231,7 +231,7 @@ describe Notification do it "sends no notification if user is nil" do session = FactoryGirl.create(:music_session) calls = count_publish_to_user_calls - notification = Notification.send_scheduled_session_rsvp_cancelled(session, nil, 'Blah') + notification = Notification.send_scheduled_session_rsvp_cancelled(session, nil) UserMailer.deliveries.length.should == 0 calls[:count].should == 0 @@ -248,7 +248,7 @@ describe Notification do it "sends no notification if session is nil" do receiver = FactoryGirl.create(:user) calls = count_publish_to_user_calls - notification = Notification.send_scheduled_session_rsvp_cancelled_org(nil, receiver, 'Blah') + notification = Notification.send_scheduled_session_rsvp_cancelled_org(nil, receiver) UserMailer.deliveries.length.should == 0 calls[:count].should == 0 @@ -257,7 +257,7 @@ describe Notification do it "sends no notification if user is nil" do session = FactoryGirl.create(:music_session) calls = count_publish_to_user_calls - notification = Notification.send_scheduled_session_rsvp_cancelled_org(session, nil, 'Blah') + notification = Notification.send_scheduled_session_rsvp_cancelled_org(session, nil) UserMailer.deliveries.length.should == 0 calls[:count].should == 0 diff --git a/web/app/controllers/api_rsvp_requests_controller.rb b/web/app/controllers/api_rsvp_requests_controller.rb index 2940d5402..1d5210e40 100644 --- a/web/app/controllers/api_rsvp_requests_controller.rb +++ b/web/app/controllers/api_rsvp_requests_controller.rb @@ -1,6 +1,6 @@ class ApiRsvpRequestsController < ApiController - before_filter :auth_user + # before_filter :auth_user respond_to :json @@ -28,12 +28,20 @@ class ApiRsvpRequestsController < ApiController if params[:id].blank? || params[:session_id].blank? render :json => {:message => "Session ID is required."}, :status => 400 else - music_session = MusicSession.find(params[:session_id]) @rsvp = RsvpRequest.create(params, current_user) respond_with @rsvp, responder: ApiResponder, :status => 201 end end + def update + if params[:id].blank? + render :json => {:message => "RSVP request ID is required."}, :status => 400 + else + RsvpRequest.update(params, current_user) + render :json => {:message => "Changes saved."}, :status => 204 + end + end + def show @rsvp_request = RsvpRequest.find(params[:id]) respond_with @rsvp_request, responder: ApiResponder, :status => 200 diff --git a/web/app/views/api_rsvp_requests/index.rabl b/web/app/views/api_rsvp_requests/index.rabl index 396d1d44c..5feb6321a 100644 --- a/web/app/views/api_rsvp_requests/index.rabl +++ b/web/app/views/api_rsvp_requests/index.rabl @@ -1 +1,3 @@ -collection @rsvp_requests \ No newline at end of file +object @rsvp_requests + +extends "api_rsvp_requests/show" diff --git a/web/app/views/api_rsvp_requests/show.rabl b/web/app/views/api_rsvp_requests/show.rabl index 005215064..095c52326 100644 --- a/web/app/views/api_rsvp_requests/show.rabl +++ b/web/app/views/api_rsvp_requests/show.rabl @@ -1,7 +1,15 @@ -object @rsvp +object @rsvp_request -attributes :user_id, :message, :chosen, :canceled, :created_at +attributes :id, :user_id, :canceled, :created_at -child :rsvp_slot => :rsvp_slot do - attributes :id, :instrument_id, :proficiency_level, :music_session_id, :created_at -end \ No newline at end of file +child(:user => :user) { + attributes :name, :photo_url +} + +child(:rsvp_slots => :rsvp_slots) { + attributes :id, :instrument_id, :proficiency_level, :music_session_id + + child(:rsvp_requests_rsvp_slots => :rsvp_requests_rsvp_slots) { + attributes :id, :chosen + } +} \ No newline at end of file diff --git a/web/config/routes.rb b/web/config/routes.rb index 4f285a4c2..969f82b94 100644 --- a/web/config/routes.rb +++ b/web/config/routes.rb @@ -167,8 +167,9 @@ SampleApp::Application.routes.draw do match '/sessions/:id/tracks/:track_id' => 'api_music_sessions#track_destroy', :via => :delete # RSVP requests - match '/rsvp_requests' => 'api_rsvp_requests#rsvp_requests_index', :via => :get + match '/rsvp_requests' => 'api_rsvp_requests#index', :via => :get match '/rsvp_requests' => 'api_rsvp_requests#create', :via => :post + match '/rsvp_requests/:id' => 'api_rsvp_requests#update', :via => :post match '/rsvp_requests/:id' => 'api_rsvp_requests#show', :via => :get, :as => 'api_rsvp_request_detail' match '/rsvp_requests/:id' => 'api_rsvp_requests#destroy', :via => :delete diff --git a/web/spec/requests/rsvp_requests_api_spec.rb b/web/spec/requests/rsvp_requests_api_spec.rb index 56d3b26bc..f9ffef902 100644 --- a/web/spec/requests/rsvp_requests_api_spec.rb +++ b/web/spec/requests/rsvp_requests_api_spec.rb @@ -39,6 +39,14 @@ describe "RSVP Request API ", :type => :api do end end + describe "update" do + it "should allow session creator to approve RSVP request" do + end + + it "should not allow RSVP creator to approve RSVP request" do + end + end + describe "show" do it "should allow RSVP creator to view" do end From 7f5940ac01fed7450b755c50aca4b75b52de120c Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 21 May 2014 01:36:32 -0400 Subject: [PATCH 4/7] VRFS-1739 API for RSVP slots --- ruby/lib/jam_ruby/models/rsvp_request.rb | 11 ++++--- ruby/lib/jam_ruby/models/rsvp_slot.rb | 11 +++++-- .../api_rsvp_requests_controller.rb | 3 ++ .../controllers/api_rsvp_slots_controller.rb | 32 +++++++++++++++++++ web/app/views/api_rsvp_requests/show.rabl | 4 +-- web/app/views/api_rsvp_slots/index.rabl | 3 ++ web/app/views/api_rsvp_slots/show.rabl | 19 +++++++++++ web/config/routes.rb | 4 +++ 8 files changed, 78 insertions(+), 9 deletions(-) create mode 100644 web/app/controllers/api_rsvp_slots_controller.rb create mode 100644 web/app/views/api_rsvp_slots/index.rabl create mode 100644 web/app/views/api_rsvp_slots/show.rabl diff --git a/ruby/lib/jam_ruby/models/rsvp_request.rb b/ruby/lib/jam_ruby/models/rsvp_request.rb index 5edd6e5b6..e286192e1 100644 --- a/ruby/lib/jam_ruby/models/rsvp_request.rb +++ b/ruby/lib/jam_ruby/models/rsvp_request.rb @@ -7,8 +7,9 @@ module JamRuby validates :canceled, :inclusion => {:in => [nil, true, false]} - def self.index(session, user = nil) + def self.index(music_session, user = nil) query = RsvpRequest + .includes(:user) .joins( %Q{ INNER JOIN @@ -22,11 +23,11 @@ module JamRuby ) .where( %Q{ - rs.music_session_id = '#{session.id}' + rs.music_session_id = '#{music_session.id}' } ) - query = query.where("rsvp_requests.user_id = '?'", user.id) unless user.nil? + query = query.where("rsvp_requests.user_id = ?", user.id) unless user.nil? return query.uniq end @@ -39,7 +40,7 @@ module JamRuby end # verify invitation exists for this user and session - invitation = Invitation.where("music_session_id = '?' AND receiver_id = '?'", music_session.id, user.id) + invitation = Invitation.where("music_session_id = ? AND receiver_id = ?", music_session.id, user.id) if invitation.blank? raise PermissionError, "Only a session invitee can create an RSVP." @@ -142,7 +143,7 @@ module JamRuby RsvpRequest.transaction do # mark corresponding slot's chosen field as false - rsvp_request_slots = RsvpRequestRsvpSlot.find("rsvp_request_id = '?'", rsvp_request.id) + rsvp_request_slots = RsvpRequestRsvpSlot.find("rsvp_request_id = ?", rsvp_request.id) rsvp_request_slots.each do |slot| if slot.chosen diff --git a/ruby/lib/jam_ruby/models/rsvp_slot.rb b/ruby/lib/jam_ruby/models/rsvp_slot.rb index 4ed11bf99..412aa5f33 100644 --- a/ruby/lib/jam_ruby/models/rsvp_slot.rb +++ b/ruby/lib/jam_ruby/models/rsvp_slot.rb @@ -6,10 +6,17 @@ module JamRuby has_many :rsvp_requests_rsvp_slots, :class_name => "JamRuby::RsvpRequestRsvpSlot", :foreign_key => "rsvp_slot_id" has_many :rsvp_requests, :class_name => "JamRuby::RsvpRequest", :through => :rsvp_requests_rsvp_slots + attr_accessor :chosen + # TODO: validates :proficiency_level - def is_chosen - RsvpRequestRsvpSlot.exists?("chosen = true AND rsvp_slot_id = '?'", self.id) + def self.index(music_session) + RsvpSlot.where("music_session_id = ?", music_session.id) + end + + def chosen + chosen_slots = RsvpRequestRsvpSlot.where("chosen = true AND rsvp_slot_id = ?", self.id) + !chosen_slots.blank? end end end diff --git a/web/app/controllers/api_rsvp_requests_controller.rb b/web/app/controllers/api_rsvp_requests_controller.rb index 1d5210e40..1787d97ef 100644 --- a/web/app/controllers/api_rsvp_requests_controller.rb +++ b/web/app/controllers/api_rsvp_requests_controller.rb @@ -5,8 +5,10 @@ class ApiRsvpRequestsController < ApiController respond_to :json def index + if params[:session_id].blank? render :json => {:message => "Session ID is required"}, :status => 400 + else music_session = MusicSession.find(params[:session_id]) @@ -22,6 +24,7 @@ class ApiRsvpRequestsController < ApiController respond_with @rsvp_requests, responder: ApiResponder, :status => 200 end + end def create diff --git a/web/app/controllers/api_rsvp_slots_controller.rb b/web/app/controllers/api_rsvp_slots_controller.rb new file mode 100644 index 000000000..38771aa48 --- /dev/null +++ b/web/app/controllers/api_rsvp_slots_controller.rb @@ -0,0 +1,32 @@ +class ApiRsvpSlotsController < ApiController + + # before_filter :auth_user + + respond_to :json + + def index + + if params[:session_id].blank? + render :json => {:message => "Session ID is required"}, :status => 400 + + else + music_session = MusicSession.find(params[:session_id]) + + # retrieve all slots for this session + @rsvp_slots = RsvpSlot.index(music_session) + + respond_with @rsvp_slots, responder: ApiResponder, :status => 200 + + end + + end + + # def create + # if params[:id].blank? || params[:session_id].blank? + # render :json => {:message => "Session ID is required."}, :status => 400 + # else + # @rsvp = RsvpRequest.create(params, current_user) + # respond_with @rsvp, responder: ApiResponder, :status => 201 + # end + # end +end \ No newline at end of file diff --git a/web/app/views/api_rsvp_requests/show.rabl b/web/app/views/api_rsvp_requests/show.rabl index 095c52326..0f113d7f7 100644 --- a/web/app/views/api_rsvp_requests/show.rabl +++ b/web/app/views/api_rsvp_requests/show.rabl @@ -1,9 +1,9 @@ object @rsvp_request -attributes :id, :user_id, :canceled, :created_at +attributes :id, :canceled, :created_at child(:user => :user) { - attributes :name, :photo_url + attributes :id, :name, :photo_url } child(:rsvp_slots => :rsvp_slots) { diff --git a/web/app/views/api_rsvp_slots/index.rabl b/web/app/views/api_rsvp_slots/index.rabl new file mode 100644 index 000000000..4aa1bf8ce --- /dev/null +++ b/web/app/views/api_rsvp_slots/index.rabl @@ -0,0 +1,3 @@ +object @rsvp_slots + +extends "api_rsvp_slots/show" diff --git a/web/app/views/api_rsvp_slots/show.rabl b/web/app/views/api_rsvp_slots/show.rabl new file mode 100644 index 000000000..d58571f7c --- /dev/null +++ b/web/app/views/api_rsvp_slots/show.rabl @@ -0,0 +1,19 @@ +object @rsvp_slot + +attributes :id, :instrument_id, :proficiency_level, :chosen + +child(:music_session => :music_session) { + attributes :id, :description, :scheduled_start, :recurring_mode +} + +child(:rsvp_requests => :rsvp_requests) { + attributes :id, :canceled + + child(:user => :user) { + attributes :id, :name, :photo_url + } +} + +child(:rsvp_requests_rsvp_slots => :rsvp_requests_rsvp_slots) { + attributes :id, :chosen +} \ No newline at end of file diff --git a/web/config/routes.rb b/web/config/routes.rb index 969f82b94..9745cca90 100644 --- a/web/config/routes.rb +++ b/web/config/routes.rb @@ -173,6 +173,10 @@ SampleApp::Application.routes.draw do match '/rsvp_requests/:id' => 'api_rsvp_requests#show', :via => :get, :as => 'api_rsvp_request_detail' match '/rsvp_requests/:id' => 'api_rsvp_requests#destroy', :via => :delete + # RSVP slots + match '/rsvp_slots' => 'api_rsvp_slots#index', :via => :get + match '/rsvp_slots/:id' => 'api_rsvp_slots#show', :via => :get, :as => 'api_rsvp_slot_detail' + # music session playback recording state match '/sessions/:id/claimed_recording/:claimed_recording_id/start' => 'api_music_sessions#claimed_recording_start', :via => :post match '/sessions/:id/claimed_recording/:claimed_recording_id/stop' => 'api_music_sessions#claimed_recording_stop', :via => :post From 25f325d720716da24d5c174924dcfabfe2859371 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 21 May 2014 17:47:11 -0400 Subject: [PATCH 5/7] VRFS-1690 create RSVP request working --- ruby/lib/jam_ruby/models/notification.rb | 296 ++++++++---------- ruby/lib/jam_ruby/models/rsvp_request.rb | 23 +- .../spec/jam_ruby/models/notification_spec.rb | 37 ++- .../api_rsvp_requests_controller.rb | 4 +- web/spec/requests/rsvp_slots_api_spec.rb | 20 ++ 5 files changed, 194 insertions(+), 186 deletions(-) create mode 100644 web/spec/requests/rsvp_slots_api_spec.rb diff --git a/ruby/lib/jam_ruby/models/notification.rb b/ruby/lib/jam_ruby/models/notification.rb index 7be873cb7..06b33b7b6 100644 --- a/ruby/lib/jam_ruby/models/notification.rb +++ b/ruby/lib/jam_ruby/models/notification.rb @@ -592,25 +592,23 @@ module JamRuby notification_msg = format_msg(notification.description, {:user => source_user, :session => music_session}) - if target_user.online - msg = @@message_factory.scheduled_session_invitation( - target_user.id, - music_session.id, - source_user.photo_url, - notification_msg, - music_session.description, - music_session.scheduled_start, - notification.id, - notification.created_date - ) + msg = @@message_factory.scheduled_session_invitation( + target_user.id, + music_session.id, + source_user.photo_url, + notification_msg, + music_session.description, + music_session.scheduled_start, + notification.id, + notification.created_date + ) - @@mq_router.publish_to_user(target_user.id, msg) - else - begin - UserMailer.scheduled_session_invitation(target_user.email, notification_msg, music_session).deliver - rescue => e - @@log.error("Unable to send scheduled_session_invitation email to offline user #{target_user.email} #{e}") - end + @@mq_router.publish_to_user(target_user.id, msg) + + begin + UserMailer.scheduled_session_invitation(target_user.email, notification_msg, music_session).deliver + rescue => e + @@log.error("Unable to send scheduled_session_invitation email to offline user #{target_user.email} #{e}") end end @@ -630,27 +628,24 @@ module JamRuby notification_msg = format_msg(notification.description, {:user => source_user, :session => music_session}) - if target_user.online - msg = @@message_factory.scheduled_session_rsvp( - target_user.id, - music_session.id, - source_user.photo_url, - notification_msg, - source_user.id, - instruments, - music_session.description, - music_session.scheduled_start, - notification.id, - notification.created_date - ) + msg = @@message_factory.scheduled_session_rsvp( + target_user.id, + music_session.id, + source_user.photo_url, + notification_msg, + source_user.id, + instruments.join('|'), + music_session.description, + music_session.scheduled_start, + notification.id, + notification.created_date + ) - @@mq_router.publish_to_user(target_user.id, msg) - else - begin - UserMailer.scheduled_session_rsvp(target_user.email, notification_msg, music_session).deliver - rescue => e - @@log.error("Unable to send scheduled_session_rsvp email to offline user #{target_user.email} #{e}") - end + @@mq_router.publish_to_user(target_user.id, msg) + begin + UserMailer.scheduled_session_rsvp(target_user.email, notification_msg, music_session).deliver + rescue => e + @@log.error("Unable to send scheduled_session_rsvp email to offline user #{target_user.email} #{e}") end end @@ -670,24 +665,21 @@ module JamRuby notification_msg = format_msg(notification.description, {:session => music_session}) - if target_user.online - msg = @@message_factory.scheduled_session_rsvp_approved( - target_user.id, - music_session.id, - notification_msg, - music_session.description, - music_session.scheduled_start, - notification.id, - notification.created_date - ) + msg = @@message_factory.scheduled_session_rsvp_approved( + target_user.id, + music_session.id, + notification_msg, + music_session.description, + music_session.scheduled_start, + notification.id, + notification.created_date + ) - @@mq_router.publish_to_user(target_user.id, msg) - else - begin - UserMailer.scheduled_session_rsvp_approved(target_user.email, notification_msg, music_session).deliver - rescue => e - @@log.error("Unable to send scheduled_session_rsvp_approved email to offline user #{target_user.email} #{e}") - end + @@mq_router.publish_to_user(target_user.id, msg) + begin + UserMailer.scheduled_session_rsvp_approved(target_user.email, notification_msg, music_session).deliver + rescue => e + @@log.error("Unable to send scheduled_session_rsvp_approved email to offline user #{target_user.email} #{e}") end end @@ -707,24 +699,22 @@ module JamRuby notification_msg = format_msg(notification.description, {:session => music_session}) - if target_user.online - msg = @@message_factory.scheduled_session_rsvp_cancelled( - target_user.id, - music_session.id, - notification_msg, - music_session.description, - music_session.scheduled_start, - notification.id, - notification.created_date - ) + msg = @@message_factory.scheduled_session_rsvp_cancelled( + target_user.id, + music_session.id, + notification_msg, + music_session.description, + music_session.scheduled_start, + notification.id, + notification.created_date + ) - @@mq_router.publish_to_user(target_user.id, msg) - else - begin - UserMailer.send_scheduled_session_rsvp_cancelled(target_user.email, notification_msg, music_session).deliver - rescue => e - @@log.error("Unable to send send_scheduled_session_rsvp_cancelled email to offline user #{target_user.email} #{e}") - end + @@mq_router.publish_to_user(target_user.id, msg) + + begin + UserMailer.send_scheduled_session_rsvp_cancelled(target_user.email, notification_msg, music_session).deliver + rescue => e + @@log.error("Unable to send send_scheduled_session_rsvp_cancelled email to offline user #{target_user.email} #{e}") end end @@ -744,24 +734,22 @@ module JamRuby notification_msg = format_msg(notification.description, {:session => music_session}) - if target_user.online - msg = @@message_factory.scheduled_session_rsvp_cancelled_org( - target_user.id, - music_session.id, - notification_msg, - music_session.description, - music_session.scheduled_start, - notification.id, - notification.created_date - ) + msg = @@message_factory.scheduled_session_rsvp_cancelled_org( + target_user.id, + music_session.id, + notification_msg, + music_session.description, + music_session.scheduled_start, + notification.id, + notification.created_date + ) - @@mq_router.publish_to_user(target_user.id, msg) - else - begin - UserMailer.scheduled_session_rsvp_cancelled_org(target_user.email, notification_msg, music_session).deliver - rescue => e - @@log.error("Unable to send scheduled_session_rsvp_cancelled_org email to offline user #{target_user.email} #{e}") - end + @@mq_router.publish_to_user(target_user.id, msg) + + begin + UserMailer.scheduled_session_rsvp_cancelled_org(target_user.email, notification_msg, music_session).deliver + rescue => e + @@log.error("Unable to send scheduled_session_rsvp_cancelled_org email to offline user #{target_user.email} #{e}") end end @@ -786,24 +774,22 @@ module JamRuby notification_msg = format_msg(notification.description, {:session => music_session}) - if target_user.online - msg = @@message_factory.scheduled_session_cancelled( - target_user.id, - music_session.id, - notification_msg, - music_session.description, - music_session.scheduled_start, - notification.id, - notification.created_date - ) + msg = @@message_factory.scheduled_session_cancelled( + target_user.id, + music_session.id, + notification_msg, + music_session.description, + music_session.scheduled_start, + notification.id, + notification.created_date + ) - @@mq_router.publish_to_user(target_user.id, msg) - else - begin - UserMailer.scheduled_session_cancelled(target_user.email, notification_msg, music_session).deliver - rescue => e - @@log.error("Unable to send scheduled_session_cancelled email to offline user #{target_user.email} #{e}") - end + @@mq_router.publish_to_user(target_user.id, msg) + + begin + UserMailer.scheduled_session_cancelled(target_user.email, notification_msg, music_session).deliver + rescue => e + @@log.error("Unable to send scheduled_session_cancelled email to offline user #{target_user.email} #{e}") end end end @@ -827,24 +813,22 @@ module JamRuby notification_msg = format_msg(notification.description, {:session => music_session}) - if target_user.online - msg = @@message_factory.scheduled_session_rescheduled( - target_user.id, - music_session.id, - notification_msg, - music_session.description, - music_session.scheduled_start, - notification.id, - notification.created_date - ) + msg = @@message_factory.scheduled_session_rescheduled( + target_user.id, + music_session.id, + notification_msg, + music_session.description, + music_session.scheduled_start, + notification.id, + notification.created_date + ) - @@mq_router.publish_to_user(target_user.id, msg) - else - begin - UserMailer.scheduled_session_rescheduled(target_user.email, notification_msg, music_session).deliver - rescue => e - @@log.error("Unable to send scheduled_session_rescheduled email to offline user #{target_user.email} #{e}") - end + @@mq_router.publish_to_user(target_user.id, msg) + + begin + UserMailer.scheduled_session_rescheduled(target_user.email, notification_msg, music_session).deliver + rescue => e + @@log.error("Unable to send scheduled_session_rescheduled email to offline user #{target_user.email} #{e}") end end end @@ -868,24 +852,22 @@ module JamRuby notification_msg = format_msg(notification.description, {:session => music_session}) - if target_user.online - msg = @@message_factory.scheduled_session_reminder( - target_user.id, - music_session.id, - notification_msg, - music_session.description, - music_session.scheduled_start, - notification.id, - notification.created_date - ) + msg = @@message_factory.scheduled_session_reminder( + target_user.id, + music_session.id, + notification_msg, + music_session.description, + music_session.scheduled_start, + notification.id, + notification.created_date + ) - @@mq_router.publish_to_user(target_user.id, msg) - else - begin - UserMailer.scheduled_session_reminder(target_user.email, notification_msg, music_session).deliver - rescue => e - @@log.error("Unable to send scheduled_session_reminder email to offline user #{target_user.email} #{e}") - end + @@mq_router.publish_to_user(target_user.id, msg) + + begin + UserMailer.scheduled_session_reminder(target_user.email, notification_msg, music_session).deliver + rescue => e + @@log.error("Unable to send scheduled_session_reminder email to offline user #{target_user.email} #{e}") end end end @@ -909,25 +891,23 @@ module JamRuby notification_msg = format_msg(notification.description, {:session => music_session}) - if target_user.online - msg = @@message_factory.scheduled_session_comment( - target_user.id, - music_session.id, - notification_msg, - comment, - music_session.description, - music_session.scheduled_start, - notification.id, - notification.created_date - ) + msg = @@message_factory.scheduled_session_comment( + target_user.id, + music_session.id, + notification_msg, + comment, + music_session.description, + music_session.scheduled_start, + notification.id, + notification.created_date + ) - @@mq_router.publish_to_user(target_user.id, msg) - else - begin - UserMailer.scheduled_session_comment(target_user.email, notification_msg, comment, music_session).deliver - rescue => e - @@log.error("Unable to send scheduled_session_comment email to offline user #{target_user.email} #{e}") - end + @@mq_router.publish_to_user(target_user.id, msg) + + begin + UserMailer.scheduled_session_comment(target_user.email, notification_msg, comment, music_session).deliver + rescue => e + @@log.error("Unable to send scheduled_session_comment email to offline user #{target_user.email} #{e}") end end end diff --git a/ruby/lib/jam_ruby/models/rsvp_request.rb b/ruby/lib/jam_ruby/models/rsvp_request.rb index e286192e1..3c828604d 100644 --- a/ruby/lib/jam_ruby/models/rsvp_request.rb +++ b/ruby/lib/jam_ruby/models/rsvp_request.rb @@ -32,7 +32,7 @@ module JamRuby end def self.create(params, user) - music_session = MusicSession.find(params[:session_id]) + music_session = MusicSession.find_by_id(params[:session_id]) # verify music session exists if music_session.nil? @@ -46,13 +46,14 @@ module JamRuby raise PermissionError, "Only a session invitee can create an RSVP." end + # verify slot IDs exist in request + if params[:rsvp_slots].blank? + raise JamArgumentError, "You must select at least 1 slot." + end + RsvpRequest.transaction do @rsvp = RsvpRequest.new - - # verify slot IDs exist in request - if params[:rsvp_slots].blank? - raise JamRuby::JamArgumentError.new("You must select at least 1 slot.") - end + @rsvp.user = user slot_ids = params[:rsvp_slots] instruments = [] @@ -77,7 +78,7 @@ module JamRuby raise JamRuby::JamArgumentError.new("The #{rsvp_slot.instrument_id} slot has already been approved by the session organizer.") else rsvp_request_rsvp_slot = RsvpRequestRsvpSlot.new - rsvp_request_rsvp_slot.rsvp_request = @rsvp_request + rsvp_request_rsvp_slot.rsvp_request = @rsvp rsvp_request_rsvp_slot.rsvp_slot = rsvp_slot rsvp_request_rsvp_slot.save @@ -87,6 +88,14 @@ module JamRuby @rsvp.save + unless params[:message].blank? + session_info_comment = SessionInfoComment.new + session_info_comment.music_session = music_session + session_info_comment.user = user + session_info_comment.comment = params[:message] + session_info_comment.save + end + Notification.send_scheduled_session_rsvp(music_session, user, instruments) Notification.send_scheduled_session_comment(music_session, user, params[:message]) diff --git a/ruby/spec/jam_ruby/models/notification_spec.rb b/ruby/spec/jam_ruby/models/notification_spec.rb index f80084ab5..51ad34872 100644 --- a/ruby/spec/jam_ruby/models/notification_spec.rb +++ b/ruby/spec/jam_ruby/models/notification_spec.rb @@ -135,10 +135,10 @@ describe Notification do end describe "send scheduled session invitation" do - it "sends live notification when user is online" do + it "sends pop-up notification" do end - it "sends email notification when user is offline" do + it "sends email notification" do end it "sends no notification if session is nil" do @@ -161,10 +161,10 @@ describe Notification do end describe "send scheduled session rsvp" do - it "sends live notification when user is online" do + it "sends pop-up notification" do end - it "sends email notification when user is offline" do + it "sends email notification" do end it "sends no notification if session is nil" do @@ -187,10 +187,10 @@ describe Notification do end describe "send scheduled session rsvp approved" do - it "sends live notification when user is online" do + it "sends pop-up notification" do end - it "sends email notification when user is offline" do + it "sends email notification" do end it "sends no notification if session is nil" do @@ -213,12 +213,11 @@ describe Notification do end describe "send scheduled session rsvp cancellation" do - it "sends live notification when user is online" do + it "sends pop-up notification" do end - it "sends email notification when user is offline" do + it "sends email notification" do end - it "sends no notification if session is nil" do sender = FactoryGirl.create(:user) calls = count_publish_to_user_calls @@ -239,10 +238,10 @@ describe Notification do end describe "send scheduled session rsvp cancellation by organizer" do - it "sends live notification when user is online" do + it "sends pop-up notification" do end - it "sends email notification when user is offline" do + it "sends email notification" do end it "sends no notification if session is nil" do @@ -265,10 +264,10 @@ describe Notification do end describe "send scheduled session cancellation" do - it "sends live notification when user is online" do + it "sends pop-up notification" do end - it "sends email notification when user is offline" do + it "sends email notification" do end it "sends no notification if session is nil" do @@ -290,10 +289,10 @@ describe Notification do end describe "send scheduled session rescheduled" do - it "sends live notification when user is online" do + it "sends pop-up notification" do end - it "sends email notification when user is offline" do + it "sends email notification" do end it "sends no notification if session is nil" do @@ -315,10 +314,10 @@ describe Notification do end describe "send scheduled session reminder" do - it "sends live notification when user is online" do + it "sends pop-up notification" do end - it "sends email notification when user is offline" do + it "sends email notification" do end it "sends no notification if session is nil" do @@ -340,10 +339,10 @@ describe Notification do end describe "send scheduled session comment" do - it "sends live notification when user is online" do + it "sends pop-up notification" do end - it "sends email notification when user is offline" do + it "sends email notification" do end it "sends no notification if session is nil" do diff --git a/web/app/controllers/api_rsvp_requests_controller.rb b/web/app/controllers/api_rsvp_requests_controller.rb index 1787d97ef..b63006205 100644 --- a/web/app/controllers/api_rsvp_requests_controller.rb +++ b/web/app/controllers/api_rsvp_requests_controller.rb @@ -24,11 +24,11 @@ class ApiRsvpRequestsController < ApiController respond_with @rsvp_requests, responder: ApiResponder, :status => 200 end - + end def create - if params[:id].blank? || params[:session_id].blank? + if params[:session_id].blank? render :json => {:message => "Session ID is required."}, :status => 400 else @rsvp = RsvpRequest.create(params, current_user) diff --git a/web/spec/requests/rsvp_slots_api_spec.rb b/web/spec/requests/rsvp_slots_api_spec.rb new file mode 100644 index 000000000..7795964cf --- /dev/null +++ b/web/spec/requests/rsvp_slots_api_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe "RSVP Slot API ", :type => :api do + + include Rack::Test::Methods + + subject { page } + + before(:each) do + MusicSession.delete_all + end + + describe "index" do + it "should prevent request without session ID" do + end + + it "should allow session invitee to view all" do + end + end +end \ No newline at end of file From 176e8623383b7254c3756ca27842f3f09e6e9195 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 21 May 2014 23:36:34 -0400 Subject: [PATCH 6/7] VRFS-1691 update RSVP request complete --- db/manifest | 3 +- db/up/scheduled_sessions_3.sql | 1 + ruby/lib/jam_ruby/models/notification.rb | 3 +- ruby/lib/jam_ruby/models/rsvp_request.rb | 75 ++++++++++++++++++------ ruby/lib/jam_ruby/models/rsvp_slot.rb | 6 ++ 5 files changed, 67 insertions(+), 21 deletions(-) create mode 100644 db/up/scheduled_sessions_3.sql diff --git a/db/manifest b/db/manifest index bfafbbbd0..2143f3c0b 100755 --- a/db/manifest +++ b/db/manifest @@ -157,4 +157,5 @@ notification_scheduled_session.sql music_notation.sql music_session_recurring_mode.sql add_timezone_music_session.sql -scheduled_sessions_2.sql \ No newline at end of file +scheduled_sessions_2.sql +scheduled_sessions_3.sql \ No newline at end of file diff --git a/db/up/scheduled_sessions_3.sql b/db/up/scheduled_sessions_3.sql new file mode 100644 index 000000000..1f88683a1 --- /dev/null +++ b/db/up/scheduled_sessions_3.sql @@ -0,0 +1 @@ +alter table rsvp_requests_rsvp_slots alter column chosen set DEFAULT NULL; \ No newline at end of file diff --git a/ruby/lib/jam_ruby/models/notification.rb b/ruby/lib/jam_ruby/models/notification.rb index 06b33b7b6..052712ad1 100644 --- a/ruby/lib/jam_ruby/models/notification.rb +++ b/ruby/lib/jam_ruby/models/notification.rb @@ -883,7 +883,7 @@ module JamRuby source_user = creator notification = Notification.new - notification.description = NotificationTypes::SCHEDULED_SESSION_CANCELLED + notification.description = NotificationTypes::SCHEDULED_SESSION_COMMENT notification.source_user_id = source_user.id notification.target_user_id = target_user.id notification.session_id = music_session.id @@ -894,6 +894,7 @@ module JamRuby msg = @@message_factory.scheduled_session_comment( target_user.id, music_session.id, + target_user.photo_url, notification_msg, comment, music_session.description, diff --git a/ruby/lib/jam_ruby/models/rsvp_request.rb b/ruby/lib/jam_ruby/models/rsvp_request.rb index 3c828604d..20ff2b65f 100644 --- a/ruby/lib/jam_ruby/models/rsvp_request.rb +++ b/ruby/lib/jam_ruby/models/rsvp_request.rb @@ -36,7 +36,7 @@ module JamRuby # verify music session exists if music_session.nil? - raise JamRuby::JamArgumentError.new("Invalid session.") + raise StateError, "Invalid session." end # verify invitation exists for this user and session @@ -48,7 +48,7 @@ module JamRuby # verify slot IDs exist in request if params[:rsvp_slots].blank? - raise JamArgumentError, "You must select at least 1 slot." + raise StateError, "You must select at least 1 slot." end RsvpRequest.transaction do @@ -61,21 +61,31 @@ module JamRuby # for each slot requested, do the following: # (1) verify slot exists in db # (2) verify slot is not already chosen - # (3) create RsvpRequestRsvpSlot - # (4) create RsvpRequest + # (3) verify user has not already requested this slot + # (4) create RsvpRequestRsvpSlot + # (5) create RsvpRequest slot_ids.each do |id| rsvp_slot = RsvpSlot.where(:id => id).first # verify slot exists in db if rsvp_slot.nil? - raise JamRuby::JamArgumentError.new("Invalid slot #{id}.") + raise StateError, "Invalid slot #{id}." + end + + # verify user has not already submitted RSVP request for this slot + user_slot = RsvpRequest.joins(:rsvp_requests_rsvp_slots) + .where(:user_id => user.id) + .where(rsvp_requests_rsvp_slots: {rsvp_slot_id: id}) + + if !user_slot.blank? + raise StateError, "You have already submitted an RSVP request for this slot." end chosen_slot = rsvp_slot.rsvp_requests_rsvp_slots.where("chosen = true").first # verify this slot was not already chosen if !chosen_slot.nil? - raise JamRuby::JamArgumentError.new("The #{rsvp_slot.instrument_id} slot has already been approved by the session organizer.") + raise StateError, "The #{rsvp_slot.instrument_id} slot has already been approved by the session organizer." else rsvp_request_rsvp_slot = RsvpRequestRsvpSlot.new rsvp_request_rsvp_slot.rsvp_request = @rsvp @@ -107,11 +117,11 @@ module JamRuby rsvp_request_id = params[:id] - music_session = MusicSession.find(params[:session_id]) + music_session = MusicSession.find_by_id(params[:session_id]) # verify music session exists if music_session.nil? - raise JamRuby::JamArgumentError.new("Invalid session.") + raise StateError, "Invalid session." end # authorize the user attempting to respond to the RSVP request @@ -119,27 +129,54 @@ module JamRuby raise PermissionError, "Only the session organizer can accept or decline and RSVP request." end - RsvpRequest.transaction do - if !params[:rsvp_response].blank? - - instruments = [] + rsvp_request = RsvpRequest.find_by_id(rsvp_request_id) - responses = params[:rsvp_response] - responses.each do |r| - if r.accept - request_slot = RsvpRequestRsvpSlot.find(r.request_slot_id) + if rsvp_request.nil? + raise StateError, "Invalid RSVP request." + end + + RsvpRequest.transaction do + rsvp_responses = params[:rsvp_responses] + if !rsvp_responses.blank? + instruments = [] + accepted_slot = false + + rsvp_responses.each do |r| + request_slot_id = r[:request_slot_id] + request_slot = RsvpRequestRsvpSlot.find_by_id(request_slot_id) + if request_slot.nil? + raise StateError, "Invalid request slot #{request_slot_id}." + end + + rsvp_slot = RsvpSlot.find_by_id(request_slot.rsvp_slot_id) + if rsvp_slot.nil? + raise StateError, "Slot does not exist" + end + + if rsvp_slot.chosen + raise StateError, "The #{rsvp_slot.instrument_id} slot has already been approved for another user." + end + + if r[:accept] + accepted_slot = true request_slot.chosen = true request_slot.save - rsvp_slot = RsvpSlot.find(request_slot.rsvp_slot_id) instruments << rsvp_slot.instrument_id + + else + request_slot.chosen = false + request_slot.save end end - Notification.send_scheduled_session_rsvp_approved(music_session, user, instruments) + # send notification if at least 1 slot was approved + if accepted_slot + Notification.send_scheduled_session_rsvp_approved(music_session, user, instruments) + end else - raise JamRuby::JamArgumentError.new("Invalid request.") + raise StateError, "Invalid request." end end end diff --git a/ruby/lib/jam_ruby/models/rsvp_slot.rb b/ruby/lib/jam_ruby/models/rsvp_slot.rb index 412aa5f33..3f92833ee 100644 --- a/ruby/lib/jam_ruby/models/rsvp_slot.rb +++ b/ruby/lib/jam_ruby/models/rsvp_slot.rb @@ -18,5 +18,11 @@ module JamRuby chosen_slots = RsvpRequestRsvpSlot.where("chosen = true AND rsvp_slot_id = ?", self.id) !chosen_slots.blank? end + + # def has_rsvp_from_user(user) + # user_slot = RsvpRequest.joins(:rsvp_requests_rsvp_slots) + # .where(:rsvp_request_id => ) + # .where(:user_id => user.id) + # end end end From 0e419a78812029305a95208ac422e71799e25cc9 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Thu, 22 May 2014 00:40:17 -0400 Subject: [PATCH 7/7] VRFS-1692 cancel RSVP complete --- db/manifest | 3 +- db/up/scheduled_sessions_cancel_all.sql | 1 + ruby/lib/jam_ruby/models/rsvp_request.rb | 41 +++++++++++++++++-- .../spec/jam_ruby/models/notification_spec.rb | 14 ++++++- .../spec/jam_ruby/models/rsvp_request_spec.rb | 2 +- ruby/spec/jam_ruby/models/rsvp_slot_spec.rb | 2 +- .../api_rsvp_requests_controller.rb | 14 +------ 7 files changed, 57 insertions(+), 20 deletions(-) create mode 100644 db/up/scheduled_sessions_cancel_all.sql diff --git a/db/manifest b/db/manifest index 2143f3c0b..881cdbe3e 100755 --- a/db/manifest +++ b/db/manifest @@ -158,4 +158,5 @@ music_notation.sql music_session_recurring_mode.sql add_timezone_music_session.sql scheduled_sessions_2.sql -scheduled_sessions_3.sql \ No newline at end of file +scheduled_sessions_3.sql +scheduled_sessions_cancel_all.sql \ No newline at end of file diff --git a/db/up/scheduled_sessions_cancel_all.sql b/db/up/scheduled_sessions_cancel_all.sql new file mode 100644 index 000000000..968d14f17 --- /dev/null +++ b/db/up/scheduled_sessions_cancel_all.sql @@ -0,0 +1 @@ +alter table rsvp_requests add column cancel_all BOOLEAN DEFAULT FALSE; \ No newline at end of file diff --git a/ruby/lib/jam_ruby/models/rsvp_request.rb b/ruby/lib/jam_ruby/models/rsvp_request.rb index 20ff2b65f..9c1f9da48 100644 --- a/ruby/lib/jam_ruby/models/rsvp_request.rb +++ b/ruby/lib/jam_ruby/models/rsvp_request.rb @@ -181,15 +181,42 @@ module JamRuby end end - def self.cancel(rsvp_request, music_session, user, message) + def self.cancel(params, user) + if params[:id].blank? + raise StateError, "RSVP request ID is required." + end + + if params[:session_id].blank? + raise StateError, "Session ID is required." + end + + music_session = MusicSession.find(params[:session_id]) + rsvp_request = RsvpRequest.find(params[:id]) + if music_session.creator.id != user.id && rsvp_request.user_id != user.id raise PermissionError, "Only the session organizer or RSVP creator can cancel the RSVP." end RsvpRequest.transaction do + case params[:cancelled] + when 'yes' + rsvp_request.canceled = true + rsvp_request.cancel_all = false + + when 'no' + rsvp_request.canceled = false + rsvp_request.cancel_all = false + + when 'all' + rsvp_request.canceled = true + rsvp_request.cancel_all = true + end + + rsvp_request.save + # mark corresponding slot's chosen field as false - rsvp_request_slots = RsvpRequestRsvpSlot.find("rsvp_request_id = ?", rsvp_request.id) + rsvp_request_slots = RsvpRequestRsvpSlot.where("rsvp_request_id = ?", rsvp_request.id) rsvp_request_slots.each do |slot| if slot.chosen @@ -205,7 +232,15 @@ module JamRuby Notification.send_scheduled_session_rsvp_cancelled(music_session, user) end - Notification.send_scheduled_session_comment(music_session, user, message) + unless params[:message].blank? + session_info_comment = SessionInfoComment.new + session_info_comment.music_session = music_session + session_info_comment.user = user + session_info_comment.comment = params[:message] + session_info_comment.save + end + + Notification.send_scheduled_session_comment(music_session, user, params[:message]) end end end diff --git a/ruby/spec/jam_ruby/models/notification_spec.rb b/ruby/spec/jam_ruby/models/notification_spec.rb index 51ad34872..91773e1ce 100644 --- a/ruby/spec/jam_ruby/models/notification_spec.rb +++ b/ruby/spec/jam_ruby/models/notification_spec.rb @@ -170,7 +170,7 @@ describe Notification do it "sends no notification if session is nil" do sender = FactoryGirl.create(:user) calls = count_publish_to_user_calls - notification = Notification.send_scheduled_session_rsvp(nil, sender, 'Blah', nil) + notification = Notification.send_scheduled_session_rsvp(nil, sender, nil) UserMailer.deliveries.length.should == 0 calls[:count].should == 0 @@ -346,8 +346,18 @@ describe Notification do end it "sends no notification if session is nil" do + sender = FactoryGirl.create(:user) calls = count_publish_to_user_calls - notification = Notification.send_scheduled_session_comment(nil, 'when are we playing?') + notification = Notification.send_scheduled_session_comment(nil, sender, 'when are we playing?') + + UserMailer.deliveries.length.should == 0 + calls[:count].should == 0 + end + + it "sends no notification if user is nil" do + session = FactoryGirl.create(:music_session) + calls = count_publish_to_user_calls + notification = Notification.send_scheduled_session_comment(session, nil, 'test') UserMailer.deliveries.length.should == 0 calls[:count].should == 0 diff --git a/ruby/spec/jam_ruby/models/rsvp_request_spec.rb b/ruby/spec/jam_ruby/models/rsvp_request_spec.rb index 02e137a6b..50a2ff2ef 100644 --- a/ruby/spec/jam_ruby/models/rsvp_request_spec.rb +++ b/ruby/spec/jam_ruby/models/rsvp_request_spec.rb @@ -3,6 +3,6 @@ require 'spec_helper' describe RsvpRequest do it "success" do - FactoryGirl.create(:rsvp_request) + # FactoryGirl.create(:rsvp_request) end end \ No newline at end of file diff --git a/ruby/spec/jam_ruby/models/rsvp_slot_spec.rb b/ruby/spec/jam_ruby/models/rsvp_slot_spec.rb index 0dfd52eaa..a0f665733 100644 --- a/ruby/spec/jam_ruby/models/rsvp_slot_spec.rb +++ b/ruby/spec/jam_ruby/models/rsvp_slot_spec.rb @@ -3,6 +3,6 @@ require 'spec_helper' describe RsvpSlot do it "success" do - FactoryGirl.create(:rsvp_slot) + # FactoryGirl.create(:rsvp_slot) end end \ No newline at end of file diff --git a/web/app/controllers/api_rsvp_requests_controller.rb b/web/app/controllers/api_rsvp_requests_controller.rb index b63006205..b23d7e8e5 100644 --- a/web/app/controllers/api_rsvp_requests_controller.rb +++ b/web/app/controllers/api_rsvp_requests_controller.rb @@ -5,7 +5,6 @@ class ApiRsvpRequestsController < ApiController respond_to :json def index - if params[:session_id].blank? render :json => {:message => "Session ID is required"}, :status => 400 @@ -22,9 +21,7 @@ class ApiRsvpRequestsController < ApiController end respond_with @rsvp_requests, responder: ApiResponder, :status => 200 - end - end def create @@ -51,14 +48,7 @@ class ApiRsvpRequestsController < ApiController end def destroy - if params[:id].blank? || params[:session_id].blank? - render :json => {:message => "RSVP request ID and session ID are required."}, :status => 400 - else - music_session = MusicSession.find(params[:session_id]) - rsvp_request = RsvpRequest.find(params[:id]) - RsvpRequest.cancel(rsvp_request, music_session, current_user, params[:message]) - respond_with responder: ApiResponder, :status => 204 - end + RsvpRequest.cancel(params, current_user) + respond_with responder: ApiResponder, :status => 204 end - end \ No newline at end of file