diff --git a/Gemfile b/Gemfile index 52a2372e2625768f74a266186a1366f29baf03d9..c6e9fd628865eaa9020a23ace522f5cc8d46b480 100644 --- a/Gemfile +++ b/Gemfile @@ -87,6 +87,9 @@ group :development do # JavaScript runtime gem 'execjs' + + # local mailbox + gem 'mailcatcher' end group :test do diff --git a/Gemfile.lock b/Gemfile.lock index b123256c77b81af2ec2fe950f124ebc7dce9b6b7..caba06c98e1d5448a33b9931d3bdd601e03048ae 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -32,8 +32,10 @@ GEM erubis (~> 2.7.0) rails-dom-testing (~> 1.0, >= 1.0.5) rails-html-sanitizer (~> 1.0, >= 1.0.2) - active_model_serializers (0.9.5) - activemodel (>= 3.2) + active_model_serializers (0.10.0) + actionpack (>= 4.0) + activemodel (>= 4.0) + railties (>= 4.0) activejob (4.2.6) activesupport (= 4.2.6) globalid (>= 0.3.0) @@ -74,10 +76,10 @@ GEM debug_inspector (>= 0.0.1) brakeman (3.3.0) builder (3.2.2) - bullet (5.0.0) + bullet (5.1.0) activesupport (>= 3.0.0) - uniform_notifier (~> 1.9.0) - byebug (8.2.5) + uniform_notifier (~> 1.10.0) + byebug (9.0.4) choice (0.2.0) chronic (0.10.2) climate_control (0.0.3) @@ -94,6 +96,7 @@ GEM connection_pool (2.2.0) curb (0.8.8) cvss (0.99.0) + daemons (1.2.3) dalli (2.7.6) data_mapper (1.2.0) dm-aggregates (~> 1.2.0) @@ -122,7 +125,7 @@ GEM debug_inspector (0.0.2) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) - devise (4.0.2) + devise (4.0.3) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 4.1.0, < 5.1) @@ -183,15 +186,14 @@ GEM multi_json equalizer (0.0.11) erubis (2.7.0) - execjs (2.6.0) + eventmachine (1.0.9.1) + execjs (2.7.0) faraday (0.9.2) multipart-post (>= 1.2, < 3) - fast_stack (0.2.0) fastercsv (1.5.5) feature (1.3.0) ffi (1.9.10) - flamegraph (0.1.0) - fast_stack + flamegraph (0.9.5) flay (2.7.0) erubis (~> 2.7.0) ruby_parser (~> 3.0) @@ -227,21 +229,30 @@ GEM nokogiri (>= 1.5.9) mail (2.6.4) mime-types (>= 1.16, < 4) - mime-types (3.0) + mailcatcher (0.6.4) + activesupport (~> 4.0) + eventmachine (= 1.0.9.1) + mail (~> 2.3) + rack (~> 1.5) + sinatra (~> 1.2) + skinny (~> 0.2.3) + sqlite3 (~> 1.3) + thin (~> 1.5.0) + mime-types (3.1) mime-types-data (~> 3.2015) - mime-types-data (3.2016.0221) + mime-types-data (3.2016.0521) mimemagic (0.3.0) mina (0.3.8) open4 (~> 1.3.4) rake mini_portile2 (2.0.0) - minitest (5.8.4) + minitest (5.9.0) minitest-reporters (1.1.9) ansi builder minitest (>= 5.0) ruby-progressbar - multi_json (1.12.0) + multi_json (1.12.1) multi_xml (0.5.5) multipart-post (2.0.0) net-http-persistent (2.9.4) @@ -275,7 +286,7 @@ GEM omniauth-oauth (~> 1.1) open4 (1.3.4) orm_adapter (0.5.0) - paper_trail (5.0.1) + paper_trail (5.1.0) activerecord (>= 3.0, < 6.0) activesupport (>= 3.0, < 6.0) request_store (~> 1.1) @@ -287,7 +298,7 @@ GEM mimemagic (= 0.3.0) paranoia (2.1.5) activerecord (~> 4.0) - parser (2.3.0.7) + parser (2.3.1.0) ast (~> 2.2) pg (0.18.4) phantomjs (2.1.1.0) @@ -303,7 +314,7 @@ GEM activesupport (>= 3.0.0) rack (1.6.4) rack-cors (0.4.0) - rack-mini-profiler (0.9.9.2) + rack-mini-profiler (0.10.1) rack (>= 1.2.0) rack-protection (1.5.3) rack @@ -352,7 +363,7 @@ GEM redis (3.3.0) redis-namespace (1.5.2) redis (~> 3.0, >= 3.0.4) - reek (4.0.1) + reek (4.0.2) codeclimate-engine-rb (~> 0.3.1) parser (~> 2.3, >= 2.3.0.6) rainbow (~> 2.0) @@ -362,23 +373,23 @@ GEM responders (2.2.0) railties (>= 4.2.0, < 5.1) rmagick (2.15.4) - rubocop (0.39.0) - parser (>= 2.3.0.7, < 3.0) + rubocop (0.40.0) + parser (>= 2.3.1.0, < 3.0) powerpack (~> 0.1) rainbow (>= 1.99.1, < 3.0) ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) ruby-graphviz (1.2.2) - ruby-progressbar (1.8.0) + ruby-progressbar (1.8.1) ruby_parser (3.8.2) sexp_processor (~> 4.1) - rubycritic (2.9.0) + rubycritic (2.9.1) colorize flay (= 2.7.0) flog (= 4.3.2) launchy (= 2.4.3) - parser (= 2.3.0.7) - reek (= 4.0.1) + parser (= 2.3.1.0) + reek (= 4.0.2) ruby_parser (~> 3.8) virtus (~> 1.0) rubyzip (1.2.0) @@ -406,8 +417,11 @@ GEM rack (~> 1.5) rack-protection (~> 1.4) tilt (>= 1.3, < 3) - slim (3.0.6) - temple (~> 0.7.3) + skinny (0.2.4) + eventmachine (~> 1.0.0) + thin (>= 1.5, < 1.7) + slim (3.0.7) + temple (~> 0.7.6) tilt (>= 1.3.3, < 2.1) spring (1.7.1) sprockets (3.6.0) @@ -423,15 +437,19 @@ GEM stringex (1.5.1) sys-uname (1.0.2) ffi (>= 1.0.0) - temple (0.7.6) + temple (0.7.7) terminal-table (1.5.2) + thin (1.5.1) + daemons (>= 1.0.9) + eventmachine (>= 0.12.6) + rack (>= 1.0.0) thor (0.19.1) thread_safe (0.3.5) - tilt (2.0.2) + tilt (2.0.4) tzinfo (1.2.2) thread_safe (~> 0.1) unicode-display_width (1.0.5) - uniform_notifier (1.9.0) + uniform_notifier (1.10.0) uuidtools (2.1.5) virtus (1.0.5) axiom-types (~> 0.1) @@ -472,6 +490,7 @@ DEPENDENCIES gitlab immigrant libarchive-static + mailcatcher mimemagic mina minitest-reporters diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index eec3dc612bb1195e9ab7561d2f3601452f956665..d904c81c1a0f597f8fa851b0364080ca2dd914bd 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -5,10 +5,10 @@ class ApplicationController < ActionController::API include PublicActivity::StoreController # tracking user in papertrail - before_filter :set_paper_trail_whodunnit + before_action :set_paper_trail_whodunnit # check if client application is allowed to consumes the API. - before_filter :allow_client_application, if: -> { Feature.active?(:allow_client_application) } + before_action :allow_client_application, if: -> { Feature.active?(:allow_client_application) } # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. @@ -23,17 +23,9 @@ class ApplicationController < ActionController::API protected def configure_permitted_parameters - registration_params = [:name, :email, :avatar, :password, :password_confirmation] - - if params[:action] == 'update' - devise_parameter_sanitizer.permit(:account_update) do |user_params| - user_params.permit(registration_params << :current_password) - end - elsif params[:action] == 'create' - devise_parameter_sanitizer.permit(:sign_in) do |user_params| - user_params.permit(registration_params << :terms_of_service) - end - end + registration_params = [:name, :email, :avatar, :password, :password_confirmation, :current_password, :terms_of_service] + devise_parameter_sanitizer.permit :sign_up, keys: registration_params + devise_parameter_sanitizer.permit :account_update, keys: registration_params end private diff --git a/app/controllers/concerns/taggable_controller.rb b/app/controllers/concerns/taggable_controller.rb index c01fccbad6f5e21c204743177c10e73ee3b62645..bfe6382eb41ec11b898128e5e2b296043ac96fbe 100644 --- a/app/controllers/concerns/taggable_controller.rb +++ b/app/controllers/concerns/taggable_controller.rb @@ -10,14 +10,14 @@ module TaggableController # POST /v1/learning_objects/1/tagging.json def tagging @owner.tag(taggable, with: [tag_params[:name]]) - render json: ActiveModel::ArraySerializer.new(taggable.tags.to_a), status: :created + render json: taggable.tags, status: :created end # DELETE /v1/learning_objects/1/untagging # DELETE /v1/learning_objects/1/untagging.json def untagging @owner.untag(taggable, tag_params[:name]) - render json: ActiveModel::ArraySerializer.new(taggable.tags.to_a), status: :ok + render json: taggable.tags, status: :ok end protected diff --git a/app/controllers/v1/search_controller.rb b/app/controllers/v1/search_controller.rb index c581c0866d8f36a120e74c290218cd404a4e6e3a..6daa430c5f4723d214784c95cad23e7da6c80918 100644 --- a/app/controllers/v1/search_controller.rb +++ b/app/controllers/v1/search_controller.rb @@ -4,30 +4,27 @@ class V1::SearchController < ApplicationController # GET v1/search # GET v1/search.json def index - render json: ActiveModel::ArraySerializer.new(SearchService.search(@search, current_user)), status: :ok - rescue => e - if e.message == 'Invalid search' - render json: @search.errors, status: :bad_request - else - render nothing: true, status: :internal_server_error - end + render json: SearchService.search(@search, current_user), status: :ok + rescue SearchService::InvalidSearchError => e + render json: @search.errors, status: :bad_request end # GET v1/search/autocomplete # GET v1/search/autocomplete.json def autocomplete - render json: ActiveModel::ArraySerializer.new(SearchService.autocomplete(@search, current_user)), status: :ok - rescue => e - if e.message == 'Invalid search' - render json: @search.errors, status: :bad_request - else - render nothing: true, status: :internal_server_error - end + render json: ArraySerializer.new(SearchService.autocomplete(@search, current_user)), status: :ok + rescue SearchService::InvalidSearchError => e + render json: @search.errors, status: :bad_request end private def set_search - @search = Search.new(params) + @search = Search.new(search_params) + end + + # Never trust parameters from the scary internet, only allow the white list through. + def search_params + params.require(:search).permit(:page, :results_per_page, :order, :query, :search_class) end end diff --git a/app/models/search.rb b/app/models/search.rb index 5f9dcf1fae8b7f66ac5edf293154ee82ff82b1a4..4f99e8fe47dd2abbb70513f4b1a1100026a3ffe6 100644 --- a/app/models/search.rb +++ b/app/models/search.rb @@ -4,7 +4,7 @@ class Search validates_presence_of :query, :results_per_page, :order, :search_class validates_numericality_of :results_per_page, greater_than: 0 - validates :search_class, inclusion: { in: %w(LearningObject Collection User) } + validates :search_class, inclusion: { in: ::SearchService::SEARCH_CLASSES } def initialize(params = {}) super(defaults.merge(params.select { |key, _value| respond_to? key })) diff --git a/app/services/search_service.rb b/app/services/search_service.rb index f5fa43b5a613e930ec42e349931e0210e91281f1..d95fe90014fe5fa37998d74c91bad69bae7e3bed 100644 --- a/app/services/search_service.rb +++ b/app/services/search_service.rb @@ -1,4 +1,8 @@ module SearchService + SEARCH_CLASSES = %w(LearningObject Collection User) + + class InvalidSearchError < StandardError; end; + def self.search(search, user) model = instance(search, user) model.search.results @@ -9,8 +13,10 @@ module SearchService model.autocomplete end + private + def self.instance(search, user) - raise 'Invalid search' unless search.valid? + raise InvalidSearchError unless search.valid? "SearchService::#{search.search_class}".constantize.new(search, user) end end diff --git a/config/environments/development.rb b/config/environments/development.rb index eb3772c3a0867beabd00a672cf5c425b64731fa7..987efaf25f29e895293756ea1e7bc3c1703b2bad 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -51,5 +51,9 @@ Rails.application.configure do # Raises error for missing translations # config.action_view.raise_on_missing_translations = true + #config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } + # Lets define mailcatcher configs + config.action_mailer.delivery_method = :smtp + config.action_mailer.smtp_settings = { address: 'localhost', port: 1025 } config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } end diff --git a/config/routes.rb b/config/routes.rb index 4289be952c2bbcdb6672d0af66e5c606415250c7..4b045a9eb95bb5f4d2c8d84a188b34fb7ff89cd8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -71,8 +71,11 @@ Rails.application.routes.draw do end # search routes - get :search, to: 'search#index' - get 'search/autocomplete', to: 'search#autocomplete' + resources :search, only: :index do + collection do + get :autocomplete + end + end resources :collections, concerns: [:followable, :sociable, :reviewable, :taggable, :versionable, :deletable, :highlights] resources :learning_objects, concerns: [:sociable, :reviewable, :taggable, :versionable, :deletable, :highlights] do diff --git a/test/controllers/v1/search_controller_test.rb b/test/controllers/v1/search_controller_test.rb index 75df1fa21f283932509d387eac8c6c8fe5d2bf93..8814eb8d694252176483d08b76c88840403a2480 100644 --- a/test/controllers/v1/search_controller_test.rb +++ b/test/controllers/v1/search_controller_test.rb @@ -9,23 +9,20 @@ class V1::SearchControllerTest < ActionController::TestCase auth_application end - test 'should get index and return :ok' do - get :index, search_class: 'LearningObject' - assert_response :ok + test 'should search return :ok' do + SearchService::SEARCH_CLASSES.each do |search_class| + get :index, search: {search_class: search_class} + assert_response :ok + end end - test 'should get index and return :bad_request' do - get :index + test 'should search with invalid class and return :bad_request' do + get :index, search: {search_class: 'invalid'} assert_response :bad_request end - test 'should get autocomplete and return :ok' do - get :autocomplete, search_class: 'LearningObject' - assert_response :ok - end - - test 'should get autocomplete and return :bad_request' do - get :autocomplete + test 'should autocomplete with invalid class and return :bad_request' do + get :autocomplete, search: {search_class: 'invalid'} assert_response :bad_request end