diff options
-rw-r--r-- | Gemfile.lock | 5 | ||||
-rw-r--r-- | users/app/controllers/v1/messages_controller.rb | 30 | ||||
-rw-r--r-- | users/app/designs/user/by_created_at_and_one_month_warning_not_sent.js | 5 | ||||
-rw-r--r-- | users/app/models/message.rb | 10 | ||||
-rw-r--r-- | users/app/models/user.rb | 37 | ||||
-rw-r--r-- | users/config/locales/en.yml | 1 | ||||
-rw-r--r-- | users/config/routes.rb | 2 | ||||
-rw-r--r-- | users/config/schedule.rb | 24 | ||||
-rw-r--r-- | users/leap_web_users.gemspec | 1 | ||||
-rw-r--r-- | users/lib/leap_web_users/engine.rb | 1 | ||||
-rw-r--r-- | users/test/functional/v1/messages_controller_test.rb | 60 |
11 files changed, 176 insertions, 0 deletions
diff --git a/Gemfile.lock b/Gemfile.lock index 09d3123..b42ac86 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -42,6 +42,7 @@ PATH leap_web_core (= 0.5.0.rc) rails_warden ruby-srp (~> 0.2.1) + whenever GEM remote: https://rubygems.org/ @@ -88,6 +89,7 @@ GEM rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) + chronic (0.9.1) client_side_validations (3.2.6) client_side_validations-simple_form (2.1.0) client_side_validations (~> 3.2.5) @@ -255,6 +257,9 @@ GEM warden (1.2.3) rack (>= 1.0) websocket-driver (0.3.1) + whenever (0.8.2) + activesupport (>= 2.3.4) + chronic (>= 0.6.3) xpath (2.0.0) nokogiri (~> 1.3) diff --git a/users/app/controllers/v1/messages_controller.rb b/users/app/controllers/v1/messages_controller.rb new file mode 100644 index 0000000..b58dfe9 --- /dev/null +++ b/users/app/controllers/v1/messages_controller.rb @@ -0,0 +1,30 @@ +module V1 + class MessagesController < ApplicationController + + before_filter :authorize_admin # not sure this is best way + respond_to :json + + # for now, will not pass unseen, so unseen will always be true + def user_messages(unseen = true) + user = User.find(params[:user_id]) + render json: (user ? user.messages : [] ) + end + + # routes ensure this is only for PUT + def mark_read + + # make sure user and message exist + if (user = User.find(params[:user_id])) && Message.find(params[:message_id]) + + user.message_ids_seen << params[:message_id] if !user.message_ids_seen.include?(params[:message_id]) #TODO: is it quicker to instead call uniq! after adding? + user.message_ids_to_see.delete(params[:message_id]) + user.save + render json: true + return + else + render json: false + end + + end + end +end diff --git a/users/app/designs/user/by_created_at_and_one_month_warning_not_sent.js b/users/app/designs/user/by_created_at_and_one_month_warning_not_sent.js new file mode 100644 index 0000000..53a95de --- /dev/null +++ b/users/app/designs/user/by_created_at_and_one_month_warning_not_sent.js @@ -0,0 +1,5 @@ +function (doc) { + if ((doc['type'] == 'User') && (doc['created_at'] != null) && (doc['one_month_warning_sent'] == null)) { + emit(doc['created_at'], 1); + } +} diff --git a/users/app/models/message.rb b/users/app/models/message.rb new file mode 100644 index 0000000..38fa71e --- /dev/null +++ b/users/app/models/message.rb @@ -0,0 +1,10 @@ +class Message < CouchRest::Model::Base + + use_database :messages + + property :text, String + + design do + end + +end diff --git a/users/app/models/user.rb b/users/app/models/user.rb index 720f5a9..6e445be 100644 --- a/users/app/models/user.rb +++ b/users/app/models/user.rb @@ -13,6 +13,10 @@ class User < CouchRest::Model::Base property :desired_service_level_code, Integer, :accessible => true property :effective_service_level_code, Integer, :accessible => true + property :message_ids_to_see, [String] + property :message_ids_seen, [String] + property :one_month_warning_sent, TrueClass + before_save :update_effective_service_level validates :login, :password_salt, :password_verifier, @@ -72,6 +76,18 @@ class User < CouchRest::Model::Base Ticket.for_user(self).limit(count).all #defaults to having most recent updated first end + def messages(unseen = true) + + message_ids = unseen ? self.message_ids_to_see : self.message_ids_to_see + self.message_ids_seen # TODO check unique? + + messages = [] + message_ids.each do |message_id| + messages << Message.find(message_id) + end + messages + + end + # DEPRECATED # # Please set the key on the identity directly @@ -110,6 +126,27 @@ class User < CouchRest::Model::Base ServiceLevel.new({id: code}) end + + def self.send_one_month_warnings + + # To determine warnings to send, need to get all users where one_month_warning_sent is not set, and where it was created greater than or equal to 1 month ago. + # TODO: might want to further limit to enabled accounts, and, based on provider's service level configuration, for particular service levels. + users_to_warn = User.by_created_at_and_one_month_warning_not_sent.endkey(Time.now-1.month) + users_to_warn.each do |user| + if !@message + # create a message for today's date + # only want to create once, and only if it will be used. + @message = Message.new(:text => I18n.t(:payment_one_month_warning, :date_in_one_month => (Time.now+1.month).strftime("%Y-%d-%m"))) + @message.save + end + + user.message_ids_to_see << @message.id + user.one_month_warning_sent = true + user.save + end + + end + protected ## diff --git a/users/config/locales/en.yml b/users/config/locales/en.yml index 934fcee..0db63eb 100644 --- a/users/config/locales/en.yml +++ b/users/config/locales/en.yml @@ -38,6 +38,7 @@ en: deactivate_account: "Deactivate the account %{username}" deactivate_description: "This will temporarily deactivate some account functionality." #todo detail exact functionality. can receive email but not send or renew client certificate? + payment_one_month_warning: "We hope you have been enjoying this service this past month. Please sign up to pay within the next month, by %{date_in_one_month}. Directions for payment are available at INSERT_URL" # # overview diff --git a/users/config/routes.rb b/users/config/routes.rb index 736b283..9a7c531 100644 --- a/users/config/routes.rb +++ b/users/config/routes.rb @@ -6,6 +6,8 @@ Rails.application.routes.draw do resources :sessions, :only => [:new, :create, :update] delete "logout" => "sessions#destroy", :as => "logout" resources :users, :only => [:create, :update, :destroy, :index] + get "user_messages/:user_id" => "messages#user_messages" + put "mark_read/:user_id/:message_id" => "messages#mark_read" end scope "(:locale)", :locale => MATCH_LOCALE do diff --git a/users/config/schedule.rb b/users/config/schedule.rb new file mode 100644 index 0000000..4ecbe7c --- /dev/null +++ b/users/config/schedule.rb @@ -0,0 +1,24 @@ +# Use this file to easily define all of your cron jobs. +# +# It's helpful, but not entirely necessary to understand cron before proceeding. +# http://en.wikipedia.org/wiki/Cron + +# Example: +# +# set :output, "/path/to/my/cron_log.log" +# +# every 2.hours do +# command "/usr/bin/some_great_command" +# runner "MyModel.some_method" +# rake "some:great:rake:task" +# end +# +# every 4.days do +# runner "AnotherModel.prune_old_records" +# end + +# Learn more: http://github.com/javan/whenever + +every 1.day, :at => '1am' do + runner "User.send_one_month_warnings" +end diff --git a/users/leap_web_users.gemspec b/users/leap_web_users.gemspec index 7d1f220..06965a7 100644 --- a/users/leap_web_users.gemspec +++ b/users/leap_web_users.gemspec @@ -19,4 +19,5 @@ Gem::Specification.new do |s| s.add_dependency "ruby-srp", "~> 0.2.1" s.add_dependency "rails_warden" + s.add_dependency "whenever" end diff --git a/users/lib/leap_web_users/engine.rb b/users/lib/leap_web_users/engine.rb index f8ed71c..61131ef 100644 --- a/users/lib/leap_web_users/engine.rb +++ b/users/lib/leap_web_users/engine.rb @@ -8,6 +8,7 @@ require "warden/session_serializer" require "warden/strategies/secure_remote_password" require "webfinger" +require "whenever" module LeapWebUsers class Engine < ::Rails::Engine diff --git a/users/test/functional/v1/messages_controller_test.rb b/users/test/functional/v1/messages_controller_test.rb new file mode 100644 index 0000000..0bc09be --- /dev/null +++ b/users/test/functional/v1/messages_controller_test.rb @@ -0,0 +1,60 @@ +require 'test_helper' + +class V1::MessagesControllerTest < ActionController::TestCase + + setup do + @message = Message.new(:text => 'a test message') + @message.save + @user = FactoryGirl.build(:user) + @user.message_ids_to_see << @message.id + @user.save + login :is_admin? => true + end + + teardown do + @user.destroy + @message.destroy + end + + test "get messages for user" do + get :user_messages, :user_id => @user.id + assert response.body.include? @message.text + assert response.body.include? @message.id + end + + test "mark message read for user" do + assert @user.message_ids_to_see.include?(@message.id) + assert !@user.message_ids_seen.include?(@message.id) + + put :mark_read, :user_id => @user.id, :message_id => @message.id + @user.reload + assert !@user.message_ids_to_see.include?(@message.id) + assert @user.message_ids_seen.include?(@message.id) + assert_json_response true + end + + test "do not get seen messages" do + put :mark_read, :user_id => @user.id, :message_id => @message.id + @user.reload + get :user_messages, :user_id => @user.id + assert !(response.body.include? @message.text) + assert !(response.body.include? @message.id) + end + + test "empty messages for non-existing user" do + get :user_messages, :user_id => 'some random string' + assert_json_response [] + end + + test "mark read responds even with bad inputs" do + put :mark_read, :user_id => 'nonsense', :message_id => 'more nonsense' + assert_json_response false + end + + test "fails if not admin" do + login :is_admin? => false + get :user_messages, :user_id => @user.id + assert_access_denied + end + +end |